突然发现百度网盘里有一堆Unity小游戏Demo的源码,为了更好的学习这些内容,我决定以博客文章的形式记录我对这些源码的理解,同时方便一些像我这样的小白学习知识。今后,争取每一到两周就写一篇关于此的文章。
这篇文章从最简单的一个开始,这个源码包叫做HeliHell,unity商店有售,但已经好久不更新了。

效果预览

Demo包含一个简单的地形与一架UH1N直升机,运行后,使用鼠标控制直升机旋转、前进后退,使用方向键控制直升机升高降低,左右平移。点击下载:3wfg
Unity直升机飞行

所有知识点

  1. 鼠标、键盘方向键输入的获取
  2. 对Transform平移与旋转的理解
  3. 刚体组件与铰链关节的作用
  4. 摄像机缓动跟随的实现

脚本一览

导入资源包后,Scenes文件夹有一个Game场景,Scripts文件夹有7个JS脚本。场景中真正用到的脚本有6个,分别是heli-land,heli-physics,helileaves,heliSmoke,PropellerRotate,UberFollow。这些脚本的作用如下:

  1. UberFollow:非常巧妙的摄像机跟踪;
  2. heli-physics:飞机控制;
  3. heli-land:飞机落地状态获取;
  4. helileaves:粒子效果控制,起飞时地面的树叶;
  5. heliSmoke:粒子效果控制,起飞时地面的烟;
  6. PropellerRotate:旋翼与主轴的旋转。

为了方便,我将这些js脚本改写为了C#脚本。

UberFollow脚本

这个脚本是摄像机跟踪脚本。
原理:根据给定的偏移量在target下创建一个空物体focus,并在FixedUpdate中逐渐将摄像机向focus物体靠拢,直至二者坐标重合。其精髓在于如下五条语句:

Vector3 temp = Vector3.zero;
temp.x = (1.0f - distanceSmooth) * transform.position.x + distanceSmooth * focus.position.x;
temp.z = (1.0f - distanceSmooth) * transform.position.z + distanceSmooth * focus.position.z;
temp.y = (1.0f - heightSmooth) * transform.position.y + heightSmooth * focus.position.y;
transform.position = temp;
transform.LookAt(target);

作者在这里给定了两个Smooth平滑参数,我们只分析其中一个轴即可。

//以X轴为例
 temp.x = (1.0f - distanceSmooth) * transform.position.x + distanceSmooth * focus.position.x;

假设focus坐标不变,即Target没动,则focus.position可以视作一个定值,假设此时摄像机没有与focus的位置重合,且摄像机x坐标为0,distanceSmooth = 0.15,则根据FixedUpdate的次数,摄像机X坐标有如下计算结果。

//第一次:0                                                                 + first
//第二次:first * 0.85                                                      + first
//第三次:first * 0.85 * 0.85 + first * 0.85                                + first
//第四次:first * 0.85 * 0.85 *0.85 + first * 0.85 * 0.85 + first * 0.85    + first

看出什么了吗?我们不妨做一个计算:

//第二次 - 第一次 = first * 0.85;
//第三次 - 第二次 = first * 0.85 * 0.85;
//第四次 - 第三次 = first * 0.85 * 0.85 * 0.85;
//……
//易得,第N+1次 - 第N次 = first * 0.85^N.

根据我们总结的公式可知,摄像机每次FixedUpdate向前移动的增量都在持续减少,也就是说,摄像机的移动速度越来越慢,它拥有了一个缓动效果!
那么,摄像机在什么时候会停止运动呢?思考可得,当摄像机位置与focus位置重合时,等式可化简为如下形式

temp.x = (0.85 + 1.5) * focus.transform.x = foucus.transform.x;

此时,摄像机的位置便不会再改变。

推广到focus位置持续改变的情况,则可以将focus在某个时刻的位置视为不变,下一帧开始后,摄像机追了一下,但是没追上,并且这帧结束后focus的位置还增加了,那么我开始新的追逐好了,这个动态过程理解起来应该不难。最后,总有一刻你会停下,此时摄像机就会缓动的追上你。嗯,同样的,其他轴的原理也是这样。

看完这个脚本,我不禁感叹:这三条语句是多么地优美,这两个参数是多么地巧妙,这一个脚本是多么地精致。代码如诗,诗如代码。

后记

未完待续。

引用资料

1、[头图]【Unity】Unity-Japan UnityChanSD角色

我来吐槽

*

*