命令(Command)模式,又称为动作(Action)或者事务(Transaction)模式,它将一个请求封装为一个对象,从而使你可用不同的请求对客户端进行参数化,然后对请求排入序列,或者记录请求日志,以及支持可撤销的操作。
传统的RPG游戏中,玩家对可操控的我方角色进行各种命令操作之后就会生成一个命令序列,接着会根据条件和随机值对不可操控的我方和敌方角色生成各种命令添加到命令序列,而战斗过程就是对这个命令序列的执行。这边是一种常见的命令模式。
本文就以此为例。
首先定义一个Battler的类型:
public class Battler
{
public Battler(string name, int hp, int attack)
{
Name = name;
HP = hp;
Attack = attack;
}
public string Name{get;set;}
public int HP {get;set;}
public int Attack{get;set;}
public bool IsGuarding{get;set;}
public void AttackToTarget(Battler target)
{
int dmg = this.Attack;
if (target.IsGuarding) {
dmg /= 2;
}
target.HP -= dmg;
}
}
拥有名字、血量、攻击力、是否防御中四个属性和攻击目标方法。伤害值就是自己的攻击力,如果目标防御中则伤害减半,最后扣除血量。
然后我们定义一个Command的抽象类:
public abstract class Command
{
public abstract void Execute();
public virtual bool CanAddCommand()
{
return true;
}
}
定义了一个抽象方法Execute和一个虚方法CanAddCommand。
接着我们定义两个子类:
public class AttackCommand : Command
{
private Battler _attacker;
private Battler _target;
public AttackCommand(Battler attacker, Battler target)
{
_attacker = attacker;
_target = target;
}
public override bool CanAddCommand()
{
if (_attacker == null || _target == null) {
return false;
}
return base.CanAddCommand ();
}
public override void Execute()
{
if (_attacker != null && _target != null) {
_attacker.AttackToTarget (_target);
Console.WriteLine (_attacker.Name + " attack " + _target.Name);
}
}
}
攻击命令的构造函数传入攻击者和目标,在执行方法里调用前者的AttackToTarget方法攻击后者。
public class GuardCommand : Command
{
private Battler _battler;
public GuardCommand(Battler battler)
{
_battler = battler;
}
public override bool CanAddCommand()
{
if (_battler == null) {
return false;
}
return base.CanAddCommand ();
}
public override void Execute()
{
if (_battler != null) {
_battler.IsGuarding = true;
Console.WriteLine (_battler.Name + " is guarding ");
}
}
}
防御命令的构造函数传入战斗者,并在执行方法里设置该对象的IsGuarding为true。
接着我们可能需要一个宏命令来保存命令列表,这是一个简版的组合模式(参考小话设计模式(九)组合模式):
public class MacroCommand : Command
{
List<Command> _commandList = new List<Command> ();
public void Add(Command cmd)
{
if (cmd.CanAddCommand ()) {
_commandList.Add (cmd);
}
}
public void Remove(Command cmd)
{
_commandList.Remove (cmd);
}
public override void Execute()
{
for (int i = 0; i < _commandList.Count; i++) {
_commandList [i].Execute ();
}
}
}
(当然并不一定非要使用组合模式)
使用:
MacroCommand mc = new MacroCommand ();
Battler wolv = new Battler ("wolv", 100, 10);
Battler prox = new Battler ("prox", 50, 20);
Battler shad = new Battler ("shad", 80, 10);
Battler fman = new Battler ("fman", 60, 20);
Battler magn = new Battler ("magn", 70, 10);
Battler myst = new Battler ("myst", 40, 5);
mc.Add (new AttackCommand (wolv, fman));
mc.Add (new AttackCommand (prox, magn));
mc.Add (new GuardCommand (shad));
List<Battler> actors = new List<Battler> ();
actors.Add (wolv);
actors.Add (prox);
actors.Add (shad);
List<Battler> enemies = new List<Battler> ();
enemies.Add (fman);
enemies.Add (magn);
enemies.Add (myst);
foreach (var enemy in enemies) {
if (enemy.HP < 50) {
mc.Add (new GuardCommand (enemy));
} else {
var r = new System.Random ();
Battler randTarget = actors [r.Next (0, actors.Count)];
mc.Add (new AttackCommand (enemy, randTarget));
}
}
mc.Execute ();
玩家自己为可操控的角色(wolv、prox、shad)添加命令,而游戏系统会生成不可操控的角色(fman、magn、myst)的命令。最后执行,就是一个RPG游戏的简化流程。命令模式的好处:
1、将被操作的对象与如何操作解耦。
2、可以操纵或扩展以及删除命令对象。
3、可以使用组合模式将多个命令组装成一个复合命令。
4、添加新的命令可以不改变其他对象。
5、命令可以自行判断自己是否可被添加或者可执行。
6、可以生成命令日志。
缺点:会产生很多命令类。(策划或者PM总会设计出一些奇奇怪怪的需求,能通过现有的类组合出来一个命令当然最好,不行的话,你只能为它再特别实现一个类)