Skip to content

英雄游戏


项目简介

设计一个游戏,可以定义各种英雄,并实现英雄间的打斗,最终得到打斗的结果。


实现方式

以面向对象思想及方式完成代码开发。


知识点

  • 类的定义、 属性、 方法
  • 构造函数、实例对象、实例属性、实例方法
  • 封装、继承、多态
  • 类型注解、导包

项目要求

实战思路

uml diagram


功能拆解

  • 定义一个英雄类:此英雄类需要包含 姓名、 血量、 攻击力、 还需要有一个方法为讲台词。
# 定义类
class Hero:
    # 定义类属性
    name = ""
    hp = 0
    power = 0

    # 定义方法
    def speak(self):
        print(f"欢迎来到英雄联盟,我的名字是{self.name}")
# 类的实例化
hero = Hero()
# 实例对象.类属性 => 即可获取类属性
print(hero.name)
# 实例对象.方法名 => 即可调用实例
hero.speak()

  • 根据英雄类,实例化不同的英雄对象:每个英雄需要在实例化的时候,就有自己的姓名、攻击力、血量
class Hero:
    def __init__(self, name, hp, power):
        # 实例属性在构造函数内被初始化
        self.name = name
        self.hp = hp
        self.power = power

    # 定义方法
    def speak(self):
        print(f"欢迎来到英雄联盟,我的名字是{self.name},我的血量为{self.hp}")


jinx = Hero("jinx", 1000, 100)
# 实例对象.方法名 => 即可调用实例
jinx.speak()

  • 实例和类的关系总结:
    1. 类只能获取、修改类变量。
    2. 类不能直接调用实例方法,实例可以直接调用实例方法。
    3. 实例对象可以获取类变量,但是当类变量和实例变量同名时,就只能获取到实例变量。
    4. 实例不能修改类变量,可以修改获取实例变量。
    5. self 就是实例本身。

  • 每个英雄的血量不可以直接被获取或者修改
class Hero:
    def __init__(self, name, hp, power):
        self.name = name
        # 私有属性
        self.__hp = hp
        self.power = power
jinx = Hero("jinx", 1000, 100)
# 报错
jinx.hp = 2000

  • 定义法师类和战士类
    • 法师类:
      1. 法师类继承于 Hero 类。
      2. 法师类多了魔力的属性
      3. 法师类多了一个放技能的方法。
    • 战士类:
      1. 战士类继承于 Hero 类。
      2. 战士的会多一个护甲的属性。
      3. 战士在初始化的时候需要多传入一个护甲信息。
class APCHero(Hero):
    def __init__(self, name, hp, power, mp):
        super().__init__(name, hp, power)
        self.mp = mp

    def speak(self):
        # print(f"欢迎来到英雄联盟,我的名字是{self.name},我的血量为{self.hp}")
        super().speak()
        print("我是大美女")

    def charm(self):
        if self.mp < 50:
            print("蓝量不足")
        else:
            print("施展魅惑技能")
            self.mp -= 50


diaochan = APCHero("貂蝉", 1200, 80, 70)
diaochan.speak()
diaochan.charm()

  • 定义一个单独的 fight 函数
    1. 在打斗之前,需要两个英雄先讲出台词。
    2. 这个 fight 函数要求实现两个英雄的多轮回合制对打功能。最后需要返回一个赢家。
    3. 创建一个测试用例文件,导入被测函数,并对它完成单元测试。
def fight(hero1: Hero, hero2: Hero):
    hero1.speak()
    hero2.speak()
    hero1_hp = hero1.hp
    hero2_hp = hero2.hp
    hero1_name = hero1.name
    hero2_name = hero2.name
    while True:
        hero1_hp = hero1_hp - 10
        hero2_hp = hero2_hp - 10
        # 当第一个英雄的血量小于0 或 当第二个英雄的血量小于0
        if hero1_hp <= 0 or hero2_hp <= 0:
            if hero1_hp > hero2_hp:
                # 字面量插值 - 字符串
                print(f"英雄{hero1_name}赢了")
                return hero1_name
            elif hero1_hp < hero2_hp:
                print("英雄赢了", hero2_name)
                return hero2_name
            else:
                return "平局"

def test_fight():
    jinx = Hero("jinx", 1000, 100)
    diaochan = APCHero("貂蝉", 1200, 80, 70)
    fight(jinx, diaochan)

  • 使用设计模式优化
    • 相关知识点:不定长参数、工厂设计模式、静态方法、类方法。
    • 需求:
      1. 现在多了一个同事小林,小林需要调用各种英雄初始化的方法,去完成他自己的逻辑。但是小林并不知道我设计了多少个类型的英雄。
      2. 所以小林需要我将我目前所有的英雄类都放在一个工厂方法中进行初始化,传入不同的参数信息,返回不同的实例对象。如此一来,小林便不需要了解细节,只需要传入对应的参数获取对应的英雄即可。
class HeroFactory:
    @staticmethod
    def create_hero(hero_type, *args):
        if hero_type == "apc":
            return APCHero(*args)
        elif hero_type == "adc":
            # ADCHero(*args)
            print("adc")
        else:
            return Hero(*args)

三种方法对比

名称 定义 调用 关键字 使用场景
普通方法 至少需要一个参数 self 实例名.方法名() 方法内部涉及到实例对象属性的操作
类方法 至少需要一个 cls 参数 类名.方法名() 或者实例名.方法名() @classmethod 如果需要对类属性,即静态变量进行限制性操作
静态方法 无默认参数 类名.方法名() 或者实例名.方法名() @staticmethod 无需类或实例参与

总结

  • 项目简介
  • 实战思路
  • 实战步骤