游戏设计模式之策略模式
某人做了一套模拟鸭子的游戏《SimDuck》,游戏中会出现各种鸭子,一边游泳戏水,一边呱呱的叫。她设计了一个鸭子的父类,并让各种鸭子继承自此类,代码结构如下。/// <summary>/// 父类鸭子/// </summary>public class Duck{public virtual void quack() { //呱呱叫 }publi...
·
某人做了一套模拟鸭子的游戏《SimDuck》,游戏中会出现各种鸭子,一边游泳戏水,一边呱呱的叫。她设计了一个鸭子的父类,并让各种鸭子继承自此类,代码结构如下。
/// <summary>
/// 父类鸭子
/// </summary>
public class Duck
{
public virtual void quack() { //呱呱叫 }
public virtual void swim() { //游泳 }
public virtual void display() { //外观 }
}
/// <summary>
/// 野鸭
/// </summary>
public class MallardDuck : Duck
{
public override void display()
{
//外观是野鸭
Debug.Log("外观是野鸭");
}
}
/// <summary>
/// 红头鸭子
/// </summary>
public class RedHeadDuck : Duck
{
public override void display()
{
//外观是红头鸭
Debug.Log("外观是红头鸭");
}
}
这时新需求来了,要让鸭子会飞!那还不简单,直接在父类的鸭子里增加一个Fly的方法,不就行了,你的代码如下。
//只需要改动鸭子的父类即可
/// <summary>
/// 父类鸭子
/// </summary>
public class Duck
{
public virtual void quack() { //呱呱叫 }
public virtual void swim() { //游泳 }
public virtual void display() { //外观 }
public virtual void fly() { //飞 }
}
这时新需求又来了,增加橡胶鸭和木头鸭。同样简单,你直接一套继承完事。这时候你的代码如下。
/// <summary>
/// 橡胶鸭
/// </summary>
public class RubberDuck : Duck
{
public override void quack()
{
//覆盖成吱吱叫
//橡胶鸭子吱吱叫,没玩过吗?
Debug.Log("覆盖成吱吱叫");
}
public override void display()
{
//外观是橡胶鸭
Debug.Log("外观是橡胶鸭");
}
public override void fly()
{
//橡胶鸭子不会飞,什么都不做
}
}
/// <summary>
/// 木头鸭子
/// </summary>
public class DecoyDuck : Duck
{
public override void quack()
{
//木头鸭子不会叫,什么这里什么都不做
}
public override void display()
{
//外观是木头鸭
Debug.Log("外观是木头鸭");
}
public override void fly()
{
//木头鸭子也不会飞,什么都不做
}
}
这时候问题就来了。 ① 相同代码在子类中不停重复,写的烦死了,你甚至都懒的使用CV大法。 ② 代码已经写死,运行时不易改变。 ③ 添加功能时造成其他鸭子不想要的改变。 ④ 你思考过后准备重构代码,把易变的模块提取成接口,你的想法如下图。
设计原则:找出变化之处,把他们独立出来。不要和那些不变的东西混在一起。
这样虽然解决了一部分的问题,但是代码还是有重复问题,实现IFly接口的类还是要写关于鸭子飞行的方法,尽管这听起来有点不可思议。所以你决定再次重构代码。
重构之后的代码增加了代码可重用性,易扩展性,可维护性。
设计原则:要针对接口编程,不要针对实现编程。
针对实现编程:
Dog dog = new Dog();
dog.Dark();
针对接口/父类编程:
Animal animal = new Dog();
animal.MakeSound()
只有在在运行是才指定具体实现的对象,我们一点不管他是什么类型,我们只关心他正确的实现MakeSound方法就好了。有了上面的思路,我们需要重新写一下我们的代码,代码如下图。
下边是鸭子行为的接口和此行为的具体类。
/// <summary>
/// 所有飞行行为的接口
/// </summary>
public interface IFlyBehaviour
{
void Fly();
}
/// <summary>
/// 所有叫行为的接口
/// </summary>
public interface IQuackBehaviour
{
void Quack();
}
/// <summary>
/// 使用翅膀飞
/// </summary>
public class FlyWithWings : IFlyBehaviour
{
public void Fly()
{
//使用翅膀飞
Debug.Log("使用翅膀飞");
}
}
/// <summary>
/// 不会飞
/// </summary>
public class FlyNoWay : IFlyBehaviour
{
public void Fly()
{
//什么都不做,不能飞
Debug.Log("什么都不做,不能飞");
}
}
/// <summary>
/// 呱呱叫
/// </summary>
public class Quack : IQuackBehaviour
{
public void Quack()
{
//呱呱的叫
Debug.Log("呱呱的叫");
}
}
/// <summary>
/// 吱吱叫
/// </summary>
public class Squeak : IQuackBehaviour
{
public void Quack()
{
//吱吱的叫
Debug.Log("吱吱的叫");
}
}
/// <summary>
/// 不会叫
/// </summary>
public class MuteQuack : IQuackBehaviour
{
public void Quack()
{
//什么都不做,不会叫
Debug.Log("什么都不做,不会叫");
}
}
下面是鸭子的抽象类,关于抽象类需要注意以下几点: ① 抽象类只能用做其他类的基类。 ② 抽象类可以包含抽象成员和普通的非抽象成员。 ③ 抽象可以继承自另一个抽象类 ④ 所有抽象类的成员必须使用override关键字实现。 ⑤ 抽象类不能创建实例。
/// <summary>
/// 鸭子抽象类
/// </summary>
public abstract class SuperNewDuck
{
public IFlyBehaviour flyBehaviour;
public IQuackBehaviour quackBehaviour;
//必须要实现的可以考虑抽象方法
public abstract void Swim();
public abstract void Display();
public void PerformFly()
{
//鸭子不会亲自处理飞的行为,而是委托给flyBehaviour引用的对象
flyBehaviour.Fly();
}
public void PerformQuack()
{
//鸭子不会亲自处理呱呱叫的行为,而是委托给quackBehaviour引用的对象
quackBehaviour.Quack();
}
public void SetFlyBehaviour(IFlyBehaviour flyType)
{
flyBehaviour = flyType;
}
public void SetQuackBehaviour(IQuackBehaviour quackType)
{
quackBehaviour = quackType;
}
}
下面是具体的鸭子类。
/// <summary>
/// 野鸭
/// </summary>
public class SuperMallardDuck : SuperNewDuck
{
public SuperMallardDuck()
{
flyBehaviour = new FlyWithWings();
quackBehaviour = new Quack();
}
public override void Display()
{
Debug.Log("外观是野鸭");
}
public override void Swim()
{
Debug.Log("在花式游泳");
}
}
/// <summary>
/// 木头鸭
/// </summary>
public class SuperMecoyDuck : SuperNewDuck
{
public SuperMecoyDuck ()
{
flyBehaviour = new FlyNoWay();
quackBehaviour = new MuteQuack();
}
public override void Display()
{
Debug.Log("外观是木头鸭");
}
public override void Swim()
{
Debug.Log("在正常游泳");
}
}
//Unity测试代码...
public class DuckSimulator : MonoBehaviour
{
private void Start()
{
//实例一只野鸭
SuperNewDuck superMallardDuck = new SuperMallardDuck();
superMallardDuck.Display();
superMallardDuck.Swim();
superMallardDuck.PerformFly();
superMallardDuck.PerformQuack();
Debug.Log("--------动态改变鸭子飞行行为-----------");
//运行时改变飞行状态
superMallardDuck.SetFlyBehaviour(new FlyWithWings());
superMallardDuck.PerformFly();
}
}
这时新需求来了,要增加一只超级鸭子,他使用的是导弹飞行器,你怎么办? ① 添加FlyWithRocket类实现自IFlyBehaviour接口 ② 给超级鸭子创建FlyWithRocket行为
整理下,最终的代码如下图。
设计原则:多用组合,少用继承。这里的少用并不是不用。
此设计模式名称:策略模式,他定义了算法族,分别封装起来。让他们之间可以互相替换,此算法独立于使用算法的客户。
设计谜题: ① 现在想象一下你要做一款动作冒险类(ACT)的游戏 ② 有一个主角会使用武器 ③ 但是同一时刻只能使用一种武器 ④ 武器之间可以随意的切换 请你仔细想想你该怎么设计你的代码的架构并运用到游戏中去...
好,现在来说说想法: ① 首先想到的应该是武器的抽象,将所有武器抽象成一个名为IWeaponBehaviour的接口,让其他的武器去实现这个接口,已达到动态改变行为的目的 ② 然后就是角色的父类,具有配备武器的属性,战斗的属性,动态改变武器类型的属性 ③ 你的代码结构可能如下
/// <summary>
/// 角色父类
/// </summary>
public class BaseCharacter
{
protected IWeaponBehaviour weaponBehaviour; //角色配备武器的属性
public virtual void fight() { } //战斗
public void SetWeapon(IWeaponBehaviour w) //切换武器
{
weaponBehaviour = w;
}
}
/// <summary>
/// 武器行为接口
/// </summary>
public interface IWeaponBehaviour
{
void useWeapon();
}
using UnityEngine;
/// <summary>
/// 武器巴雷特狙击枪
/// </summary>
public class Barrett : IWeaponBehaviour
{
public void useWeapon()
{
Debug.Log("使用巴雷特狙击枪");
}
}
using UnityEngine;
/// <summary>
/// 武器加特林
/// </summary>
public class Gatling : IWeaponBehaviour
{
public void useWeapon()
{
Debug.Log("使用武器加特灵机关枪");
}
}
using UnityEngine;
/// <summary>
/// 武器石头
/// </summary>
public class Stone : IWeaponBehaviour
{
public void useWeapon()
{
Debug.Log("使用石头攻击");
}
}
using UnityEngine;
/// <summary>
/// 武器剑
/// </summary>
public class Sword : IWeaponBehaviour
{
public void useWeapon()
{
Debug.Log("使用剑");
}
}
/// <summary>
/// 角色皇帝
/// </summary>
public class King : BaseCharacter
{
public King()
{
weaponBehaviour = new Barrett();
}
public override void fight()
{
weaponBehaviour.useWeapon();
}
}
/// <summary>
/// 角色皇后
/// </summary>
public class Queen : BaseCharacter
{
public Queen()
{
weaponBehaviour = new Gatling();
}
public override void fight()
{
weaponBehaviour.useWeapon();
}
}
/// <summary>
/// 角色骑士
/// </summary>
public class Knight : BaseCharacter
{
public Knight()
{
weaponBehaviour = new Sword();
}
public override void fight()
{
weaponBehaviour.useWeapon();
}
}
/// <summary>
/// 角色侏儒(或者叫哥布林哈哈)
/// </summary>
public class Troll : BaseCharacter
{
public Troll()
{
weaponBehaviour = new Stone();
}
public override void fight()
{
weaponBehaviour.useWeapon();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class WarSimulator : MonoBehaviour
{
void Start()
{
BaseCharacter king = new King();
BaseCharacter queen = new Queen();
king.fight();
king.SetWeapon(new Sword());
king.fight();
Debug.Log("---------分割线----------");
queen.fight();
}
}
最后再来复习一下我们学习的设计模式 ① 此设计模式名为策略模式 ② 其核心设计思想是封装可互相替换的算法和策略,每一个算法都独立于使用算法的客户
更多推荐



所有评论(0)