欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python 坦克大战小游戏

程序员文章站 2024-02-20 08:13:40
...
import pygame
import random
SCREEN_WIDHT = 850
SCRREN_HEIGHT = 500
COLOR_GREEN = pygame.color.Color('LightCyan')
COLOR_RED = pygame.color.Color('red')
# COLOR_GREEN = pygame.color.Color('#3CB371')
# COLOR_GREEN = pygame.color.Color(60,179,113)

class BaseTank:
    def __init__(self,x,y):
        self.direction = 'U'# U,D,L,R,
        self.images = {
            'U':pygame.image.load('tank_img/p1tankU.gif'),
            'D':pygame.image.load('tank_img/p1tankD.gif'),
            'L':pygame.image.load('tank_img/p1tankL.gif'),
            'R':pygame.image.load('tank_img/p1tankR.gif'),
        }
        self.image = self.images[self.direction]
        self.live = True
        self.speed = 3
        # 获取图片默认的区域
        self.rect = self.image.get_rect()
        self.rect.centerx = x
        self.rect.centery = y

        # v2.6 记录移动之前的坐标
        self.oldx = 0
        self.oldy = 0

    # v1.4新增坦克的移动方法
    def move(self):
        # v2.6 实时记录移动之前的坐标
        self.oldx = self.rect.centerx
        self.oldy = self.rect.centery
        if self.direction == 'U':
            # 做边界判断处理
            if self.rect.centery > self.rect.height // 2:
                self.rect.centery -= self.speed
        elif self.direction == 'D':
            if self.rect.centery < SCRREN_HEIGHT - self.rect.height // 2:
                self.rect.centery += self.speed
        elif self.direction == 'L':
            if self.rect.centerx > self.rect.height // 2:
                self.rect.centerx -= self.speed
        elif self.direction == 'R':
            if self.rect.centerx < SCREEN_WIDHT - self.rect.height // 2:
                self.rect.centerx += self.speed
    # v2.6 新增坐标还原方法
    def stay(self):
        self.rect.centerx = self.oldx
        self.rect.centery = self.oldy
    # v2.6 新增坦克是否撞墙的方法
    def hit_wall(self):
        for wall in Game.wall_list:
            if pygame.sprite.collide_rect(self,wall):
                self.stay()
    # 将坦克加入到窗口中
    def display_tank(self):
        self.image = self.images[self.direction]
        # 在指定的位置绘制坦克
        Game.window.blit(self.image,self.rect)
    # v1.9 增加射击方法
    def shot(self):
        return Bullet(self)
class HeroTank(BaseTank):
    # v1.5 重写父类中的move方法,增加按键控制
    def move(self):# key
        # 获取所有按键的状态列表
        all_key = pygame.key.get_pressed()
        if all_key[pygame.K_UP]:
            # 1.修改坦克的方向属性
            self.direction = 'U'
            # 2.调用父类的移动super
            super(HeroTank, self).move()
        elif all_key[pygame.K_DOWN]:
            self.direction = 'D'
            super(HeroTank, self).move()
        elif all_key[pygame.K_LEFT]:
            self.direction = 'L'
            super(HeroTank, self).move()
        elif all_key[pygame.K_RIGHT]:
            self.direction = 'R'
            super(HeroTank, self).move()
# v1.6 完善敌方坦克类
class EnemyTank(BaseTank):
    def __init__(self,x,y):
        super(EnemyTank, self).__init__(x,y)
        # 速度
        self.speed = random.randint(2,5)
        # 敌方出生时的方向(随机方向)
        self.direction = self.random_direaction()
        # 敌方坦克的图片集
        self.images = {
            'U': pygame.image.load('tank_img/enemy1U.gif'),
            'D': pygame.image.load('tank_img/enemy1D.gif'),
            'L': pygame.image.load('tank_img/enemy1L.gif'),
            'R': pygame.image.load('tank_img/enemy1R.gif'),
        }
        # 图片
        self.image = self.images[self.direction]
        # v1.7 新增记录移动步数的变量
        self.step = 0
        # v2.1 计数器
        self.count = 0
    #v1.7 重写move方法
    def move(self):
        if self.step < 40:
            super(EnemyTank, self).move()
            self.step += 1
        else:
            # 重新生成方向
            self.random_direaction()
            # 步数还原
            self.step = 0
    # v2.1 重写父类中的射击方法
    def shot(self):
        self.count += 1
        if self.count == 50:
            self.count = random.randint(1,10)
            return Bullet(self)
    def random_direaction(self):
        self.direction = random.choice(['U','D','L','R'])
        return self.direction
# v1.9 实现子弹类
class Bullet:
    def __init__(self,tank:BaseTank):
        # 速度
        self.speed = 10
        # 方向
        self.direction = tank.direction
        # 图片
        self.image = pygame.image.load('tank_img/enemymissile.gif')
        # 是否活着
        self.live = True
        # 初始位置
        self.rect = self.image.get_rect()
        if self.direction == 'U':
            self.rect.centerx = tank.rect.centerx
            self.rect.centery = tank.rect.centery - tank.rect.height//2 - self.rect.height//2
        elif self.direction == 'D':
            self.rect.centerx = tank.rect.centerx
            self.rect.centery = tank.rect.centery + tank.rect.height // 2 + self.rect.height // 2
        elif self.direction == 'L':
            self.rect.centery = tank.rect.centery
            self.rect.centerx = tank.rect.centerx - tank.rect.height//2 - self.rect.height//2
        elif self.direction == 'R':
            self.rect.centery = tank.rect.centery
            self.rect.centerx = tank.rect.centerx + tank.rect.height // 2 + self.rect.height // 2
    # v2.0 更新子弹的移动方法
    def move(self):
        if self.direction == 'U':
            if self.rect.centery > self.rect.height // 2:
                self.rect.centery -= self.speed
            else:
                self.live = False
        elif self.direction == 'D':
            if self.rect.centery < SCRREN_HEIGHT - self.rect.height // 2:
                self.rect.centery += self.speed
            else:
                self.live = False
        elif self.direction == 'L':
            if self.rect.centerx > self.rect.height // 2:
                self.rect.centerx -= self.speed
            else:
                self.live = False
        elif self.direction == 'R':
            if self.rect.centerx < SCREEN_WIDHT - self.rect.height // 2:
                self.rect.centerx += self.speed
            else:
                self.live = False
    # v2.2 新增子弹是否打中
    def hit_enemy_tank(self):
        for etank in Game.enemy_tank_list:
            # 如果碰撞到一起
            if pygame.sprite.collide_rect(self,etank):
                self.live = False
                etank.live = False
                # v2.3 创建一个爆炸对象
                boom = Boom(etank)
                Game.boom_list.append(boom)
    # v2.8 新增子弹打中我方坦克的逻辑
    def hit_hero_tank(self):
        if pygame.sprite.collide_rect(self,Game.P1):
            self.live = False
            #创建一个爆炸,存储到爆炸列表中
            boom = Boom(Game.P1)
            Game.boom_list.append(boom)
            Game.P1.live = False

    # v2.5 子弹碰撞墙壁的处理
    def hit_wall(self):
        for wall in Game.wall_list:
            if pygame.sprite.collide_rect(self,wall):
                self.live = False
    # 将子弹加入到窗口中
    def display_bullet(self):
        Game.window.blit(self.image,self.rect)
# v2.4 完善墙壁类
class Wall:
    def __init__(self,x,y):
        self.image = pygame.image.load('tank_img/steels.gif')
        self.rect = self.image.get_rect()
        self.rect.centerx = x
        self.rect.centery = y
    def display_wall(self):
        Game.window.blit(self.image,self.rect)
# v2.3 完善爆炸类
class Boom:
    def __init__(self,tank):
        pass
        self.live = True
        self.images = [
            pygame.image.load('tank_img/blast0.gif'),
            pygame.image.load('tank_img/blast1.gif'),
            pygame.image.load('tank_img/blast2.gif'),
            pygame.image.load('tank_img/blast3.gif'),
            pygame.image.load('tank_img/blast4.gif'),
            pygame.image.load('tank_img/blast5.gif')
        ]
        self.image_index = 0
        self.image = self.images[self.image_index]
        self.rect = tank.rect
        self.rect.centerx -= self.rect.width//2
        self.rect.centery -= self.rect.height//2
        # self.rect.centerx = tank.rect.centerx
    def display_boom(self):
        if self.image_index < len(self.images):
            self.image = self.images[self.image_index]
            Game.window.blit(self.image,self.rect)
            self.image_index += 1
        else:
            self.image_index = 0
            self.live = False
class Music:
    pygame.mixer.init()
    @classmethod
    def play(cls,filename):
        # 1.加载音乐文件
        pygame.mixer.music.load(filename)
        # 2.播放文件
        pygame.mixer.music.play()


class Game:
    # 用来存储游戏窗口
    window = None
    P1 = None
    # v1.6 存储敌方坦克的列表
    enemy_tank_list = []
    # v1.9 新增存储我方子弹的列表
    hero_bullet = []
    # v2.1 新增存储敌方子弹的列表
    enemy_bullet = []
    # v2.3 存储所有爆炸效果的列表
    boom_list = []
    # v2.4 新增存储所有墙壁的列表
    wall_list = []
    # v1.2 创建坦克坦克
    def creat_hero_tank(self):
        if not Game.P1:
            Game.P1 = HeroTank(200,300)
            # v2.7 播放音效
            # Music.play('tank_img/start.wav')
            # Music().play('tank_img/start.wav')
            Music().__class__.play('tank_img/start.wav')
    # v1.2 加载我方坦克
    def load_hero_tank(self):
        # v2.8增加我方坦克的状态判断
        if Game.P1 and Game.P1.live:
            Game.P1.display_tank()
            # v1.4 调用坦克的移动方法
            Game.P1.move()
            # v2.6 调用坦克是否撞墙的方法
            Game.P1.hit_wall()
        else:
            Game.P1 = None
    # v1.6 创建敌方坦克
    def creat_enemy_tanks(self):
        for i in range(5):
            etank = EnemyTank(100*random.randint(1,6),100)
            Game.enemy_tank_list.append(etank)
    # v1.6 加载敌方坦克
    def load_enemy_tanks(self):
        for etank in Game.enemy_tank_list:
            if etank.live:
                etank.display_tank()
                # v1.7 调用移动方法
                etank.move()
                # v2.6 调用坦克是否撞墙的方法
                etank.hit_wall()
                # v2.1 调用敌方发射
                b = etank.shot()
                if isinstance(b,Bullet):
                    Game.enemy_bullet.append(b)
            # v2.2 新增,删除挂掉的敌方坦克
            else:
                Game.enemy_tank_list.remove(etank)
    # v2.3 加载所有爆炸效果
    def load_booms(self):
        for boom in Game.boom_list:
            if boom.live:
                boom.display_boom()
            else:
                Game.boom_list.remove(boom)
    # v1.8  使用指定的字体绘制指定的内容,得到一个表面
    # v2.9 优化方法
    def get_content_surface(self,content,fontname,fontsize):
        # 字体模块初始化
        pygame.font.init()
        font = pygame.font.SysFont(fontname,fontsize)
        sf = font.render(content,True,COLOR_RED)
        return sf
    #v1.8 加载文字提示
    def load_text_sf(self):
        Game.window.blit(self.get_content_surface(f'剩余敌方坦克{len(Game.enemy_tank_list)}辆','kaiti',20),(5,5))
    # v1.1新增事件处理方法
    def deal_events(self):
        # 获取到产生的所有事件
        all_events = pygame.event.get()
        for event in all_events:
            if event.type == pygame.QUIT:
                print('点击退出按钮,退出程序')
                exit(1)
            elif event.type == pygame.MOUSEMOTION:
                # print('鼠标事件')
                pass
            elif event.type == pygame.KEYDOWN:
                print('键盘事件,按键键盘按键')
                print(event)
                if event.key == pygame.K_SPACE:
                    print('发射子弹')
                    # v1.9调用发射方法
                    # v2.8 新增我方坦克状态判断处理
                    if Game.P1 and Game.P1.live:
                        b = Game.P1.shot()
                        # 将子弹存到列表中
                        Game.hero_bullet.append(b)
                        print(f'当前的游戏中得子弹总数量为:{len(Game.hero_bullet)}')
    # v2.0 完成所有我方子弹的加载
    def load_hero_bullet(self):
        for bullet in Game.hero_bullet:
            if bullet.live:
                bullet.display_bullet()
                # 调用子弹的移动方法
                bullet.move()
                # v2.2 调用子弹是否打中敌方坦克的方法
                bullet.hit_enemy_tank()
                # v2.5 我方子弹调用是否碰撞墙壁的方法
                bullet.hit_wall()
            else:
                Game.hero_bullet.remove(bullet)
    # v2.1 加载敌方子弹
    def load_enemy_bullet(self):
        for ebullet in Game.enemy_bullet:
            if ebullet.live:
                ebullet.display_bullet()
                ebullet.move()
                # v2.5 调用子弹是否打中墙壁的方法
                ebullet.hit_wall()
                # v2.8 调用是否打中我方坦克
                if Game.P1 and Game.P1.live:
                    ebullet.hit_hero_tank()
    # v2.4 创建墙壁
    def creat_walls(self):
        for i in range(1,6):
            wall = Wall(150*i,400)
            Game.wall_list.append(wall)
    #v2.4 负责加载墙壁
    def load_walls(self):
        for wall in Game.wall_list:
            wall.display_wall()
    def start_game(self):
        # 加载游戏窗口
        pygame.display.init()
        Game.window = pygame.display.set_mode((SCREEN_WIDHT,SCRREN_HEIGHT))
        pygame.display.set_caption('tank v2.4')
        #v1.2 调用创建我方坦克
        self.creat_hero_tank()
        # v1.6 调用创建敌方坦克
        self.creat_enemy_tanks()
        # v2.4 调用创建墙壁的方法
        self.creat_walls()
        while True:
            Game.window.fill(COLOR_GREEN)
            # v2.4 调用加载所有的墙壁方法
            self.load_walls()
            # v1.2 调用加载我方坦克
            self.load_hero_tank()
            # v1.6 调用加载敌方坦克
            self.load_enemy_tanks()
            # v2.0 调用加载我方子弹的方法
            self.load_hero_bullet()
            # v2.1 调用加载敌方子弹的方法
            self.load_enemy_bullet()
            # v2.3 调用加载所有的爆炸效果的方法
            self.load_booms()
            # v1.1 调用事件处理方法
            self.deal_events()
            # v1.7 调用加载文字提示的方法
            self.load_text_sf()
            # v2.9 调用游戏结束方法
            self.game_over()
            # 刷新窗口
            pygame.display.update()
            #v1.5 增加休眠处理
            pygame.time.wait(12)

    # v2.9 游戏结束设置
    def game_over(self):
        if not Game.P1:
            over_sf = self.get_content_surface('Game Over','kaiti',40)
            Game.window.blit(over_sf, (SCREEN_WIDHT // 2 - 50, SCRREN_HEIGHT // 2))
game = Game()
game.start_game()
相关标签: python python