TimeBake:part1
来源:互联网 发布:win10仿linux主题 编辑:程序博客网 时间:2024/05/16 08:44
原文链接:http://www.manew.com/thread-98803-1-1.html
如何建立一个波斯王子风格的时间倒回系统 Part1
在这个教程中,我们在中建一个小游戏,游戏中玩家可以倒回(它也可以修改适用于其他系统中)。第一部分我们学习这个系统的基本部分,下一部分,我们完善它,并让它有更多功能。
首先,我们看一下用到的游戏的样子,然后,在创建最终的小游戏之前,我们再来看使用到的技术,给你打个基础。
基本功能的演示
你需要用到Unity最新的版本,并有一些相关经验。如果你想核对自己的代码,源代码可以下载。
准备好了吧,现在开始。
以前是如何使用的时间倒回的?
波斯王子:时之沙 是第一款真正把时间倒回机制溶入到其游戏性中的游戏。当你死亡之后,你不能马上重新加载,而是倒退到你死亡前几秒,马上再试一次。
波斯王子:遗忘之沙, 时之沙系列完美地整合了时间倒回机制到游戏中,避免了破坏沉侵的快速重载。
这种倒回机制不仅仅整合到了游戏中,在故事和游戏世界也同样适用,整个故事中它也被反复提及。
一个有这种系统的游戏是《时空幻境(Braid)》,它也是围绕着时间的游戏。在Overwatch中,主人公Tracer有一种可以把自己重置到几秒种之前的位置的能力,即使是多人游戏,她也可以倒回她的时间。
如果你玩《超级房车赛(GRID)》中发生了崩溃,你有机会回到崩溃之前的某个时间点。
其他的用法
这个系统不仅仅只用于及时存储,另一种用法是用在赛车游戏和异步多人游戏游戏中的幽灵。Replays 重播
重播是这个功能另一个比较有趣的用法。这可以在《燥热(SUPERHOT)》,Worms series系列,和大多数运动游戏中看到。
运动回放的工作方式和电视上呈现的方式是相同的,一个动作可能从不同的角度被重复播放。要做到这种,不是记录视频,而是要记录用户的动作,允许重播采用不同的相机角度和地点。Wormsseries系列是以一种幽默的方式使用它的,在实时的重播中呈现得有些滑稽或者有特效的杀人者。
《燥热(SUPERHOT)》也是记录你的动作。当你完成游戏后,你的整个进度会被回放并显示几秒钟内发生的实际动作。
《超级食肉男孩(Super Meat Boy)》使用的方式更有趣。当你完成一个关卡,你会看到之前你所有的尝试一个挨着一个,最后是你的最终的一次。
《超级食肉男孩(Super Meat Boy)》的关卡最后,你之前所有的尝试都会被同时回放。
Time-Trial Ghosts 时间偿试幽灵
当你在空跑道上,为了最好的成绩赛车时,会使用到赛车幽灵(Race-Ghosting)技术。同一时间,你在跟一个幽灵比赛,它是一辆像幽灵一样透明的车,总是你在前面挡住你的。你不能撞上它,这样你可以集中精力得到好的成绩。
比起你一个人进行自我挑战,这种方式要有趣得多。从极品飞车(Need for Speed )到大金刚赛车(Diddy Kong Racing)大部分赛车游戏都有这个功能。
Trackmania Nations 上跟幽灵赛车的比赛。这个游戏有一个银牌难度,就是如果我打败了它,就可以得到银牌。注意这个重叠的汽车模型不是并不是实体,可以穿过它。
Multiplayer-Ghosts 多人游戏中的幽灵
异步的多人游戏中有另一种用法。这是一种非常不常见的用法,通过记录玩家的数据并发送给其他玩家,来进行多人匹配,随后可以挑战第一个玩家进行战斗。数据就是应用的“时间偿试幽灵(time-trial-ghost)”的方式,只有这样,你才可能跟其他的玩家进行挑战。Trackmania-games(一种反重力赛车体验游戏)中体现了这种形式,游戏中可以挑战一些难度。记录的赛事,会给你一个挑战对手,并得到一定的奖励。
Movie-Editing 电影编辑
少数游戏提供这类的功能,使用得好这是个有趣的工具。《军团要塞2(Team Fortress 2 )》就内置了重播编辑器,这样你可以创建你自己的剪辑。
《军团要塞2(Team Fortress 2 )》的重播编辑器。记录了一个赛事就可以从任何角度来看它,并不限于玩家视线。
一旦创建了这个功能,你就可以记录,观看之前的比赛了。重要的一点就是一切都要记录下来,而不仅仅是你的视野中的物体。这意味着,你可以移动记录的游戏世界,看到每个物体,从头到尾对他们进行控制。
如何创建
为了测试系统,我们需要建一个简单的游戏来测试,现在创建一个。The Player 玩家
在场景中创建一个立方体,作为我们的角色。再创建一个名为Player.cs的C#脚本,加一个如下的Update()函数:
[C#] 纯文本查看 复制代码
1
2
3
4
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的面,两个物体使不用同的材质,这样,我们不至于在一个虚空中移动它。看上去像这样:
试一试,你可以用WSAD或是上下健移动你的立方体。
时间控制TimeController
现在创建一个新的C#脚本,命名为TimeController.cs,创建一个空的GameObject,绑定脚本。脚本处理实时记录和随时倒回游戏。
为了让它运行,我们会记录玩家的所有移动。当我们按下倒回按钮时,可以修正玩家的坐标。为此,首先要创建一个变量来保存玩家,像这样:
[C#] 纯文本查看 复制代码
public
GameObject player;
把 player 对象 设到 TimeController 的属性槽上去,这样可以访问玩家对象和它的数据。
然后,我们需要创建一个数组来保存玩家的数据:
[C#] 纯文本查看 复制代码
1
2
3
4
5
public
ArrayList playerPositions;
void
Start()
{
playerPositions =
new
ArrayList();
}
按下来我们需要持续地记录player的位置。我们会存下player最后一帧的位置,6帧前的位置,8秒前的位置(不论多长时间都会被记录下来)。当我们点击按钮想要倒回去时,通过我们的数据,可以一帧一帧地设置之前的位置,从而形成时间倒回的功能。
首先,我们存一下数据:
[C#] 纯文本查看 复制代码
1
2
3
void
FixedUpdate()
{
playerPositions.Add (player.transform.position);
}
在FixedUpdate()函数中,我们存了数据。FixedUpdate被每秒被调用至少50次(或者任何你设置的周期),这个函数允许设一个固定的调用间隔来记录数据。Updat()函数的执行依赖于CPU的处理,在Update中处理会更困难一些。
这代码保存player每一帧的位置。现在我们需要应用它。
我们增加一个检查项查看倒回按钮是否被按下。像这样,我们需要一个boolean变量:
[C#] 纯文本查看 复制代码
public
bool
isReversing =
false
;
增加一个检查在Update函数中,设 置我们是否需要对游戏进行倒回操作:
[C#] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
void
Update()
{
if
(Input.GetKey(KeyCode.Space))
{
isReversing =
true
;
}
else
{
isReversing =
false
;
}
}
当游戏执行倒回操作时,我们应用数据而不再记录数据。记录数据和应用player位置的代码,像下面这样:
[C#] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
void
FixedUpdate()
{
if
(!isReversing)
{
playerPositions.Add (player.transform.position);
}
else
{
player.transform.position = (Vector3) playerPositions[playerPositions.Count - 1];
playerPositions.RemoveAt(playerPositions.Count - 1);
}
}
整个TimeController脚本,如下:
[C#] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
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);
}
}
}
另外,不要忘记给player脚本增加一个检查,检查TimeController当前是倒回状态还是记录,只有当它不是倒回状态时,才可能移动立方体,否则可能产生BUG:
[C#] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
using
UnityEngine;
using
System.Collections;
public
class
Player: MonoBehaviour
{
private
TimeController timeController;
void
Start()
{
timeController = FindObjectOfType(
typeof
(TimeController))
as
TimeController;
}
void
Update()
{
if
(!timeController.isReversing)
{
transform.Translate (Vector3.forward * 3.0f * Time.deltaTime * Input.GetAxis (
"Vertical"
));
transform.Rotate (Vector3.up * 200.0f * Time.deltaTime * Input.GetAxis (
"Horizontal"
));
}
}
}
在启动的时候,新加的代码自动在场景中到TimeController对象,在整个运行过程中检查它是在倒回还是在移动。只有当不在倒回状态下,我们才可以控制移动物体。
现在,你可以在世界中移动,按下空格健进行回放你的动作。如果你下载下本贴的附件中已经建好的包,你可以打开TimeRewindingFunctionality01 ,试一试。
(测试的时候,要一直按着空格健)
等一下,为什么我们简单的立方体总是保持着它最后的方向?因为我们没有记录它的旋转!
你需要另一个数据来存储旋转值,在最开始的时候初始化它,在处理位置数据的地方保存和应用数据。
[C#] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
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
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);
}
}
}
再试一下。TimeRewindingFunctionality02 是修改后的版本,现在我们的Player立方体可以回退了,并且看上去跟当时的状态一模一样。
总结
我们建立了一个可用的时间倒回系统的简单的原型,但它还没有完成。这个系列的下一部分,我们会让它更稳定和多样,再加一些好的效果。下面是我们还需要做的:
- 每12帧记录一次,解决巨大的数据量
- 只保存最后75个Player的位置和旋转数据,以保正数据不会变得巨大,而引起崩溃。
之前只有player的属性,后面我们还会扩展一下这个系统:
- 记录更多的数据,而不是仅仅是player
- 增加特效显示回放正在进行
- 使用一个自定义类保存玩家位置和旋转数据,而不是用数组。
原文标题:How to Build a Prince-of-Persia-Style Time-Rewind System, Part 1
原文链接:https://gamedevelopment.tutsplus.com/tutorials/how-to-build-a-prince-of-persia-style-time-rewind-system-part-1--cms-26090
0 0
- TimeBake:part1
- part1
- PART1
- PART1
- SyntaxHighlighter part1
- MileStone(part1)
- struts part1
- js part1
- Part1:CException
- Graphs part1
- RxAndroid--Part1
- my_string-part1
- linux-part1
- html-part1
- za_na part1
- WebRTC Part1
- Hibernate Part1
- Struts2 Part1
- 函数指针使用(转)
- 004HTC VIVE拾取东西和触碰
- 缩略词 —— 计算机/编程/互联网/IT
- 技术干货
- 复杂Powershell案例-获取批量建立账户的状态-PS代码的编写!
- TimeBake:part1
- JavaEE复习笔记(4)——JSP2和Servlet3.0的新特性
- Razor基础语法简介
- 损失函数:Hinge Loss(max margin)
- zuul源码分析之Filter管理
- cs231n: CNN训练的提醒
- week5、study the protocol of 802.1ag and config the topology of olt agg and bng
- RtlInitUnicodeString、IoCreateDevice、IoCreateSymbolicLink、IoDeleteDevice 四个 API 驱动函数的使用
- Loss Function view---关于损失函数的介绍