Unity如何实现时间倒退效果
发表于2017-02-15
有一些游戏提供了回退的功能,那么在游戏开发中如何实现时间倒退这个效果呢,相信会有很多人想知道,为此下面就给大家介绍下实现时间倒退效果的方法,一起来看看吧。
一个简单的思路就是用Stack来记录物体的Position和Rotation,当需要时间回退的时候就Pop出来,赋值到物体上。不过为了可以进行拓展,比如只能回退到某段时间内的,而不是一下子回退到最开始的地方,我们需要剔除太久之前的信息。如下图:

因此我选择使用List而不是Stack。
代码(完整代码在最下面)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //Pos Vector3 pos = this .transform.position; float horizontal = Input.GetAxis( "Horizontal" ); float vertical = Input.GetAxis( "Vertical" ); if (Mathf.Abs(horizontal) > 0.0001f) //左右移动 { pos.x += Time.deltaTime * horizontal * Speed; } if (Mathf.Abs(vertical) > 0.0001f) //上下移动 { pos.y += Time.deltaTime * vertical * Speed; } this .transform.position = pos; HistoryPos.Add(pos); |
这里HistoryPos就是我们用来存储历史位置的List,我们每帧都存储物体的位置。
当我们需要时间回退时,可以每帧调用下面的代码:
1 2 3 4 5 6 | if (HistoryPos.Count > 0) { int index = HistoryPos.Count - 1; this .transform.position = HistoryPos[index]; HistoryPos.RemoveAt(index); } |
这就是每次取出最后的位置(即最新的),赋值到物体上
当我们需要限制时间回退的时间跨度,可以在HistoryPos.Add后加上下面这些代码:
1 2 3 4 5 6 | HistoryPos.Add(pos); if (ShouldLimit && HistoryPos.Count > Limit) { HistoryPos.RemoveAt(0); } |
因为旋转是雷同的,因此就不贴代码出来了。
改进
1.这里我们是每帧都记录信息,这样List的大小很容易暴走,因此我们可以每隔一段时间来记录,然后要时间回退的时候就进行插值。
2.通常我们的物体都带有动画,这时倒播动画就行。如果在时间回退过程中存在多个动画,我们就需要自己设计数据结构来保存某个时刻对应的动画和动画状态。
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | using UnityEngine; using System.Collections; using System.Collections.Generic; ///
/// 就是利用Stack的原理来获取历史位置 /// 如果同时有动画,把动画倒放就是 /// public class TBPlayer : MonoBehaviour { public int Speed = 3; public int RotateSpeed = 100; public bool ShouldLimit = false ; public int Limit = 100; //可以存放的坐标上限 private List private List private bool _IsTimeBack = false ; void Start () { HistoryPos = new List HistoryRot = new List } void Update () { if (_IsTimeBack) TimeBack(); else ControlPos(); } void ControlPos() { //Pos Vector3 pos = this .transform.position; float horizontal = Input.GetAxis( "Horizontal" ); float vertical = Input.GetAxis( "Vertical" ); if (Mathf.Abs(horizontal) > 0.0001f) //左右移动 { pos.x += Time.deltaTime * horizontal * Speed; } if (Mathf.Abs(vertical) > 0.0001f) //上下移动 { pos.y += Time.deltaTime * vertical * Speed; } this .transform.position = pos; HistoryPos.Add(pos); //Rotation Quaternion rot = this .transform.rotation; Vector3 rotv = rot.eulerAngles; float rotate = Input.GetAxis( "Fire1" ); if (Mathf.Abs(rotate) > 0.0001f) { rotv.z += Time.deltaTime * rotate * RotateSpeed; } rot = Quaternion.Euler(rotv); this .transform.rotation = rot; HistoryRot.Add(rot); if (ShouldLimit && HistoryPos.Count > Limit) { HistoryPos.RemoveAt(0); HistoryRot.RemoveAt(0); } } void TimeBack() { if (HistoryPos.Count > 0) { int index = HistoryPos.Count - 1; this .transform.position = HistoryPos[index]; HistoryPos.RemoveAt(index); } if (HistoryRot.Count > 0) { int index = HistoryRot.Count - 1; this .transform.rotation = HistoryRot[index]; HistoryRot.RemoveAt(index); } } void OnGUI() { if (GUILayout.Button( "时间倒流" )) { _IsTimeBack = true ; } if (GUILayout.Button( "Reset" )) { HistoryRot.Clear(); HistoryPos.Clear(); _IsTimeBack = false ; } } } |
效果
