坦克大战的需求
坦克大战游戏包含很多个物体,现在要对这些物体进行总结
类名 | 包含的操作 | 包含的属性 |
---|---|---|
敌方坦克类 | 射击,移动,显示 | 生命,速度,伤害,方向,类型 |
我方坦克类 | 射击,移动,显示 | 生命,速度,伤害,方向,装甲,等级 |
子弹类 | 移动,显示 | 方向,伤害,发射源,速度 |
墙壁类、草类、石砖类、河类 | 显示 | 是否可以摧毁 |
音效类 | 播放,停止,设置音乐 | - |
爆炸效果类 | 显示 | 是否可以摧毁 |
主类 | … | … |
物体总结完毕后,规划一下窗口的大小,下面是我设置的窗口大小
素材链接:百度网盘
链接:https://pan.baidu.com/s/19sCyH7rp37f6DzRj0iXDCA?pwd=tkdz
提取码:tkdz
坦克大战代码链接:百度网盘
链接:https://pan.baidu.com/s/1qFV-0hi0cgZS1Hnvx6mK6Q?pwd=90tk
提取码:90tk
开始编写坦克大战
一切都准备就绪啦,现在开始编写坦克大战的代码吧
1. 搭建主类框架
主类是整个游戏运作的类,当然你也可以不用使用类,直接创建一个函数也可以,这里使用了面向对象的思想
import pygame
SCREEN_WIDTH = 1100
SCREEN_HEIGHT = 600
BACKGROUND_COLOR = pygame.Color(0, 0, 0)
FONT_COLOR = (255, 255, 255)
class MainGame:
# 窗口Surface对象
window = None
def __init__(self):
pass
def 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 sys
SCREEN_WIDTH = 1100
SCREEN_HEIGHT = 600
BACKGROUND_COLOR = pygame.Color(0, 0, 0)
FONT_COLOR = (255, 255, 255)
class MainGame:
# 窗口Surface对象
window = None
def __init__(self):
pass
def 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.sprite
class ParentObject(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
4. 初始化我方坦克类
创建我方坦克类,并且继承基类
import pygame as pg
import pygame.image
from ParentObject import ParentObject
class 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 = x
self.rect.top = y
# 速度
self.accumulation: float = 0
self.speed = 2
# 移动开关
self.stop = True
# 等级
self.level = 1
# 伤害
self.damage = 1
def move(self):
pass
def shot(self):
pass
def draw(self):
pass
5. 完善我方坦克的移动
accumulation 可以更细的控制坦克的移动速度
当 accumulation 累加到 1 时,坦克移动一次,如果不设置这个属性,即使是速度每次 +1,坦克移动的也速度很快,所以增加这个属性,可以减慢坦克的移动速度
def move(self):
if self.accumulation >= 1:
self.accumulation = 0
if self.direction == 'LEFT':
if self.rect.left > 0:
self.rect.left -= self.speed
elif self.direction == 'UP':
if self.rect.top > 0:
self.rect.top -= self.speed
elif self.direction == 'DOWN':
if self.rect.top < 555:
self.rect.top += self.speed
elif self.direction == 'RIGHT':
if self.rect.left < 855:
self.rect.left += self.speed
else:
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 ParentObject
class 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 = x
self.rect.top = y
# 速度
self.accumulation: float = 0
self.speed = 2
# 移动开关
self.stop = True
# 碰撞前的坐标
self.prvX = self.rect.left
self.prvY = self.rect.top
# 等级
self.level = 1
# 伤害
self.damage = 1
def move(self):
if self.accumulation >= 1:
self.accumulation = 0
if self.direction == 'LEFT':
if self.rect.left > 0:
self.rect.left -= self.speed
elif self.direction == 'UP':
if self.rect.top > 0:
self.rect.top -= self.speed
elif self.direction == 'DOWN':
if self.rect.top < 555:
self.rect.top += self.speed
elif self.direction == 'RIGHT':
if self.rect.left < 855:
self.rect.left += self.speed
else:
self.accumulation += 0.20
def shot(self):
pass
def 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 sys
from PlayerTank import PlayerTank
SCREEN_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
def __init__(self):
pass
def 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 = False
elif event.key == pygame.K_s:
MainGame.playerTank.direction = 'DOWN'
MainGame.playerTank.stop = False
elif event.key == pygame.K_a:
MainGame.playerTank.direction = 'LEFT'
MainGame.playerTank.stop = False
elif event.key == pygame.K_d:
MainGame.playerTank.direction = 'RIGHT'
MainGame.playerTank.stop = False
elif event.key == pygame.K_j:
print('j按下')
if event.type == pygame.KEYUP:
print('键盘按键抬起')
if event.key == pygame.K_w:
MainGame.playerTank.stop = True
elif event.key == pygame.K_s:
MainGame.playerTank.stop = True
elif event.key == pygame.K_a:
MainGame.playerTank.stop = True
elif event.key == pygame.K_d:
MainGame.playerTank.stop = True
if __name__ == '__main__':
MainGame().startGame()
运行结果
7. 初始化子弹类
现在已经实现了坦克的移动啦,下面就要实现坦克的发射子弹
实际上,子弹也是一幅图片,当我们按下开火按键后,就在坦克的正前方画出子弹,随着时间的流逝,要让子弹按照当前方向一直运动下去
import pygame
from ParentObject import ParentObject
class 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.direction
self.image: pygame.Surface = self.images[self.direction]
self.rect = self.image.get_rect()
# 坦克发射子弹的位置
if self.direction == 'UP':
self.rect.left = tank.rect.left + 17.5
self.rect.top = tank.rect.top - 25
elif self.direction == 'DOWN':
self.rect.left = tank.rect.left + 17.5
self.rect.top = tank.rect.top + 25
elif self.direction == 'LEFT':
self.rect.left = tank.rect.left - 25
self.rect.top = tank.rect.top + 17.5
elif self.direction == 'RIGHT':
self.rect.left = tank.rect.left + 25
self.rect.top = tank.rect.top + 17.5
# 速度
self.accumulationMax: float = 0
self.accumulation = 0.25
self.speed = 10
# 销毁开关
self.isDestroy = False
# 发射源
self.source = tank
# 伤害
self.damage = tank.damage
def move(self, explodeList):
pass
def draw(self, window):
window.blit(self.image, self.rect)
下面是确定子弹的位置:
子弹图片是25x10,坦克发射子弹是在坦克中间位置发射
8. 完善子弹的移动
子弹的初始位置确定后,一旦创建出来,就要一直移动下去,实际上就是刷新屏幕,然后把原来位置上的子弹图片去掉,在新的位置上再画出子弹图片,坦克移动的原理也是这样
修改move函数,添加检查子弹出界函数
def move(self):
if self.accumulation >= 1:
self.accumulation = 0
if self.direction == 'LEFT':
self.rect.left -= self.speed
elif self.direction == 'UP':
self.rect.top -= self.speed
elif self.direction == 'DOWN':
self.rect.top += self.speed
elif self.direction == 'RIGHT':
self.rect.left += self.speed
# 检查子弹是否出界
self.checkBullet()
else:
self.accumulation += 0.20
def checkBullet(self):
toDestroy = False
# 如果出界,就设置为销毁
if self.rect.top < 0 or self.rect.top > 600:
toDestroy = True
if self.rect.left < 0 or self.rect.right > 900:
toDestroy = True
if toDestroy:
self.isDestroy = True
子弹类完整代码
import pygame
from ParentObject import ParentObject
class 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.direction
self.image: pygame.Surface = self.images[self.direction]
self.rect = self.image.get_rect()
# 坦克发射子弹的位置
if self.direction == 'UP':
self.rect.left = tank.rect.left + 17.5
self.rect.top = tank.rect.top - 25
elif self.direction == 'DOWN':
self.rect.left = tank.rect.left + 17.5
self.rect.top = tank.rect.top + 25
elif self.direction == 'LEFT':
self.rect.left = tank.rect.left - 25
self.rect.top = tank.rect.top + 17.5
elif self.direction == 'RIGHT':
self.rect.left = tank.rect.left + 25
self.rect.top = tank.rect.top + 17.5
# 速度
self.accumulationMax: float = 0
self.accumulation = 0.25
self.speed = 10
# 销毁开关
self.isDestroy = False
# 发射源
self.source = tank
# 伤害
self.damage = tank.damage
def move(self):
if self.accumulation >= 1:
self.accumulation = 0
if self.direction == 'LEFT':
self.rect.left -= self.speed
elif self.direction == 'UP':
self.rect.top -= self.speed
elif self.direction == 'DOWN':
self.rect.top += self.speed
elif self.direction == 'RIGHT':
self.rect.left += self.speed
# 检查子弹是否出界
self.checkBullet()
else:
self.accumulation += 0.20
def checkBullet(self):
toDestroy = False
# 如果出界,就设置为销毁
if self.rect.top < 0 or self.rect.top > 600:
toDestroy = True
if self.rect.left < 0 or self.rect.right > 900:
toDestroy = True
if toDestroy:
self.isDestroy = True
def draw(self, window):
window.blit(self.image, self.rect)
9. 完善坦克开火
有了子弹类,就可以让坦克发射子弹了
在主类中设置玩家子弹数量的最大值
... ...
class MainGame:
# 窗口Surface对象
window = None
# 指定的子弹个数,这里设置玩家的子弹最多只有3颗子弹可以同时在场
playerBulletNumber = 3
# 玩家坦克
playerTank = None
... ...
修改坦克的 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 = False
elif event.key == pygame.K_s:
MainGame.playerTank.direction = 'DOWN'
MainGame.playerTank.stop = False
elif event.key == pygame.K_a:
MainGame.playerTank.direction = 'LEFT'
MainGame.playerTank.stop = False
elif event.key == pygame.K_d:
MainGame.playerTank.direction = 'RIGHT'
MainGame.playerTank.stop = False
elif 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 = True
elif event.key == pygame.K_s:
MainGame.playerTank.stop = True
elif event.key == pygame.K_a:
MainGame.playerTank.stop = True
elif 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()
运行游戏看看结果
当我们按下开火键时,子弹就发射了
注意:MainGame.playerBulletList 需要定义出来,否则程序运行会报错
添加位置如下:
... ...
class MainGame:
# 窗口Surface对象
window = None
playerBulletList = []
playerBulletNumber = 3
# 玩家坦克
playerTank = None
... ...
10. 实现敌方坦克类
现在我的坦克可以移动和开火,那么就要有点靶子给我们练习了,是时候实现敌方坦克类了
敌方坦克类的大部分代码跟我方坦克类一样,这里可以用继承,即父类是坦克类,实现两个类的一样的代码,然后我方坦克类和敌方坦克类继承这个类,不过我没有使用继承
import random
import pygame
import pygame.image
from ParentObject import ParentObject
from Bullet import Bullet
class 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 = x
self.rect.top = y
# 速度
self.accumulationMax: float = 0
self.accumulation = 0.1
speed = 0
maxBulletCount = 0
damage = 1
if self.type[0] == 1:
speed = 3
self.level = 1
maxBulletCount = 1
elif self.type[0] == 2:
speed = 5
self.level = 2
maxBulletCount = 1
damage = 3
elif self.type[0] == 3:
speed = 7
self.level = 1
maxBulletCount = 3
damage = 2
elif self.type[0] == 4:
speed = 6
self.level = 2
maxBulletCount = 3
damage = 1
self.speed = speed
# 移动开关
self.stop = True
# 开火开关
self.fire = True
# 步数
self.step = 30
# 伤害
self.damage = damage
# 子弹个数
self.bulletCount = 0
self.maxBulletCount = maxBulletCount
def move(self):
"""
新增步数变量, 当坦克移动时, 步数进行减少, 当步数小于等于0的时候, 修改地方坦克的方向
:return: None
"""
if self.stop:
if self.step <= 0:
self.direction = self.randDirection()
self.step = 30
else:
if self.accumulationMax >= 1:
self.accumulationMax = 0
if self.direction == 'LEFT':
if self.rect.left > 0:
self.rect.left -= self.speed
elif self.direction == 'UP':
if self.rect.top > 0:
self.rect.top -= self.speed
elif self.direction == 'DOWN':
if self.rect.top < 555:
self.rect.top += self.speed
elif self.direction == 'RIGHT':
if self.rect.left < 855:
self.rect.left += self.speed
self.step -= 1
else:
self.accumulationMax += self.accumulation
def shot(self):
if self.fire:
if self.bulletCount < self.maxBulletCount:
num = random.randint(0, 100)
if num == 5 or num == 6:
self.bulletCount += 1
return Bullet(self)
return None
def 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 = 3
self.level = 1
maxBulletCount = 1
elif self.type[0] == 2:
speed = 5
self.level = 2
maxBulletCount = 1
damage = 3
elif self.type[0] == 3:
speed = 7
self.level = 1
maxBulletCount = 3
damage = 2
elif self.type[0] == 4:
speed = 6
self.level = 2
maxBulletCount = 3
damage = 1
坦克的移动
step是步数,每次按照30次循环作为一次坦克的操作
if self.stop:
if self.step <= 0:
# 随机产生一个方向,接下来朝这个方向移动
self.direction = self.randDirection()
self.step = 30
else:
if self.accumulationMax >= 1:
self.accumulationMax = 0
if self.direction == 'LEFT':
if self.rect.left > 0:
self.rect.left -= self.speed
elif self.direction == 'UP':
if self.rect.top > 0:
self.rect.top -= self.speed
elif self.direction == 'DOWN':
if self.rect.top < 555:
self.rect.top += self.speed
elif self.direction == 'RIGHT':
if self.rect.left < 855:
self.rect.left += self.speed
self.step -= 1
else:
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 += 1
return Bullet(self)
return None
在上面的代码中,下面的变量用来设置敌方坦克是否移动,是否开火
# 移动开关
self.stop = True
# 开火开关
self.fire = True
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 -= 1
MainGame.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 -= 1
MainGame.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 sys
from PlayerTank import PlayerTank
from EnemyTank import EnemyTank
SCREEN_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):
pass
def 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 = False
elif event.key == pygame.K_s:
MainGame.playerTank.direction = 'DOWN'
MainGame.playerTank.stop = False
elif event.key == pygame.K_a:
MainGame.playerTank.direction = 'LEFT'
MainGame.playerTank.stop = False
elif event.key == pygame.K_d:
MainGame.playerTank.direction = 'RIGHT'
MainGame.playerTank.stop = False
elif 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 = True
elif event.key == pygame.K_s:
MainGame.playerTank.stop = True
elif event.key == pygame.K_a:
MainGame.playerTank.stop = True
elif 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)
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 -= 1
MainGame.enemyTankList.remove(tank)
def initEnemyTank(self, number):
y = 0
position = [0, 425, 850]
index = 0
for i in range(number):
x = position[index]
enemyTank = EnemyTank(x, y)
MainGame.enemyTankList.append(enemyTank)
index += 1
def drawEnemyBullet(self):
for bullet in MainGame.enemyTankBulletList:
if not bullet.isDestroy:
bullet.draw(MainGame.window)
bullet.move()
else:
bullet.source.bulletCount -= 1
MainGame.enemyTankBulletList.remove(bullet)
if __name__ == '__main__':
MainGame().startGame()
运行一下,看看结果
文章来源:https://www.toymoban.com/news/detail-785212.html
哈哈,终于实现发射子弹啦文章来源地址https://www.toymoban.com/news/detail-785212.html
到了这里,关于学习 Python 之 Pygame 开发坦克大战(二)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!