【游戏设计模式】-回放系统的实现以及思路
之前看过关于回放系统的实现,于是为了深入该系统的学习,便实现了一个简单的回放系统,下文是该系统的思路。
该文以《【游戏设计模式】之二 论撤消重做、回放系统的实现:命令模式》为主要思路,毛大的这类文章都是很好的设计模式思路在游戏上的应用,很推荐看看。
简介
回放系统主要是应用命令模式来实现,关于命令模式我就不再详细描述,可以先看上面的推荐文章。
先了解回放系统的需求,简单来说就是可以让用户查看到某一时间游戏的状态,首先要知道游戏一开始的状态是确定的,而游戏状态的变化很大程度上都是玩家的操作(即命令),那么我们只要保存玩家输出的操作,就可以从初始状态一直执行玩家在对应时间的操作,到达目标时间时,该状态也就是某一时间游戏状态,从而实现了回放系统。
为了方便了解该项目,下为该项目的类图:
由于项目主要是为了体现命令模式实现如何制作回放系统,所以仅实现了二维棋盘本地双人移动的简单操作。
基本功能
主界面如图:
该项目可以通过W/S/A/D或者小键盘的上下左右实现两个小球的移动,移动后可以点击【Save Commands】按钮,来将命令集保存为Json文件,再点击【Load Commands】按钮便进入回放模式,如下图:
回放模式界面图示
回放模式可以自动播放或者拖拽进度条来实现调整播放进度,具体可以查看带代码。
【Rest】按钮可以返回至游戏初始状态。
以上为项目的基本功能。
思路
在初始化阶段需要将所有Controller以id存入核心类的字典中,方便后续回放时通过id获取Controller实现命令。
//Controller在Start函数时会将自己存入
private void Start()
{
//初始化输入模块
circleInput.SetController(this);
//存入ReplayManager中
id=ReplayManager.GetInstance().InputController(this)
//位置归到地图原点
Rest();
}
再是关于命令记录,在玩家输入按键时会生成一个命令对象,执行命令后会将该命令转换为CommandData类对象存入,该类包含由创建该命令的Controller的id值,创建时的相对帧数,该命令属于什么命令的枚举对象,以及多个参数转换string,用于存储命令的数据信息。
记录完后可以通过将CommandData类转换为Json实现本地化存储,当需要回放时则读取Json重新转换为CommandData,再根据CommandData的信息转换为Command实现到Controller中。
其中为了绑定当前帧与当前状态,将当前帧的数据curFrame设置为属性,并在该属性修改时调用函数让当前Controller状态一同变化,代码如下:
/// <summary>
/// 当前播放帧
/// </summary>
private int _curFrame;
/// <summary>
/// 当前播放帧属性
/// </summary>
public int curFrame
{
get => _curFrame;
set => GoTargetFrame(value);
}
/// <summary>
/// 到达目标帧
/// </summary>
/// <param name="targetFrame"></param>
private void GoTargetFrame(int targetFrame)
{
//判断是否使用Excute或者Undo
bool isExcute = targetFrame > _curFrame;
while (_curFrame != targetFrame)
{
_curFrame += (isExcute?1:-1);
if (isExcute &&_curFrame >= commands[curIndex].frame ||
!isExcute && _curFrame <= commands[curIndex].frame)
{
//对对应id的controller使用CommandData来进行操作
//ControlByCommandData就是实现上述的函数,具体可以查看项目源码
controllerDic[commands[curIndex].id].
ControlByCommandData(commands[curIndex],isExcute);
curIndex=Mathf.Clamp(curIndex+(isExcute?1:-1),0,commands.Count-1);
}
}
}
以上就是比较主要的思路,实际上比较麻烦的主要是UI方面,也就是进度条,这里就不细讲了,具体可以查看项目源码。