该文章是《Unity直升机飞行HeliHell源码分析》系列文章的第二篇。
源代码下载及前文请见《Unity直升机飞行HeliHell源码分析(1)-摄像机缓动跟踪》。

铰链关节

飞行效果主要通过铰链关节实现,首先分析本项目中铰链关节的结构:
层级视图

层级视图中可以找到一个空物体ChopperControllerHuey,它是铰链关节的末端,通过其挂载的HingeJoint脚本可知,其关节上一端是ChopperLinkHuey,同理可得最上一端是chopper。
铰链关节的几个关键属性如下:

Connected Body 连接体
为刚体指定的关节连接物,如不设定,则与世界相连
Anchor 锚点
主体摇摆围绕的锚点坐标,基于本地坐标系
Axis 坐标轴
摇摆方向的坐标,基于本地坐标系

铰链关节调节起来很麻烦,略过不表。

Heli_land脚本与Heli_Physics脚本

Heli_land脚本是检测直升机是否落地脚本,代码非常简单。Heli_Physics脚本是直升机的控制脚本,主要处理用户输入,代码也不复杂。
在此复习一下碰撞发生的条件:

//当两个物体相撞,至少有一个物体挂载刚体组件且该刚体组件未勾选Is Kinematic,并且二者都有碰撞体且碰撞体均未勾选is Trigger时,会回调OnCollisionXXXX 系列方法。
//当两个物体相撞,至少有一个物体挂载刚体组件且该刚体组件未勾选Is Kinematic,并且二者都有碰撞体且任意一个碰撞体勾选is Trigger时,会回调OnTriggerXXXX系列方法。
//当刚体组件勾选了Is Kinematic时,他将不会因外力作用发生物理效果,但是它仍然能影响其他正常刚体,其他正常刚体也会回调对应的碰撞方法。

首先分析Heli_land脚本,其挂载在Chopper上,关键代码如下:

private void OnCollisionEnter(Collision collision)
{
    hit = true;
}

当碰撞发生时,将hit值更新为True;
分析Heli_physics脚本,其挂载在ChopperControllerHuey上。

mousex = (Input.mousePosition.x - (Screen.width / 2)) / (Screen.width / 2);
mousex = Mathf.Clamp(mousex, -1.0f, 1.0f);
mousey = (Input.mousePosition.y - (Screen.height / 2)) / (Screen.height / 2);
mousey = Mathf.Clamp(mousey, -1.0f, 1.0f);

上面四行代码主要处理鼠标输入,input.mousePosition获取的坐标点以屏幕左下角为原点,使用input.mousePosition.x - 屏幕宽度的一半 可以获得原点在屏幕中心时的鼠标坐标,而后将这个值除以屏幕宽度的一半,可以获取位置比例,最后通过Mathf.Clamp限制一下取值,效果为当鼠标指针超出屏幕左右边界时,值为-1或1,当在屏幕内时,值为对应的比例。y轴同理。
本脚本大量使用了类似摄像机脚本中的缓动方法,以旋转为例:

rot = rot * 0.9f + mousex * 0.1f;
m_Transform.Rotate(0, rot, 0);

这段代码表明,旋转的值会缓慢积累,rot增量越来越小(当mousex不变时),此时旋转增加速度会越来越慢:当反向旋转时,会缓慢减小正向旋转速度,最终过渡到反向旋转。
后面的平移等大多使用此原理,不再解释。
代码中稍微复杂的一部分是上升与下降,这部分代码如下:

 //获取方向输入
var vert = Input.GetAxis("Vertical");
 if (vert > 0)
{//按↑键
        updown = 0.5f * Input.GetAxis("Vertical");
        //set hit false
        heliModel.GetComponent<Heli_land>().hit = false;//向上移动,将碰撞标志设置为false
        GetComponent<Rigidbody>().isKinematic = true;//向上移动,关闭动力学模拟
}
else if (heliModel.GetComponent<Heli_land>().hit)
{//vert小于0并且处于进入碰撞的状态,着陆
        GetComponent<Rigidbody>().isKinematic = false;//开启动力学模拟,/前进后退,左右平移,重置0,此时飞机就动不了了。                                       
        forw = 0;
        side = 0;
        alti = 0;
} else if (vert < 0)
 {//vert小于0并且没有碰撞,说明此时是下降状态
        updown = 0.5f * vert;
}else
{//vert为0
        updown = 0;
}
//向前飞/向后飞时会增加/减少一些高度。
alti = alti * 0.995f + (updown + (forw * updown)) * 0.005f;
m_Transform.Translate(0, alti, 0);

这段代码的关键点在于根据Hit值判断是否着陆,通过设置刚体的isKinematic控制飞机是否能飞行。 当isKinematic 勾选时,飞行控制点将不受外力影响。也就是说,我们将其设置在哪个坐标,它就会在哪个坐标。
由于飞行控制点处于铰链关节中,因此上面两节关节也会跟着动起来。

引用资料

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

我来吐槽

*

*

1位绅士参与评论

  1. mikusa09-28 10:32 回复

    Google ad太影响体验了……