学习 Python 之 Pygame 开发坦克大战(二)

坦克大战的需求

坦克大战游戏包含很多个物体,现在要对这些物体进行总结

类名包含的操作包含的属性
敌方坦克类射击,移动,显示生命,速度,伤害,方向,类型
我方坦克类射击,移动,显示生命,速度,伤害,方向,装甲,等级
子弹类移动,显示方向,伤害,发射源,速度
墙壁类、草类、石砖类、河类显示是否可以摧毁
音效类播放,停止,设置音乐
爆炸效果类显示是否可以摧毁
主类

物体总结完毕后,规划一下窗口的大小,下面是我设置的窗口大小
在这里插入图片描述
素材链接:百度网盘
链接: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()

运行结果
在这里插入图片描述
主类中运用到的知识,都是学习 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

这里需要设置坦克的边界范围
防止坦克跑出窗口

坦克的图片是45×45
在这里插入图片描述

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)


下面是确定子弹的位置:

子弹图片是25×10,坦克发射子弹是在坦克中间位置发射

在这里插入图片描述

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. 完善坦克开火

有了子弹类,就可以让坦克发射子弹了

修改坦克的 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()

运行游戏看看结果

当我们按下开火键时,子弹就发射了

在这里插入图片描述

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()

运行一下,看看结果
在这里插入图片描述

哈哈,终于实现发射子弹啦

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
扎眼的阳光的头像扎眼的阳光普通用户
上一篇 2023年3月5日 下午10:38
下一篇 2023年3月5日 下午10:39

相关推荐