# coding:utf-8
'''
坦克与坦克碰撞检测
'''
import pygame
from pygame.sprite import collide_rect
from time import sleep
import random
pygame.init()
#设置通用属性
BG_COLOR = pygame.Color(112, 128, 144)
TEXT_COLOR = pygame.Color(255, 0, 0)
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 720
class Tank:
'''
坦克类
'''
def __init__(self) -> None:
self.live = True
#定义坦克原来位置
self.old_left = 0
self.old_top = 0
def display_tank(self) -> None:
'''
显示坦克
'''
try:
self.image = self.images.get(self.direction)
MainGame.window.blit(self.image, self.rect)
except BaseException as e:
print(e)
def move(self) -> None:
'''
坦克移动类
'''
#记录坦克出生的位置,为了方便还原碰撞后的位置
self.old_left = self.rect.left
self.old_top = self.rect.top
if self.direction == 'L':
#判断是否在左边界
if self.rect.left > 0:
#修改坦克的位置,离左边的距离-操作
self.rect.left = self.rect.left - self.speed
elif self.direction == 'R':
if self.rect.left + self.rect.width < SCREEN_WIDTH:
#修改坦克的位置,离右边的距离-操作
self.rect.left = self.rect.left + self.speed
elif self.direction == 'U':
if self.rect.top > 0:
#修改坦克的位置,离上边的距离-操作
self.rect.top = self.rect.top - self.speed
elif self.direction == 'D':
if self.rect.top + self.rect.height < SCREEN_HEIGHT:
#修改坦克的位置,离下边的距离-操作
self.rect.top = self.rect.top + self.speed
def shot(self) -> None:
'''
射击类
'''
pass
def tank_hit_wall(self) -> None:
#坦克不能穿墙
for wall in MainGame.wall_list:
#检测当前坦克是否与墙壁发生碰撞
if pygame.sprite.collide_rect(self, wall):
self.rect.left = self.old_left
self.rect.top = self.old_top
def tank_collide_tank(self, tank) ->None:
#判断是否都存活
if self.live and tank.live:
#检测坦克之前碰撞
if pygame.sprite.collide_rect(self, tank):
self.rect.left = self.old_left
self.rect.top = self.old_top
# def tank_collide_tank(self, tank) -> None:
# # 增加对tank实例和live属性的检查
# if isinstance(tank, Tank) and hasattr(tank, 'live'):
# if self.live and tank.live:
# if pygame.sprite.collide_rect(self, tank):
# self.rect.left = self.old_left
# self.rect.top = self.old_top
# else:
# print("Warning: One of the tanks is not alive.")
# else:
# print("Error: The provided object is not a valid Tank instance or lacks the 'live' attribute.")
class MyTank(Tank):
'''
我方坦克类
'''
def __init__(self, left:int, top:int) -> None:
super(MyTank, self).__init__()
#加载我方坦克图片
self.images = {
'U':pygame.image.load('./tanke/img/p1tankU.gif'),
'D':pygame.image.load('./tanke/img/p1tankD.gif'),
'L':pygame.image.load('./tanke/img/p1tankL.gif'),
'R':pygame.image.load('./tanke/img/p1tankR.gif')
}
#设置坦克方向
self.direction = 'U'
#获取图片信息
self.image = self.images.get(self.direction)
#获取图形矩形
self.rect = self.image.get_rect()
#涉资坦克出生位置
self.rect.left = left
self.rect.top = top
#设置坦克移动速度
self.speed = 20
#设置移动开关
self.remove = False
class EnemyTank(Tank):
'''
敌方坦克类
'''
def __init__(self, left, top, speed) -> None:
super(EnemyTank, self).__init__()
#加载敌方坦克图片
self.images = {
'U':pygame.image.load('./tanke/img/enemy1U.gif'),
'D':pygame.image.load('./tanke/img/enemy1D.gif'),
'L':pygame.image.load('./tanke/img/enemy1L.gif'),
'R':pygame.image.load('./tanke/img/enemy1R.gif')
}
#设置坦克方向
self.direction = self.rand_direction()
#读取图片信息
self.image = self.images.get(self.direction)
#读取图片矩形
self.rect = self.image.get_rect()
#设置敌方坦克位置
self.rect.left = left
self.rect.top = top
#设置移动速度,定义了难度
self.speed = speed
#self.stop = True
#设置移动的步长
self.step = 10
def rand_direction(self):
#生成敌方坦克随机方向
chice = random.randint(1, 4)
#生成随机方向
if chice == 1:
return 'U'
elif chice == 2:
return 'D'
elif chice == 3:
return 'L'
elif chice == 4:
return "R"
def rand_move(self) -> None:
#随机移动
# self.direction = self.rand_direction()
# self.move()
#判断步长是否为0,小于0,更换方向。
if self.step <= 0:
self.direction = self.rand_direction()
self.step = 10
else:
self.move()
self.step -= 1
def shot(self):
num = random.randint(1, 100)
if num < 7:
return Bullet(self)
class Bullet:
'''
子弹类
'''
def __init__(self, tank) -> None:
#加载子弹图片
self.image = pygame.image.load('./tanke/img/enemymissile.gif')
#获取子弹方向
self.direction = tank.direction
#获取子弹图形
self.rect = self.image.get_rect()
#设置子弹位置
if self.direction == "L":
#子弹的位置= 坦克的位置-子弹的宽度
self.rect.left = tank.rect.left - self.rect.width
#子弹的位置= 坦克的位置+坦克的高度/2 - 子弹的高度/2
self.rect.top = tank.rect.top + tank.rect.height/2 - self.rect.height/2
elif self.direction == "R":
#子弹的位置= 坦克的位置+坦克的宽度
self.rect.left = tank.rect.left + tank.rect.width
#子弹的位置= 坦克的位置+坦克的高度/2 - 子弹的高度/2
self.rect.top = tank.rect.top + tank.rect.height/2 - self.rect.height/2
elif self.direction == "U":
#子弹的位置 = 坦克的位置+坦克的宽度/2 - 子弹的宽度/2
self.rect.left = tank.rect.left + tank.rect.width/2 - self.rect.width/2
#子弹的位置 = 坦克的位置 - 子弹的高度
self.rect.top = tank.rect.top - self.rect.height
elif self.direction == "D":
#子弹的位置= 坦克的位置+子弹的宽度/2 - 子弹的宽度/2
self.rect.left = tank.rect.left + tank.rect.width/2 - self.rect.width/2
#子弹的位置= 坦克的位置+坦克的高度
self.rect.top = tank.rect.top + tank.rect.height
#设置子弹的速度
self.speed = 10
#设置子弹状态
self.live = True
def display_bullet(self) -> None:
'''
显示子弹
'''
MainGame.window.blit(self.image, self.rect)
def move(self) -> None:
'''
子弹移动,根据子弹生成的方向移动
'''
if self.direction == "L":
#判断子弹是否超出屏幕
if self.rect.left > 0:
self.rect.left -= self.speed
else:
self.live = False
elif self.direction == "R":
#判断子弹是否超出屏幕
# if self.rect.right + self.rect.width < SCREEN_WIDTH:
if self.rect.right + self.rect.width/2 < SCREEN_WIDTH:
self.rect.right += self.speed
else:
self.live = False
elif self.direction == "U":
#判断子弹是否超出屏幕
if self.rect.top > 0:
self.rect.top -= self.speed
else:
self.live = False
elif self.direction == "D":
#判断子弹是否超出屏幕
if self.rect.top + self.rect.height < SCREEN_HEIGHT:
self.rect.top += self.speed
else:
self.live = False
def hit_enemy_tank(self):
for e_tank in MainGame.enemy_tank_list:
#判断子弹是否击中坦克
if collide_rect(self, e_tank):
#爆炸效果
explode = Explode(e_tank)
MainGame.explode_list.append(explode)
#修改子弹状态
self.live = False
e_tank.live = False
def hit_my_tank(self):
#判断我方坦克是否活着
if MainGame.my_tank and MainGame.my_tank.live:
#添加我方坦克
MainGame.my_tank.display_tank()
#判断敌方子弹是否击中我方坦克,开挂之一
if collide_rect(self, MainGame.my_tank):
#爆炸效果
explode = Explode(MainGame.my_tank)
MainGame.explode_list.append(explode)
#修改子弹状态
self.live = False
MainGame.my_tank.live = False
def hit_wall(self) -> None:
#碰撞墙壁
for wall in MainGame.wall_list:
#判断是否碰撞
if collide_rect(self, wall):
self.live = False
#修改墙壁HP
wall.hp -= 1
#判断墙壁是否存活
if wall.hp <= 0:
wall.live = False
class Wall:
'''
墙壁类
'''
def __init__(self, left, top) -> None:
#加载图片
self.image = pygame.image.load('./tanke/img/steels.gif')
#获取墙壁图形
self.rect = self.image.get_rect()
#设置墙体位置
self.rect.left = left
self.rect.top = top
#设置墙壁HP
self.hp = 3
#设置状态
self.live = True
def display_wall(self) -> None:
'''
显示墙壁
'''
# print("主窗口添加墙壁")
MainGame.window.blit(self.image, self.rect)
class Explode:
'''
爆炸类
'''
def __init__(self, tank:Tank) -> None:
#加载爆炸效果图片
self.images = [
pygame.image.load('./tanke/img/blast0.gif'),
pygame.image.load('./tanke/img/blast1.gif'),
pygame.image.load('./tanke/img/blast2.gif'),
pygame.image.load('./tanke/img/blast3.gif'),
pygame.image.load('./tanke/img/blast4.gif'),
]
#设置爆炸效果的位置
self.rect = tank.rect
#设置爆炸的效果索引
self.step = 0
#获取需要渲染的图像
self.image = self.images[self.step]
#设置爆炸的状态
self.live = True
def display_explode(self) -> None:
'''
显示爆炸
'''
if self.step < len(self.images):
self.image = self.images[self.step]
self.step += 1
#绘制爆炸效果
MainGame.window.blit(self.image, self.rect)
else:
#初始化爆炸索引
self.step = 0
#设置爆炸状态
self.live = False
class Music:
'''
音效类
'''
def __init__(self) -> None:
pass
def play_music(self) -> None:
'''
播放音效
'''
pass
class MainGame:
#设置窗口
window = None
#设置我防坦克
my_tank = None
'''
游戏窗口类
'''
#存放地方坦克列表,设置敌方坦克数量
enemy_tank_list = []
#设置敌方坦克数量
enemy_tank_count = 6
#存放我方子弹
my_bullet_list = []
#存放敌方子弹
enemy_bullet_list = []
#存储爆炸效果列表
explode_list = []
#存储墙体列表
wall_list = []
def __init__(self) -> None:
pass
def start_game(self) -> None:
#创建窗口
MainGame.window = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
#设置窗口名称
pygame.display.set_caption("坦克大战")
#创建我方坦克
MainGame.my_tank = MyTank(300, 360)
#创建敌方坦克
self.create_enemy_tank()
#显示墙壁
self.create_wall()
while True:
sleep(0.05)
#给窗口设置颜色
MainGame.window.fill(BG_COLOR)
#调用文字方法
# num = 6
text = self.get_text_surface(f'敌方坦克数量{len(MainGame.enemy_tank_list)}')
#应用文字
MainGame.window.blit(text,(10, 10))
#获取事件功能
self.get_event()
#显示敌方坦克
self.display_enemy_tank()
#判断我方坦克是否存活
if MainGame.my_tank and MainGame.my_tank.live:
#显示我方坦克
MainGame.my_tank.display_tank()
else:
#我方坦克变成空,解决死后被集中仍然爆炸问题
MainGame.my_tank = None
#判断我方坦克是否存活
if MainGame.my_tank and MainGame.my_tank.live:
if MainGame.my_tank.remove:
#移动坦克
MainGame.my_tank.move()
#调用检查墙壁是否碰撞
MainGame.my_tank.tank_hit_wall()
for enemy in MainGame.enemy_tank_list:
#判断坦克碰撞
MainGame.my_tank.tank_collide_tank(enemy)
#显示我方子弹
self.display_my_bullet()
#显示敌方子弹
self.display_enemy_bullet()
#显示爆炸效果
self.display_explode()
#显示墙壁
self.display_wall()
#更新窗口内容
pygame.display.update()
def create_wall(self) -> None:
top = 300
for i in range(6):
wall = Wall(i * 120, top)
#添加墙体到列表中
MainGame.wall_list.append(wall)
def display_wall(self) -> None:
#显示墙壁
for wall in MainGame.wall_list:
if wall.live:
# print("显示墙壁")
wall.display_wall()
else:
MainGame.wall_list.remove(wall)
def create_my_tank(self) -> None:
#创建我方坦克
MainGame.my_tank = MyTank(300, 250)
def display_explode(self) -> None:
'''
显示爆炸效果
'''
for explode in MainGame.explode_list:
if explode.live:
#显示爆炸效果
explode.display_explode()
else:
MainGame.explode_list.remove(explode)
def display_my_bullet(self) -> None:
for my_bullet in MainGame.my_bullet_list:
#判断子弹是否存活
if my_bullet.live:
#显示我方子弹
my_bullet.display_bullet()
my_bullet.move()
#判断我方子弹是否击中墙壁
my_bullet.hit_wall()
#判断我方坦克是击中对方坦克
my_bullet.hit_enemy_tank()
else:
MainGame.my_bullet_list.remove(my_bullet)
def create_enemy_tank(self) -> None:
self.enemy_top = 10
self.enemy_speed = 5
#创建敌方坦克
for i in range(self.enemy_tank_count):
left = random.randint(0, 500)
# speed = random.randint(1, 15)
e_tank = EnemyTank(left, self.enemy_top, self.enemy_speed)
self.enemy_tank_list.append(e_tank)
def display_enemy_tank(self) -> None:
#显示敌方坦克
for e_tank in self.enemy_tank_list:
#判断敌方坦克是否存活
if e_tank.live:
e_tank.display_tank()
#移动敌方坦克
e_tank.rand_move()
#判断是否与墙壁碰撞
e_tank.tank_hit_wall()
#判断是否与我方坦克碰撞
e_tank.tank_collide_tank(MainGame.my_tank)
#发射子弹
e_bullet = e_tank.shot()
#将子弹加到列表中
if e_bullet: #判断是否有子弹
MainGame.enemy_bullet_list.append(e_bullet)
else:
# 从列表移除坦克
self.enemy_tank_list.remove(e_tank)
def display_enemy_bullet(self) -> None:
#显示敌方子弹
for e_bullet in MainGame.enemy_bullet_list:
if e_bullet.live:
#如果子弹存活,显示子弹
e_bullet.display_bullet()
e_bullet.move()
#判断是否击中我方坦克
e_bullet.hit_my_tank()
#判断是否击中墙壁
e_bullet.hit_wall()
else:
#如果子弹不存活,移除列表
MainGame.enemy_bullet_list.remove(e_bullet)
def get_text_surface(self, text:str):
'''
获取文字图片
'''
#初始化方法
pygame.font.init()
#定义字体
font = pygame.font.SysFont('consolas', 18)
#传入参数,颜色,抗锯齿等
text_surface = font.render(text, True, TEXT_COLOR)
#返回文字信息
return text_surface
def get_event(self) -> None:
#获取事件并遍历
event_list = pygame.event.get()
for event in event_list:
#判断类型,并输入对应内容跟
if event.type == pygame.QUIT:
#点击关闭按钮
self.end_game()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_F1:
#按下ECS,重新生成我方坦克
self.create_my_tank()
#判断我方坦克是否存活
if MainGame.my_tank and MainGame.my_tank.live:
if event.key == pygame.K_LEFT:
print("坦克向左移动")
MainGame.my_tank.direction = "L"
#修改坦克移动的状态为True
MainGame.my_tank.remove = True
# MainGame.my_tank.move()
elif event.key == pygame.K_RIGHT:
print("坦克向右移动")
#修改坦克移动的状态为True
MainGame.my_tank.direction = "R"
MainGame.my_tank.remove = True
# MainGame.my_tank.move()
elif event.key == pygame.K_UP:
print("坦克向上移动")
#修改坦克移动的状态为True
MainGame.my_tank.direction = "U"
MainGame.my_tank.remove = True
# MainGame.my_tank.move()
elif event.key == pygame.K_DOWN:
print("坦克向下移动")
#修改坦克移动的状态为True
MainGame.my_tank.direction = "D"
MainGame.my_tank.remove = True
elif event.key == pygame.K_SPACE:
if len(MainGame.my_bullet_list) < 5:
#创建并发射子弹
print("发射子弹")
m_bullet = Bullet(MainGame.my_tank)
MainGame.my_bullet_list.append(m_bullet)
# MainGame.my_tank.move()
#修改坦克的状态
if event.type == pygame.KEYUP and event.key in (pygame.K_LEFT, pygame.K_RIGHT, pygame.K_UP, pygame.K_DOWN):
#判断我方坦克是否存活
if MainGame.my_tank and MainGame.my_tank.live:
#修改坦克移动状态
MainGame.my_tank.remove = False
def end_game(self) -> None:
print("游戏结束")
exit()
'''
结束游戏
'''
if __name__ == "__main__":
#调用主类窗口方法
MainGame().start_game()
PS D:\workfolder\py> & E:/python_env/tanke/Scripts/python.exe d:/workfolder/py/tanke/class_22_坦克碰撞检测.py
pygame 2.6.0 (SDL 2.28.4, Python 3.12.1)
Hello from the pygame community. https://www.pygame.org/contribute.html
坦克向上移动
Traceback (most recent call last):
File "d:\workfolder\py\tanke\class_22_坦克碰撞检测.py", line 637, in <module>
MainGame().start_game()
File "d:\workfolder\py\tanke\class_22_坦克碰撞检测.py", line 440, in start_game
self.display_enemy_tank()
File "d:\workfolder\py\tanke\class_22_坦克碰撞检测.py", line 535, in display_enemy_tank
e_tank.tank_collide_tank(MainGame.my_tank)
File "d:\workfolder\py\tanke\class_22_坦克碰撞检测.py", line 80, in tank_collide_tank
if self.live and tank.live:
^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'live'
请看下要怎么修改,谢谢。