Python街机模块键盘操作的双棍射击游戏示例

"""
键盘操作的双棍射击游戏示例
双棍就是街机游戏厅里的操作角色方向的那两根杆子,由于没有双棍,所以把相关的代码去掉了。

"""
import arcade
import random
import math
import os
import pprint

SCREEN_WIDTH = 1024
SCREEN_HEIGHT = 768
SCREEN_TITLE = "Python街机模块键盘操作的双棍射击游戏示例,译者:李兴球"
MOVEMENT_SPEED = 4
BULLET_SPEED = 10
BULLET_COOLDOWN_TICKS = 10
ENEMY_SPAWN_INTERVAL = 1
ENEMY_SPEED = 1 

  
class Player(arcade.sprite.Sprite):
    def __init__(self, filename):
        super().__init__(filename=filename, scale=0.4, center_x=SCREEN_WIDTH/2, center_y=SCREEN_HEIGHT/2)
        self.shoot_up_pressed = False
        self.shoot_down_pressed = False
        self.shoot_left_pressed = False
        self.shoot_right_pressed = False


class Enemy(arcade.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__(filename='images/bumper.png', scale=0.5, center_x=x, center_y=y)

    def follow_sprite(self, player_sprite):
        """
        本函数会让敌人朝玩家角色的方向移动

        We use the 'min' function here to get the sprite to line up with
        the target sprite, and not jump around if the sprite is not off
        an exact multiple of ENEMY_SPEED.
        """
        # 如果敌人的y坐标小于飞船的y坐标,那么就让它向上移动。每次移动的最大距离是ENEMY_SPEED
        if self.center_y < player_sprite.center_y:
            self.center_y += min(ENEMY_SPEED, player_sprite.center_y - self.center_y)
        elif self.center_y > player_sprite.center_y:
            self.center_y -= min(ENEMY_SPEED, self.center_y - player_sprite.center_y)
        # 如果敌人在玩家左边,就让它向右移动,每次移动的最大距离是ENEMY_SPEED
        if self.center_x < player_sprite.center_x:
            self.center_x += min(ENEMY_SPEED, player_sprite.center_x - self.center_x)
        elif self.center_x > player_sprite.center_x:
            self.center_x -= min(ENEMY_SPEED, self.center_x - player_sprite.center_x)


class MyGame(arcade.Window):
    def __init__(self, width, height, title):
        super().__init__(width, height, title)

        # 设置游戏工作目录,如果用python -m启动时需要,否则不需要
        file_path = os.path.dirname(os.path.abspath(__file__))
        os.chdir(file_path)

        arcade.set_background_color(arcade.color.DARK_MIDNIGHT_BLUE)
        self.game_over = False
        self.score = 0
        self.tick = 0
        self.bullet_cooldown = 0
        self.player = Player("images/playerShip2_orange.png") # 新建玩家角色
        self.bullet_list = arcade.SpriteList()                # 子弹列表
        self.enemy_list = arcade.SpriteList()                 # 敌人列表
        arcade.window_commands.schedule(self.spawn_enemy, ENEMY_SPAWN_INTERVAL)# 计划安排定时任务
 

    def spawn_enemy(self, elapsed):
        """生成敌人"""
        if self.game_over:
            return
        x = random.randint(0, SCREEN_WIDTH)
        y = random.randint(0, SCREEN_HEIGHT)
        self.enemy_list.append(Enemy(x, y))

    def update(self, delta_time):
        self.tick += 1
        if self.game_over:
            return

        self.bullet_cooldown += 1             # 这是计数器,用来限制发射击子弹不要太快

        for enemy in self.enemy_list:         # 每个敌人都朝向玩家移动
            enemy.follow_sprite(self.player)


        # Keyboard input - shooting
        if self.player.shoot_right_pressed and self.player.shoot_up_pressed:
            self.spawn_bullet(0+45)
        elif self.player.shoot_up_pressed and self.player.shoot_left_pressed:
            self.spawn_bullet(90+45)
        elif self.player.shoot_left_pressed and self.player.shoot_down_pressed:
            self.spawn_bullet(180+45)
        elif self.player.shoot_down_pressed and self.player.shoot_right_pressed:
            self.spawn_bullet(270+45)
        elif self.player.shoot_right_pressed:
            self.spawn_bullet(0)
        elif self.player.shoot_up_pressed:
            self.spawn_bullet(90)
        elif self.player.shoot_left_pressed:
            self.spawn_bullet(180)
        elif self.player.shoot_down_pressed:
            self.spawn_bullet(270)

        self.enemy_list.update()
        self.player.update()
        self.bullet_list.update()
        # 玩家和敌人组的碰撞检测
        ship_death_hit_list = arcade.check_for_collision_with_list(self.player, self.enemy_list)
        if len(ship_death_hit_list) > 0: # 碰到任一个这个列表长度都是大于0,所以游戏结束了
            self.game_over = True
        for bullet in self.bullet_list:  # 每颗子弹和敌人组的碰撞检测
            bullet_killed = False
            enemy_shot_list = arcade.check_for_collision_with_list(bullet, self.enemy_list)
            # 遍历碰到的每个敌人,删除它们.
            for enemy in enemy_shot_list:
                enemy.kill()
                bullet.kill()
                bullet_killed = True
                self.score += 1
            if bullet_killed:
                continue

    def on_key_press(self, key, modifiers):
        if key == arcade.key.W:
            self.player.change_y = MOVEMENT_SPEED
            self.player.angle = 0
        elif key == arcade.key.A:
            self.player.change_x = -MOVEMENT_SPEED
            self.player.angle = 90
        elif key == arcade.key.S:
            self.player.change_y = -MOVEMENT_SPEED
            self.player.angle = 180
        elif key == arcade.key.D:
            self.player.change_x = MOVEMENT_SPEED
            self.player.angle = 270
        elif key == arcade.key.RIGHT:
            self.player.shoot_right_pressed = True
        elif key == arcade.key.UP:
            self.player.shoot_up_pressed = True
        elif key == arcade.key.LEFT:
            self.player.shoot_left_pressed = True
        elif key == arcade.key.DOWN:
            self.player.shoot_down_pressed = True

    def on_key_release(self, key, modifiers):
        if key == arcade.key.W:
            self.player.change_y = 0
        elif key == arcade.key.A:
            self.player.change_x = 0
        elif key == arcade.key.S:
            self.player.change_y = 0
        elif key == arcade.key.D:
            self.player.change_x = 0
        elif key == arcade.key.RIGHT:
            self.player.shoot_right_pressed = False
        elif key == arcade.key.UP:
            self.player.shoot_up_pressed = False
        elif key == arcade.key.LEFT:
            self.player.shoot_left_pressed = False
        elif key == arcade.key.DOWN:
            self.player.shoot_down_pressed = False

    def spawn_bullet(self, angle_in_deg):
        # self.bullet_cooldown的值没有到达TICKS值是直接返回
        if self.bullet_cooldown < BULLET_COOLDOWN_TICKS:
            return
        self.bullet_cooldown = 0

        bullet = arcade.Sprite("images/laserBlue01.png", 0.75)

        # 把子弹放到飞船坐标
        start_x = self.player.center_x
        start_y = self.player.center_y
        bullet.center_x = start_x
        bullet.center_y = start_y

        #  
        bullet.angle = angle_in_deg
        angle_in_rad = math.radians(angle_in_deg)

        # 设置子弹移动方向
        bullet.change_x = math.cos(angle_in_rad) * BULLET_SPEED
        bullet.change_y = math.sin(angle_in_rad) * BULLET_SPEED

        # 增加子弹到相应的列表
        self.bullet_list.append(bullet)

    def on_draw(self):
        # 重量所有角色
        arcade.start_render()

        
        self.bullet_list.draw()
        self.enemy_list.draw()
        self.player.draw()

        # 画文本,把得分情况放到屏幕上
        output = f"Score: {self.score}"
        arcade.draw_text(output, 10, 20, arcade.color.WHITE, 14)

        # 结束语
        if self.game_over:
            arcade.draw_text("Game Over", SCREEN_WIDTH/2, SCREEN_HEIGHT/2, arcade.color.WHITE, 100, width=SCREEN_WIDTH,
                             align="center", anchor_x="center", anchor_y="center")


if __name__ == "__main__":
    game = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
    arcade.run()