unity3d俯视角简易移动控制脚本及其易错点小分享

涉及方法:

  1. 本文分别采用了 ‘collider+rigidbody’ / ‘character controller’ 两种方式共四种情况来分享

  2. 在代码中input采用unity基本的“Horizontal”+“Vertical”输入获取(unity新的那个input system用过包但还没自己实际了解过嘿嘿(●ˇ∀ˇ●))

  3. 如果是collider+rigidbody的话,使用的是rigidbody的MovePosition()方法

    如果是character controller的话使用的是该控制器的simplemove函数(对应还有一个move函数,区别在于simplemove在被调用时将使物体具备重力,move函数则和translate相似。)

  4. 对于鼠标控制方向的情况通过raycast+getpoint方法获取当前主摄像机与鼠标产生的射线找到与平面的交点,然后使用transform组件的LookAt方法调整朝向。

【使用character controller】准备:

  1. 在unity中创建好基本的平面和胶囊作为场景和角色(蓝色为z轴,代表transform.forward,即正面)
  2. 为平面添加collider(带2d的是2D项目使用的组件),为胶囊(角色)添加character controller(新建的胶囊会自带一个collider1,在添加character controller后会自带另一个collider2,所以可以将collider1删掉)

【使用character controller】编写move脚本:

第一种情况:角色移动方向与角色朝向一致:

{一些细节和注意事项都写在代码注释中辣~}

using UnityEngine;

/**
 * 功能:角色移动控制[角色移动时永远朝正面]
 * 
 */
public class move : MonoBehaviour
{
    //速度变量
    public float speed;
    //定义角色控制器
    public CharacterController cc;
    void Start()
    {
        //组件cc变量
        cc = transform.GetComponent<CharacterController>();

        //速度赋值
        speed = 10;
    }

    void Update()
    {
        //调用移动方法
        move_by_cc();
    }
    
    void move_by_cc()
    {
        //水平方向的输入获取(即A-D键或←→键)
        float x = Input.GetAxisRaw("Horizontal");
        //垂直方面的输入获取
        float z = Input.GetAxisRaw("Vertical");

        /* GetAxis()的返回值初始为0,在-1到1之间变化,对应的GetAxisRaw()则不会变化,直接返回1或-1 */

        /*上面的input使用GetAxis()时在松开按键后角色会继续移动一小部分距离,会产生一种“冰面滑动”的效果,
        使用GetAxisRaw()的话则即按即动,即松即停*/
        if (Mathf.Abs(x)>0.1f || Mathf.Abs(z) > 0.1f)
        {
            //移动方向
            Vector3 toward_dir = new Vector3(x, 0, z);

            //角色朝向与移动方向一致
            transform.LookAt(transform.position + toward_dir);

            //不同于move函数,这里以秒为单位不能*Time.deltatime,不然会无法移动
            cc.SimpleMove(transform.forward * speed);
        }
    }
}

第二种情况:角色移动方向可以不与角色朝向一致(键盘控制移动,鼠标控制朝向):

{一些细节和注意事项都写在代码注释中辣~}

using UnityEngine;

/**
 * 功能:角色移动控制[键盘控制移动,鼠标控制朝向]
 * 
 */
public class move2 : MonoBehaviour
{
    //速度变量
    public float speed;
    //定义角色控制器
    public CharacterController cc;
    //摄像机
    public Camera viewCamera;
    void Start()
    {
        //组件cc变量
        cc = transform.GetComponent<CharacterController>();

        //速度赋值
        speed = 5;

        //当前主摄像机
        viewCamera = Camera.main;
    }

    void Update()
    {
        //调用移动方法
        move_by_cc();
    }
    void move_by_cc()
    {
    /*移动部分*/
        //水平方向的输入获取(即A-D键或←→键)
        float x = Input.GetAxisRaw("Horizontal");
        //垂直方面的输入获取
        float z = Input.GetAxisRaw("Vertical");

        /* GetAxis()的返回值初始为0,在-1到1之间变化,对应的GetAxisRaw()则不会变化,直接返回1或-1 */

        /*上面的input使用GetAxis()时在松开按键后角色会继续移动一小部分距离,会产生一种“滑滑”的效果,
        使用GetAxisRaw()的话则即按即动,即送即停*/
        if (Mathf.Abs(x) > 0.1f || Mathf.Abs(z) > 0.1f)
        {
            //移动方向
            Vector3 toward_dir = new Vector3(x,0,z);
            //不同于move函数,这里以秒为单位不能*Time.deltatime,不然会无法移动
            //(normalized指单位化,即此时该向量不具备大小仅具备方向)
            cc.SimpleMove(toward_dir.normalized * speed);
        }

    /*鼠标朝向部分*/
        //生成从摄像机发射的射线,该射线穿过当前鼠标位置【因为视角透视原因,鼠标位置并不能代表实际我们希望的朝向,所以需要通过射线找到与平面的实际交点】
        Ray ray = viewCamera.ScreenPointToRay(Input.mousePosition);
        //创建一个平面,第一个参数和第二个参数构成法线,且平面穿过第二个参数点
        Plane groundPlane = new Plane(Vector3.up, Vector3.zero);

        //平面与射线相交返回发射点到相交点的距离
        float rayDistance;

        Vector3 point = Vector3.zero;
        //Raycast计算相交点并返回距离
        if (groundPlane.Raycast(ray, out rayDistance))
        {
            //获取到射线与平面的相交点,耶~
            point = ray.GetPoint(rayDistance);

            //指示线打印-用于测试
            //Debug.DrawLine(ray.origin, point, Color.red);

        }
        //角色朝向-鼠标预期方向
        Vector3 heightCorrectedPoint = new Vector3(point.x, transform.position.y, point.z);
        transform.LookAt(heightCorrectedPoint);
    }
}

------------------------------------------------------------------另一种方式------------------------------------------------------------------

【使用collider+rigidbody】准备:

  1. 在unity中创建好基本的平面和胶囊作为场景和角色(蓝色为z轴,代表transform.forward,即正面)
  2. 为平面添加collider(带2d的是2D项目使用的组件),为胶囊(角色)添加collider和rigidbody(带2d的是2D项目使用的组件)并锁定x,z轴的旋转防止角色倒下。


【使用collider+rigidbody】】编写move脚本:

第一种情况:角色移动方向与角色朝向一致:

{一些细节和注意事项都写在代码注释中辣~}

using UnityEngine;

/**
 * 功能:角色移动控制[角色移动时永远朝正面]
 * 
 */
public class move3 : MonoBehaviour
{
    //速度变量
    public float speed;
    //Rb刚体
    public Rigidbody rb;
    void Start()
    {
        //组件rb变量
        rb = transform.GetComponent<Rigidbody>();

        //速度赋值
        speed = 7;
    }
//【将Update函数修改为FixedUpdate,并且deltaTime修改为fixedDeltatime移动似乎会流畅一些,感觉有一点点变化,但是也不确定是不是心理作用】
    void Update()
    {
        //调用移动方法
        move_by_rb();
    }
    void move_by_rb()
    {
        //水平方向的输入获取(即A-D键或←→键)
        float x = Input.GetAxisRaw("Horizontal");
        //垂直方面的输入获取
        float z = Input.GetAxisRaw("Vertical");

        /* GetAxis()的返回值初始为0,在-1到1之间变化,对应的GetAxisRaw()则不会变化,直接返回1或-1 */

        /*上面的input使用GetAxis()时在松开按键后角色会继续移动一小部分距离,会产生一种“滑滑”的效果,
        使用GetAxisRaw()的话则即按即动,即送即停*/
        if (Mathf.Abs(x) > 0.1f || Mathf.Abs(z) > 0.1f)
        {
            //移动方向
            Vector3 toward_dir = new Vector3(x, 0, z);

            //角色朝向移动方向
            transform.LookAt(transform.position + toward_dir);

            //MovePosition方法,以帧为单位,记得*Time.deltaTime哦~(normalized指单位化,即此时该向量不具备大小仅具备方向)
            rb.MovePosition(rb.position + toward_dir.normalized * speed * Time.deltaTime);
        }
    }
}

第二种情况:角色移动方向可以不与角色朝向一致(键盘控制移动,鼠标控制朝向):

{一些细节和注意事项都写在代码注释中辣~}

using UnityEngine;

/**
 * 功能:角色移动控制[角色移动时永远朝正面]
 * 
 */
public class move4 : MonoBehaviour
{
    //速度变量
    public float speed;
    //Rb刚体
    public Rigidbody rb;
    //摄像机
    public Camera viewCamera;
    void Start()
    {
        //组件rb变量
        rb = transform.GetComponent<Rigidbody>();

        //速度赋值
        speed = 7;

        //主摄像机
        viewCamera = Camera.main;
    }
    //【将Update函数修改为FixedUpdate,并且deltaTime修改为fixedDeltatime移动似乎会流畅一些,感觉有一点点变化,但是也不确定是不是心理作用】
    void Update()
    {
        //调用移动方法
        move_by_rb();
    }
    void move_by_rb()
    {
    /*移动部分*/
        //水平方向的输入获取(即A-D键或←→键)
        float x = Input.GetAxisRaw("Horizontal");
        //垂直方面的输入获取
        float z = Input.GetAxisRaw("Vertical");

        /* GetAxis()的返回值初始为0,在-1到1之间变化,对应的GetAxisRaw()则不会变化,直接返回1或-1 */

        /*上面的input使用GetAxis()时在松开按键后角色会继续移动一小部分距离,会产生一种“滑滑”的效果,
        使用GetAxisRaw()的话则即按即动,即送即停*/
        if (Mathf.Abs(x) > 0.1f || Mathf.Abs(z) > 0.1f)
        {
            //移动方向
            Vector3 toward_dir = new Vector3(x, 0, z);

            //MovePosition方法,以帧为单位,记得*Time.deltaTime哦~(normalized指单位化,即此时该向量不具备大小仅具备方向)
            rb.MovePosition(rb.position + toward_dir.normalized * speed * Time.deltaTime);
        }
    /*鼠标朝向部分*/
        //生成从摄像机发射的射线,该射线穿过当前鼠标位置【因为视角透视原因,鼠标位置并不能代表实际我们希望的朝向,所以需要通过射线找到与平面的实际交点】
        Ray ray = viewCamera.ScreenPointToRay(Input.mousePosition);
        //创建一个平面,第一个参数和第二个参数构成法线,且平面穿过第二个参数点
        Plane groundPlane = new Plane(Vector3.up, Vector3.zero);

        //平面与射线相交返回发射点到相交点的距离
        float rayDistance;

        Vector3 point = Vector3.zero;
        //Raycast计算相交点并返回距离
        if (groundPlane.Raycast(ray, out rayDistance))
        {
            //获取到射线与平面的相交点,耶~
            point = ray.GetPoint(rayDistance);

            //指示线打印-用于测试
            //Debug.DrawLine(ray.origin, point, Color.red);

        }
        //角色朝向-鼠标预期方向
        Vector3 heightCorrectedPoint = new Vector3(point.x, transform.position.y, point.z);
        transform.LookAt(heightCorrectedPoint);
    }
}

以上就是本次的分享内容辣~(●ˇ∀ˇ●),可以看到两种方式的本质操作是没有什么较大的区别的,仅在于准备部分的不同和代码中对应组件的调用不同而已,不过这里还要告诉大家的是,一般使用character controller是为了自定义一些更‘游戏化’的物理效果,而collider+rigidbody的方法则更偏向于真实物理世界,但是很多时候游戏不一定越真实就越舒服。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>