游戲中的时间倒回系统
The Player 玩家
首先, 在场景中创建一个立方体,作为我们的角色。再创建一个名为Player.cs的C#脚本,加一个如下的Update()函数:
void Update()
{
transform.Translate (Vector3.forward * 3.0f * Time.deltaTime * Input.GetAxis ("Vertical"));
transform.Rotate (Vector3.up * 200.0f * Time.deltaTime * Input.GetAxis ("Horizontal"));
}
这个脚本通过健盘上的上下健产生简单的移动,把这个脚本绑定到立方体上。当你点击Play运行,你就能够移动。
倾斜相机,当我们移动时,以便可以从上面观察立方体。最后,创建一个Floor的面,两个物体使不用同的材质便於觀察;
时间控制TimeController
现在创建一个新的C#脚本,命名为TimeController.cs,创建一个空的GameObject,绑定脚本。脚本处理实时记录和随时倒回游戏。
为了让它运行,我们会记录玩家的所有移动。当我们按下倒回按钮时,可以修正玩家的坐标。脚本如下:
using UnityEngine;
using System.Collections;
public class TimeController: MonoBehaviour
{
public GameObject player;
public ArrayList playerPositions;
public bool isReversing = false;
void Start()
{
playerPositions = new ArrayList();
}
void Update()
{
if(Input.GetKey(KeyCode.Space))
{
isReversing = true;
}
else
{
isReversing = false;
}
}
void FixedUpdate()
{
if(!isReversing)
{
playerPositions.Add (player.transform.position);
}
else
{
player.transform.position = (Vector3) playerPositions[playerPositions.Count - 1];
playerPositions.RemoveAt(playerPositions.Count - 1);
}
}
}
在启动的时候,新加的代码自动在场景中到TimeController对象,在整个运行过程中检查它是在倒回还是在移动。只有当不在倒回状态下,我们才可以控制移动物体。
此外,還需要另一个数据来存储旋转值,在最开始的时候初始化它,在处理位置数据的地方保存和应用数据。最後代碼如下:
using UnityEngine;
using System.Collections;
public class TimeController: MonoBehaviour
{
public GameObject player;
public ArrayList playerPositions;
public ArrayList playerRotations;
public bool isReversing = false;
void Start()
{
playerPositions = new ArrayList();
playerRotations = new ArrayList();
}
void Update()
{
if(Input.GetKey(KeyCode.Space))
{
isReversing = true;
}
else
{
isReversing = false;
}
}
void FixedUpdate()
{
if(!isReversing)
{
playerPositions.Add (player.transform.position);
playerRotations.Add (player.transform.localEulerAngles);
}
else
{
player.transform.position = (Vector3) playerPositions[playerPositions.Count - 1];
playerPositions.RemoveAt(playerPositions.Count - 1);
player.transform.localEulerAngles = (Vector3) playerRotations[playerRotations.Count - 1];
playerRotations.RemoveAt(playerRotations.Count - 1);
}
}
}
记录少量的数据,使用插入值
现在,我们每秒要记录50次,Player的位置和旋转数据。这个数据量太大了,一些移动设备上处理能力相对较低,复杂游戏使用这种数据量会变得特别的引人注意。
使用一个自定义类来保存Player数据
现在我们是使用的两个数据来存player的位置和旋转。这样做的话,我们需要记得 记录和取数据 同时有两个地方,这会产生潜在的问题。 我们可以怎么做呢,创建一个单独的类来存存这些数据。
public class Keyframe
{
public Vector3 position;
public Vector3 rotation;
public Keyframe(Vector3 position,Vector3 rotation)
{
this.position = position;
this.rotation = rotation;
}
}
經過改良,代碼如下:
using UnityEngine;
using System.Collections;
public class Keyframe
{
public Vector3 position;
public Vector3 rotation;
public Keyframe(Vector3 position,Vector3 rotation)
{
this.position = position;
this.rotation = rotation;
}
}
public class TimeController : MonoBehaviour
{
//public ArrayList playerPosition;
//public ArrayList playerRotation;
public Transform player;
public ArrayList keyframes;
public int keyFrame = 5; //表示FixedUpdate函數每被調用5次,就記錄數據;
private int frameCounter = 0;
public bool isReversing = false;
private Camera camera;
private int reverseCounter = 0;
private Vector3 currentPosition;
private Vector3 previousPosition;
private Vector3 currentRotation;
private Vector3 previousRotation;
private bool firstRun = true;
void Start()
{
//playerPosition = new ArrayList();
//playerRotation = new ArrayList();
keyframes = new ArrayList();
camera = Camera.main;
}
void Update()
{
if (Input.GetKey(KeyCode.Space))
{
isReversing = true;
}
else
{
isReversing = false;
firstRun = true;
}
}
void FixedUpdate()
{
if(!isReversing) //時間倒回未啓用則記錄數據
{
if(frameCounter<keyFrame)
{
frameCounter += 1;
}
else
{
frameCounter = 0;
//playerPosition.Add(player.transform.position);
//playerRotation.Add(player.transform.localEulerAngles);
keyframes.Add(new Keyframe(player.position,player.localEulerAngles));
}
}
else //時間倒囘開始
{
if(reverseCounter>0)
{
reverseCounter -= 1;
}
else
{
//player.transform.position = (Vector3)playerPosition[playerPosition.Count - 1];
//playerPosition.RemoveAt(playerPosition.Count - 1);
//player.transform.localEulerAngles = (Vector3)playerRotation[playerRotation.Count - 1];
//playerRotation.RemoveAt(playerRotation.Count - 1);
RestorePositions();
reverseCounter = keyFrame;
}
if(firstRun)
{
firstRun = false;
RestorePositions();
}
float interPolation = (float)reverseCounter / (float)keyFrame;
player.position = Vector3.Lerp(previousPosition, currentPosition, interPolation);
player.localEulerAngles = Vector3.Lerp(previousRotation,currentRotation,interPolation);
}
if(keyframes.Count>128) //只記錄游戲結束前128個數據
{
keyframes.RemoveAt(0);
}
}
void RestorePositions()
{
int lastIndex = keyframes.Count - 1;
int secondToLastIndex = keyframes.Count - 2;
if(secondToLastIndex>=0)
{
currentPosition = (keyframes[lastIndex] as Keyframe).position;
previousPosition = (keyframes[secondToLastIndex] as Keyframe).position;
currentRotation = (keyframes[lastIndex] as Keyframe).rotation;
previousRotation = (keyframes[secondToLastIndex] as Keyframe).rotation;
keyframes.RemoveAt(lastIndex);
}
}
}