学习 Python 之 Pygame 开发坦克大战(二)
- 坦克大战的需求
- 开始编写坦克大战
- 1. 搭建主类框架
- 2. 获取窗口中的事件
- 3. 创建基类
- 4. 初始化我方坦克类
- 5. 完善我方坦克的移动
- 5. 完善我方坦克的显示
- 6. 在主类中加入我方坦克并完成坦克移动
- 7. 初始化子弹类
- 8. 完善子弹的移动
坦克大战的需求
类名 | 包含的操作 | 包含的属性 |
---|---|---|
敌方坦克类 | 射击,移动,显示 | 生命,速度,伤害,方向,类型 |
我方坦克类 | 射击,移动,显示 | 生命,速度,伤害,方向,装甲,等级 |
子弹类 | 移动,显示 | 方向,伤害,发射源,速度 |
墙壁类、草类、石砖类、河类 | 显示 | 是否可以摧毁 |
音效类 | 播放,停止,设置音乐 | - |
爆炸效果类 | 显示 | 是否可以摧毁 |
主类 | … | … |
运行窗口
素材链接:百度网盘
链接:https://pan.baidu.com/s/19sCyH7rp37f6DzRj0iXDCA?pwd=tkdz
提取码:tkdz
开始编写坦克大战
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()
运行结果
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类
pygame.sprite类可以用来检测物体碰撞
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.isResurrecting = False
# 碰撞前的坐标
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:
self.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:
self.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)