Unity命令模式

在游戏开发中,输入处理往往是最早实现、但最容易被低估的部分。

一开始我们只关心"按下按键角色动了没有",但随着需求增加,问题会逐渐暴露出来:

回放、录像、AI 行为复现、技能连招记录......

如果直接在输入代码里写逻辑,这些需求几乎无法实现。

本文将通过一个 Unity 示例,演示如何使用指令模式将"输入行为"抽象为可记录、可回放的指令对象,从而构建一个简单但可扩展的输入回放系统。

1.定义指令接口

cs 复制代码
public interface ICommand
{
    void Execute();
}

2.定义具体指令

攻击指令

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AttackCommand : ICommand
{
    private Player player;

    public AttackCommand(Player player)
    {
        this.player = player;
    }

    public void Execute()
    {
        player.Attack();
    }
}

移动指令(这里记录的是位置直接瞬移的)

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveCommand : ICommand
{
    private Player player;
    private Vector3 direction;

    public MoveCommand(Player player, Vector3 direction)
    {
        this.player = player;
        this.direction = direction;
    }

    public void Execute()
    {
        player.Move(direction);
    }
}

3.创建指令记录器

这里主要是三个方法开始记录,添加记录,获取所有的记录。这里我们只能存一个指令集合,如果需要保存多条可以考虑用字典加集合来实现。

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class CommandRecord
{
    public float time;        // 发生时间
    public ICommand command;  // 执行的命令
}
//指令记录器
public class CommandRecorder
{
    public List<CommandRecord> records = new List<CommandRecord>();
    private float startTime;
    //开始记录
    public void StartRecord()
    {
        records.Clear();
        startTime = Time.time;
    }
    //添加指令
    public void Record(ICommand command)
    {
        records.Add(new CommandRecord
        {
            time = Time.time - startTime,
            command = command
        });
    }
    //获取记录的指令集
    public List<CommandRecord> GetRecords()
    {
        return records;
    }
}

4.创建回放指令播放器

主要提供一个携程来实现依次读取指令的逻辑。

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//回放指令播放器
public class ReplayPlayer  
{
    public IEnumerator Play(List<CommandRecord> records)
    {
        Debug.Log("开始回放");
        float lastTime = 0f;

        foreach (var record in records)
        {
            //record.time表示第一条指令发生的时间,后续的等待时间就是上一条指令时间和下一条指令的差值
            yield return new WaitForSeconds(record.time - lastTime);
            record.command.Execute();
            lastTime = record.time;
        }
        Debug.Log("回放结束");
    }
}

5.指令输入

玩家脚本负责处理具体的指令逻辑。

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public void Attack()
    {
        Debug.Log("玩家攻击");
    }
    public void Move(Vector3 direction)
    {
        gameObject.transform.position = direction;
        Debug.Log("玩家移动"+ direction);
    }
}

输入处理器负责创建指令。

cs 复制代码
using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InputHandler : MonoBehaviour
{
    public Player player;
    //回放记录仪
    CommandRecorder recorder;

    void Start()
    {
        recorder = new CommandRecorder();
        recorder.StartRecord();
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.J))
        {
            var cmd = new AttackCommand(player);
            cmd.Execute();
            recorder.Record(cmd);
        }

        if (Input.GetKeyDown(KeyCode.W))
        {
            Debug.Log("位置记录");
            var cmd = new MoveCommand(player,player.transform.position);
            cmd.Execute();
            recorder.Record(cmd);
        }
        if (Input.GetKeyDown(KeyCode.S))
        {
            ReplayPlayer myPlayer = new ReplayPlayer();
            StartCoroutine(myPlayer.Play(recorder.GetRecords()));
        }
    }
}

6.运行结果

将玩家和输入采集脚本挂载到场景中的一个方块上,并拖动方块的位置,每拖动一次点击游戏面板按下W键记录一次,多记录几次后按下S键就可以看到方块沿着我们之间记录位置的顺序移动了,并且控制台打印出了对应的结果。

7.总结

通过这个示例可以看到,指令模式的价值并不只是"把操作包成一个类",

而是让行为本身成为一种可以被保存、传递和重放的数据

当输入被抽象为指令后,我们可以非常自然地实现回放、撤销、宏操作,甚至让 AI 直接复用玩家的行为逻辑。

在实际项目中,指令模式常常与输入系统、状态机、网络同步等模块结合使用,是构建复杂交互系统的重要基础。

如果你发现自己的输入逻辑越来越难维护,那么也许问题不在功能实现上,而在于行为没有被抽象出来

相关推荐
叶帆15 天前
【YFIOs】用C#开发硬件之设备上云
开发语言·unity·c#
久数君15 天前
AI三维建模工具“造形家”:地理场景三维化的高效解决方案
unity·glb·ai算法·ai三维建模工具·地图框选·造形家·城市建筑模型
会思考的猴子16 天前
Unity VFX 属性 Postion 和 TargetPostion
unity
hai31524754316 天前
九章编程法 · 猜数字游戏 (GW-BASIC 重构版) *
人工智能·microsoft·游戏引擎·游戏程序
心前阳光16 天前
Unity资源导入之自动化资源导入
unity·自动化·游戏引擎
心前阳光16 天前
Unity之2021.3.45f2c1发布安卓程序遇到的问题
android·unity·游戏引擎
纪纯16 天前
PicoVR Unity Integration SDK 3.4 常用交互API
unity·游戏引擎·vr·pico
龙智DevSecOps解决方案16 天前
3A 游戏优化技术栈:如何打通引擎级分析工具与 DevOps 持续集成管线?
unity·性能优化·游戏开发·技术美术·perforce·unrealengine
葛兰岱尔16 天前
从 SolidWorks 到 Three.js,从 Inventor 到 Unity——制造业CAD模型“几何-语义一体化“转换,不再是天方夜谭!
开发语言·javascript·unity
鼎艺创新科技16 天前
三维电子沙盘中OSGB倾斜摄影数据的加载与渲染
游戏引擎·cocos2d