iT邦幫忙

2022 iThome 鐵人賽

DAY 17
0

說到經典的射擊遊戲,那絕對少不了小蜜蜂,這有一些影片參考,也是接下來要復刻的遊戲

Yes

Yes

昨天的最後發現玩家往右走,仍會超出螢幕,可以透過在 Playerprint(self.play_rect_area) 或在 play_rect_area 的源頭 Game 發現 play_rect_area 的寬是 WIDTH 其值為1000,跟我們的螢幕寬 800 不太一樣,於是我們有兩種解決方式:

  1. Game 改變 play_rect_area 的值

    # Game.py
    WIDTH = 800
    
  2. 複寫 PaiaGamescene 物件,將 width=800 改成 width=1000

    # Game.py
    class Game(PaiaGame):
        def __init__(self, user_num):
            self.scene = Scene(width=1000, height=600, color="#4FC3F7", bias_x=0, bias_y=0)
    

這裡使用(1.),接下來,今天的實作內容我們要新增怪物

建立怪物類別

  1. 新增怪物的圖片,可以在第14天的文章「公開!開發遊戲的所有資源」中找到免費開源的圖庫連結。

    • 後續文章,怪物圖片名為 mob.png
  2. 複製並貼上 Player.pysrc ,改名為 Mob.py,將其 class name 也改為 Mob,並修改內容,刪除 Mob 不需要的程式碼。

    • 行動指令

      ~~DOWN_CMD = "DOWN"
      UP_CMD = "UP"
      RIGHT_CMD = "RIGHT"
      LEFT_CMD = "LEFT"
      SHOOT_CMD = "SHOOT"~~
      
    • image_id 改成 “mob”

      # before
      class Mob(pygame.sprite.Sprite):
          def __init__(self, construction: dict, **kwargs):
      		self.image_id = "1P"
      
      # after
      class Mob(pygame.sprite.Sprite):
          def __init__(self, construction: dict, **kwargs):
              self.image_id = "mob"
      
      • 程式碼中,所有原本是 f"{self.id}P" 的,也都改成 self.image_id
    • update 刪除 commandact

      # before
      class Mob(pygame.sprite.Sprite):
          def update(self, command: dict) -> None:
              """
              更新玩家資料
              :param command:
              :return:
              """
              self.used_frame += 1
              self.rect.center += self.vel
              self.act(command[self.id])
      
      # after
      class Mob(pygame.sprite.Sprite):
          def update(self) -> None:
              """
              更新怪物資料
              :param command:
              :return:
              """
              self.used_frame += 1
              self.rect.center += self.vel
      
      def act(self, action: list) -> None:
          if SHOOT_CMD in action:
              self.shoot()
          if LEFT_CMD in action and self.rect.left > self.play_rect_area.left:
              self.move_left()
          elif RIGHT_CMD in action and self.rect.right < self.play_rect_area.right:
              self.move_right()
          elif UP_CMD in action and DOWN_CMD not in action and self.rect.top > self.play_rect_area.top:
              self.move_up()
          elif DOWN_CMD in action and UP_CMD not in action and self.rect.bottom < self.play_rect_area.bottom:
              self.move_down()
          else:
              self.vel = Vec(0, 0)
      
    • 然後因為我沒有打算讓怪物往上走,所以也刪除 move_up 的行為

      def move_up(self):
          self.vel.y = -self.speed
      

    最後,別忘了在 README.md 文件,新增圖片來源或圖片作者

    ## Image Sours
    
    Mob Image —— @金吉局 繪師
    

初始化怪物

  • 將所有怪物儲存在 pygame.sprite.Group() 類,方便後續更新與碰撞時使用

  • 將怪物從 (x, y) = (50, 50) 開始,每次迴圈遞增50(怪物的寬),建立怪物,並在到達螢幕寬、一半高之前停止迴圈

  • 將製造出的怪物,儲存進 mobsall_sprites

    class BattleMode:
        def __init__(self, play_rect_area: pygame.Rect):
            # init mobs
            self.mobs = pygame.sprite.Group()
            count = 0
            for x in range(50, self.scene_width - 50, 50):
                for y in range(50, self.height_center, 50):
                    count += 1
                    mob = Mob(create_construction(f"mob_{count}", count, (x, y), (50, 50)), play_rect_area=play_rect_area)
                    self.mobs.add(mob)
            self.all_sprites.add(*self.mobs)
    
  • 要注意使用變數時,變數宣告的順序喔!

繪製怪物

新增 Mob 圖片初始化資料

class BattleMode:
    def get_init_image_data(self):
        init_image_data = []
        for mob in self.mobs:
            if isinstance(mob, Mob):
                init_image_data.append(mob.get_obj_init_data())

新增 Mob 圖片更新資料

class BattleMode:
    def get_obj_progress_data(self) -> list:
        obj_progress_data = []
        for mob in self.mobs:
            if isinstance(mob, Mob):
                obj_progress_data.append(mob.get_obj_progress_data())
  • 注意,繪製的順序是根據清單的順序,一層一層畫上去的!

今日畫面

  • 最後小小修改一下玩家初始化的位置

    # before
    class BattleMode:
        def __init__(self, play_rect_area: pygame.Rect):
            self.player_1P = Player(create_construction(get_ai_name(0), 0, (0, 0), (50, 50)), play_rect_area=play_rect_area)
            self.player_2P = Player(create_construction(get_ai_name(1), 1, (SCENE_WIDTH-50, SCENE_HEIGHT-50), (50, 50)), play_rect_area=play_rect_area)
    
    # after
    class BattleMode:
        def __init__(self, play_rect_area: pygame.Rect):
            self.player_1P = Player(create_construction(get_ai_name(0), 0, (self.width_center//2-50, self.scene_height-50), (50, 50)), play_rect_area=play_rect_area)
            self.player_2P = Player(create_construction(get_ai_name(1), 1, (self.width_center+self.width_center//2, SCENE_HEIGHT-50), (50, 50)), play_rect_area=play_rect_area)
    

day17_end_view

本日進度完整程式碼 點我


上一篇
實作!TankMan全攻略(X) → 雙人射擊遊戲(O)
下一篇
實作!雙人射擊遊戲 —— 怪物移動
系列文
用程式寫遊戲給AI玩30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言