学习 Python 之 Pygame 开发坦克大战(三)
坦克大战物体碰撞检测编写
我的素材放到了百度网盘里,里面还有原版坦克大战素材,我都放在一起来,我的素材是从原版改的,各位小伙伴可以直接用或者自己改一下再用,做出适合自己的素材
素材链接:百度网盘
链接:https://pan.baidu.com/s/19sCyH7rp37f6DzRj0iXDCA?pwd=tkdz
提取码:tkdz
那我们就继续编写坦克大战吧
1. 实现敌方坦克与我方坦克间的碰撞
由于已经完成了屏幕中出现敌方和我方坦克,现在的问题就是如果我方坦克碰到了敌方坦克,应该是走不动的,即不能穿模
在敌方坦克类和我方坦克类中加入两个属性,用来记录上一次的位置,如果是你用的是继承,那么在父类加入
# 碰撞前的坐标
self.prvX = self.rect.left
self.prvY = self.rect.top
修改我方坦克类move()函数
def move(self):
if self.accumulation >= 1:
self.accumulation = 0
# 记录上一次的位置
self.prvX = self.rect.left
self.prvY = self.rect.top
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
修改敌方坦克类move()函数
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
# 记录上一次的位置
self.prvX = self.rect.left
self.prvY = self.rect.top
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
加入碰撞检测函数
在两个类中都加入
pygame.sprite.collide_rect()
函数用来检测物体碰撞,该物体必须继承pygame.sprite.Sprite类
def collideEnemyTank(self, enemyTankList):
# 遍历全部敌方坦克,检查有没有碰撞
for enemyTank in enemyTankList:
if pygame.sprite.collide_rect(self, enemyTank):
# 如果碰撞了,就保持原来的位置
self.rect.left = self.prvX
self.rect.top = self.prvY
def collidePlayerTank(self, playerTank):
# 遍历全部敌方坦克,检查有没有碰撞
if pygame.sprite.collide_rect(self, playerTank):
# 如果碰撞了,就保持原来的位置
self.rect.left = self.prvX
self.rect.top = self.prvY
在坦克移动下面调用该函数
while 1:
# 设置背景颜色
MainGame.window.fill(BACKGROUND_COLOR)
# 获取窗口事件
self.getPlayingModeEvent()
# 显示我方坦克
MainGame.playerTank.draw(MainGame.window)
# 我方坦克移动
if not MainGame.playerTank.stop:
MainGame.playerTank.move()
MainGame.playerTank.collideEnemyTank(MainGame.enemyTankList)
# 显示我方坦克子弹
self.drawPlayerBullet(MainGame.playerBulletList)
# 展示敌方坦克
self.drawEnemyTank()
# 展示敌方坦克子弹
self.drawEnemyBullet()
# 更新窗口
pygame.display.update()
# 遍历坦克列表,展示坦克并且移动
for tank in MainGame.enemyTankList:
# 坦克还有生命值
if tank.life > 0:
tank.draw(MainGame.window)
tank.move()
tank.collidePlayerTank(MainGame.playerTank)
bullet = tank.shot()
if bullet is not None:
MainGame.enemyTankBulletList.append(bullet)
# 坦克生命值为0,就从列表中剔除
else:
MainGame.enemyTankCurrentCount -= 1
MainGame.enemyTankList.remove(tank)
完整的我方坦克代码
import pygame as pg
import pygame.image
from ParentObject import ParentObject
from Teach.Bullet import Bullet
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
# 记录上一次的位置
self.prvX = self.rect.left
self.prvY = self.rect.top
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):
return Bullet(self)
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)
def collideEnemyTank(self, enemyTankList):
# 遍历全部敌方坦克,检查有没有碰撞
for enemyTank in enemyTankList:
if pygame.sprite.collide_rect(self, enemyTank):
# 如果碰撞了,就保持原来的位置
self.rect.left = self.prvX
self.rect.top = self.prvY
完整的敌方坦克代码
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.prvX = self.rect.left
self.prvY = self.rect.top
# 速度
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 loseLife(self, value = 1):
self.life -= value
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
# 记录上一次的位置
self.prvX = self.rect.left
self.prvY = self.rect.top
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]
def collidePlayerTank(self, playerTank):
# 遍历全部敌方坦克,检查有没有碰撞
if pygame.sprite.collide_rect(self, playerTank):
# 如果碰撞了,就保持原来的位置
self.rect.left = self.prvX
self.rect.top = self.prvY
完整的主类代码
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()
# 展示敌方坦克
self.drawEnemyTank()
# 显示我方坦克
MainGame.playerTank.draw(MainGame.window)
# 我方坦克移动
if not MainGame.playerTank.stop:
MainGame.playerTank.move()
MainGame.playerTank.collideEnemyTank(MainGame.enemyTankList)
# 显示我方坦克子弹
self.drawPlayerBullet(MainGame.playerBulletList)
# 展示敌方坦克子弹
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()
tank.collidePlayerTank(MainGame.playerTank)
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()
运行一下,看看结果
2. 实现敌方坦克间的碰撞
敌方坦克与敌方坦克也是不能穿模的,需要实现一下
这个的代码跟上面的差不多
在敌方坦克类中添加敌方坦克间碰撞检测函数
def collideEnemyTank(self, enemyTankList):
for tank in enemyTankList:
if pygame.sprite.collide_rect(self, tank) and tank != self:
self.rect.left = self.prvX
self.rect.top = self.prvY
在主类中的drawEnemyTank()函数中调用
# 遍历坦克列表,展示坦克并且移动
for tank in MainGame.enemyTankList:
# 坦克还有生命值
if tank.life > 0:
tank.draw(MainGame.window)
tank.move()
tank.collidePlayerTank(MainGame.playerTank)
tank.collideEnemyTank(MainGame.enemyTankList)
bullet = tank.shot()
if bullet is not None:
MainGame.enemyTankBulletList.append(bullet)
# 坦克生命值为0,就从列表中剔除
else:
MainGame.enemyTankCurrentCount -= 1
MainGame.enemyTankList.remove(tank)
完整的drawEnemyTank()函数
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()
tank.collidePlayerTank(MainGame.playerTank)
tank.collideEnemyTank(MainGame.enemyTankList)
bullet = tank.shot()
if bullet is not None:
MainGame.enemyTankBulletList.append(bullet)
# 坦克生命值为0,就从列表中剔除
else:
MainGame.enemyTankCurrentCount -= 1
MainGame.enemyTankList.remove(tank)
运行一下,看看结果
发现实现啦
3. 实现玩家子弹与敌方坦克间的碰撞
在两个坦克类中增加生命值减少函数,默认情况下,每次减少一点生命值
def loseLife(self, value = 1):
self.life -= value
在子弹类中增加碰撞检测函数
def playerBulletCollideEnemyTank(self, enemyTankList):
# 循环遍历坦克列表,检查是否发生了碰撞
for tank in enemyTankList:
if pygame.sprite.collide_rect(tank, self):
# 坦克被击中就扣减生命值
tank.loseLife(self.damage)
# 把子弹设置为销毁状态
self.isDestroy = True
在drawPlayerBullet()函数中调用
def drawPlayerBullet(self, playerBulletList):
# 遍历整个子弹列表,如果是没有被销毁的状态,就把子弹显示出来,否则从列表中删除
for bullet in playerBulletList:
if not bullet.isDestroy:
bullet.draw(MainGame.window)
bullet.move()
bullet.playerBulletCollideEnemyTank(MainGame.enemyTankList)
else:
playerBulletList.remove(bullet)
运行一下,我们就可以看到击中敌方坦克时,敌方坦克会变色,因为生命值发生了变化
4. 实现敌方子弹与我方坦克间的碰撞 和 玩家复活
首先我们要考虑到,当玩家生命值不为0时,被击中了需要在出生的复活,此时需要给玩家坦克类添加一个属性,用来判断此时玩家是否要复活
# 重生
self.isResurrecting = False
在子弹类中增加碰撞检测函数
def enemyBulletCollidePlayerTank(self, playerTank):
# 玩家坦克生命值为0,不用检测
if playerTank.life <= 0:
return
# 检测是否发生碰撞
if pygame.sprite.collide_rect(playerTank, self):
# 发生碰撞先减少护甲,护甲为0时扣减生命值
if playerTank.armor > 0:
playerTank.armor -= self.damage
playerTank.armor = max(0, playerTank.armor)
else:
playerTank.loseLife(self.damage)
playerTank.life = max(0, playerTank.life)
if playerTank.life != 0:
playerTank.isResurrecting = True
# 让子弹销毁
self.isDestroy = True
在drawEnemyBullet()函数中调用
def drawEnemyBullet(self):
for bullet in MainGame.enemyTankBulletList:
if not bullet.isDestroy:
bullet.draw(MainGame.window)
bullet.move()
bullet.enemyBulletCollidePlayerTank(MainGame.playerTank)
else:
bullet.source.bulletCount -= 1
MainGame.enemyTankBulletList.remove(bullet)
修改我方坦克类的draw()函数
def draw(self, window, x, y):
# 坦克生命中为0,表示已经死亡,不再展示坦克
if self.life <= 0:
return
# 判断坦克是否复合
if self.isResurrecting:
# 把坦克位置设置为指定的重生位置
self.rect.left = x
self.rect.top = y
self.isResurrecting = False
self.direction = 'UP'
# 获取展示的对象
self.image = self.images[max(self.armor - 1, 0)][self.direction]
# 画出图片
window.blit(self.image, self.rect)
在while循环中修改draw()函数参数
# 显示我方坦克
MainGame.playerTank.draw(MainGame.window, PLAYER_TANK_POSITION[0], PLAYER_TANK_POSITION[1])
测试一下,看看能不能实现玩家死亡
5. 爆炸效果类
现在敌方坦克和我方坦克都可以被消灭了,但是消灭的效果不是很好,下面增加一个爆炸效果类,更好地看到坦克被消灭
import pygame.image
class Explode:
def __init__(self, object, size):
# 获取爆炸对象的位置
self.rect = object.rect
self.images = []
self.images.append([
pygame.image.load('../Image/Explode/Explode50x50.png'),
pygame.image.load('../Image/Explode/Explode50x50.png'),
pygame.image.load('../Image/Explode/Explode50x50.png'),
pygame.image.load('../Image/Explode/Explode50x50.png'),
pygame.image.load('../Image/Explode/Explode50x50.png')
])
self.images.append([
pygame.image.load('../Image/Explode/Explode25x25.png'),
pygame.image.load('../Image/Explode/Explode25x25.png'),
pygame.image.load('../Image/Explode/Explode25x25.png'),
pygame.image.load('../Image/Explode/Explode25x25.png'),
pygame.image.load('../Image/Explode/Explode25x25.png')
])
self.mode = 0
if size == 25:
self.mode = 1
self.index = 0
self.image = self.images[self.mode][self.index]
self.isDestroy = False
def draw(self, window):
pass
这里我分为了两种爆炸效果,它们的烦恼不同,即爆炸效果的图片大小不一样,为了让爆炸效果能看到,而不是一闪而过,我让图片加载五次
修改draw()函数
def draw(self, window):
# 根据索引获取爆炸对象, 添加到主窗口
# 让图像加载五次,这里可以换成五张大小不一样的爆炸图片,可以实现让爆炸效果从小变大的效果
if self.index < len(self.images):
self.image = self.images[self.mode][self.index]
self.index += 1
window.blit(self.image, self.rect)
else:
self.isDestroy = True
self.index = 0
在主类中添加爆炸列表
class MainGame:
...
# 爆炸列表
explodeList = []
...
在主类中增加显示爆炸效果函数
def drawExplode(self):
for e in MainGame.explodeList:
if e.isDestroy:
MainGame.explodeList.remove(e)
else:
e.draw(MainGame.window)
在while循环中调用drawExplode()
while 1:
# 设置背景颜色
MainGame.window.fill(BACKGROUND_COLOR)
# 获取窗口事件
self.getPlayingModeEvent()
# 展示敌方坦克
self.drawEnemyTank()
# 显示我方坦克
MainGame.playerTank.draw(MainGame.window, PLAYER_TANK_POSITION[0], PLAYER_TANK_POSITION[1])
# 我方坦克移动
if not MainGame.playerTank.stop:
MainGame.playerTank.move()
MainGame.playerTank.collideEnemyTank(MainGame.enemyTankList)
# 显示我方坦克子弹
self.drawPlayerBullet(MainGame.playerBulletList)
# 展示敌方坦克子弹
self.drawEnemyBullet()
# 展示爆炸效果
self.drawExplode()
# 更新窗口
pygame.display.update()
6. 为子弹爆炸添加爆炸效果
子弹爆炸添加爆炸效果
修改子弹类的move()函数
def move(self, explodeList):
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(explodeList)
else:
self.accumulation += 0.20
def checkBullet(self, explodeList):
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:
explode = Explode(self, 25)
explodeList.append(explode)
self.isDestroy = True
此时如果子弹击中边界,就会出现爆炸效果
修改调用处
def drawPlayerBullet(self, playerBulletList):
# 遍历整个子弹列表,如果是没有被销毁的状态,就把子弹显示出来,否则从列表中删除
for bullet in playerBulletList:
if not bullet.isDestroy:
bullet.draw(MainGame.window)
bullet.move(MainGame.explodeList)
bullet.playerBulletCollideEnemyTank(MainGame.enemyTankList)
else:
playerBulletList.remove(bullet)
def drawEnemyBullet(self):
for bullet in MainGame.enemyTankBulletList:
if not bullet.isDestroy:
bullet.draw(MainGame.window)
bullet.move(MainGame.explodeList)
bullet.enemyBulletCollidePlayerTank(MainGame.playerTank)
else:
bullet.source.bulletCount -= 1
MainGame.enemyTankBulletList.remove(bullet)
试一试效果,看看子弹出界有没有爆炸效果
7. 为坦克爆炸添加爆炸效果
修改 子弹类中 playerBulletCollideEnemyTank() 函数和 enemyBulletCollidePlayerTank() 函数
def playerBulletCollideEnemyTank(self, enemyTankList, explodeList):
# 循环遍历坦克列表,检查是否发生了碰撞
for tank in enemyTankList:
if pygame.sprite.collide_rect(tank, self):
tank.loseLife(self.damage)
# 把子弹设置为销毁状态
self.isDestroy = True
if tank.life == 0:
# 增加爆炸效果
explode = Explode(tank, 50)
explodeList.append(explode)
def enemyBulletCollidePlayerTank(self, playerTank, explodeList):
# 玩家坦克生命值为0,不用检测
if playerTank.life <= 0:
return
# 检测是否发生碰撞
if pygame.sprite.collide_rect(playerTank, self):
# 发生碰撞先减少护甲,护甲为0时扣减生命值
if playerTank.armor > 0:
playerTank.armor -= self.damage
playerTank.armor = max(0, playerTank.armor)
else:
playerTank.loseLife(self.damage)
# 增加爆炸效果
explode = Explode(playerTank, 50)
explodeList.append(explode)
playerTank.life = max(0, playerTank.life)
if playerTank.life != 0:
playerTank.isResurrecting = True
# 让子弹销毁
self.isDestroy = True
修改调用处
def drawPlayerBullet(self, playerBulletList):
# 遍历整个子弹列表,如果是没有被销毁的状态,就把子弹显示出来,否则从列表中删除
for bullet in playerBulletList:
if not bullet.isDestroy:
bullet.draw(MainGame.window)
bullet.move(MainGame.explodeList)
bullet.playerBulletCollideEnemyTank(MainGame.enemyTankList, MainGame.explodeList)
else:
playerBulletList.remove(bullet)
def drawEnemyBullet(self):
for bullet in MainGame.enemyTankBulletList:
if not bullet.isDestroy:
bullet.draw(MainGame.window)
bullet.move(MainGame.explodeList)
bullet.enemyBulletCollidePlayerTank(MainGame.playerTank, MainGame.explodeList)
else:
bullet.source.bulletCount -= 1
MainGame.enemyTankBulletList.remove(bullet)
完整的敌方坦克类代码
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.prvX = self.rect.left
self.prvY = self.rect.top
# 速度
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 = False
# 开火开关
self.fire = False
# 步数
self.step = 30
# 伤害
self.damage = damage
# 子弹个数
self.bulletCount = 0
self.maxBulletCount = maxBulletCount
def loseLife(self, value = 1):
self.life -= value
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
# 记录上一次的位置
self.prvX = self.rect.left
self.prvY = self.rect.top
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]
def collidePlayerTank(self, playerTank):
# 遍历全部敌方坦克,检查有没有碰撞
if pygame.sprite.collide_rect(self, playerTank):
# 如果碰撞了,就保持原来的位置
self.rect.left = self.prvX
self.rect.top = self.prvY
def collideEnemyTank(self, enemyTankList):
for tank in enemyTankList:
if pygame.sprite.collide_rect(self, tank) and tank != self:
self.rect.left = self.prvX
self.rect.top = self.prvY
完整的我方坦克类代码
import pygame
import pygame.image
from ParentObject import ParentObject
from Bullet import Bullet
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: pygame.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
# 记录上一次的位置
self.prvX = self.rect.left
self.prvY = self.rect.top
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):
return Bullet(self)
def draw(self, window, x, y):
# 坦克生命中为0,表示已经死亡,不再展示坦克
if self.life <= 0:
return
# 判断坦克是否复合
if self.isResurrecting:
# 把坦克位置设置为指定的重生位置
self.rect.left = x
self.rect.top = y
self.isResurrecting = False
self.direction = 'UP'
# 获取展示的对象
self.image = self.images[max(self.armor - 1, 0)][self.direction]
# 画出图片
window.blit(self.image, self.rect)
def collideEnemyTank(self, enemyTankList):
# 遍历全部敌方坦克,检查有没有碰撞
for enemyTank in enemyTankList:
if pygame.sprite.collide_rect(self, enemyTank):
# 如果碰撞了,就保持原来的位置
self.rect.left = self.prvX
self.rect.top = self.prvY
def loseLife(self, value = 1):
self.life -= value
完整的主类代码
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 = []
# 爆炸列表
explodeList = []
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()
# 展示敌方坦克
self.drawEnemyTank()
# 显示我方坦克
MainGame.playerTank.draw(MainGame.window, PLAYER_TANK_POSITION[0], PLAYER_TANK_POSITION[1])
# 我方坦克移动
if not MainGame.playerTank.stop:
MainGame.playerTank.move()
MainGame.playerTank.collideEnemyTank(MainGame.enemyTankList)
# 显示我方坦克子弹
self.drawPlayerBullet(MainGame.playerBulletList)
# 展示敌方坦克子弹
self.drawEnemyBullet()
# 展示爆炸效果
self.drawExplode()
# 更新窗口
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(MainGame.explodeList)
bullet.playerBulletCollideEnemyTank(MainGame.enemyTankList, MainGame.explodeList)
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()
tank.collidePlayerTank(MainGame.playerTank)
tank.collideEnemyTank(MainGame.enemyTankList)
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(MainGame.explodeList)
bullet.enemyBulletCollidePlayerTank(MainGame.playerTank, MainGame.explodeList)
else:
bullet.source.bulletCount -= 1
MainGame.enemyTankBulletList.remove(bullet)
def drawExplode(self):
for e in MainGame.explodeList:
if e.isDestroy:
MainGame.explodeList.remove(e)
else:
e.draw(MainGame.window)
if __name__ == '__main__':
MainGame().startGame()
完整的子弹类代码
import pygame
from ParentObject import ParentObject
from Explode import Explode
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):
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(explodeList)
else:
self.accumulation += 0.20
def checkBullet(self, explodeList):
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:
explode = Explode(self, 25)
explodeList.append(explode)
self.isDestroy = True
def draw(self, window):
window.blit(self.image, self.rect)
def playerBulletCollideEnemyTank(self, enemyTankList, explodeList):
# 循环遍历坦克列表,检查是否发生了碰撞
for tank in enemyTankList:
if pygame.sprite.collide_rect(tank, self):
tank.loseLife(self.damage)
# 把子弹设置为销毁状态
self.isDestroy = True
if tank.life == 0:
# 增加爆炸效果
explode = Explode(tank, 50)
explodeList.append(explode)
def enemyBulletCollidePlayerTank(self, playerTank, explodeList):
# 玩家坦克生命值为0,不用检测
if playerTank.life <= 0:
return
# 检测是否发生碰撞
if pygame.sprite.collide_rect(playerTank, self):
# 发生碰撞先减少护甲,护甲为0时扣减生命值
if playerTank.armor > 0:
playerTank.armor -= self.damage
playerTank.armor = max(0, playerTank.armor)
else:
playerTank.loseLife(self.damage)
# 增加爆炸效果
explode = Explode(playerTank, 50)
explodeList.append(explode)
playerTank.life = max(0, playerTank.life)
if playerTank.life != 0:
playerTank.isResurrecting = True
# 让子弹销毁
self.isDestroy = True
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/122780.html