pygame编写的坦克游戏(五)

坦克和子弹:

1、坦克基类

不管是敌人的坦克还是玩家的坦克都有大部分相同的属性,不同的地方在于:

a)敌人有红坦克,打了可以生成宝物

b)敌人有装甲坦克一枪打不烂,会变颜色

c)敌人的坦克样式不同,行进速度也不一样

d)敌人可以被道具炸死

e)敌人可以被道具定住

f)敌人的子弹可以穿过敌人坦克,形成子弹叠加效果

g)玩家出生时有无敌效果

h)玩家可以加一众宝物

i)玩家不开启友军伤害


2、敌坦克

敌坦克只能是电脑控制,所以要有AI程序,太聪明了不行,游戏难度太大,可以简单实现,除了前进、转向就是开火

当然可以加上些随机转向功能,随机值向下倾向要大一些,不然老是在最上边转悠也不好。

开火也不能开个不停,也随机开火


3、玩家坦克

要考虑到加五星、加钢盔改变外形的问题,行进与不行进油门声音应该不同

玩家坦克的等级决定火力和装甲,加一个五星可以提升一个等级,火力和装甲会有相应变化,0级开始,最多到3级


4、子弹

火力等级从0开始,1级子弹加速,2级双弹,3级最强(双弹且可以打烂钢板)

方向、火力、速度由坦克开火时的状态来决定,开火后坦克就消耗一颗子弹,子弹爆炸或被抵消后才能装填下一发

子弹在前进的过程中不停检测是否碰到障碍物,直到碰到边界为止

子弹击中敌方的子弹会抵消,没有爆炸效果

玩家的子弹击中另一玩家子弹直接消失,无友军伤害


5、坦克工厂

负责生成双方的坦克,第一关敌人方不生产装甲坦克,每关最多出3个红坦克


import random
from block import *
from const import *
        
class Bullet(Block):
    #子弹
    def __init__(self, tank, dir):
        Block.__init__(self, tank.engine, tank.who, E_000, (0, 0), 0, LIST_BULLET)
        self.tank = tank        
        self.level = tank.fire_level
        self.dir = dir
        self.mode = 1 #正常
        self.can_through = True
        self.speed = 8 if self.level > 0 else 4
        self.image_change(E_000, 0)
        self.effect_time = 0 #特效开始时间
        self.width = self.image.get_width()
        self.height = self.image.get_height()        
        pos = [tank.pos[0] + 16, tank.pos[1] + 16] #坦克的中心点
        offset = DIR_CFG[self.dir]["move"]
        pos[0] += offset[0] * 12
        pos[1] += offset[1] * 12 #坐标调整到炮口位置
        self.pos = [pos[0] - self.width // 2, pos[1] - self.height // 2]
    
    def move(self):
        if self.mode == 1:#正常状态才移动            
            targets = self.test_hit()
            if len(targets) > 0:
                for target in targets:
                    self.hit_target(target)
            else:
                offset = DIR_CFG[self.dir]["move"]
                self.pos[0] += offset[0] * self.speed
                self.pos[1] += offset[1] * self.speed
                pt = [self.pos[0] + self.width // 2, self.pos[1] + self.height // 2]

                if not self.engine.in_bound(pt):#超出边界就爆炸
                    if self.who !=  WHO_ENEMY:
                        self.engine.play_sound(SND_BORDER)
                    self.speed = 0
                    self.mode = 2 #爆炸
                    self.image_change(E_EXP, 3)
                    #纠正子弹爆炸位置,不可超出边界
                    if pt[0] < 32:pt[0] = 32
                    if pt[0] > 32 + 13 * 32:pt[0] = 32 + 13 * 32
                    if pt[1] < 32:pt[1] = 32
                    if pt[1] > 32 + 13 * 32:pt[1] = 32 + 13 * 32
                    self.pos = [pt[0] - self.image.get_width() // 2, pt[1] - self.image.get_height() // 2]
                    self.effect_time = self.engine.timer #特效开始时间
        
    def test_hit(self):
        #TODO 直接比较对象区域太慢,还是在地图上生成一个表格,每次只判断当前坐标上下左右8个16*16元素
        #产生一个前进后的坐标,并得到一个Rect
        #收缩Rect(-1, -1)后,与所有地面障碍和活动目标比较,判断是允许前进
        blocks = []
        src_rc = pygame.Rect(self.pos, (self.width, self.height))
        
        hit_enemy = False
        for block in self.engine.get_blocks(True):
            if hit_enemy:#一颗子弹只能击中一辆敌方坦克
                break
            if block == self or block == self.tank or block.who == self.who or block.mode !=  1:
                continue
            if block.can_destory and src_rc.colliderect(block.get_rect()) :
                blocks += [block]
                if block.who !=  WHO_BALK and block.who !=  self.who:
                    hit_enemy = True
        return blocks
    
    def hit_target(self, target):
        #击中敌人的子弹,抵销        
        target.hit(self)
        #击中已方单位(坦克或子弹)或 对方子弹
        #玩家子弹击中玩家 或 击中玩家,但玩家是无敌模式
        if target.who == self.who or target.type == self.type \
            or (self.who in PLAYER_CFG and target.who in PLAYER_CFG) \
            or (target.who in PLAYER_CFG and target.is_god):
            self.die()
        else:
            pt = [self.pos[0] + self.width // 2, self.pos[1] + self.height // 2]
            self.speed = 0
            self.mode = 2 #爆炸
            self.can_through = True
            self.image_change(E_EXP, 5)
            self.pos = [pt[0] - self.image.get_width() // 2, pt[1] - self.image.get_height() // 2]
            self.effect_time = self.engine.timer #特效开始时间
            
    def hit(self, bullet):
        if bullet.who == self.who or bullet.type == self.type:
            self.die()
                
    def inc_counter(self, timer):
        #累计一定时间后进行帧切换
        #-1销毁 0出生 1正常 2爆炸
        if self.mode == 1:
            self.move()
        else:
            super().inc_counter(timer)
            #只有击中边界才自爆
            if self.type == E_EXP and timer - 15 > self.effect_time:#闪烁5帧后状态销毁
                self.die()
            
    def die(self):
        if self.tank:
            if self.tank.send_bullet > 0:
                self.tank.send_bullet -=  1
            else:
                self.tank.send_bullet = 0
        super().die()
        
class Tank(Block):
    #坦克
    def __init__(self, engine, who, type, pos, dealy):
        Block.__init__(self, engine, who, type, pos, dealy, LIST_TANKS)
        self.born_type = type
        self.dir = DIR_UP #方向
        self.mode = 0 #出生
        self.killer = -1 #被谁干掉的          
        self.speed_level = 1 #速度等级(只在init中设置)
        self.max_bullets = 1 #弹药数
        self.send_bullet = 0 #已发射弹药
        self.fire_level = 0 #火力等级(弹药速度和弹药数)
        self.is_god = True #是否无敌
        self.effect_time = 0 #特效开始时间
        self.turn_time = 0 #转向时间,帧率太高时连发太快,所以要转向不能直接前进

    def bomb(self):
        self.mode = 2 #爆炸
        self.die_type = self.type
        self.speed_level = 0
        self.can_through = True
        self.scale = True
        self.animation = True
        self.die_pos = self.pos
        pt = [self.pos[0] + self.width // 2, self.pos[1] + self.height // 2]        
        self.image_change(E_EXP, 5)
        self.pos = [pt[0] - self.image.get_width() // 2, pt[1] - self.image.get_height() // 2] #动画切换后再计算位置
        self.effect_time = self.engine.timer #特效开始时间
        
    def hit(self, bullet):
        if bullet.who == self.who or self.is_god:
            return
        if bullet.who in PLAYER_CFG and self.who in PLAYER_CFG:
            #TODO 玩家的子弹打玩家,被击中者定住
            return
        #被炸死的红坦克不产生宝物(必须是被玩家干掉的)
        if self.who == WHO_ENEMY and self.type in (T_E1S, T_E2S, T_E3S, T_E4S) and bullet.who in PLAYER_CFG:
            Food(self.engine)
        
        #装甲0级的一枪就爆
        if self.type in (T_E1N, T_E1S, T_E2N, T_E2S, T_E3N, T_E3S, T_E4N, T_Y01, T_G01):
            self.killer = bullet.who
            self.bomb()
        else:
            self.engine.play_sound(SND_STEEL)
            if self.who == WHO_ENEMY:
                self.image_change({T_E4S:T_E4G, T_E4G:T_E4Y, T_E4Y:T_E4N}[self.type])
            else:
                self.add_fire(-1)                

    def test_move(self):
        
        #产生一个前进后的坐标,并得到一个Rect
        #收缩Rect(-1, -1)后,与所有地面障碍和活动目标比较,判断是允许前进
        kv = DIR_CFG[self.dir]["move"]
        prepos = [self.pos[0], self.pos[1]]
        if kv[0] !=  0:
            prepos[0] += kv[0] * self.speed_level * MOVE_PIXES
        if kv[1] !=  0:
            prepos[1] += kv[1] * self.speed_level * MOVE_PIXES
        if prepos[0] < 32 or prepos[0] > 13 * 32 or prepos[1] < 32 or prepos[1] > 13 * 32:
            return False
        
        #TODO 前进正面没有障碍允许前进,所以收缩Rect
        if self.dir in (DIR_UP, DIR_DOWN):
            pt = [prepos[0]+4, prepos[1]]
            src_rc = pygame.Rect(pt, (self.width-8, self.height))
        else:
            pt = [prepos[0], prepos[1]+4]
            src_rc = pygame.Rect(pt, (self.width, self.height-8))
            
        if self.dir == DIR_UP:
            src_rc = src_rc.clip(src_rc.move(0, -16))
        elif self.dir == DIR_DOWN:
            src_rc = src_rc.clip(src_rc.move(0, 16))
        elif self.dir == DIR_LEFT:
            src_rc = src_rc.clip(src_rc.move(-16, 0))
        else:
            src_rc = src_rc.clip(src_rc.move(16, 0))
            
        for block in self.engine.get_blocks():
            if block == self or block.can_through:
                continue
            if src_rc.colliderect(block.get_rect()):
                return False
        return True        

    def move(self, dir):
        if self.mode !=  1:
            return
        if self.dir !=  dir:
            self.dir = dir
            self.turn_time = self.engine.timer
            #调整方向时,自动纠正坐标位置
            self.pos[0] = 32 + (self.pos[0] - 32 + 8) // 16 * 16
            self.pos[1] = 32 + (self.pos[1] - 32 + 8) // 16 * 16
            self.image_change()
        elif self.engine.timer - 2 > self.turn_time and self.test_move():
            self.frame_delay = 5
            kv = DIR_CFG[self.dir]["move"]
            if kv[0] !=  0:
                self.pos[0] += kv[0] * self.speed_level * MOVE_PIXES
            if kv[1] !=  0:
                self.pos[1] += kv[1] * self.speed_level * MOVE_PIXES
            if self.engine.food !=  None and self.engine.food.mode == 1 and self.who in PLAYER_CFG:
                src_rc = pygame.Rect(self.pos, (32, 32))
                if src_rc.colliderect(self.engine.food.get_rect()):
                    self.engine.food.eat(self)

    def attack(self):
        #开火(生成子弹发射, 2级火力可以发射2枚)        
        if self.send_bullet < self.max_bullets:
            self.send_bullet += 1
            Bullet(self, self.dir)
            if self.who !=  WHO_ENEMY:
                self.engine.play_sound(SND_SHOOT)

    def die(self):
        self.engine.kill_tank(self.who, self.die_type, self.killer)
        super().die()
        
class Enemy(Tank):
    def __init__(self, engine, who, type, pos, speed):
        Tank.__init__(self, engine, who, E_NEW, pos, 5)
        self.type = type
        self.dir = DIR_DOWN
        self.dir_fail = 0
        self.can_through = True #刚出生时允许通过   
        self.speed_level = speed
        self.travel_time = 0 #行进时间
        self.policy_interval = random.randrange(300, 500, 40) #转向决策时间间隔
        self.effect_time = self.engine.timer

    def ai(self):
        if self.test_move():
            self.can_through = False
            self.dir_fail = 0            
            if self.travel_time == 0:
                self.travel_time = self.engine.timer
            #长时间向时一方向行驶时,隔几秒产生一个决策,是否变向(非前进方向)
            if self.engine.timer - self.travel_time > self.policy_interval:
                self.travel_time = 0
                dirs = list(DIR_CFG.keys());
                dirs.remove(self.dir)
                dirs += [DIR_LEFT, DIR_DOWN, DIR_DOWN, DIR_RIGHT]                
                dir = random.choice(dirs)
                self.move(dir)
            else:
                self.move(self.dir)
        else:
            self.dir_fail += 1
            #5次转向失败,等半秒再试
            if self.dir_fail < 5 or self.dir_fail % 30 == 29:
                dirs = list(DIR_CFG.keys());
                dirs.remove(self.dir)
                dir = random.choice(dirs)
                self.move(dir)
        if self.send_bullet < self.max_bullets and random.randint(0, 20) == 15:
            self.attack()

    def inc_counter(self, timer):
        #-1销毁 0出生 1正常 2爆炸
        if self.mode == 1 and not self.engine.enemy_stopped:
            self.ai() #如果两个坦克夹在一起,先出生的先离开
        super().inc_counter(timer)
        if self.mode == 0:
            if timer - 50 > self.effect_time:#闪烁10帧后状态变成正常
                self.mode = 1                
                self.is_god = False
                self.animation = not self.engine.enemy_stopped
                self.image_change()
        else:
            if self.type == E_EXP and timer - 25 > self.effect_time:
                if self.killer == -1:
                    self.die()
                else:
                    self.frame_delay = 0
                    self.scale = False
                    self.animation = False
                    self.dir = DIR_UP #分数的图片不旋转
                    self.pos = [self.die_pos[0], self.die_pos[1] + 8]
                    next_type = {T_E1N:S_100, T_E1S:S_100, T_E2N:S_200, T_E2S:S_200, T_E3N:S_300, T_E3S:S_300, T_E4N:S_400}[self.die_type]
                    self.image_change(next_type)
                    self.effect_time = timer
            if self.type in (S_100, S_200, S_300, S_400) and timer - 20 > self.effect_time:#闪烁5帧后状态销毁
                self.die()
        
class Player(Tank):
    def __init__(self, engine, who, type, level):
        Tank.__init__(self, engine, who, E_NEW, [32+4*32, 32+12*32] if who == WHO_PLAYER1 else [32+8*32, 32+12*32], 5)
        self.type = type        
        self.speed_level = 2 #速度等级
        self.fire_level = 0 #初始火力等级
        self.add_fire(level)
        self.god_duration = MYGOD_DURATION
        self.god_images = self.engine.get_frame(E_CAP)
        
    def add_fire(self, add_level = 1):
        old_level = self.fire_level
        self.fire_level += add_level #火力等级(弹药速度和弹药数)
        if self.fire_level > 3:self.fire_level = 3
        self.max_bullets = 1 if self.fire_level < 2 else 2 #弹药数        
        if old_level !=  self.fire_level:
            self.engine.players[self.who]["Level"] = self.fire_level
            self.image_change(TANK_CFG[self.who][self.fire_level]) #改变形象
        
    def set_god(self):
        self.god_duration = TOGOD_DURATION #10秒
        self.is_god = True
        self.effect_time = self.engine.timer
        
    def inc_counter(self, timer):
        #-1销毁 0出生 1正常 2爆炸
        super().inc_counter(timer)
        if self.mode == 0:
            if timer - 25 > self.effect_time:#闪烁10帧后状态变成正常
                self.mode = 1
                self.is_god = True
                self.can_through = False
                self.image_change()
                self.effect_time = timer
                self.animation = False #无方向操作时坦克履带不动(帧序号要变,但不改变帧图像)
        elif self.mode == 1:
            if timer - self.god_duration > self.effect_time:#闪烁5秒后状态变成正常
                self.mode = 1                
                self.is_god = False
        else:
            if self.type == E_EXP and timer - 25 > self.effect_time:
                self.die()
                
    def draw(self, screen):
        super().draw(screen)
        if self.is_god and self.mode == 1 and not self.engine.pause:
            cap_img = self.god_images[self.frame_index % 2]
            screen.blit(cap_img, self.pos)
        
class TankFactory:
    #坦克工厂
    def __init__(self, engine, stage):
        self.engine = engine
        self.stage = stage
        self.next_pos = 0        
        self.foods = 3 #带宝的坦克数,出一个少一个
        self.player1 = None #玩家1对象(用于操作方法和开火)
        self.player2 = None #玩家2对象(用于操作方法和开火)
        self.new_player(WHO_PLAYER1)
        self.new_player(WHO_PLAYER2)
        self.new_enemy()
        self.new_enemy()
        self.new_enemy()

    def new_player(self, who):
        dict = self.engine.players[who]
        if dict["Life"] > 0:
            dict["Life"] -=  1
            level = dict["Level"]
            print(level)
            type = {WHO_PLAYER1:T_Y01, WHO_PLAYER2:T_G01}[who]
            self.engine.players[who]["Object"] = Player(self.engine, who, type + level, level)
            
    def new_enemy(self):
        if self.engine.enemy_count > 0:
            #随机生成敌坦火力等级
            if self.stage == 1:
                if self.foods > 0:
                    type = random.choice([T_E1N, T_E1S, T_E2N, T_E3N, T_E1N, T_E3S, T_E2N, T_E2S, T_E3N])
                else:
                    type = random.choice([T_E1N, T_E2N, T_E3N])                
            else:
                #1/3机率生成重型坦克
                if self.foods > 0:
                    type = random.choice([T_E1N, T_E2N, T_E3N, T_E1S, T_E2S, T_E3S, T_E4N, T_E4G, T_E4Y, T_E4S])
                else:
                    type = random.choice([T_E1N, T_E2N, T_E3N, T_E4N, T_E4G, T_E4Y])
                
            if type in (T_E1S, T_E2S, T_E3S, T_E4S):
                self.foods -=  1
                
            if type == T_E2N or type == T_E2S:
                speed = 1
            else:
                speed = 0.6
            self.engine.enemy_count -=  1
            Enemy(self.engine, WHO_ENEMY, type, [32+((self.next_pos % 3)*6)*32, 32], speed)
            self.next_pos += 1


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值