学习 Python 之 Pygame 开发坦克大战(二)
学习 Python 之 Pygame 开发坦克大战(二)
- 坦克大战的需求
- 开始编写坦克大战
- 1. 搭建主类框架
- 2. 获取窗口中的事件
- 3. 创建基类
- 4. 初始化我方坦克类
- 5. 完善我方坦克的移动
- 5. 完善我方坦克的显示
- 6. 在主类中加入我方坦克并完成坦克移动
- 7. 初始化子弹类
- 8. 完善子弹的移动
- 9. 完善坦克开火
- 10. 实现敌方坦克类
- 11. 在主类中加入敌方坦克并完成坦克移动和开火
坦克大战的需求
坦克大战游戏包含很多个物体,现在要对这些物体进行总结
| 类名 | 包含的操作 | 包含的属性 |
|---|---|---|
| 敌方坦克类 | 射击,移动,显示 | 生命,速度,伤害,方向,类型 |
| 我方坦克类 | 射击,移动,显示 | 生命,速度,伤害,方向,装甲,等级 |
| 子弹类 | 移动,显示 | 方向,伤害,发射源,速度 |
| 墙壁类、草类、石砖类、河类 | 显示 | 是否可以摧毁 |
| 音效类 | 播放,停止,设置音乐 | - |
| 爆炸效果类 | 显示 | 是否可以摧毁 |
| 主类 | … | … |
物体总结完毕后,规划一下窗口的大小,下面是我设置的窗口大小

素材链接:百度网盘
链接:https://pan.baidu.com/s/19sCyH7rp37f6DzRj0iXDCA?pwd=tkdz
提取码:tkdz
开始编写坦克大战
一切都准备就绪啦,现在开始编写坦克大战的代码吧
1. 搭建主类框架
主类是整个游戏运作的类,当然你也可以不用使用类,直接创建一个函数也可以,这里使用了面向对象的思想
import pygameSCREEN_WIDTH = 1100
SCREEN_HEIGHT = 600
BACKGROUND_COLOR = pygame.Color(0, 0, 0)
FONT_COLOR = (255, 255, 255)
class MainGame:# 窗口Surface对象window = Nonedef __init__(self):passdef startGame(self):# 初始化展示模块pygame.display.init()# 设置窗口大小size = (SCREEN_WIDTH, SCREEN_HEIGHT)# 初始化窗口MainGame.window = pygame.display.set_mode(size)# 设置窗口标题pygame.display.set_caption('Tank Battle')while 1:# 设置背景颜色MainGame.window.fill(BACKGROUND_COLOR)# 更新窗口pygame.display.update()if __name__ == '__main__':MainGame().startGame()
运行结果

主类中运用到的知识,都是学习 Python 之 Pygame 开发坦克大战(一)中所包含的
2. 获取窗口中的事件
坦克大战关键是对玩家自己的坦克进行操控,消灭敌人的坦克,所以键盘事件响应是必不可少的
def getPlayingModeEvent(self):# 获取所有事件eventList = pygame.event.get()for event in eventList:if event.type == pygame.QUIT:sys.exit()if event.type == pygame.KEYDOWN:print('键盘按键按下')if event.key == pygame.K_w:print('w按下')elif event.key == pygame.K_s:print('s按下')elif event.key == pygame.K_a:print('a按下')elif event.key == pygame.K_d:print('d按下')elif event.key == pygame.K_j:print('j按下')if event.type == pygame.KEYUP:print('键盘按键抬起')if event.key == pygame.K_w:print('w抬起')elif event.key == pygame.K_s:print('s抬起')elif event.key == pygame.K_a:print('a抬起')elif event.key == pygame.K_d:print('d抬起')
获取窗口中的事件,用于玩家操控坦克、发射坦克子弹等操作
游戏中,我规定aswd操控坦克,j攻击,当然你也可以上下左右键移动,空格攻击,如果你想设置双人游戏,这也是可以的
import pygame
import sysSCREEN_WIDTH = 1100
SCREEN_HEIGHT = 600
BACKGROUND_COLOR = pygame.Color(0, 0, 0)
FONT_COLOR = (255, 255, 255)
class MainGame:# 窗口Surface对象window = Nonedef __init__(self):passdef startGame(self):# 初始化展示模块pygame.display.init()# 设置窗口大小size = (SCREEN_WIDTH, SCREEN_HEIGHT)# 初始化窗口MainGame.window = pygame.display.set_mode(size)# 设置窗口标题pygame.display.set_caption('Tank Battle')while 1:# 设置背景颜色MainGame.window.fill(BACKGROUND_COLOR)# 获取窗口事件self.getPlayingModeEvent()# 更新窗口pygame.display.update()def getPlayingModeEvent(self):# 获取所有事件eventList = pygame.event.get()for event in eventList:if event.type == pygame.QUIT:sys.exit()if event.type == pygame.KEYDOWN:print('键盘按键按下')if event.key == pygame.K_w:print('w按下')elif event.key == pygame.K_s:print('s按下')elif event.key == pygame.K_a:print('a按下')elif event.key == pygame.K_d:print('d按下')elif event.key == pygame.K_j:print('j按下')if event.type == pygame.KEYUP:print('键盘按键抬起')if event.key == pygame.K_w:print('w抬起')elif event.key == pygame.K_s:print('s抬起')elif event.key == pygame.K_a:print('a抬起')elif event.key == pygame.K_d:print('d抬起')if __name__ == '__main__':MainGame().startGame()
3. 创建基类
坦克操控事件完成后,现在就是实现坦克啦,首先场景中的物体都需要发生物体间的碰撞,简单来说就是检测两个屏幕上的图片是否发生了重叠,如果发生了,就要触发一些事件,这里创建ParentObject类,用于继承pygame.sprite.Sprite类
pygame.sprite.Sprite类可以用来检测物体碰撞,是pygame提供的一个类,非常的方便
import pygame.spriteclass ParentObject(pygame.sprite.Sprite):def __init__(self):super().__init__()
4. 初始化我方坦克类
创建我方坦克类,并且继承基类
import pygame as pg
import pygame.image
from ParentObject import ParentObjectclass PlayerTank(ParentObject):def __init__(self, x, y, order, amour):""":param x: 坦克横坐标:param y: 坦克纵坐标:param order: 玩家坦克序号,1表示一号玩家,2表示二号玩家:param amour: 坦克初始护甲"""super().__init__()self.images = []if order == 1:self.images.append({'UP': pygame.image.load('../Image/Player1/45x45/UP1.png'),'DOWN': pygame.image.load('../Image/Player1/45x45/DOWN1.png'),'LEFT': pygame.image.load('../Image/Player1/45x45/LEFT1.png'),'RIGHT': pygame.image.load('../Image/Player1/45x45/RIGHT1.png')})self.images.append({'UP': pygame.image.load('../Image/Player1/45x45/UP2.png'),'DOWN': pygame.image.load('../Image/Player1/45x45/DOWN2.png'),'LEFT': pygame.image.load('../Image/Player1/45x45/LEFT2.png'),'RIGHT': pygame.image.load('../Image/Player1/45x45/RIGHT2.png')})self.images.append({'UP': pygame.image.load('../Image/Player1/45x45/UP3.png'),'DOWN': pygame.image.load('../Image/Player1/45x45/DOWN3.png'),'LEFT': pygame.image.load('../Image/Player1/45x45/LEFT3.png'),'RIGHT': pygame.image.load('../Image/Player1/45x45/RIGHT3.png')})self.images.append({'UP': pygame.image.load('../Image/Player1/45x45/UP4.png'),'DOWN': pygame.image.load('../Image/Player1/45x45/DOWN4.png'),'LEFT': pygame.image.load('../Image/Player1/45x45/LEFT4.png'),'RIGHT': pygame.image.load('../Image/Player1/45x45/RIGHT4.png')})self.images.append({'UP': pygame.image.load('../Image/Player1/45x45/UP5.png'),'DOWN': pygame.image.load('../Image/Player1/45x45/DOWN5.png'),'LEFT': pygame.image.load('../Image/Player1/45x45/LEFT5.png'),'RIGHT': pygame.image.load('../Image/Player1/45x45/RIGHT5.png')})self.images.append({'UP': pygame.image.load('../Image/Player1/45x45/UP6.png'),'DOWN': pygame.image.load('../Image/Player1/45x45/DOWN6.png'),'LEFT': pygame.image.load('../Image/Player1/45x45/LEFT6.png'),'RIGHT': pygame.image.load('../Image/Player1/45x45/RIGHT6.png')})# 生命self.life = 3# 装甲self.armor = amour# 方向self.direction = 'UP'# 根据护甲选择坦克的样子self.image: pg.Surface = self.images[max(self.armor - 1, 0)][self.direction]self.rect = self.image.get_rect()self.rect.left = xself.rect.top = y# 速度self.accumulation: float = 0self.speed = 2# 移动开关self.stop = True# 等级self.level = 1# 伤害self.damage = 1def move(self):passdef shot(self):passdef draw(self):pass
5. 完善我方坦克的移动
accumulation 可以更细的控制坦克的移动速度
当 accumulation 累加到 1 时,坦克移动一次,如果不设置这个属性,即使是速度每次 +1,坦克移动的也速度很快,所以增加这个属性,可以减慢坦克的移动速度
def move(self):if self.accumulation >= 1:self.accumulation = 0if self.direction == 'LEFT':if self.rect.left > 0:self.rect.left -= self.speedelif self.direction == 'UP':if self.rect.top > 0:self.rect.top -= self.speedelif self.direction == 'DOWN':if self.rect.top < 555:self.rect.top += self.speedelif self.direction == 'RIGHT':if self.rect.left < 855:self.rect.left += self.speedelse:self.accumulation += 0.20
这里需要设置坦克的边界范围
防止坦克跑出窗口
坦克的图片是45x45

5. 完善我方坦克的显示
坦克的显示就是把坦克的图片显示在窗口中
def draw(self, window):# window传入主窗口# 坦克生命中为0,表示已经死亡,不再展示坦克if self.life <= 0:return# 获取展示的对象self.image = self.images[max(self.armor - 1, 0)][self.direction]window.blit(self.image, self.rect)
我方坦克类完整代码
import pygame as pg
import pygame.image
from ParentObject import ParentObjectclass PlayerTank(ParentObject):def __init__(self, x, y, order, amour):""":param x: 坦克横坐标:param y: 坦克纵坐标:param order: 玩家坦克序号,1表示一号玩家,2表示二号玩家:param amour: 坦克初始护甲"""super().__init__()self.images = []if order == 1:self.images.append({'UP': pygame.image.load('../Image/Player1/45x45/UP1.png'),'DOWN': pygame.image.load('../Image/Player1/45x45/DOWN1.png'),'LEFT': pygame.image.load('../Image/Player1/45x45/LEFT1.png'),'RIGHT': pygame.image.load('../Image/Player1/45x45/RIGHT1.png')})self.images.append({'UP': pygame.image.load('../Image/Player1/45x45/UP2.png'),'DOWN': pygame.image.load('../Image/Player1/45x45/DOWN2.png'),'LEFT': pygame.image.load('../Image/Player1/45x45/LEFT2.png'),'RIGHT': pygame.image.load('../Image/Player1/45x45/RIGHT2.png')})self.images.append({'UP': pygame.image.load('../Image/Player1/45x45/UP3.png'),'DOWN': pygame.image.load('../Image/Player1/45x45/DOWN3.png'),'LEFT': pygame.image.load('../Image/Player1/45x45/LEFT3.png'),'RIGHT': pygame.image.load('../Image/Player1/45x45/RIGHT3.png')})self.images.append({'UP': pygame.image.load('../Image/Player1/45x45/UP4.png'),'DOWN': pygame.image.load('../Image/Player1/45x45/DOWN4.png'),'LEFT': pygame.image.load('../Image/Player1/45x45/LEFT4.png'),'RIGHT': pygame.image.load('../Image/Player1/45x45/RIGHT4.png')})self.images.append({'UP': pygame.image.load('../Image/Player1/45x45/UP5.png'),'DOWN': pygame.image.load('../Image/Player1/45x45/DOWN5.png'),'LEFT': pygame.image.load('../Image/Player1/45x45/LEFT5.png'),'RIGHT': pygame.image.load('../Image/Player1/45x45/RIGHT5.png')})self.images.append({'UP': pygame.image.load('../Image/Player1/45x45/UP6.png'),'DOWN': pygame.image.load('../Image/Player1/45x45/DOWN6.png'),'LEFT': pygame.image.load('../Image/Player1/45x45/LEFT6.png'),'RIGHT': pygame.image.load('../Image/Player1/45x45/RIGHT6.png')})# 生命self.life = 3# 装甲self.armor = amour# 方向self.direction = 'UP'# 根据护甲选择坦克的样子self.image: pg.Surface = self.images[max(self.armor - 1, 0)][self.direction]self.rect = self.image.get_rect()self.rect.left = xself.rect.top = y# 速度self.accumulation: float = 0self.speed = 2# 移动开关self.stop = True# 重生self.isResurrecting = False# 碰撞前的坐标self.prvX = self.rect.leftself.prvY = self.rect.top# 等级self.level = 1# 伤害self.damage = 1def move(self):if self.accumulation >= 1:self.accumulation = 0if self.direction == 'LEFT':if self.rect.left > 0:self.rect.left -= self.speedelif self.direction == 'UP':if self.rect.top > 0:self.rect.top -= self.speedelif self.direction == 'DOWN':if self.rect.top < 555:self.rect.top += self.speedelif self.direction == 'RIGHT':if self.rect.left < 855:self.rect.left += self.speedelse:self.accumulation += 0.20def shot(self):passdef draw(self, window):# 坦克生命中为0,表示已经死亡,不再展示坦克if self.life <= 0:return# 获取展示的对象self.image = self.images[max(self.armor - 1, 0)][self.direction]# 画出图片window.blit(self.image, self.rect)
6. 在主类中加入我方坦克并完成坦克移动
我方坦克类中移动和显示函数实现后,就要让它们在主类中调用
添加类变量playerTank,用于存放我方坦克的对象
修改循环中的代码
while 1:# 设置背景颜色MainGame.window.fill(BACKGROUND_COLOR)# 获取窗口事件self.getPlayingModeEvent()# 显示我方坦克MainGame.playerTank.draw(MainGame.window)# 我方坦克移动if not MainGame.playerTank.stop:MainGame.playerTank.move()# 更新窗口pygame.display.update()
完整我方坦克类代码
import pygame
import sysfrom PlayerTank import PlayerTankSCREEN_WIDTH = 1100
SCREEN_HEIGHT = 600
BACKGROUND_COLOR = pygame.Color(0, 0, 0)
FONT_COLOR = (255, 255, 255)
PLAYER_TANK_POSITION = (325, 550)class MainGame:# 窗口Surface对象window = None# 玩家坦克playerTank = Nonedef __init__(self):passdef startGame(self):# 初始化展示模块pygame.display.init()# 设置窗口大小size = (SCREEN_WIDTH, SCREEN_HEIGHT)# 初始化窗口MainGame.window = pygame.display.set_mode(size)# 设置窗口标题pygame.display.set_caption('Tank Battle')# 初始化我方坦克MainGame.playerTank = PlayerTank(PLAYER_TANK_POSITION[0], PLAYER_TANK_POSITION[1], 1, 1)while 1:# 设置背景颜色MainGame.window.fill(BACKGROUND_COLOR)# 获取窗口事件self.getPlayingModeEvent()# 显示我方坦克MainGame.playerTank.draw(MainGame.window)# 我方坦克移动if not MainGame.playerTank.stop:MainGame.playerTank.move()# 更新窗口pygame.display.update()def getPlayingModeEvent(self):# 获取所有事件eventList = pygame.event.get()for event in eventList:if event.type == pygame.QUIT:sys.exit()"""stop属性用来控制坦克移动,当键盘按键按下时,坦克可以移动,一直按住一直移动,当按键抬起时,停止移动如果没有该属性,按一下按键移动一次,按一下移动一下,不能一直按住一直移动"""if event.type == pygame.KEYDOWN:print('键盘按键按下')if event.key == pygame.K_w:MainGame.playerTank.direction = 'UP'MainGame.playerTank.stop = Falseelif event.key == pygame.K_s:MainGame.playerTank.direction = 'DOWN'MainGame.playerTank.stop = Falseelif event.key == pygame.K_a:MainGame.playerTank.direction = 'LEFT'MainGame.playerTank.stop = Falseelif event.key == pygame.K_d:MainGame.playerTank.direction = 'RIGHT'MainGame.playerTank.stop = Falseelif event.key == pygame.K_j:print('j按下')if event.type == pygame.KEYUP:print('键盘按键抬起')if event.key == pygame.K_w:MainGame.playerTank.stop = Trueelif event.key == pygame.K_s:MainGame.playerTank.stop = Trueelif event.key == pygame.K_a:MainGame.playerTank.stop = Trueelif event.key == pygame.K_d:MainGame.playerTank.stop = Trueif __name__ == '__main__':MainGame().startGame()
运行结果

7. 初始化子弹类
现在已经实现了坦克的移动啦,下面就要实现坦克的发射子弹
实际上,子弹也是一幅图片,当我们按下开火按键后,就在坦克的正前方画出子弹,随着时间的流逝,要让子弹按照当前方向一直运动下去
import pygame
from ParentObject import ParentObjectclass Bullet(ParentObject):def __init__(self, tank):super().__init__()self.images = {'UP': pygame.image.load('../Image/Bullet/Bullet(UP).png'),'DOWN': pygame.image.load('../Image/Bullet/Bullet(DOWN).png'),'LEFT': pygame.image.load('../Image/Bullet/Bullet(LEFT).png'),'RIGHT': pygame.image.load('../Image/Bullet/Bullet(RIGHT).png')}# 方向self.direction = tank.directionself.image: pygame.Surface = self.images[self.direction]self.rect = self.image.get_rect()# 坦克发射子弹的位置if self.direction == 'UP':self.rect.left = tank.rect.left + 17.5self.rect.top = tank.rect.top - 25elif self.direction == 'DOWN':self.rect.left = tank.rect.left + 17.5self.rect.top = tank.rect.top + 25elif self.direction == 'LEFT':self.rect.left = tank.rect.left - 25self.rect.top = tank.rect.top + 17.5elif self.direction == 'RIGHT':self.rect.left = tank.rect.left + 25self.rect.top = tank.rect.top + 17.5# 速度self.accumulationMax: float = 0self.accumulation = 0.25self.speed = 10# 销毁开关self.isDestroy = False# 发射源self.source = tank# 伤害self.damage = tank.damagedef move(self, explodeList):passdef draw(self, window):window.blit(self.image, self.rect)
下面是确定子弹的位置:
子弹图片是25x10,坦克发射子弹是在坦克中间位置发射

8. 完善子弹的移动
子弹的初始位置确定后,一旦创建出来,就要一直移动下去,实际上就是刷新屏幕,然后把原来位置上的子弹图片去掉,在新的位置上再画出子弹图片,坦克移动的原理也是这样
修改move函数,添加检查子弹出界函数
def move(self):if self.accumulation >= 1:self.accumulation = 0if self.direction == 'LEFT':self.rect.left -= self.speedelif self.direction == 'UP':self.rect.top -= self.speedelif self.direction == 'DOWN':self.rect.top += self.speedelif self.direction == 'RIGHT':self.rect.left += self.speed# 检查子弹是否出界self.checkBullet()else:self.accumulation += 0.20def checkBullet(self):toDestroy = False# 如果出界,就设置为销毁if self.rect.top < 0 or self.rect.top > 600:toDestroy = Trueif self.rect.left < 0 or self.rect.right > 900:toDestroy = Trueif toDestroy:self.isDestroy = True
子弹类完整代码
import pygame
from ParentObject import ParentObjectclass Bullet(ParentObject):def __init__(self, tank):super().__init__()self.images = {'UP': pygame.image.load('../Image/Bullet/Bullet(UP).png'),'DOWN': pygame.image.load('../Image/Bullet/Bullet(DOWN).png'),'LEFT': pygame.image.load('../Image/Bullet/Bullet(LEFT).png'),'RIGHT': pygame.image.load('../Image/Bullet/Bullet(RIGHT).png')}# 方向self.direction = tank.directionself.image: pygame.Surface = self.images[self.direction]self.rect = self.image.get_rect()# 坦克发射子弹的位置if self.direction == 'UP':self.rect.left = tank.rect.left + 17.5self.rect.top = tank.rect.top - 25elif self.direction == 'DOWN':self.rect.left = tank.rect.left + 17.5self.rect.top = tank.rect.top + 25elif self.direction == 'LEFT':self.rect.left = tank.rect.left - 25self.rect.top = tank.rect.top + 17.5elif self.direction == 'RIGHT':self.rect.left = tank.rect.left + 25self.rect.top = tank.rect.top + 17.5# 速度self.accumulationMax: float = 0self.accumulation = 0.25self.speed = 10# 销毁开关self.isDestroy = False# 发射源self.source = tank# 伤害self.damage = tank.damagedef move(self):if self.accumulation >= 1:self.accumulation = 0if self.direction == 'LEFT':self.rect.left -= self.speedelif self.direction == 'UP':self.rect.top -= self.speedelif self.direction == 'DOWN':self.rect.top += self.speedelif self.direction == 'RIGHT':self.rect.left += self.speed# 检查子弹是否出界self.checkBullet()else:self.accumulation += 0.20def checkBullet(self):toDestroy = False# 如果出界,就设置为销毁if self.rect.top < 0 or self.rect.top > 600:toDestroy = Trueif self.rect.left < 0 or self.rect.right > 900:toDestroy = Trueif toDestroy:self.isDestroy = Truedef draw(self, window):window.blit(self.image, self.rect)
9. 完善坦克开火
有了子弹类,就可以让坦克发射子弹了
修改坦克的 shot() 函数
def shot(self):return Bullet(self)
修改 getPlayingModeEvent() 函数
当 j键按下,发射子弹
def getPlayingModeEvent(self):# 获取所有事件eventList = pygame.event.get()for event in eventList:if event.type == pygame.QUIT:sys.exit()"""stop属性用来控制坦克移动,当键盘按键按下时,坦克可以移动,一直按住一直移动,当按键抬起时,停止移动如果没有该属性,按一下按键移动一次,按一下移动一下,不能一直按住一直移动"""if event.type == pygame.KEYDOWN:print('键盘按键按下')if event.key == pygame.K_w:MainGame.playerTank.direction = 'UP'MainGame.playerTank.stop = Falseelif event.key == pygame.K_s:MainGame.playerTank.direction = 'DOWN'MainGame.playerTank.stop = Falseelif event.key == pygame.K_a:MainGame.playerTank.direction = 'LEFT'MainGame.playerTank.stop = Falseelif event.key == pygame.K_d:MainGame.playerTank.direction = 'RIGHT'MainGame.playerTank.stop = Falseelif event.key == pygame.K_j:# 判断子弹数量是否超过指定的个数if len(MainGame.playerBulletList) < MainGame.playerBulletNumber:bullet = MainGame.playerTank.shot()MainGame.playerBulletList.append(bullet)if event.type == pygame.KEYUP:print('键盘按键抬起')if event.key == pygame.K_w:MainGame.playerTank.stop = Trueelif event.key == pygame.K_s:MainGame.playerTank.stop = Trueelif event.key == pygame.K_a:MainGame.playerTank.stop = Trueelif event.key == pygame.K_d:MainGame.playerTank.stop = True
子弹有了,但是没有显示在窗口,此时写一个函数让子弹在窗口显示出来
def drawPlayerBullet(self, playerBulletList):# 遍历整个子弹列表,如果是没有被销毁的状态,就把子弹显示出来,否则从列表中删除for bullet in playerBulletList:if not bullet.isDestroy:bullet.draw(MainGame.window)bullet.move()else:playerBulletList.remove(bullet)
有了函数还需要调用,在while循环中加入该函数
while 1:# 设置背景颜色MainGame.window.fill(BACKGROUND_COLOR)# 获取窗口事件self.getPlayingModeEvent()# 显示我方坦克MainGame.playerTank.draw(MainGame.window)# 我方坦克移动if not MainGame.playerTank.stop:MainGame.playerTank.move()# 显示我方坦克子弹self.drawPlayerBullet(MainGame.playerBulletList)# 更新窗口pygame.display.update()
运行游戏看看结果
当我们按下开火键时,子弹就发射了

10. 实现敌方坦克类
现在我的坦克可以移动和开火,那么就要有点靶子给我们练习了,是时候实现敌方坦克类了
敌方坦克类的大部分代码跟我方坦克类一样,这里可以用继承,即父类是坦克类,实现两个类的一样的代码,然后我方坦克类和敌方坦克类继承这个类,不过我没有使用继承
import random
import pygame
import pygame.imagefrom ParentObject import ParentObject
from Bullet import Bulletclass EnemyTank(ParentObject):def __init__(self, x, y):super().__init__()types = [(1, 3), (2, 1), (3, 2), (4, 10)]# 随机产生一种坦克self.type = types[random.randint(0, len(types) - 1)]up = []down = []left = []right = []for i in range(1, self.type[1] + 1):up.append(pygame.image.load('../Image/Enemy/EnemyTank' + str(self.type[0]) + '/EnemyTank'+ str(self.type[0]) + 'Lv' + str(i) + '(UP).png'))down.append(pygame.image.load('../Image/Enemy/EnemyTank' + str(self.type[0]) + '/EnemyTank'+ str(self.type[0]) + 'Lv' + str(i) + '(DOWN).png'))left.append(pygame.image.load('../Image/Enemy/EnemyTank' + str(self.type[0]) + '/EnemyTank' + str(self.type[0]) + 'Lv' + str(i) + '(LEFT).png'))right.append(pygame.image.load('../Image/Enemy/EnemyTank' + str(self.type[0]) + '/EnemyTank' + str(self.type[0]) + 'Lv' + str(i) + '(RIGHT).png'))self.images = {'UP': up,'DOWN': down,'LEFT': left,'RIGHT': right}# 生命self.life = self.type[1]# 方向self.direction = 'DOWN'self.image: pygame.Surface = self.images[self.direction][self.life - 1]self.rect = self.image.get_rect()self.rect.left = xself.rect.top = y# 速度self.accumulationMax: float = 0self.accumulation = 0.1speed = 0maxBulletCount = 0damage = 1if self.type[0] == 1:speed = 3self.level = 1maxBulletCount = 1elif self.type[0] == 2:speed = 5self.level = 2maxBulletCount = 1damage = 3elif self.type[0] == 3:speed = 7self.level = 1maxBulletCount = 3damage = 2elif self.type[0] == 4:speed = 6self.level = 2maxBulletCount = 3damage = 1self.speed = speed# 移动开关self.stop = True# 开火开关self.fire = True# 步数self.step = 30# 伤害self.damage = damage# 子弹个数self.bulletCount = 0self.maxBulletCount = maxBulletCountdef loseLife(self, value = 1):self.life -= valuedef move(self):"""新增步数变量, 当坦克移动时, 步数进行减少, 当步数小于等于0的时候, 修改地方坦克的方向:return: None"""if self.stop:if self.step <= 0:self.direction = self.randDirection()self.step = 30else:if self.accumulationMax >= 1:self.accumulationMax = 0if self.direction == 'LEFT':if self.rect.left > 0:self.rect.left -= self.speedelif self.direction == 'UP':if self.rect.top > 0:self.rect.top -= self.speedelif self.direction == 'DOWN':if self.rect.top < 555:self.rect.top += self.speedelif self.direction == 'RIGHT':if self.rect.left < 855:self.rect.left += self.speedself.step -= 1else:self.accumulationMax += self.accumulationdef shot(self):if self.fire:if self.bulletCount < self.maxBulletCount:num = random.randint(0, 100)if num == 5 or num == 6:self.bulletCount += 1return Bullet(self)return Nonedef draw(self, window):# 获取展示的对象self.image = self.images[self.direction][self.life - 1]window.blit(self.image, self.rect)def randDirection(self):directions = ['UP', 'DOWN', 'LEFT', 'RIGHT']index = random.randint(0, 3)return directions[index]
我实际上设置了四种坦克的种类,下面types变量中记录着四种种类的序号和生命值,我规定,生命中不同坦克的样子也不同
types = [(1, 3), (2, 1), (3, 2), (4, 10)]
# 随机产生一种坦克
self.type = types[random.randint(0, len(types) - 1)]
元组中的第二个元素表示生命值, 直接获取
# 生命
self.life = self.type[1]
根据方向和生命值获取对应的图片
self.image: pygame.Surface = self.images[self.direction][self.life - 1]
坦克的属性
根据坦克种类的不同,伤害、等级、生命值也不同
等级是子弹的穿透性,1级只能打烂砖墙,2级可以打烂石墙,3级以上可以打烂黑曜石墙,这个黑曜石墙是我自己新加入的,我们以前玩的坦克大战是没有的,而且规则和我这个不太一样,当然你也可以修改,比如给坦克加入能量,当坦克在一定时间内连续击杀多个敌人,可以获得怒气,短时间伤害提升,这些都可以实现
speed = 0
maxBulletCount = 0
damage = 1
# 每种坦克都有不同的属性
if self.type[0] == 1:speed = 3self.level = 1maxBulletCount = 1
elif self.type[0] == 2:speed = 5self.level = 2maxBulletCount = 1damage = 3
elif self.type[0] == 3:speed = 7self.level = 1maxBulletCount = 3damage = 2
elif self.type[0] == 4:speed = 6self.level = 2maxBulletCount = 3damage = 1
坦克的移动
step是步数,每次按照30次循环作为一次坦克的操作
if self.stop:if self.step <= 0:# 随机产生一个方向,接下来朝这个方向移动self.direction = self.randDirection()self.step = 30else:if self.accumulationMax >= 1:self.accumulationMax = 0if self.direction == 'LEFT':if self.rect.left > 0:self.rect.left -= self.speedelif self.direction == 'UP':if self.rect.top > 0:self.rect.top -= self.speedelif self.direction == 'DOWN':if self.rect.top < 555:self.rect.top += self.speedelif self.direction == 'RIGHT':if self.rect.left < 855:self.rect.left += self.speedself.step -= 1else:self.accumulationMax += self.accumulation
坦克开火
随机产生一个0到100的数字,当为5或者6时,坦克就开火
def shot(self):if self.fire:if self.bulletCount < self.maxBulletCount:num = random.randint(0, 100)if num == 5 or num == 6:self.bulletCount += 1return Bullet(self)return None
11. 在主类中加入敌方坦克并完成坦克移动和开火
在主类中创建类变量
class MainGame:# 窗口Surface对象window = None# 玩家坦克playerTank = None# 玩家子弹playerBulletList = []playerBulletNumber = 3# 敌人坦克enemyTankList = []enemyTankTotalCount = 5# 用来给玩家展示坦克的数量enemyTankCurrentCount = 5# 敌人坦克子弹enemyTankListBulletList = []
创建展示敌方坦克函数
这里我规定一次展示三辆敌方坦克,当全部被消灭后,再展示3辆,直到全部被消灭
def drawEnemyTank(self):# 如果当前坦克为0,那么就该重新生成坦克if len(MainGame.enemyTankList) == 0:# 一次性产生三个,如果剩余坦克数量超过三,那只能产生三个n = min(3, MainGame.enemyTankTotalCount)# 如果最小是0,就说明敌人坦克没有了,那么就赢了if n == 0:print('赢了')return# 没有赢的话,就产生n个坦克self.initEnemyTank(n)# 总个数减去产生的个数MainGame.enemyTankTotalCount -= n# 遍历坦克列表,展示坦克并且移动for tank in MainGame.enemyTankList:# 坦克还有生命值if tank.life > 0:tank.draw(MainGame.window)tank.move()bullet = tank.shot()if bullet is not None:MainGame.enemyTankBulletList.append(bullet)# 坦克生命值为0,就从列表中剔除else:MainGame.enemyTankCurrentCount -= 1MainGame.enemyTankList.remove(tank)
坦克开火之后子弹加入敌方坦克子弹列表,把里面的子弹画出来,然后在while中调用它
def drawEnemyBullet(self):for bullet in MainGame.enemyTankBulletList:if not bullet.isDestroy:bullet.draw(MainGame.window)bullet.move()else:bullet.source.bulletCount -= 1MainGame.enemyTankBulletList.remove(bullet)
while 1:# 设置背景颜色MainGame.window.fill(BACKGROUND_COLOR)# 获取窗口事件self.getPlayingModeEvent()# 显示我方坦克MainGame.playerTank.draw(MainGame.window)# 我方坦克移动if not MainGame.playerTank.stop:MainGame.playerTank.move()# 显示我方坦克子弹self.drawPlayerBullet(MainGame.playerBulletList)# 展示敌方坦克self.drawEnemyTank()# 展示敌方坦克子弹self.drawEnemyBullet()# 更新窗口pygame.display.update()
完整的主类代码
import pygame
import sysfrom PlayerTank import PlayerTank
from EnemyTank import EnemyTankSCREEN_WIDTH = 1100
SCREEN_HEIGHT = 600
BACKGROUND_COLOR = pygame.Color(0, 0, 0)
FONT_COLOR = (255, 255, 255)
PLAYER_TANK_POSITION = (325, 550)class MainGame:# 窗口Surface对象window = None# 玩家坦克playerTank = None# 玩家子弹playerBulletList = []playerBulletNumber = 3# 敌人坦克enemyTankList = []enemyTankTotalCount = 5# 用来给玩家展示坦克的数量enemyTankCurrentCount = 5# 敌人坦克子弹enemyTankBulletList = []def __init__(self):passdef startGame(self):# 初始化展示模块pygame.display.init()# 设置窗口大小size = (SCREEN_WIDTH, SCREEN_HEIGHT)# 初始化窗口MainGame.window = pygame.display.set_mode(size)# 设置窗口标题pygame.display.set_caption('Tank Battle')# 初始化我方坦克MainGame.playerTank = PlayerTank(PLAYER_TANK_POSITION[0], PLAYER_TANK_POSITION[1], 1, 1)while 1:# 设置背景颜色MainGame.window.fill(BACKGROUND_COLOR)# 获取窗口事件self.getPlayingModeEvent()# 显示我方坦克MainGame.playerTank.draw(MainGame.window)# 我方坦克移动if not MainGame.playerTank.stop:MainGame.playerTank.move()# 显示我方坦克子弹self.drawPlayerBullet(MainGame.playerBulletList)# 展示敌方坦克self.drawEnemyTank()# 展示敌方坦克子弹self.drawEnemyBullet()# 更新窗口pygame.display.update()def getPlayingModeEvent(self):# 获取所有事件eventList = pygame.event.get()for event in eventList:if event.type == pygame.QUIT:sys.exit()"""stop属性用来控制坦克移动,当键盘按键按下时,坦克可以移动,一直按住一直移动,当按键抬起时,停止移动如果没有该属性,按一下按键移动一次,按一下移动一下,不能一直按住一直移动"""if event.type == pygame.KEYDOWN:if event.key == pygame.K_w:MainGame.playerTank.direction = 'UP'MainGame.playerTank.stop = Falseelif event.key == pygame.K_s:MainGame.playerTank.direction = 'DOWN'MainGame.playerTank.stop = Falseelif event.key == pygame.K_a:MainGame.playerTank.direction = 'LEFT'MainGame.playerTank.stop = Falseelif event.key == pygame.K_d:MainGame.playerTank.direction = 'RIGHT'MainGame.playerTank.stop = Falseelif event.key == pygame.K_j:# 判断子弹数量是否超过指定的个数if len(MainGame.playerBulletList) < MainGame.playerBulletNumber:bullet = MainGame.playerTank.shot()MainGame.playerBulletList.append(bullet)if event.type == pygame.KEYUP:if event.key == pygame.K_w:MainGame.playerTank.stop = Trueelif event.key == pygame.K_s:MainGame.playerTank.stop = Trueelif event.key == pygame.K_a:MainGame.playerTank.stop = Trueelif event.key == pygame.K_d:MainGame.playerTank.stop = Truedef drawPlayerBullet(self, playerBulletList):# 遍历整个子弹列表,如果是没有被销毁的状态,就把子弹显示出来,否则从列表中删除for bullet in playerBulletList:if not bullet.isDestroy:bullet.draw(MainGame.window)bullet.move()else:playerBulletList.remove(bullet)def drawEnemyTank(self):# 如果当前坦克为0,那么就该重新生成坦克if len(MainGame.enemyTankList) == 0:# 一次性产生三个,如果剩余坦克数量超过三,那只能产生三个n = min(3, MainGame.enemyTankTotalCount)# 如果最小是0,就说明敌人坦克没有了,那么就赢了if n == 0:print('赢了')return# 没有赢的话,就产生n个坦克self.initEnemyTank(n)# 总个数减去产生的个数MainGame.enemyTankTotalCount -= n# 遍历坦克列表,展示坦克并且移动for tank in MainGame.enemyTankList:# 坦克还有生命值if tank.life > 0:tank.draw(MainGame.window)tank.move()bullet = tank.shot()if bullet is not None:MainGame.enemyTankBulletList.append(bullet)# 坦克生命值为0,就从列表中剔除else:MainGame.enemyTankCurrentCount -= 1MainGame.enemyTankList.remove(tank)def initEnemyTank(self, number):y = 0position = [0, 425, 850]index = 0for i in range(number):x = position[index]enemyTank = EnemyTank(x, y)MainGame.enemyTankList.append(enemyTank)index += 1def drawEnemyBullet(self):for bullet in MainGame.enemyTankBulletList:if not bullet.isDestroy:bullet.draw(MainGame.window)bullet.move()else:bullet.source.bulletCount -= 1MainGame.enemyTankBulletList.remove(bullet)if __name__ == '__main__':MainGame().startGame()
运行一下,看看结果

哈哈,终于实现发射子弹啦
相关文章:
学习 Python 之 Pygame 开发坦克大战(二)
学习 Python 之 Pygame 开发坦克大战(二)坦克大战的需求开始编写坦克大战1. 搭建主类框架2. 获取窗口中的事件3. 创建基类4. 初始化我方坦克类5. 完善我方坦克的移动5. 完善我方坦克的显示6. 在主类中加入我方坦克并完成坦克移动7. 初始化子弹类8. 完善子…...
短视频时代是靠什么赚钱的,介绍常见的5种方式,简单明了
目前,短视频越来越火热,大家都知道做短视频可以赚钱,那么究竟是靠什么赚钱的,又有几个人知道呢?短视频创业有个人、有团队,怎么实现团队的生存和发展。 常见的几种变现方式有: 1、平台分成 各…...
关于CentOS维护的几条简单命令
1、检查/etc/passwd这个文件里面有没有异常用户名2、通过命令top查看是否有异常进程,按M键对进程进行排序3、通过命令netstat -lnpt,查看是否有异常端口号4、通过命令ll -a /proc/PID,查看异常进程执行文件所在位置5、通过命令kill -9 PID&am…...
PoW 、PoS , DPoS 算法
PoW 、PoS , DPoS 算法 在区块链领域,多采用 PoW 工作量证明算法、PoS 权益证明算法,以及 DPoS 代理权 益证明算法,以上三种是业界主流的共识算法,这些算法与经典分布式一致性算法不同的是 融入了经济学博弈的概念。 …...
SpringCloud(PS)远程调用--Feign
远程调用RestTemplate远程调用RestTemplate方式调用存在的问题Http客户端Feign实现步骤自定义配置Feign优化Feign性能优化——连接池配置最佳实践RestTemplate远程调用 Bean // LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();}Autowiredprivat…...
2023年全国最新二级建造师精选真题及答案1
百分百题库提供二级建造师考试试题、二建考试预测题、二级建造师考试真题、二建证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 11.当事人未依照法律、行政法规规定办理租赁合同登记备案手续的,租赁合同…...
HydroD 实用教程(四)水动力模型
目 录一、前言二、Hydro Properties2.1 Compartment Properties2.2 Rudder and Thruster2.3 Wind Properties三、Hydro Structure3.1 Load Cross Sections四、Loading Conditions4.1 Mass Model4.2 Second Order Surface Model4.3 Wadam Offbody Points4.4 Additional Matrices…...
vue项目第七天
项目中模块操做业务使用ajax(需要使用接口认证)修改封装的findData发送ajax请求管理员列表内部搜索业务复用之前的findData 方法即可实现整个查询业务。实现退出业务在下拉菜单上添加事件以及属性。用户退出登录,二次登录系统菜单可能不存在的…...
拂晓·微信机器人
前言 本项目是基于千寻微信框架进行的功能开发,采用SpringBoot青云客机器人进行开发。 千寻初衷是想开源一个框架的写法,并不是为了用来运营,因此功能不全,所以使用和适配前请查看是否与自己需求匹配。 因此本文主要通过千寻客…...
React:Hooks工作机制
Hooks规则 React Hooks的使用,有两个规则: Hooks只能在函数组件中使用;不能在条件、循环或者嵌套函数中使用hook。确保每一次渲染中都按照同样的顺序被调用,import React, {useState } from "react"; export default function PersonalInfoComponent() {const […...
基于深度神经网络的3D模型合成【Transformer vs. CNN】
本文介绍用于3D模型合成的transformer网络与深度卷积网络。 推荐:使用 NSDT场景设计器 快速搭建 3D场景。 1、概述 从单一视角合成 3D 数据是一种基本的人类视觉功能,这对计算机视觉算法来说极具挑战性,这是一个共识。 但在 3D 传感器&#…...
前端面试题整理之HMTL篇(一)
HTML面试题(一) 前言: 面试题及答案解析,大部分来自网络整理,我自己做了一些简化,如果想了解的更多,可以搜索一下,前端面试题宝典微信公众号或者查百度,另外如果出现错误…...
【论文速递】ICLR2018 - 用于小样本语义分割的条件网络
【论文速递】ICLR2018 - 用于小样本语义分割的条件网络 【论文原文】:CONDITIONAL NETWORKS FOR FEW-SHOT SEMANTIC SEGMENTATION(Workshop track - ICLR 2018) 【作者信息】:Kate Rakelly Evan Shelhamer Trevor Darrell Alexe…...
本地生成动漫风格 AI 绘画 图像|Stable Diffusion WebUI 的安装和局域网部署教程
Stable Diffusion WebUI 的安装和部署教程1. 简介2. 安装环境2.1 Windows2.2 Linux3. 运行4. 模型下载链接5. 局域网部署5.1 Windows5.2 Linux6. 其他资源1. 简介 先放一张WebUI的图片生成效果图,以给大家学习的动力 :) 怎么样,…...
用一行Python代码,为图片上水印版权!
今天一个朋友跟我吐槽:前段时间,我辛辛苦苦整理的一份XX攻略,分享给自己的一些朋友,结果今天看到有人堂而皇之地拿着这份攻略图片去引流,并声称是自己整理的,真是岂有此理!他自己总结吃一堑长一…...
java中的lambda表达式
java中的lambda表达式java中的lambda表达式语法参数的不同写法代码块的不同写法函数式接口运用方法引用object::instanceMethodClass::staticMethodClass::instanceMethod什么是lambda表达式? 带参数变量的表达式。 java中的lambda表达式 我对java中lambda表达式是这…...
0.1opencv库VS环境配置
opencv环境配置 感谢大家学习这门教程。本系列文章首发于公众号【周旋机器视觉】。 这个这门课程的第一篇文章,主要是opencv环境配置。 本教程的环境为 Visual Studio 2019CMake 3.22.3opencv 4.6.0windows 10 1、opencv的源码下载与安装 直接访问opencv官网&…...
第五十七章 树状数组(二)
第五十七章 树状数组(二)一、差分的缺陷二、树状数组与差分三、例题题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1提示样例 1 解释:数据规模与约定代码一、差分的缺陷 差分的作用是能够在O(1)的时间内给一段区间加上相同的数字&am…...
比特币的网络
比特币的网络 1. DNS-seed 在比特币网络中,初始节点发现一共有两种方式。 第一种叫做 DNS-seed,又称 DNS 种子节点,DNS 就是中心化域名查询服务,比特币的 社区维护者会维护一些域名。 比如 seed.bitcoin.sipa.be 这个域名就是由比特币的核心开发者 Sipa 维护的,如果我…...
ChatGPT的模型介绍及GO语言实现API
ChatGPT除了大家熟悉的GPT3之外,还有其他辅助模型,比如处理代码的以及有害信息过滤的系统。总的来说是下面三个组成:GPT-3:一组能够理解和生成自然语言的模型CodexLimited beta:一组可以理解和生成代码的模型ÿ…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
