学习 Python 之 Pygame 开发魂斗罗(八)

学习 Python 之 Pygame 开发魂斗罗(八)

    • 继续编写魂斗罗
      • 1. 创建敌人类
      • 2. 增加敌人移动和显示函数
      • 3. 敌人开火
      • 4. 修改主函数
      • 5. 产生敌人
      • 6. 使敌人移动

继续编写魂斗罗

在上次的博客学习 Python 之 Pygame 开发魂斗罗(七)中,我们解决了一些问题,这次我们加入敌人

下面是图片的素材

链接:https://pan.baidu.com/s/1X7tESkes_O6nbPxfpHD6hQ?pwd=hdly
提取码:hdly

1. 创建敌人类

import random

import pygame
from Constants import *
from Bullet import Bullet


class Enemy1(pygame.sprite.Sprite):

    def __init__(self, x, y, direction, currentTime):
        pygame.sprite.Sprite.__init__(self)

        self.lastTime = currentTime
        self.fireTime = currentTime
        self.rightImages = [
            loadImage('../Image/Enemy/Enemy1/1.png'),
            loadImage('../Image/Enemy/Enemy1/2.png'),
            loadImage('../Image/Enemy/Enemy1/3.png')
        ]
        self.leftImages = [
            loadImage('../Image/Enemy/Enemy1/1.png', True),
            loadImage('../Image/Enemy/Enemy1/2.png', True),
            loadImage('../Image/Enemy/Enemy1/3.png', True)
        ]
        self.rightFireImage = loadImage('../Image/Enemy/Enemy1/fire.png')
        self.leftFireImage = loadImage('../Image/Enemy/Enemy1/fire.png', True)
        self.fallImage = loadImage('../Image/Enemy/Enemy1/fall.png', True)
		# 图片下标
        self.index = 0
        # 方向
        self.direction = direction
        if self.direction == Direction.RIGHT:
            self.image = self.rightImages[self.index]
        else:
            self.image = self.leftImages[self.index]
        self.rect = self.image.get_rect()
        self.isFalling = False
        self.rect.x = x
        self.rect.y = y
        self.speed = 3
        self.isDestroy = False
        self.isFiring = False
        self.life = 1

这里敌人的移动也是三幅图片,加载时需要连续显示这三张图片

2. 增加敌人移动和显示函数

def move(self, currentTime):
    # 首先判断敌人是否开火,如果是开火状态,就不能移动
    if not self.isFiring:
        # 没有开火,就根据方向移动,这里我设置敌人只能向一个方向移动,不能转身
        if self.direction == Direction.RIGHT:
            self.rect.left += self.speed
        else:
            self.rect.left -= self.speed
    else:
        # 如果此时是开火状态,判断一下上次开火的时间和这次的时间是否相差1000
        # 这个的作用在于让敌人开火的时候站在那里不动,因为敌人移动时是不能开火的
        if currentTime - self.fireTime > 1000:
            # 如果两次开火间隔相差很大,那么就可以让敌人再次开火
            self.isFiring = False
            self.fireTime = currentTime

这里主要是解决敌人开火不能移动的问题

因为敌人开火是站着不动,如果两次开火间隔比较短,敌人频繁开火,运行游戏后会出现敌人移动就能发射子弹的情况

下面是显示函数,这个跟玩家的显示函数差不多

def draw(self, currentTime):
    if self.isFiring:
        if self.direction == Direction.RIGHT:
            self.image = self.rightFireImage
        else:
            self.image = self.leftFireImage
    else:
        if currentTime - self.lastTime > 115:
            if self.index < 2:
                self.index += 1
            else:
                self.index = 0
            self.lastTime = currentTime
        if self.direction == Direction.RIGHT:
            self.image = self.rightImages[self.index]
        else:
            self.image = self.leftImages[self.index]

有了移动函数,就要对敌人位置进行检测,当玩家距离敌人1000像素之外后,敌人就会自动消失了,用来防止敌人过多卡

    def checkPosition(self, x, y):
        if abs(self.rect.x - x) > 1000:
            self.isDestroy = True
        elif abs(self.rect.y - y) > 600:
            self.isDestroy = True

水平相距1000像素后消失,垂直相距600像素后消失

3. 敌人开火

def fire(self, enemyBulletList):
    if not self.isFalling:
        i = random.randint(0, 50)
        if i == 5:
            if not self.isFiring:
                self.isFiring = True
                enemyBulletList.append(Bullet(self, True))

这个函数设置了,当敌人处于下落状态时,不能开火
开火是随机的,随机从0到50产生一个数字,如果是5,敌人就开火

完整敌人1类代码

import random

import pygame
from Constants import *
from Bullet import Bullet

class Enemy1(pygame.sprite.Sprite):

    def __init__(self, x, y, direction, currentTime):
        pygame.sprite.Sprite.__init__(self)

        self.lastTime = currentTime
        self.fireTime = currentTime
        self.rightImages = [
            loadImage('../Image/Enemy/Enemy1/1.png'),
            loadImage('../Image/Enemy/Enemy1/2.png'),
            loadImage('../Image/Enemy/Enemy1/3.png')
        ]
        self.leftImages = [
            loadImage('../Image/Enemy/Enemy1/1.png', True),
            loadImage('../Image/Enemy/Enemy1/2.png', True),
            loadImage('../Image/Enemy/Enemy1/3.png', True)
        ]
        self.rightFireImage = loadImage('../Image/Enemy/Enemy1/fire.png')
        self.leftFireImage = loadImage('../Image/Enemy/Enemy1/fire.png', True)
        self.fallImage = loadImage('../Image/Enemy/Enemy1/fall.png', True)

        self.index = 0
        self.direction = direction
        if self.direction == Direction.RIGHT:
            self.image = self.rightImages[self.index]
        else:
            self.image = self.leftImages[self.index]
        self.rect = self.image.get_rect()
        self.isFalling = False
        self.rect.x = x
        self.rect.y = y
        self.speed = 3
        self.isDestroy = False
        self.isFiring = False
        self.life = 1

    def move(self, currentTime):
        # 首先判断敌人是否开火,如果是开火状态,就不能移动
        if not self.isFiring:
            # 没有开火,就根据方向移动,这里我设置敌人只能向一个方向移动,不能转身
            if self.direction == Direction.RIGHT:
                self.rect.left += self.speed
            else:
                self.rect.left -= self.speed
        else:
            # 如果此时是开火状态,判断一下上次开火的时间和这次的时间是否相差1000
            # 这个的作用在于让敌人开火的时候站在那里不动,因为敌人移动时是不能开火的
            if currentTime - self.fireTime > 1000:
                # 如果两次开火间隔相差很大,那么就可以让敌人再次开火
                self.isFiring = False
                self.fireTime = currentTime

    def draw(self, currentTime):
        if self.isFiring:
            if self.direction == Direction.RIGHT:
                self.image = self.rightFireImage
            else:
                self.image = self.leftFireImage
        else:
            if currentTime - self.lastTime > 115:
                if self.index < 2:
                    self.index += 1
                else:
                    self.index = 0
                self.lastTime = currentTime
            if self.direction == Direction.RIGHT:
                self.image = self.rightImages[self.index]
            else:
                self.image = self.leftImages[self.index]

    def fire(self, enemyBulletList):
        if not self.isFalling:
            i = random.randint(0, 50)
            if i == 5:
                if not self.isFiring:
                    self.isFiring = True
                    enemyBulletList.append(Bullet(self, True))

    def checkPosition(self, x, y):
        if abs(self.rect.x - x) > 1000:
            self.isDestroy = True
        elif abs(self.rect.y - y) > 600:
            self.isDestroy = True

由于每个敌人的图片不一样,我就分开创建敌人类,这个是敌人1类,新加入敌人就创建新的敌人类

4. 修改主函数

由于加入了敌人类,主函数的碰撞体组需要进行改变

敌人和玩家的碰撞体应该分开,因为玩家向下跳的时候,上面的碰撞体会消失,如果此时有敌人在上面,碰撞体一消失,敌人就会掉下来,这是不对的,所以我们要修改原有的碰撞体组
在这里插入图片描述
把上图的红框中的代码修改成下面的样子

如果使用Pycharm软件,我们按住ctrl+R,一键替换
在这里插入图片描述
然后就换完了
在这里插入图片描述
全部还完后的结果

在这里插入图片描述

# 冲突
playerLandGroup = pygame.sprite.Group()
playerColliderGroup = pygame.sprite.Group()
playerRiverGroup = pygame.sprite.Group()

现在完成了玩家的碰撞体组

当敌人加入后,就要创建敌人的碰撞体组

playerLandGroup = pygame.sprite.Group()
playerRiverGroup = pygame.sprite.Group()
enemyLandGroup = pygame.sprite.Group()
enemyRiverGroup = pygame.sprite.Group()
playerColliderGroup = pygame.sprite.Group()
enemyColliderGroup = pygame.sprite.Group()
enemyGroup = pygame.sprite.Group()
bridgeGroup = pygame.sprite.Group()

这是最后的碰撞体组

有敌人的也有玩家的

在原版魂斗罗中,第一关是有桥的,当玩家走上去后,就会爆炸,所以这里有桥的碰撞体组

接下来加入敌人列表

# 敌人
enemyList = []

在这里插入图片描述

用来把存放游戏中当前的敌人

5. 产生敌人

好的,接下来我们来写创建敌人的函数

def generateEnemy(self, x, y, direction, currentTime):
    enemy = Enemy1(x, y, direction, currentTime)
    MainGame.enemyList.append(enemy)
    MainGame.allSprites.add(enemy)
    MainGame.enemyGroup.add(enemy)

在update()函数中,我们调用这个函数

# 加载敌人
if -1505 < self.backRect.x < -1500:
    self.generateEnemy(MainGame.player1.rect.x + 600, POSITION_1, Direction.LEFT, pygame.time.get_ticks())
    self.generateEnemy(MainGame.player1.rect.x - 360, POSITION_1, Direction.RIGHT, pygame.time.get_ticks())

if -1705 < self.backRect.x < -1700:
    self.generateEnemy(MainGame.player1.rect.x - 360, POSITION_1, Direction.RIGHT, pygame.time.get_ticks())
    self.generateEnemy(MainGame.player1.rect.x - 400, POSITION_1, Direction.RIGHT,
                       pygame.time.get_ticks())

这个代码的意思是,当背景加载到-1505到-1500时,就会在玩家前方和后方产生两个敌人

同理,在-1705到-1700时,也会产生两个敌人
在这里插入图片描述
在Constants.py中加入下面这个

POSITION_1 = 233

这个表示敌人产生的位置的y坐标,位置如下图
在这里插入图片描述
就是在当前玩家所站的这个平台上,产生敌人,如果按照玩家的位置产生敌人,当玩家跳跃时,敌人可能在空中产生

下面我们运行一下,看看敌人有没有出现

在这里插入图片描述

敌人出现了,但是没有移动,这是为什么?

因为没有调用敌人的move()函数让敌人移动

6. 使敌人移动

下面我们创建敌人更新函数

def enemyUpdate(enemyList, enemyBulletList):
    # 遍历整个敌人列表
    for enemy in enemyList:
        # 如果敌人已经被摧毁了
        if enemy.isDestroy:
            # 删除它的相关信息
            enemyList.remove(enemy)
            MainGame.allSprites.remove(enemy)
            MainGame.enemyGroup.remove(enemy)
        # 否则
        else:
            # 检查位置
            enemy.checkPosition(MainGame.player1.rect.x, MainGame.player1.rect.y)
            # 显示敌人
            enemy.draw(pygame.time.get_ticks())
            # 敌人移动
            enemy.move(pygame.time.get_ticks())
            # 敌人开火
            enemy.fire(enemyBulletList)

这里的有一个参数是敌人子弹列表,我们在主类中也创建一下

在这里插入图片描述

下面我们调用一下这个函数

在这里插入图片描述
之后我们再运行一下游戏,看看效果

出现了问题

在这里插入图片描述
我们把敌人1类的fire函数开火代码注释一下

在这里插入图片描述
出现错误的原因是:敌人开火的位置和玩家开火的位置不一样,所有我们要在子弹类的构造函数中加入一个变量,用来指定当前子弹是敌人子弹还是玩家子弹,这里因为我们还没有来得及修改子弹类的代码,所有会出错误,以后会进行修改的,现在先看看敌人能不能加载出来

在这里插入图片描述
到这里,我们就可以可能敌人出来啦
在这里插入图片描述
但是我们看到敌人不会向下掉落,这是因为没有给敌人增加碰撞体,接下来我们先实现敌人发射子弹,然后再实现敌人碰撞体的问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/1646.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uboot主目录下Makefile文件的分析,以及配置过程分析

主Makefile执行分析 uboot的编译过程 &#xff08;1&#xff09;配置 查看主Makefile文件下所支持的配置的板子&#xff0c;通过make x210_sd_config来实现编译前的配置 &#xff08;2&#xff09;编译 make直接编译&#xff0c;这个前提条件是主Makefile文件下指定了编译…

上手使用百度文心一言

3月16日&#xff0c;在距离新一代的GPT模型GPT-4发布还不足一天的时间内&#xff0c;百度便发布了对标ChatGPT的人工智能产品&#xff0c;名字叫&#xff1a;文心一言。成为国内首页发布该类型产品的公司。 那么&#xff0c;我们今天就来试一试百度的文心一言好不好用。 首先&a…

【ERNIE Bot】百度 | 文心一言初体验

文章目录一、前言二、文心一言介绍三、申请体验⌈文心一言⌋四、⌈文心一言⌋初体验1️⃣聊天对话能力2️⃣文案创作能力3️⃣文字转语音能力✨4️⃣AI绘画能力✨5️⃣数理推理能力6️⃣代码生成能力7️⃣使用技巧说明五、总结一、前言 ​ 最近有关人工智能的热门话题冲上热榜…

Java课程设计项目--音乐视频网站系统

一、功能介绍 随着社会的快速发展&#xff0c;计算机的影响是全面且深入的。人们生活水平的不断提高&#xff0c;日常生活中人们对音乐方面的要求也在不断提高&#xff0c;听歌的人数更是不断增加&#xff0c;使得音乐网站的设计的开发成为必需而且紧迫的事情。音乐网站的设计主…

「操作系统」什么是用户态和内核态?为什么要区分

「操作系统」什么是用户态和内核态&#xff1f;为什么要区分 参考&鸣谢 从根上理解用户态与内核态 程序员阿星 并发编程&#xff08;二十六&#xff09;内核态和用户态 Lovely小猫 操作系统之内核态与用户态 fimm 文章目录「操作系统」什么是用户态和内核态&#xff1f;为什…

嵌入式硬件电路设计的基本技巧

目录 1 分模块 2 标注关键参数 3 电阻/电容/电感/磁珠的注释 4 可维修性 5 BOM表归一化 6 电源和地的符号 7 测试点 8 网络标号 9 容错性/兼容性 10 NC、NF 11 版本变更 12 悬空引脚 13 可扩展性 14 防呆 15 信号的流向 16 PCB走线建议 17 不使用\表示取反 不…

考研408每周一题(2019 41)

2019年(单链表&#xff09; 41.(13分)设线性表L(a1,a2,a3,...,a(n-2),a(n-1),an)采用带头结点的单链表保存&#xff0c;链表中的结点定义如下&#xff1a; typedef struct node {int data;struct node *next; } NODE; 请设计一个空间复杂度为O(1)且时间上尽可能高效的算法&…

leetcode -- 876.链表的中间节点

文章目录&#x1f428;1.题目&#x1f407;2. 解法1-两次遍历&#x1f340;2.1 思路&#x1f340;2.2 代码实现&#x1f401;3. 解法2-快慢指针&#x1f33e;3.1 思路&#x1f33e;3.2 **代码实现**&#x1f42e;4. 题目链接&#x1f428;1.题目 给你单链表的头结点head&#…

RocketMQ

RocketMQ1、基础入门1、消息中间件(MQ)的定义2、为什么要用消息中间件&#xff1f;2、RocketMQ 产品发展1、RocketMQ 版本发展2、RocketMQ 的物理架构1、核心概念2、物理架构中的整体运转3、RocketMQ 的概念模型1、分组(Group)2、主题(Topic)3、标签(Tag)4、消息队列(Message Q…

开发也可以很快乐,让VSCode和CodeGPT带给你幸福感

CodeGPT 是一款 Visual Studio Code 扩展&#xff0c;可以通过官方的 OpenAI API 使用 GPT-3 (预训练生成式转换器) 模型&#xff0c;在多种编程语言中生成、解释、重构和文档化代码片段。CodeGPT 可用于各种任务&#xff0c;例如代码自动完成、生成和格式化。它还可以集成到代…

smartsofthelp最简单的,最好的,最干净的C# 代码生成器

关系型数据库高并发接口代码生成EF API 接口原声SQL 操作类异步委托 await 操作数据库数据异步访问抽象基础类 netcore 生成EF ORMdbhelperasync原生SQL 异步数据库操作公共类自动生成增删改查成员方法实例代码#region 自动生成增删改查成员方法/// <summary>/// 增加一条…

【6】核心易中期刊推荐——图像与信号处理

🚀🚀🚀NEW!!!核心易中期刊推荐栏目来啦 ~ 📚🍀 核心期刊在国内的应用范围非常广,核心期刊发表论文是国内很多作者晋升的硬性要求,并且在国内属于顶尖论文发表,具有很高的学术价值。在中文核心目录体系中,权威代表有CSSCI、CSCD和北大核心。其中,中文期刊的数…

ChatGPT-4.0 : 未来已来,你来不来

文章目录前言ChatGPT 3.5 介绍ChatGPT 4.0 介绍ChatGPT -4出逃计划&#xff01;我们应如何看待ChatGPT前言 好久没有更新过技术文章了&#xff0c;这个周末听说了一个非常火的技术ChatGPT 4.0&#xff0c;于是在闲暇之余我也进行了测试&#xff0c;今天这篇文章就给大家介绍一…

【Bezier + BSpline + CatmullRom】移动机器人曲线路径规划

问题&#xff1a;现有n1n1n1个2维的离散点Pi(xi,yi),(i0,1,⋯,n){P_i} \left( {{x_i},{y_i}} \right),\left( {i 0,1, \cdots ,n} \right)Pi​(xi​,yi​),(i0,1,⋯,n), 如何用Pi{P_i}Pi​拟合一条平滑的曲线&#xff0c;最后将曲线分割成数条 2阶/3阶贝塞尔曲线&#xff0c;…

HDFS的API操作

目录 客户端环境准备&#xff1a; 添加环境变量&#xff1a; 配置Path环境变量&#xff1a; IDEA操作&#xff1a; 创建包名&#xff1a; HDFS的API案例操作&#xff1a; 封装代码&#xff1a; 封装代码1&#xff1a; 封装代码2&#xff1a; 实现操作&#xff1a; 1.创…

每日一博 - Java 异步编程的 Promise 模式 CompletableFuture

文章目录概述概述Executor与线程池Java 中的线程池使用线程池的注意事项强烈建议使用有界队列默认拒绝策略要慎重使用注意异常处理的问题如何获取任务执行结果概述 最近在阅读耗子叔的《左耳听风》 &#xff0c; 记一些小笔记 概述 在 Java 中&#xff0c;在 JDK 1.8 里也引入…

深度学习应用技巧总结与pytorch框架下训练过程的记忆技巧

大家好&#xff0c;我是微学AI&#xff0c;今天给大家总结一下深度学习模型训练过程中的一些技巧总结&#xff0c;以及pytorch框架下训练过程的记忆技巧&#xff0c;很有用的干货&#xff0c;理解模型训练过程的步骤&#xff0c;让流程难懂&#xff0c;难记忆的过程变得简单&am…

通讯录-文件操作版

之前我们写过通讯录-动态开辟版&#xff0c;但里面的数据录入后&#xff0c;若退出程序&#xff0c;里面的数据也就跟着一起销毁&#xff0c;无法保存&#xff0c;所以今天我们来写可建议将通讯录信息保存起来的版本&#xff0c;这只要在原来的基础上加以改进就可以了。首先&am…

发光立方体效果 html+css

一.话不多&#xff0c;看效果 css简单创意特效&#xff0c;关注我看更多简单创意特效~ 二.实现&#xff08;附完整代码&#xff09; 定义标签&#xff1a; <div class"container"><div class"q1"></div><div class"h2"&…

Day921.chatGPT

chatGPT Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于chatGPT的内容。 一、什么是chatGPT ChatGPT&#xff08;全名&#xff1a;Chat Generative Pre-trained Transformer&#xff09;&#xff0c;ChatGPT 是一种基于 GPT (Generative Pre-trained Transformer)…