加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

Unity基础包 刚体FPS RigidbodyFirstPersonController 脚本研究

(2016-11-08 20:03:58)
标签:

unity3d

unity

基础包

刚体第一人称

rigidbodyfirstperson

分类: Unity起航

版本:unity 5.3.4  语言:C#

 

今天又研究了一个脚本。

 

刚体的第一人称,不过这个脚本没有像之前的FPS脚本一样,加那么多另外的脚本,唯一一个就是MouseLook,这个脚本我们之前分析过了,就不再赘述了。


所以整个看下来都是一个比较完整的FPS模型,个人喜欢用这个刚体实现,因为以后用其他什么力都比较方便。

 

下面上代码:

// 刚体FPS移动主脚本,用刚体和胶囊组件代替了Character Controller组件

// 提醒一下,这个脚本有两百多行,稍微有点混乱的,建议先看看都有些什么属性留个印象,然后按照Start、Update、FixedUpdate一步步按照逻辑执行顺寻看过去会轻松很多

[RequireComponent(typeof (Rigidbody))]

[RequireComponent(typeof (CapsuleCollider))]

public class RigidbodyFirstPersonController : MonoBehaviour

{

    // 内部类,移动设置

    [Serializable]

    public class MovementSettings

    {

        public float ForwardSpeed = 8.0f;   // 向前走的最大速度

        public float BackwardSpeed = 4.0f;  // 后退的最大速度

        public float StrafeSpeed = 4.0f;    // 倾斜走的最大速度

        public float RunMultiplier = 2.0f;   // 奔跑速度跟行走速度的比例

 

    public KeyCode RunKey = KeyCode.LeftShift;  //跑步的按键

 

        public float JumpForce = 30f;   //起跳的力

 

        public AnimationCurve SlopeCurveModifier = new AnimationCurve(new Keyframe(-90.0f, 1.0f), new Keyframe(0.0f, 1.0f), new Keyframe(90.0f, 0.0f));  //斜面移动的调节曲线,默认是0到90,从1减到0

        [HideInInspector] public float CurrentTargetSpeed = 8f; //当前的目标速度

 

#if !MOBILE_INPUT

        private bool m_Running; //当前是否在跑步状态,只有在非手机平台上有效

#endif

        // 更新目标的速度。

        public void UpdateDesiredTargetSpeed(Vector2 input)

        {

        if (input == Vector2.zero) return;  //没有输入不处理

 

if (input.x > 0 || input.x < 0)     //x轴(即水平方向)有输入,速度为倾斜速度

{

CurrentTargetSpeed = StrafeSpeed;

}

if (input.y < 0)    //y小于0,速度为后退速度

{

CurrentTargetSpeed = BackwardSpeed;

}

if (input.y > 0)    //y大于0,前进速度,这个写在最后,使其优先级最高,即如果即倾斜又前进,则为前进速度

{

CurrentTargetSpeed = ForwardSpeed;

}

#if !MOBILE_INPUT

        if (Input.GetKey(RunKey))   //奔跑状态下,速度默认乘以2

        {

        CurrentTargetSpeed *= RunMultiplier;

        m_Running = true;

        }

        else

        {

        m_Running = false;

        }

#endif

        }

 

#if !MOBILE_INPUT

        public bool Running

        {

            get { return m_Running; }

        }

#endif

    }

 

    // 内部类,高级设置

    [Serializable]

    public class AdvancedSettings

    {

        public float groundCheckDistance = 0.01f; //判断当前是否着陆离地面的距离(设置为0.01f应该是比较好的)

        public float stickToGroundHelperDistance = 0.5f; // stops the character //停止角色运动的距离

        public float slowDownRate = 20f; //当没有输入时,控制器缓慢停下时的比例

        public bool airControl; //当角色在空中时,用户是否能控制角色

        [Tooltip("set it to 0.1 or more if you get stuck in wall")] //鼠标悬停在下面属性上会显示该提示,当然中文是不行的

        public float shellOffset; //减小半径用于减少墙面对卡住角色的影响(值为0.1f是比较好的)

    }

 

    // 这边开始是主类的代码

    public Camera cam;  //当前的相机,组件组织结构跟非刚体的PFS脚本是一样的,分为角色层和镜头层

    public MovementSettings movementSettings = new MovementSettings();  //内部类,移动设置

    public MouseLook mouseLook = new MouseLook();   //我们的老朋友,鼠标控制角色和镜头旋转

    public AdvancedSettings advancedSettings = new AdvancedSettings();  //内部类,高级设置

 

    private Rigidbody m_RigidBody;  //角色的刚体

    private CapsuleCollider m_Capsule;  //胶囊碰撞体

    private float m_YRotation;  //y轴的旋转,这个变量好像并没有什么用

    private Vector3 m_GroundContactNormal;      //与地面接触的法线向量

    private bool m_Jump, m_PreviouslyGrounded, m_Jumping, m_IsGrounded; //当前跳跃、着陆等的一些状态bool变量

 

    // 获取当前的速度

    public Vector3 Velocity

    {

        get { return m_RigidBody.velocity; }

    }

 

    // 当前的状态是否着陆了

    public bool Grounded

    {

        get { return m_IsGrounded; }

    }

 

    // 是否在跳跃中

    public bool Jumping

    {

        get { return m_Jumping; }

    }

 

    // 是否在奔跑状态

    public bool Running

    {

        get

        {

#if !MOBILE_INPUT  //只有在非手机平台上才能奔跑

return movementSettings.Running;

#else

        return false;

#endif

        }

    }

 

    // 初始化

    private void Start()

    {

        m_RigidBody = GetComponent<</span>Rigidbody>();

        m_Capsule = GetComponent<</span>CapsuleCollider>();

        mouseLook.Init (transform, cam.transform);  //给入角色层transform和相机transform

    }

 

    // 每帧更新

    private void Update()

    {

        RotateView();   //旋转角色和镜头

 

        if (CrossPlatformInputManager.GetButtonDown("Jump") && !m_Jump)

        {

            m_Jump = true;

        }

    }

 

    // 固定更新

    private void FixedUpdate()

    {

        GroundCheck();  //判断当前是否在陆地上

        Vector2 input = GetInput(); //获取输入

 

        // input有输入,并且可以控制的情况,处理速度

        if ((Mathf.Abs(input.x) > float.Epsilon || Mathf.Abs(input.y) > float.Epsilon) && (advancedSettings.airControl || m_IsGrounded))

        {

            // 总是以Camera的正方向作为前进方向

            Vector3 desiredMove = cam.transform.forward*input.y + cam.transform.right*input.x;

            desiredMove = Vector3.ProjectOnPlane(desiredMove, m_GroundContactNormal).normalized;    //将速度投射到法线的斜面,并将其单位化

 

            desiredMove.x = desiredMove.x*movementSettings.CurrentTargetSpeed;  //计算各轴的速度

            desiredMove.z = desiredMove.z*movementSettings.CurrentTargetSpeed;

            desiredMove.y = desiredMove.y*movementSettings.CurrentTargetSpeed;

 

            if (m_RigidBody.velocity.sqrMagnitude <</span>

                (movementSettings.CurrentTargetSpeed*movementSettings.CurrentTargetSpeed))  //当前速度小于目标速度,则加个冲力,我最初的实现是直接该刚体的速度,导致损失了其他的力,比如重力、炸弹爆炸对角色的冲力

            {

                m_RigidBody.AddForce(desiredMove*SlopeMultiplier(), ForceMode.Impulse); //斜率跟冲力做计算

            }

        }

 

        if (m_IsGrounded)

        {

            m_RigidBody.drag = 5f;  //在地上把空气阻力设置为5f

 

            // 要跳跃了,空气阻力设置为0,着陆状态到跳跃状态的一个中间状态

            if (m_Jump)

            {

                m_RigidBody.drag = 0f;

                m_RigidBody.velocity = new Vector3(m_RigidBody.velocity.x, 0f, m_RigidBody.velocity.z); //保存x、z轴速度

                m_RigidBody.AddForce(new Vector3(0f, movementSettings.JumpForce, 0f), ForceMode.Impulse);   //在y轴上使用一个冲力

                m_Jumping = true;   //正式进入jump状态

            }

 

            // 没有速度,不在跳跃,并且刚体的速度小于1,则使刚体休眠,节约cpu运算

            if (!m_Jumping && Mathf.Abs(input.x) < float.Epsilon && Mathf.Abs(input.y) < float.Epsilon && m_RigidBody.velocity.magnitude < 1f)

            {

                m_RigidBody.Sleep();

            }

        }

        else

        {

            m_RigidBody.drag = 0f;  //空中,空气阻力设置为0,否则跳得很矮

            if (m_PreviouslyGrounded && !m_Jumping)

            {

                StickToGroundHelper();  //跳跃完成后把刚体粘到地上

            }

        }

        m_Jump = false;

    }

 

    // 斜率跟冲力做计算

    private float SlopeMultiplier()

    {

        float angle = Vector3.Angle(m_GroundContactNormal, Vector3.up); //计算地面斜面法线和z轴正方向的角度

        return movementSettings.SlopeCurveModifier.Evaluate(angle); //根据斜率曲线,计算出当前应该给与物体冲力的比例,斜面斜率到90的时候,即使按了方向键也没有冲力

    }

 

    // 粘到地上助手

    private void StickToGroundHelper()

    {

        RaycastHit hitInfo;

        if (Physics.SphereCast(transform.position, m_Capsule.radius * (1.0f - advancedSettings.shellOffset), Vector3.down, out hitInfo,

                                ((m_Capsule.height/2f) - m_Capsule.radius) +

                                advancedSettings.stickToGroundHelperDistance, ~0, QueryTriggerInteraction.Ignore))   //丢球到地上

        {

            if (Mathf.Abs(Vector3.Angle(hitInfo.normal, Vector3.up)) < 85f)

            {

                m_RigidBody.velocity = Vector3.ProjectOnPlane(m_RigidBody.velocity, hitInfo.normal);    //刚体的速度映射到斜面上

            }

        }

    }

 

    // 获取输入

    private Vector2 GetInput()

    {

            

        Vector2 input = new Vector2

            {

                x = CrossPlatformInputManager.GetAxis("Horizontal"),

                y = CrossPlatformInputManager.GetAxis("Vertical")

            };

movementSettings.UpdateDesiredTargetSpeed(input);

        return input;

    }

 

    // 旋转镜头和角色

    private void RotateView()

    {

        // 当游戏暂停时忽略鼠标移动的影响

        if (Mathf.Abs(Time.timeScale) < float.Epsilon) return;

        

 

        // 在旋转之前获取一下旋转角度

        float oldYRotation = transform.eulerAngles.y;

 

        mouseLook.LookRotation (transform, cam.transform);  //MouseLook处理角色镜头旋转

 

        // 只有在方向键可以控制角色时,才进入判断,其他情况反正xz平面速度为0嘛

        if (m_IsGrounded || advancedSettings.airControl)

        {

            // 旋转刚体的速度,以符合新的角色旋转角度

            Quaternion velRotation = Quaternion.AngleAxis(transform.eulerAngles.y - oldYRotation, Vector3.up);  //获取旋转角度差

            m_RigidBody.velocity = velRotation*m_RigidBody.velocity;    //相乘即旋转增加该角度,这个之前计算出欧拉角相乘是一样的(严格来说是用一个欧拉角来计算出一个Quaternion对象)

        }

    }

 

    // 用一个球从胶囊中间丢下,看看是否碰撞陆地,来判断是否在陆地上,这跟非刚体的FPS脚本是一样的

    private void GroundCheck()

    {

        m_PreviouslyGrounded = m_IsGrounded;

        RaycastHit hitInfo;

        if (Physics.SphereCast(transform.position, m_Capsule.radius * (1.0f - advancedSettings.shellOffset), Vector3.down, out hitInfo,

                                ((m_Capsule.height/2f) - m_Capsule.radius) + advancedSettings.groundCheckDistance, ~0, QueryTriggerInteraction.Ignore))  //有一些不同的是这边用到了几个高级属性,用于减少球的半径,增加投球的距离,使其倾向于判断胶囊底部的碰撞,而非侧边的碰撞

        {

            m_IsGrounded = true;

            m_GroundContactNormal = hitInfo.normal; //获取法线

        }

        else

        {

            m_IsGrounded = false;

            m_GroundContactNormal = Vector3.up;

        }

 

        // 前个固定一帧的状态是不在地上,现在在地上的情况,跳跃状态结束

        if (!m_PreviouslyGrounded && m_IsGrounded && m_Jumping)

        {

            m_Jumping = false;

        }

    }

}

 

嗯嗯,今天分析了4个脚本,感觉前途还很遥远。

 

下边是我自己的一些心情,没兴趣的读者玩家们可以跳过了。

 

很多时候描绘自己的梦想很轻松,自己想要成为什么,但实际上做起来又是另一会事,总会抱怨自己的环境有那些欠缺,但就是不肯自己动手做些什么,拖沓的病、以及大量的计划也让我应接不暇,然而这些东西也只能自己在博文中发一发,说自己未来一定有一个团队的,但是……

 

刚刚看了一圈独立游戏的开发者,发现那些游戏我大部分都玩过,说来惭愧,有些我自己都奉为神作的作品自己一分钱也没有花。

 

多数作品是一些团队完成的,分工明确、目标一致,资金上可能缺一点,不过挺过来的相对都是成功的。有几句话挺触动我的,说是团队的核心成员基本上是最要好的朋友,有不同的技能,但有相同的梦想。

 

想起一个用虚幻引擎做鹿的一个游戏的小青年,初中?反正10几岁吧,就跟着自己的同学组建了一个三人的团队,令人惊叹。

 

感慨自己没有这样一起怀有相同梦想人的同时,认真思考,可能最终只能一个人来做。

 

不过美术、音乐、策划都是相当需要时间的一件事情,自己除了程序以外又什么也没接触过。

 

我自己的创作作品的计划一拖再拖,美其名曰没有时间,现在还有很多前置的计划,不过谁知道是不是内心的恐惧抓住了我,让我不敢开始。我经历过太多太多的失败了,从来也不是那些成功者的一员,或许将来会好一些吧。

 

每次借口说自己没认真做,但是认真做了就能成功吗?我的眼光、能力和性格始终摆在那里,不进不退。我觉得最现实的就是磨练好自己unity技术,拿个还算不错的工资,能够自己生活就行了,过得平凡一些。

 

有时候确实会疑惑,为什么没有超人的能力,还成天想着那些不切实际的梦想。如果没有会不会幸福很多。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有