为了账号安全,请及时绑定邮箱和手机立即绑定

游戏编程之命令模式

标签:
深度学习

1、什么是命令模式

最近看了《游戏编程模式》这本书,里面介绍了游戏开发时常用的设计模式,当然这些设计模式不只是在开发游戏时才管用,它们同样适用于其他软件开发,适用于各种语言。这里我记录一下自己的学习笔记以及结合unity的使用方法。命令模式是常用的设计模式之一,它的定义是这样:将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。这个定义听起来似乎晦涩难懂,下面用unity游戏开发的例子来说明:

 

2、对客户进行参数化

比如在游戏开发中,产品经理给你提了这样一个需求:按下按键A,控制角色攻击;按下按键B,控制角色奔跑;按下按键C,控制角色跳跃。面对这样一个简单的需求,我们或许会这样写:

复制代码

void HandleInput()
{    if (Input.GetKeyDown(KeyCode.A))
    {
        Attack();
    }    else if (Input.GetKeyDown(KeyCode.B))
    {
        Run();
    }    else if (Input.GetKeyDown(KeyCode.C))
    {
        Jump();
    }
}

复制代码

然后,产品经理又提了需求,用户可以自定义按键功能,在很多游戏中都有做这样的功能,为了实现这样的功能,我们应该将这些对Attack()和Run()的调用转化成可以变换的东西,下面用命令模式来重写一下这个功能:

先定义一个抽象类Command作为基类,再定义具体的子类来重写Excute();


复制代码

public abstract class Command{    public abstract void Excute(GameActor actor);
}public class AttackCommand : Command
{    public override void Excute()
    {        //攻击逻辑    }
}public class RunCommand : Command
{    public override void Excute()
    {        //奔跑逻辑    }
}public class JumpCommand : Command
{    public override void Excute()
    {        //跳跃逻辑    }
}

复制代码

在MonoBehaviour的Update函数中,每帧去监听用户输入,并返回对应的command


复制代码

public class GameControl : MonoBehaviour
{    private Command buttonA;    private Command buttonB;    private Command buttonC;    private void Start()
    {
        buttonA = new AttackCommand();
        buttonB = new JumpCommand();
        buttonC = new RunCommand();
    }    private void Update()
    {
        Command cmd = HandleInput();        if (cmd != null)
        {
            cmd.Excute(actor);
        }
    }    //处理用户输入
    private Command HandleInput()
    {        if (Input.GetKeyDown(KeyCode.A))
        {            return buttonA;
        }        else if (Input.GetKeyDown(KeyCode.B))
        {            return buttonB;
        }        else if (Input.GetKeyDown(KeyCode.C))
        {            return buttonC;
        }        else
        {            return null;
        }
    }

}

复制代码

这样,在按键触发和函数调用中间就加了一层Command,如果要自定义按键功能,直接修改Button对应的Command就行了。现在我们也可以修改一下上面的代码,让我们可以用这套机制去控制任意角色对象,只需将要控制的角色对象传进来即可:

复制代码

using System.Collections;using System.Collections.Generic;using UnityEngine;public class GameActor { }public class Actor1 : GameActor { }public class Actor2 : GameActor { }public abstract class Command{    public abstract void Excute(GameActor actor);
}public class AttackCommand : Command
{    public override void Excute(GameActor actor)
    {        //攻击逻辑    }
}public class RunCommand : Command
{    public override void Excute(GameActor actor)
    {        //奔跑逻辑    }
}public class JumpCommand : Command
{    public override void Excute(GameActor actor)
    {        //跳跃逻辑    }
}public class GameControl : MonoBehaviour
{    private Command buttonA;    private Command buttonB;    private Command buttonC;    private GameActor actor;    private void Start()
    {
        buttonA = new AttackCommand();
        buttonB = new JumpCommand();
        buttonC = new RunCommand();

        actor = new Actor1();
    }    private void Update()
    {
        Command cmd = HandleInput();        if (cmd != null)
        {
            cmd.Excute(actor);
        }
    }    //处理用户输入
    private Command HandleInput()
    {        if (Input.GetKeyDown(KeyCode.A))
        {            return buttonA;
        }        else if (Input.GetKeyDown(KeyCode.B))
        {            return buttonB;
        }        else if (Input.GetKeyDown(KeyCode.C))
        {            return buttonC;
        }        else
        {            return null;
        }
    }

}

复制代码

3、支持可撤销的操作

命令模式在需要支持可撤销操作的情况下也能轻松应对,假如我们需要给玩家提供撤销移动操作的功能时,我们可以先把玩家输入产生的command存入栈中(或者其他数据结构),在撤销时,从栈中取出栈顶的Command,再调用该Command的Undo(),就实现了撤销功能(Undo()为撤销方法,与Excute()相反),代码如下:

复制代码

using System.Collections;using System.Collections.Generic;using UnityEngine;public class GameActor {    public Transform selfTra;    public void Move(Vector3 offset)
    {
        selfTra.Translate(offset);
    }
}public class Actor1 : GameActor { }public class Actor2 : GameActor { }public abstract class Command{    public abstract void Excute(GameActor actor);//执行
    public abstract void Undo(GameActor actor);//撤销}public class MoveCommand : Command
{    public Vector3 moveOffset;    public MoveCommand(Vector3 offset)
    {
        moveOffset = offset;
    }    public override void Excute(GameActor actor)
    {
        actor.Move(moveOffset);
    }    public override void Undo(GameActor actor)
    {
        actor.Move(-moveOffset);
    }
}public class CommandControl : MonoBehaviour
{    private Command moveCommand;    private GameActor actor;    private Stack<Command> commandStack;    private void Start()
    {
        moveCommand = new MoveCommand(Vector3.one);
        actor = new Actor1();
        commandStack = new Stack<Command>();
    }    private void Update()
    {
        Command cmd = HandleInput();        if (cmd != null)
        {
            commandStack.Push(cmd);
            cmd.Excute(actor);
        }
    }    //需要撤销操作时调用这个函数
    public void PlayReverse()
    {        if (commandStack.Count > 0)
        {
            commandStack.Pop().Undo(actor);
        }
    }    //处理用户输入
    public Command HandleInput()
    {        if (Input.GetKeyDown(KeyCode.A))
        {            return new MoveCommand(new Vector3(2, 4, 5));
        }        if (Input.GetKeyDown(KeyCode.B))
        {            return new MoveCommand(new Vector3(1, 2, 4));
        }        else 
        {            return null;
        }
    }

}

复制代码

上面代码中, 每次产生一个command时就将它存到Stack中,当需要撤销操作时,就取出Stack顶部的command,并执行它的Undo(),按照这种方法,可以实现多重撤销。

 

4、总结

通过上面的例子,我们再看命令模式的定义:将一个请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。现在我们差不多明白了命令模式的用法,它优点很明显,缺点也是有的:第一个优点是类间解耦,调用者和接收者之间没有任何依赖关系,调用者在实现功能时只需调用Command抽象类的Excute方法即可,不需要关注是哪个接收者执行;第二个优点是可扩展性,Command的子类可以很容易地扩展;缺点是如果有大量命令,那么Command的子类将会非常庞大。我们在实际开发中,应该发挥出命令模式的优点,并结合其他模式,减少Command子类庞大的问题。

原文出处:https://www.cnblogs.com/IAMTOM/p/10190554.html  

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消