学习 Python 之 Pygame 开发魂斗罗(八)
在上次的博客学习 Python 之 Pygame 开发魂斗罗(七)中,我们解决了一些问题,这次我们加入敌人
下面是图片的素材
链接:https://pan.baidu.com/s/1X7tESkes_O6nbPxfpHD6hQ?pwd=hdly
提取码:hdly
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]
有了移动函数,就要对敌人位置进行检测,当玩家距离敌人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像素后消失
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类,新加入敌人就创建新的敌人类
由于加入了敌人类,主函数的碰撞体组需要进行改变
敌人和玩家的碰撞体应该分开,因为玩家向下跳的时候,上面的碰撞体会消失,如果此时有敌人在上面,碰撞体一消失,敌人就会掉下来,这是不对的,所以我们要修改原有的碰撞体组

把上图的红框中的代码修改成下面的样子
如果使用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 = []

用来把存放游戏中当前的敌人
好的,接下来我们来写创建敌人的函数
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()函数让敌人移动
下面我们创建敌人更新函数
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函数开火代码注释一下

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

到这里,我们就可以可能敌人出来啦

但是我们看到敌人不会向下掉落,这是因为没有给敌人增加碰撞体,接下来我们先实现敌人发射子弹,然后再实现敌人碰撞体的问题
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub
我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain
这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o