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

news2025/1/15 22:31:17

学习 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.coloradmin.cn/o/387669.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通信网络-Socket、Java中的网络支持、多线程服务器

前言 通信网络-Socket、Java中的网络支持、多线程服务器 场景&#xff1a;使用java网络创建一个聊天室 博客地址&#xff1a;芒果橙的个人博客 文章目录前言通信网络-SocketTCP/IPTCP/IP 模型端口Java中的网络支持概念1. InetAddress2. URL3. Socket4. Datagram多线程服务器应用…

使用ECharts打造一个数据可视化面板

使用ECharts打造一个数据可视化面板1. 使用技术2. 案例适配方案3. 基础设置4. header 布局5. mainbox 主体模块6. 公共面板模块 panel7. 柱形图 bar 模块&#xff08;布局&#xff09;8. 中间布局9. ECharts 介绍10. ECharts 体验11. ECharts 基础配置12. 柱状图图表&#xff0…

MDK软件使用技巧

本文主要汇总MDK软件使用技巧 一、字体大小及颜色修改 第一步点击工具栏的这个小扳手图标 进去后显示如下&#xff0c;先设置 Encoding 为&#xff1a;Chinese GB2312(Simplified)&#xff0c;然后设置 Tab size 为&#xff1a;4 以更好的支持简体中文&#xff0c;否则&…

Java 多线程 --- 线程协作 wait/notify

Java 多线程 --- 线程协作 wait/notifywait / notifyObject.wait() , Object.notify() / notifyAll()notify 和 wait 的原理notify会导致死锁的问题wait / notify的开销以及问题wait / notify 在多线程中, 如果程序拿到锁之后, 但是没有满足指定条件而不能继续往下执行, 我们可…

外网用户打不开公司的网站?web服务器端口映射到公网

我们经常会遇到这样的情景&#xff0c;在公司内部可以打开公司的网站&#xff0c;在家里或者外网却打不开&#xff0c;按照网上的做法&#xff0c;重新启动了服务器和iis&#xff0c;还是不行。许多用户设置了路由器端口映射功能&#xff0c;但是端口映射不成功怎么办&#xff…

2023河南新乡市第二届职业技能大赛“网络安全”项目B模块比赛任务书

2023河南新乡市第二届职业技能大赛“网络安全”项目 比赛任务书一、竞赛时间二、竞赛阶段及内容B-1 Windows操作系统渗透测试B-2 Wireshark数据包分析B-3 CMS网站渗透B-4 未公开B-5 未公开一、竞赛时间 总计&#xff1a;180分钟 二、竞赛阶段及内容 &#xff08;一&#xff…

嵌入式系统实验——【玄武F103开发板】实现两个LED小灯闪烁

目录一、实验文件main.cstm32f10x.h二、实验思路&#xff08;一&#xff09;打开两个LED小灯1.在玄武F103开发板上找到LED0、LED1对应的GPIO控制寄存器2.找到GPIOB、GPIOE的地址3.打开APB2外设时钟的使能寄存器4.对GPIO寄存器进行设置对端口输出寄存器进行设置&#xff08;二&a…

Go语言基准测试(benchmark)三部曲之三:提高篇

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 本篇概览 -《Go语言基准测试(benchmark)三部曲》已近尾声&#xff0c;经历了《基础篇》和《内存篇》的实战演练&#xff0c;相信您已熟练掌握了基准测试的常…

Spring Security 实现自定义登录和认证(1)

1 SpringSecurity 1.1 导入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId> </dependency>1.2 编写配置类 在spring最新版中禁用了WebSecurityConfigurerAdapter…

《Linux运维实战:基于ansible一键离线部署容器版MySQL一主两从集群(5.6.50、5.7.30、8.0.30)》

一、部署背景 由于业务系统的特殊性&#xff0c;我们需要针对不同的客户环境部署 MySQL主从复制集群&#xff0c;由于大都数用户都是专网环境&#xff0c;无法使用外网&#xff0c;为了更便捷&#xff0c;高效的部署&#xff0c;针对业务系统的特性&#xff0c;我这边编写了基于…

【C++修炼之路】24.哈希应用--位图

每一个不曾起舞的日子都是对生命的辜负 哈希应用--位图哈希应用&#xff1a;位图一.提出问题二.位图概念三.位图代码四.位图应用五.经典问题哈希应用&#xff1a;位图 一.提出问题 问题&#xff1a; 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#x…

信息服务上线渗透检测网络安全检查报告和解决方案3(系统漏洞扫描、相对路径覆盖RPO漏洞、nginx漏洞修复)

系列文章目录 信息服务上线渗透检测网络安全检查报告和解决方案2(安装文件信息泄漏、管理路径泄漏、XSS漏洞、弱口令、逻辑漏洞、终极上传漏洞升级)信息服务上线渗透检测网络安全检查报告和解决方案 文章目录系列文章目录前言一、中风险漏洞相对路径覆盖(RPO)漏洞二、低风险漏…

【2021.12.28】ctf逆向中的迷宫问题(含exe及wp)

【2021.12.28】ctf逆向中的迷宫问题&#xff08;含exe及wp&#xff09; 文章目录【2021.12.28】ctf逆向中的迷宫问题&#xff08;含exe及wp&#xff09;1、迷宫简介&#xff08;1&#xff09;简单例子&#xff08;2&#xff09;一般的迷宫代码2、二维迷宫&#xff08;1&#xf…

LeetCode 460. LFU 缓存 -- 哈希查询+双向链表

LFU 缓存 困难 634 相关企业 请你为 最不经常使用&#xff08;LFU&#xff09;缓存算法设计并实现数据结构。 实现 LFUCache 类&#xff1a; LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象 int get(int key) - 如果键 key 存在于缓存中&#xff0c;则获取键…

IP网络广播对讲系统的先进性及系统功能

IP网络广播对讲系统是一套基于TCP/IP网络的纯数字IP网络对讲机系统。在物理结构上&#xff0c;IP网络广播系统与标准IP网络集成。它不仅真正实现了基于TCP/IP网络的数字音频广播、直播和点播&#xff0c;而且借助TCP/IP网络的优势&#xff0c;突破了传统模拟广播系统的局限性、…

用Python写个猜数字游戏,写游戏难道比玩游戏还好玩(12)

小朋友们好&#xff0c;大朋友们好&#xff01;我是猫妹&#xff0c;一名爱上Python编程的小学生。欢迎和猫妹一起&#xff0c;趣味学Python。今日主题开学差不多有1个月了吧&#xff1f;猫妹刚刚从寒假的玩玩玩模式切换到上学的学学学模式。你呢&#xff1f;这是猫妹的课程表&…

嵌入式中backtrace的使用

大家好&#xff0c;我是bug菌&#xff5e; backtrace主要用于调试程序时&#xff0c;能够打印出程序在运行过程中的函数调用栈&#xff0c;以帮助开发者快速定位程序出现异常或崩溃的原因。 通过backtrace的输出&#xff0c;开发者可以了解程序在哪个函数出现问题&#xff0c…

APM飞控使用动捕等外部定位

本文初次写于2023.03.03&#xff0c;pixhawk飞控应该是刷写了ArduPilot 4.1以上的版本。 机载计算机通过WIFI和vrpn_ros_client获取动捕系统&#xff08;vicon或者nokov&#xff09;的无人机定位数据&#xff08;x&#xff0c;y&#xff0c;z四元数&#xff09;&#xff0c;然…

HMM-维特比算法

HMM-维特比算法&#xff08;viterbi&#xff09;HMM回顾隐马科夫链解法&#xff1a;维特比算法&#xff08;Viterbi&#xff09;HMM回顾 最终的公式可以解释主要分为两个部分&#xff1a; P(xi|yi)&#xff0c;发射概率&#xff0c;字面意思是从一个词性中发射/生成出某一个单…

【Java】Spring Boot下的MVC

文章目录Spring MVC程序开发1. 什么是Spring MVC&#xff1f;1.1 MVC定义1.2 MVC 和 Spring MVC 的关系2. 为什么学习Spring MVC&#xff1f;3. 怎么学习Spring MVC&#xff1f;3.1 Spring MVC的创建和连接3.1.1 创建Spring MVC项目3.1.2 RequestMapping 注解介绍3.1.3 Request…