iT邦幫忙

2022 iThome 鐵人賽

DAY 21
0

此文前半:快速講解怪物射擊子彈;後半:實作新增遊戲參數

上次的最後講到,射擊有冷卻時間這件事,對於 AI 玩遊戲來說需要嗎?

那我想答案是不需要的,因為 AI 每個指令,都是在計算判斷後出現的結果,自然應該每次發送一次射擊命令,遊戲就應該執行,而不用等冷卻時間;程式在玩遊戲,也不會有人手速的問題。

Mob 的射擊

  1. 初始化子彈儲存位置射擊時間變數

    class Mob(pygame.sprite.Sprite):
        def __init__(self, construction: dict, **kwargs):
            self.last_shoot_frame = 0
            self.shoot_cd = random.randint(120, 300)
            self.bullets = pygame.sprite.Group()
    
    • shoot_cd 這次我簡單在初始化Mob時,隨機期值為 120~300 之間的 frame,大家可以有更多不同的做法喔!
  2. 更新子彈判斷何時應該射擊

    class Mob(pygame.sprite.Sprite):
        def update(self) -> None:
            self.bullets.update()
            if self.used_frame - self.last_shoot_frame > self.shoot_cd:
                self.shoot()
    
  3. 更新子彈射擊時間建立子彈物件並添加進 Group

    class Mob(pygame.sprite.Sprite):
        def shoot(self):
            self.last_shoot_frame = self.used_frame
            _id = "mob"
            _no = random.randint(1, 6)
            bullet = Bullet(construction=create_construction(_id=_id, _no=_no
                                                             , _init_pos=self.rect.center, _init_size=(12, 27))
                            , play_rect_area=self.play_rect_area)
            self.bullets.add(bullet)
    
  4. 重構獲取物件更新資料,加入 bullet 的更新資料

    class Mob(pygame.sprite.Sprite):
        def get_obj_progress_data(self) -> dict or list:
            progress_date_list = []
            for bullet in self.bullets:
                if isinstance(bullet, Bullet):
                    progress_date_list.append(bullet.get_obj_progress_data())
            progress_date_list.append(create_image_view_data(self.image_id, *self.rect.topleft, self.rect.width, self.rect.height, self.angle))
            return progress_date_list
    
  5. 修正 BattleModeget_obj_progress_data 函式

    class BattleMode:
        def get_obj_progress_data(self) -> list:
            obj_progress_data = []
            for player in self.players:
                if isinstance(player, Player):
                    obj_progress_data.extend(player.get_obj_progress_data())
            for mob in self.mobs:
                if isinstance(mob, Mob):
                    obj_progress_data.extend(mob.get_obj_progress_data())
            if self.obj_rect_list:
                obj_progress_data.extend(self.obj_rect_list)
    
            return obj_progress_data
    
    • 將原本的 obj_progress_data.append(mob.get_obj_progress_data())改成 obj_progress_data.extend(mob.get_obj_progress_data())
    • 關於 list.append()list.extend() 的比較,請看 .append VS .extend

新增遊戲參數 is_manual

# game_config.json before
"game_params": [
  ]
# game_config.json after
"game_params": [
   {
     "name": "is_manual",
     "verbose": "使否手動遊玩",
     "type": "str",
     "choices": [
       {
         "verbose": "是",
         "value": "1"
       },
       {
         "verbose": "否",
         "value": ""
        }
     ],
     "help": "'1' enables the player to fire continuously.",
     "default": 0
   }
]

遊戲判斷參數並傳遞參數給遊玩模式

  1. is_manual 儲存在 Game

    # before
    class Game(PaiaGame):
        def __init__(self, user_num):
            super().__init__(user_num)
    
    # after
    class Game(PaiaGame):
        def __init__(self, user_num, is_manual: str):
            super().__init__(user_num)
            self.is_manual = False
            if is_manual:
                self.is_manual = True
    
  2. is_manual 傳給 BattleMode

    # before
    class Game(PaiaGame):
        def set_game_mode(self):
            play_rect_area = pygame.Rect(0, 0, WIDTH, HEIGHT)
            game_mode = BattleMode(play_rect_area)
            return game_mode
    
    # after
    class Game(PaiaGame):
        def set_game_mode(self):
            play_rect_area = pygame.Rect(0, 0, WIDTH, HEIGHT)
            game_mode = BattleMode(play_rect_area, self.is_manual)
            return game_mode
    

遊玩模式儲存參數並傳遞參數給Player

  1. is_manual 儲存在 BattleMode並傳給Player

    # before
    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)
    
    # after
    class BattleMode:
        def __init__(self, play_rect_area: pygame.Rect, is_manual: bool):
            self.is_manual = is_manual
            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, is_manual=is_manual)
            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, is_manual=is_manual)
    
  2. BattleModereset 函式,重新初始化自己時,傳遞 is_manual

    # before
    class BattleMode:
        def reset(self) -> None:
            self.__init__(self.play_rect_area)
    
    # after
    class BattleMode:
        def reset(self) -> None:
            self.__init__(self.play_rect_area, self.is_manual)
    

新增 Player 射擊冷卻時間判斷

# before
class Player(pygame.sprite.Sprite):
   def __init__(self, construction: dict, **kwargs):
        self.shoot_cd = 10
        self.speed = 10
# after
class Player(pygame.sprite.Sprite):
   def __init__(self, construction: dict, **kwargs):
        self.is_manual = kwargs["is_manual"]
        self.shoot_cd = 0
        if self.is_manual:
            self.shoot_cd = 10

更改遊戲指令

# before
-f 30 -i ./ml/ml_play_manual.py -i ./ml/ml_play_manual.py .
# after
-f 30 -i ./ml/ml_play_manual.py -i ./ml/ml_play_manual.py . --is_manual 1

day21_is_manual_end_view.png

# 若是輸入以下,則 is_manual 使用默認參數 0,玩家可以連續射擊
-f 30 -i ./ml/ml_play_manual.py -i ./ml/ml_play_manual.py .

day21_end_view.png

本日進度完整程式碼 點我

今日檔案更新有:

  1. BattleMode
  2. Game
  3. Mob
  4. Player
  5. game_config

上一篇
實作!雙人射擊遊戲 —— 玩家射擊
下一篇
實作!雙人射擊遊戲 —— 子彈命中
系列文
用程式寫遊戲給AI玩30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言