学习 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)



















