当前位置: 首页 > news >正文

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

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

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

继续编写魂斗罗

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

下面是图片的素材

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

1. 创建敌人类

import randomimport pygame
from Constants import *
from Bullet import Bulletclass Enemy1(pygame.sprite.Sprite):def __init__(self, x, y, direction, currentTime):pygame.sprite.Sprite.__init__(self)self.lastTime = currentTimeself.fireTime = currentTimeself.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 = directionif 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 = Falseself.rect.x = xself.rect.y = yself.speed = 3self.isDestroy = Falseself.isFiring = Falseself.life = 1

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

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

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

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

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

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

def draw(self, currentTime):if self.isFiring:if self.direction == Direction.RIGHT:self.image = self.rightFireImageelse:self.image = self.leftFireImageelse:if currentTime - self.lastTime > 115:if self.index < 2:self.index += 1else:self.index = 0self.lastTime = currentTimeif 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 = Trueelif 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 = TrueenemyBulletList.append(Bullet(self, True))

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

完整敌人1类代码

import randomimport pygame
from Constants import *
from Bullet import Bulletclass Enemy1(pygame.sprite.Sprite):def __init__(self, x, y, direction, currentTime):pygame.sprite.Sprite.__init__(self)self.lastTime = currentTimeself.fireTime = currentTimeself.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 = 0self.direction = directionif 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 = Falseself.rect.x = xself.rect.y = yself.speed = 3self.isDestroy = Falseself.isFiring = Falseself.life = 1def move(self, currentTime):# 首先判断敌人是否开火,如果是开火状态,就不能移动if not self.isFiring:# 没有开火,就根据方向移动,这里我设置敌人只能向一个方向移动,不能转身if self.direction == Direction.RIGHT:self.rect.left += self.speedelse:self.rect.left -= self.speedelse:# 如果此时是开火状态,判断一下上次开火的时间和这次的时间是否相差1000# 这个的作用在于让敌人开火的时候站在那里不动,因为敌人移动时是不能开火的if currentTime - self.fireTime > 1000:# 如果两次开火间隔相差很大,那么就可以让敌人再次开火self.isFiring = Falseself.fireTime = currentTimedef draw(self, currentTime):if self.isFiring:if self.direction == Direction.RIGHT:self.image = self.rightFireImageelse:self.image = self.leftFireImageelse:if currentTime - self.lastTime > 115:if self.index < 2:self.index += 1else:self.index = 0self.lastTime = currentTimeif 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 = TrueenemyBulletList.append(Bullet(self, True))def checkPosition(self, x, y):if abs(self.rect.x - x) > 1000:self.isDestroy = Trueelif 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函数开火代码注释一下

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

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

相关文章:

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

学习 Python 之 Pygame 开发魂斗罗&#xff08;八&#xff09;继续编写魂斗罗1. 创建敌人类2. 增加敌人移动和显示函数3. 敌人开火4. 修改主函数5. 产生敌人6. 使敌人移动继续编写魂斗罗 在上次的博客学习 Python 之 Pygame 开发魂斗罗&#xff08;七&#xff09;中&#xff0…...

Lesson11---分类问题

11.1 逻辑回归 11.1.1 广义线性回归 课程回顾 线性回归&#xff1a;将自变量和因变量之间的关系&#xff0c;用线性模型来表示&#xff1b;根据已知的样本数据&#xff0c;对未来的、或者未知的数据进行估计 11.1.2 逻辑回归 11.1.2.1 分类问题 分类问题&#xff1a;垃圾…...

Python基础学习12——异常

在Python中&#xff0c;会使用“异常”这个十分特殊的对象来管理程序执行期间发生的错误&#xff0c;即报错。本文将介绍一下python基础的处理异常的方法以及一些基本的异常类型。 异常处理方法 try-except代码块 当我们编写程序时&#xff0c;我们可以编写一个try-except代…...

[日常练习]练习17:链表头插法、尾插法练习

[日常练习]练习17&#xff1a;链表头插法、尾插法练习练习17描述输入输出输入示例1输出示例1输入示例2输出示例2代码演示&#xff1a;总结练习17 【日常练习】 链表头插法、尾插法练习 描述 输入3 4 5 6 7 9999一串整数&#xff0c;9999代表结束&#xff0c;通过头插法新建链…...

第十四届蓝桥杯模拟赛(第三期)试题与题解 C++

目录 一、填空题 &#xff08;一&#xff09;最小的十六进制(答案&#xff1a;2730) &#xff08;二&#xff09;Excel的列(答案&#xff1a;BYT) &#xff08;三&#xff09;相等日期(答案&#xff1a;70910) &#xff08;四&#xff09;多少种取法(答案&#xff1a;189)…...

关于 “宏“

起源 宏 Macro"这个词源于希腊语 “makros”&#xff0c;意为“大的&#xff0c;长的” 延伸使用 随后用于计算机领域是&#xff0c;在汇编语言时用于描述一大堆的汇编指令。 只要用宏指令&#xff0c;就是直接用的一大堆的汇编指令&#xff08;有点函数的味道&#xf…...

1.2 CSS标签选择器,类选择器

CSS选择器&#xff1a; 根据不同的需求选出不同的标签&#xff0c;进行美化装饰 1. 标签选择器 标签选择器(元素选择器)&#xff1a;用 HTML标签名作为选择器&#xff0c;按标签名称进行分类&#xff0c;为页面某一类标签指定统一的CSS样式 作用: 可以把某一类标签全部选中&…...

【Linux】进程等待 | 详解 wait/waitpid 的 status 参数

&#x1f923; 爆笑教程 &#x1f449; 《看表情包学Linux》&#x1f448; 猛戳订阅 &#x1f525; &#x1f4ad; 写在前面&#xff1a;在上一章中我们讲解了进程创建与进程终止&#xff0c;本章我们开始讲解进程等待。进程等待这部分知识相较于前面还是较为复杂的&#xff0…...

OpenAI眼中的无线调优策略

问&#xff1a;无线调优策略该怎么优化无线调优是指对无线网络的各种参数进行优化&#xff0c;以提高网络性能和用户体验。以下是几个无线调优策略&#xff1a;频谱分配&#xff1a;通过优化频谱的分配&#xff0c;可以提高网络的容量和覆盖范围。在频谱分配时&#xff0c;需要…...

DataX入门

目录 1. DataX介绍 2. DataX支持的常用数据源类型 3. 设计理念 4. DataX框架设计 4.1. Reader 4.2. Writer 4.3. Framework 5. DataX的运行流程 6. DataX与Sqoop对比 7. 部署 8. 配置详解 9. 案例 同步MySql到HDFS 9.1. 整体结构 9.2. mySqlReader 9.2.1. …...

第二章SpringBoot基础学习

文章目录SpringBoot依赖管理特性依赖管理开发导入starter场景启动器SpringBoot自动配置特性自动配好Tomcat自动配好SpringMVC默认的包结构各种配置拥有默认值按需加载所有自动配置项SpringBoot注解底层注解ConfigurationImport导入组件Conditional条件装配ImportResource导入Sp…...

B - Build Roads (最小生成树 + 打表)

https://vjudge.net/problem/Gym-103118B/origin 在猫的国度里&#xff0c;有n个城市。猫国国王想要修n -1条路来连接所有的城市。第i市有一家ai经验价值的建筑公司。要在第i市和第j市之间修建公路&#xff0c;两个城市的建筑公司需要相互合作。但是&#xff0c;在修路的过程中…...

k8s管理工具

k8s管理工具 文章目录k8s管理工具Kuboard&#xff08;&#x1f495;17.3k&#xff09;KubeOperator&#xff08;&#x1f495;4.6k&#xff09;Rainbond&#xff08;&#x1f495;3.8k&#xff09;KubeSphere&#xff08;&#x1f495;12k&#xff09;Kuboard&#xff08;&…...

Cannot start compiler The output path is not specified for module mystatic(已解决)

1.背景&#xff1a;今天在idea上写了一些代码&#xff0c;右键run竟然跑不起来了&#xff0c;而且右下角的Event Log还报错。报错内容如下图&#xff1a;2.报错原因&#xff1a;项目代码和编译器的输出路径不在一块&#xff0c;导致idea无法找到模块的output path&#xff08;输…...

python入门应该怎么学习

国外Python的使用率非常高&#xff0c;但在国内Python是近几年才火起来&#xff0c;Python正处于高速上升期市场对于Python开发人才的需求量急剧增加&#xff0c;学习Python的前景比较好。 Python应用领域广泛&#xff0c;意味着选择Python的同学在学成之后可选择的就业领域有…...

不懂命令, 如何将代码托管到Gitee上

1.注册码云注册地址 : https://gitee.com2. 新建仓库第一步 : 创建仓库第二步 : 给仓库起名字创建好仓库后, 我们就有了一个网络上的仓库 : 3. 将网络上的仓库克隆到本地在克隆仓库之前, 我们需要先在电脑上安装以下两个工具 >>这两个软件一定要按顺序安装, 先安装第一个…...

Mysql常见面试题总结

1、什么是存储引擎 存储引擎指定了表的类型&#xff0c;即如何存储和索引数据&#xff0c;是否支持事务&#xff0c;同时存储引擎也决定了表在计算机中的存储方式。 2、查看数据库支持哪些存储引擎使用什么命令&#xff1f; -- 查看数据库支持的存储引擎 show engines; 或者 …...

python第一周作业

作业1&#xff1a;1、PPT上五个控制台界面2、要求定义两个数&#xff0c;并且交换它们的值&#xff08;请使用多种方式&#xff0c;越多越好&#xff09;作业1作业2&#xff1a;判断一个数&#xff0c;是否是2的指数2的指数0000 0010 0000 00010000 0100 0000 00110000 1000 00…...

FLoyd算法的入门与应用

目录 一、前言 二、FLoyd算法 1、最短路问题 2、Floyd算法 3、Floyd的特点 4、Floyd算法思想&#xff1a;动态规划 三、例题 1、蓝桥公园&#xff08;lanqiaoOJ题号1121&#xff09; 2、路径&#xff08;2021年初赛 lanqiaoOJ题号1460&#xff09; 一、前言 本文主要…...

303. 区域和检索 - 数组不可变

303. 区域和检索 - 数组不可变 给定一个整数数组 nums&#xff0c;处理以下类型的多个查询: 计算索引 left 和 right &#xff08;包含 left 和 right&#xff09;之间的 nums 元素的 和 &#xff0c;其中 left < right 实现 NumArray 类&#xff1a; NumArray(int[] num…...

Spring Cloud融合Nacos配置加载优先级 | Spring Cloud 8

一、前言 Spring Cloud Alibaba Nacos Config 目前提供了三种配置能力从 Nacos 拉取相关的配置&#xff1a; A&#xff1a;通过内部相关规则(应用名、扩展名、profiles)自动生成相关的 Data Id 配置B&#xff1a;通过 spring.cloud.nacos.config.extension-configs的方式支持…...

LeetCode 236.二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以是它自己的祖…...

awk简单实例(持续更新中)

一 概述 awk命令是一种分析和处理文本文件的编程工具。它的功能非常强大&#xff0c;是Linux/Unix系统中最常用的过滤工具。 awk内建变量&#xff1a; NF 整个数据行(即$0)拥有的字段总数 NR 当前awk所处理的数据行的编号 $0 当前awk所处理的数据行 $1 数据行的第1个字段 $2 数…...

react动态路由组件的封装

react动态路由组件的封装 我这篇比较全面 首先下载包 npm i react-router-dom5 这里为什么要用5的版本为啥不用最新的&#xff0c;原因在于老版本跟新版本写法不一样 老版本 import { HashRouter, Route, Switch, Redirect } from react-router-dom;render() {return (<Ha…...

Vue项目中引入高德地图步骤详解

高德地图API官网&#xff1a;高德开放平台 | 高德地图API。 目录 一、案例效果 二、开发准备 1. 注册高德开放平台账号 2. 创建应用添加 key 值 三、项目中使用地图组件 1. npm 获取高德地图 API 2.在项目中新建 MapContainer.vue 文件&#xff0c;用作地图组件。 3.在…...

软件测试用例篇(2)

功能测试界面测试兼容性测试安全测试易用性测试性能测试 针对有需求的案例来设计测试用例:邮箱注册&#xff0c;部分测试用例 https://zay1xofb7z6.feishu.cn/mindnotes/bmncnKD5Ak6GSZl3PRlWDgF9z3g#mindmap 一)等价类: 场景需求:姓名长度是6-200位&#xff0c;那么如何进行设…...

leetcode题解-27. Remove Element

给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考虑数组中超出新长度后面…...

【fly-iot飞凡物联】(4):在linux系统上搭建arduino环境,可以使用离线包,导入到arduino上即可。

目录前言1&#xff0c;关于2&#xff0c;然后就可以找到ESP32&#xff0c;ESP8266的主版3&#xff0c;方法2&#xff0c;github下载&#xff0c;然后手动添加到ide中吧4&#xff0c;总结前言 本文的原文连接是: https://blog.csdn.net/freewebsys/article/details/108971807 未…...

java实例解析类图中【关联、组合和聚合】的区别

总目录链接==>> AutoSAR入门和实战系列总目录 文章目录 聚合Composition聚合与组合的区别关联是两个独立类之间的关系,它通过它们的对象建立关联。关联可以是一对一、一对多、多对一、多对多。在面向对象的编程中,一个对象与另一个对象通信以使用该对象提供的功能和服…...

基于m-p条件查询代码生成

目录 起因 演示 使用 0.自定义注解 1.定义一个dto的条件查询类 2.调用主程序 效果图 小结 代码 注解 Dto类 完整代码 起因 最近两天一直写后台管理统计的增删改查(很少写增删改查&#xff0c;所以不是很熟练)&#xff0c;几乎每个表都要涉及到条件查询的业务&#xf…...