iT邦幫忙

2025 iThome 鐵人賽

DAY 10
0
自我挑戰組

順著感覺走!從零開始的 Python & Vibe Coding 遊戲創作系列 第 10

第十天: Class (類別):建構複雜遊戲物件的藍圖

  • 分享至 

  • xImage
  •  

各位開發者們,在經歷了變數、資料型態、條件判斷、迴圈、函式與模組的學習後,我們已經掌握了 Python 程式設計的基本骨架。今天,我們將邁入物件導向程式設計(Object-Oriented Programming, OOP)的核心概念:Class (類別)。這個概念對於建構像我們《奇幻卡牌競技場》這樣複雜的遊戲,至關重要。

Python 零基礎新手入門 #10 Class (類別):

https://www.youtube.com/watch?v=AJfZvl9Hsn4&list=TLGGSr9Ziz99uDIxMzA5MjAyNQ&t=2s

什麼是類別?物件導向的基石

你可以將 Class (類別) 想像成一個家族裡的 「家長」。每個從這個「家長」衍生出的「子子孫孫」,我們稱之為 「物件」(Objects)。這些物件會繼承「家長」的某些特質,例如長相、個性、身高和體型等,這些靜態特徵在程式中被稱為 「屬性」(Attributes)。除了靜態特質,每個子孫也從家長那邊繼承了相同的生活技能,像是說話、行走、跑步等動作,這些動態行為在 Python 中則被稱為 「方法」(Methods)。
事實上,我們之前所學到的任何資料類型,例如數字 (int, float)、字串 (str)、清單 (list) 和字典 (dictionary) 等,它們底層都是源自於 Class 的概念。每個從 Class 衍生出去的物件,都各自記錄著不同的資料和數據,而每個 Class 也都有專屬的方法,可用來執行各項特定的任務。

建立與實例化類別:從藍圖到實體

在 Python 中建立 Class 的流程非常簡單。假設我們要為《奇幻卡牌競技場》遊戲建立一個 「遊戲角色」(Game Character) 的 Class。通常,Class 名稱的命名規範會採用 首字大寫 的原則。

# 定義一個遊戲角色的類別
class GameCharacter:
    # 共同屬性,所有新創建的角色都會有這些初始值
    level = 1
    experience = 0

# 實例化兩個遊戲角色物件
player1 = GameCharacter()
player2 = GameCharacter()

print(f"玩家1的等級: {player1.level}, 經驗值: {player1.experience}") # 輸出: 玩家1的等級: 1, 經驗值: 0
print(f"玩家2的等級: {player2.level}, 經驗值: {player2.experience}") # 輸出: 玩家2的等級: 1, 經驗值: 0

在這個例子中,GameCharacter 就是我們的藍圖 (Class),而 player1 和 player2 則是從這個藍圖「實例化」出來的具體物件。它們各自擁有獨立的記憶體空間,但都具備 Class 所定義的共同屬性,例如 level 和 experience。

函式:物件的誕生與初始化

雖然 Class 可以設定共同屬性,但每個獨立的物件也需要有自己獨特的屬性,例如角色名稱。如果每次都手動設定,當屬性很多時會非常麻煩。這時,init (initialize 的縮寫,意為初始化) 這個特殊的函式就派上用場了。
init 是一個在 物件被創建時會自動執行 的函式。它允許我們在創建物件的同時,直接傳遞參數來設定物件的初始屬性。

class GameCharacter:
    # __init__ 函式用於初始化物件
    def __init__(self, name, age):
        self.name = name  # 將傳入的 name 賦值給物件本身的 name 屬性
        self.age = age    # 將傳入的 age 賦值給物件本身的 age 屬性
        self.level = 1
        self.experience = 0

# 在創建物件時直接設定姓名和年齡
player1 = GameCharacter("英雄阿明", 25)
player2 = GameCharacter("魔導小華", 30)

print(f"{player1.name} 的年齡是 {player1.age},等級是 {player1.level}")
print(f"{player2.name} 的年齡是 {player2.age},等級是 {player2.level}")

init 函式中,self 參數代表了 「正在被建立的物件本身」。透過 self.屬性名稱 = 值,我們可以將傳入的引數賦值給物件的對應屬性。

定義方法:賦予物件行為能力

方法 (Methods) 簡單來說,就是 Class 中的函式,但它專屬於該類別的物件,用來執行特定的任務。就像遊戲角色除了名字、年齡等屬性外,還具備攻擊、防禦、施展魔法等特殊技能。

class GameCharacter:
    def __init__(self, name, health, attack_power):
        self.name = name
        self.health = health
        self.attack_power = attack_power
        self.shield = 0 # 新增護盾屬性

    # 攻擊方法
    def attack(self, target):
        damage = self.attack_power
        print(f"{self.name} 對 {target.name} 發動了攻擊,造成 {damage} 點傷害。")
        target.take_damage(damage) # 目標受到傷害

    # 受傷方法 (將在綜合練習中詳細說明)
    def take_damage(self, amount):
        actual_damage = amount - self.shield # 傷害扣除護盾
        if actual_damage < 0:
            actual_damage = 0
            self.shield -= amount # 護盾吸收傷害
        else:
            self.shield = 0 # 護盾歸零
            self.health -= actual_damage # 生命值減少
        if self.health < 0:
            self.health = 0
        print(f"{self.name} 受到 {actual_damage} 點傷害,剩餘 HP: {self.health}, 護盾: {self.shield}")

    # 顯示狀態的方法
    def show_status(self):
        print(f"{self.name} - HP: {self.health}, 護盾: {self.shield}")

# 創建角色
player1 = GameCharacter("勇者阿明", 100, 20)
enemy = GameCharacter("哥布林", 50, 10)

player1.attack(enemy) # 勇者阿明對哥布林發動了攻擊...
enemy.show_status() # 哥布林 - HP: 30, 護盾: 0

在 Class 中定義方法與定義普通函式類似,但它的第一個參數總是 self,它讓方法能夠存取物件自身的屬性。

繼承 (Inheritance):程式碼的重用與擴展

在實際開發中,我們可能會有多種類型的角色,例如「戰士」、「法師」等,它們可能都共享一些基本功能(如攻擊、受傷),但又各有獨特的技能。這時,「繼承」 的概念就能派上用場。
繼承允許一個 Class (子類別) 重用另一個 Class (父類別) 的屬性和方法。這可以大幅簡化程式碼,提高可維護性。例如,我們可以讓「戰士」Class 繼承「遊戲角色」Class:

class Warrior(GameCharacter): # 戰士繼承遊戲角色
    def __init__(self, name, health, attack_power, armor):
        super().__init__(name, health, attack_power) # 呼叫父類別的 __init__
        self.armor = armor # 戰士特有的屬性

    def special_attack(self, target):
        damage = self.attack_power * 1.5
        print(f"{self.name} 發動特殊攻擊,對 {target.name} 造成 {damage} 點傷害!")
        target.take_damage(damage)

# 創建一個戰士物件
my_warrior = Warrior("鐵壁戰士", 120, 25, 10)
my_warrior.show_status() # 繼承自 GameCharacter 的方法

# 戰士可以發動普通攻擊和特殊攻擊
enemy_goblin = GameCharacter("小哥布林", 40, 5)
my_warrior.attack(enemy_goblin)
my_warrior.special_attack(enemy_goblin)

透過在子類別名稱旁使用圓括號標註父類別名稱 (class Warrior(GameCharacter):),Warrior 就繼承了 GameCharacter 的所有屬性和能力。super().init(...) 則用於呼叫父類別的初始化函式,確保基本屬性得到正確設定 [自行補充,但符合繼承的原理]。

綜合練習:回合制戰鬥遊戲的 Class 應用

讓我們將 Class 的概念應用到一個回合制戰鬥遊戲的設計中,這也是您《奇幻卡牌競技場》的基礎。
在 player.py 中定義的 Player 類別就是這樣一個實例。它包含了角色的核心屬性,如 名稱 (name)、生命值 (health)、護盾 (shield)、最大生命值 (max_health) 和 額外行動次數 (extra_moves)。更重要的是,它定義了關鍵方法:

• take_damage(amount, pierce_shield=False):處理角色受到的傷害。這個方法會根據是否有護盾、以及傷害是否為穿透傷害來精確計算實際扣除的生命值。
• heal(amount):讓角色恢復生命值。
• add_shield(amount):增加角色的護盾值。
• reset_for_turn():重置每回合的狀態,例如清除額外行動次數。

在 gamemanager.py 中,GameManager 類別會利用這些 Player 物件來管理遊戲流程。它會實例化玩家和敵人,並通過調用它們的方法來模擬戰鬥。
例如,遊戲中的「攻擊」動作,不再是簡單的數值相減,而是調用目標角色的 take_damage 方法。這個方法內部已經包含了複雜的邏輯,例如先扣除護盾,再扣除生命值。

在遊戲的回合制戰鬥邏輯中,我們還可以整合 random 模組 來實現隨機性。例如,敵人的「防禦動作」可以隨機選擇「格擋」或「閃避」。如果「閃避」成功,則可能不受到傷害,失敗則受到全額傷害。特殊攻擊也可能因為精準度問題,導致傷害值減半。這些判斷都可以透過 random.choice() 或 random.randint() 函式來實現。
戰鬥流程的簡化範例:

import random # 引入 random 模組

# 假設這是簡化的 Player 類別
class Player:
    def __init__(self, name, health, attack_power):
        self.name = name
        self.health = health
        self.attack_power = attack_power
        self.shield = 0
        self.max_health = health

    def take_damage(self, amount):
        if self.shield > 0:
            if amount >= self.shield:
                actual_damage = amount - self.shield
                self.shield = 0
                self.health -= actual_damage
            else:
                self.shield -= amount
                actual_damage = 0
        else:
            actual_damage = amount
            self.health -= actual_damage
        if self.health < 0:
            self.health = 0
        print(f"  {self.name} 受到 {actual_damage} 點傷害。")

    def heal(self, amount):
        self.health = min(self.health + amount, self.max_health)
        print(f"  {self.name} 恢復 {amount} HP。")

    def add_shield(self, amount):
        self.shield += amount
        print(f"  {self.name} 獲得 {amount} 護盾。")

# 假設這是遊戲主邏輯的一部分
player_char = Player("勇者", 100, 20)
enemy_char = Player("惡魔", 80, 15)

print("--- 戰鬥開始 ---")
player_char.add_shield(10) # 玩家增加護盾

# 玩家攻擊回合
print(f"{player_char.name} 的回合:")
player_char.attack(enemy_char)
print(f"{enemy_char.name} 剩餘 HP: {enemy_char.health}")

# 敵人攻擊回合,隨機防禦
print(f"{enemy_char.name} 的回合:")
enemy_attack_value = enemy_char.attack_power
defense_choice = random.choice(["格擋", "閃避"]) # 隨機選擇防禦方式

if defense_choice == "格擋":
    print(f"  {player_char.name} 選擇格擋!傷害減半。")
    player_char.take_damage(enemy_attack_value * 0.5)
elif defense_choice == "閃避":
    print(f"  {player_char.name} 嘗試閃避!")
    if random.randint(0, 1) == 0: # 50% 機率閃避成功
        print("  閃避成功!未受到傷害。")
    else:
        print("  閃避失敗!受到全額傷害。")
        player_char.take_damage(enemy_attack_value)

print(f"{player_char.name} 剩餘 HP: {player_char.health}")

print("--- 戰鬥結束 ---")

這個綜合範例展示了如何使用 Class 來封裝角色的屬性和行為,並在遊戲邏輯中調用這些方法,同時結合 random 模組來增加遊戲的趣味性和不確定性。

恭喜您!今天我們深入學習了 Python 中 Class (類別) 的概念,它為我們建構複雜、有組織的遊戲物件提供了強大的藍圖。我們學會了如何定義類別、創建物件、初始化屬性、定義方法,以及利用繼承來重用程式碼。這些知識將是您開發《奇幻卡牌競技場》不可或缺的基石。明天,我們將綜合應用所有基礎語法,為更大型的專案開發做準備!


上一篇
第九天: Module (模組):高效管理與重用程式碼
下一篇
第十一天: Python 核心技能總複習:從基礎到專案實踐的飛躍
系列文
順著感覺走!從零開始的 Python & Vibe Coding 遊戲創作12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言