从零开发游戏需要学习的c#模块,第五章(继承,多态和封装)
今天我们来进行学习继承
游戏中,哥布林和史莱姆都是敌人,它们有共同的属性(血量、攻击力),又有各自独特的行为。这时就轮到继承出场了。
我们先来了解两个简单的概念,子类和父类
1. 抽出敌人的父类
// 这个类定义了所有敌人的“公共模板”
class Enemy
{
public string name;
public int hp;
public int attackPower;
// 基类构造函数
public Enemy(string enemyName, int enemyHp, int enemyAttack)
{
name = enemyName;
hp = enemyHp;
attackPower = enemyAttack;
}
// 通用受伤逻辑
public void TakeDamage(int damage)
{
hp = hp - damage;
Console.WriteLine(name + “ 受到了” + damage + “点伤害!剩余HP:” + hp);
if (hp <= 0)
{
// 后面讲多态时会让它更有用
Console.WriteLine(name + “ 被消灭了!”);
}
}
// 通用攻击,用 virtual 表示子类可以重写它
public virtual void Attack()
{
Console.WriteLine(name + “ 发动了攻击,造成” + attackPower + “点伤害!”);
}
}
2. 创建子类,继承敌人
子类的语法是 class 子类名 : 父类名。
// 哥布林继承了 Enemy 的一切
class Goblin : Enemy
{
// 哥布林独有字段
public int stealAmount;
// 子类构造函数,需要用 base() 关键字调用父类的构造函数
public Goblin(string name, int hp, int attack, int steal) : base(name, hp, attack)
{
stealAmount = steal;
}
// 哥布林特有技能
public void Steal()
{
Console.WriteLine(name + “ 偷走了你” + stealAmount + “个金币!”);
}
// 重写父类的 Attack 方法
public override void Attack()
{
Console.WriteLine(name + “ 用匕首刺击,造成” + attackPower + “点伤害!”);
}
}
现在来说一下多态
现在,Goblin 同时拥有从 Enemy 继承来的 TakeDamage 和自己独有的 Steal 方法。
现在最关键的一步来了。如果游戏里有10种敌人,我不想针对每种敌人都写一段攻击逻辑。我想写一个通用的 ProcessBattle 方法,不管传进来的是哥布林还是巨龙,都能正确地执行它们自己的攻击。
多态 的意思就是:一个父类的引用,可以指向任意一个子类的对象,并且调用方法时,会自动执行那个子类特有的版本。
在 Main 里写这个通用方法:
// 这个方法是多态的精髓
static void ProcessBattle(Enemy someEnemy)
{
Console.WriteLine(“\n--- 遭遇敌人:” + someEnemy.name + “ ---”);
someEnemy.Attack(); // 它调用的是谁的Attack?取决于传进来的是什么!
someEnemy.TakeDamage(30);
}
然后在 Main 里实验:
static void Main(string[] args)
{
Goblin bob = new Goblin(“哥布林小偷”, 50, 12, 10);
Slime slime = new Slime(“绿色史莱姆”, 35, 6);
// 核心:Goblin 和 Slime 都是 Enemy,所以都能作为参数传入
ProcessBattle(bob);
ProcessBattle(slime);
// 观察输出,“哥布林”会用匕首刺击,“史莱姆”会跳起来砸你。
// 同样的 someEnemy.Attack() 代码,却产生了完全不同的行为。
}
ProcessBattle 的参数类型是 Enemy,它能接受任何 Enemy 的子类。当调用 someEnemy.Attack() 时,程序会自动查你传进来的真实对象是哥布林还是史莱姆,然后执行它们各自重写后的 Attack 方法。这就是多态。
最后是封装
保护数据——封装
目前所有字段都是 public,外部可以随意修改。这很危险:别人写 slime.hp = -9999; 怎么办?
封装 就是用 private 把数据藏起来,再提供安全的公开入口(通常用属性)进ji'che行访问。
改造 Enemy 基类的 hp 字段:
class Enemy
{
// 字段设为私有,外部无法直接访问
private int hp;
// 用属性 (Property) 来提供安全读写
public int Hp
{
get { return hp; } // 读的时候,直接返回私有hp
set
{
// 写的时候,加上规则!血量不能小于0
if (value < 0)
{
hp = 0;
Console.WriteLine(name + “的血量已归零。”);
}
else
{
hp = value;
}
}
}
public string Name { get; private set; } // 公共读,私有写,名字只有自己能设
// 构造函数里,现在要设置属性,而不是字段
public Enemy(string name, int hp, int attack)
{
Name = name;
Hp = hp; // 这里会触发属性的 set 逻辑
AttackPower = attack;
}
// ... 其他代码,使用 Hp 和 Name 替代原来的字段 ...
}
现在你从外部访问 enemy1.Hp = -100;,血量会被安全地设为0。
今天提到的知识点比较多,大家要认真学习,简单来说,继承就是设置一个怪物模板,把其它怪物直接套用上去,不用一个一个设置血量,攻击等模板。多态是筛选和分流,封装是保护代码不会轻易的报错,提供一个安全的阈值。
今天的学习内容就此结束,关注我,下一期更精彩
更多推荐
所有评论(0)