时空幻境|如何实现时间倒流的魔法

前言


时空幻境是一款很有趣的独立游戏,游戏最大的一个特点是可以让时间倒流,回到前面的位置,灵活运用时间倒退的功能,触发各种机关,完成关卡。

时空幻境|如何实现时间倒流的魔法_第1张图片

关于时间倒流的思考


如何让游戏内的时间倒退,这是一个有趣的问题,在第一次这个游戏的时候,还是蛮震惊的,或许是第一次遇到这种类型的游戏,时间倒退的机制让人眼前一亮,当时并没有多想,现在从程序的角度思考,如何实现这一机制呢?

命令模式


使用命令模式来实现是一种可能的方法。

命令模式:将请求封装成对象,以便使用不同的请求、日志、队列等来参数化其他对象。命令模式也支持撤销操作。

定义看起来有些晦涩,其实并不难理解。在做游戏的时候,我们会将玩家的输入操作转换为游戏内的一个动作。比如按↑跳跃之类的。命令模式定义了一个Comand基类,而其他操作都会继承这个基类,并实现execute()和undo(),大概是这样的:

class Command {
public:
    //GameActor 是对应游戏中的“游戏对象”类
    virtual void execute(GameActor& actor) = 0;
    virtual void undo(GameActor& actor) = 0;
};

class JumpCommand : Command {
public:
    virtual void execute(GameActor& actor) {
        actor.jump();
    }
    virtual void undo(GameActor& actor) {
        actor.undoJump();
    }
};

这样我们就把游戏内对象的动作封装到了Command类中,控制一个角色就相当于执行一系列Command命令。这样做的好处是,我们可以记录用户的操作,把命令保存在一个stack中,当时间倒退的时候,只需要取出一个个Command命令,然后执行undo操作就可以了。

命令模式的好处是实现完各种命令以后,实现时间倒退,前进,加速减速就很简单了,只要不断执行execute或者undo就好了。缺点也很明显,这些命令并不好实现……

在时空幻境中,主角的动作其实比较少,只有跑跑跳跳,还是比较好实现的,但是也有很多其他的物体,这些物体也在运动,并可能与主角进行交互,所以如果用命令模式,实现起来还是相当复杂的。

备忘录模式


另一种方式比较暴力,就是备忘录模式,也就是保存游戏状态。

备忘录模式(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。

这个模式不用多说,就是每一帧将当前世界的状态做一个快照保存下来,然后当时间倒退的时候直接读取状态就好了。这种方法的好处就是:很好写,速度快。缺点当然就是比较耗费内存。

究竟有多么消耗内存呢?其实可以大概计算一下,假设一个关卡中有20个需要记录状态的游戏对象,每个对象大概需要6~7个属性(位置,移动速度和其他属性等,这里不太确定,不过我觉得需要的属性应该不多),出了位置以外,其他属性应该并不需要很高的精度,可以进行优化,不过这里还是先按照8个字节来算。那么游戏运行1个小时大概需要的内存就是:
一帧需要的内存 = 2068 = 960(byte)**
一秒如果设定为60帧,那么内存消耗 = 960 * 60 = 57600 (byte)
** 一个小时的内存消耗 = 57600 * 3600 / 1024 / 1024 = 197 (MB)**

接近200MB的消耗!这确实非常多,但是也并非不可行。还是有很多优化方案的,比如减少保存属性的字节。或者采用增量的保存方式,不是没帧都去保存所有值,而是保存改变值,这样会节省大量无效的内存,因为大部分时间,玩家可能是站在某个地方傻傻不动的。

利用上面的方法应该可以剩下一大半内存,另外,应该很少有人在一个章节玩一个小时,大概10~20分钟基本都可以完成了。实际上这个数字还算比较理想的了。

关于作者的帖子


本来到这里差不多结束了,不过我搜到了一个帖子:https://news.ycombinator.com/item?id=9484197

这个,大概是作者对于游戏机制实现的一些回答(从语气上看应该是吧),虽然英语很渣,不过大概可以看懂一些。

大概是说,作者大部分情况下记录了世界的状态,而不是记录事件(除了第五章)。实际上作者上述两个模式都有使用,并且表示使用命令模式非常令人烦躁,而且编写相反状态也非常复杂。

时空幻境|如何实现时间倒流的魔法_第2张图片
时空幻境|如何实现时间倒流的魔法_第3张图片

作者还表示,和与内存的消耗相比,CPU的消耗是更大的问题,所以他选择了使用保存状态的方法,在Xbox 360上,实际上在内存缓冲区消耗完之前,大概可以运行30-45分钟的样子。CPU消耗大应该是命令模式在需要执行命令很多的情况下可能会进行大量的计算,并且如果玩家进行加速操作,可能就更伤了。

另外,如果缓冲区被消耗完,那么就抛弃掉最开始的状态,这样虽然你并不能回到最初的状态,但是作者表示,好像并没有什么注意到这一点。

不知道有没有更好的实现方法,感觉其实都挺暴力的……

你可能感兴趣的:(时空幻境|如何实现时间倒流的魔法)