Unity 2D角色控制器(橫板滾軸)

unity自帶的2D剛體在坡上會往下滑。而且也是會有可能穿牆,或碰撞到其他物體時鬼畜抖動。

設計思路

1、利用射線進行判斷碰撞防止穿牆:

原理傳送門

2、在地面時往地面發射兩條射線,獲得的兩個點,兩個點形成的線,這個線是角色的移動向量。

下面是一個自定義的一個橫板2D角色控制器,包含有簡單的移動和跳躍。

代碼:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CharacterController2D : MonoBehaviour
{
    /// <summary>
    /// 角色移動軸
    /// </summary>
    public Vector2 axis;
    /// <summary>
    /// 角色受到的重力
    /// </summary>
    public float gravity = 10;
    /// <summary>
    /// 角色限制的移動速度
    /// </summary>
    public float moveSpeed = 30;

    /// <summary>
    /// 中心點偏移
    /// </summary>
    public Vector3 offset;

    /// <summary>
    /// 最大速度向量
    /// </summary>
    public float maxSpeed = 100;

    /// <summary>
    /// 前長度:該值決定角色到前牆的碰撞距離
    /// </summary>
    public float forwardLength = 1;
    /// <summary>
    /// 底長度:該值決定角色到地面的距離
    /// </summary>
    public float bottonLnegth = 0.5f;
    /// <summary>
    /// 底下的先與旁邊的線偏移距離
    /// </summary>
    public float bottonForwardOffset = 0.25f;


    /// <summary>
    /// 上一次跳躍的到現在時間
    /// </summary>
    public float JumpTime { get => jumpTime; }
    /// <summary>
    /// 返回X軸的絕對值
    /// </summary>
    public float AxisAbs { get => axisAbs; }
    /// <summary>
    /// 角色是否進行了X反轉
    /// </summary>
    public bool ReversalX { get => reversalX; }
    /// <summary>
    /// 角色是否在地面
    /// </summary>
    public bool OnGround { get => onGround; }
    /// <summary>
    /// 角色的實際位移速度
    /// </summary>
    public Vector3 Velocity { get => velocity; }

    private bool reversalX;
    private float axisAbs;
    private bool onGround;
    private Vector3 velocity;
    private float jumpTime;


    private void OnDrawGizmos()
    {
        Gizmos.color = Color.green;
        Gizmos.DrawLine(transform.position + offset, transform.position + offset + Forward * (forwardLength + AxisAbs * Time.deltaTime));
        Vector3 forward = transform.position + offset + Forward * bottonForwardOffset;
        Gizmos.DrawLine(transform.position + offset, (transform.position + offset) + Vector3.down * bottonLnegth);
        Gizmos.DrawLine(forward, forward + Vector3.down * bottonLnegth);
    }


    void Update()
    {
        if (axis.x < 0)
        {
            reversalX = true;
        }
        else if (axis.x > 0)
        {
            reversalX = false;
        }
        axisAbs = Mathf.Abs(axis.x);
        IsOnGround();
        velocity = Vector3.ClampMagnitude(velocity, maxSpeed);
        transform.position += velocity * Time.deltaTime;

        jumpTime += Time.deltaTime;
    }


    /// <summary>
    /// 跳躍
    /// </summary>
    public void Jump()
    {
        if (OnGround)
        {
            onGround = false;
            velocity += Vector3.up * 20;
            jumpTime = 0;
        }
    }


    /// <summary>
    /// 獲取角色前面
    /// </summary>
    public Vector3 Forward
    {
        get
        {
            Vector3 forward = transform.right;
            forward.x = ReversalX ? forward.x * -1 : forward.x;
            return forward;
        }
    }

    private void IsOnGround()
    {
        RaycastHit2D bottonHit, forwardHit;
        GetBottonRaycastHit(out bottonHit, out forwardHit);

        if (jumpTime > 0.2 && bottonHit && forwardHit)
        {
            onGround = true;

            velocity.y = 0;
            transform.position = new Vector3(
                transform.position.x,
              bottonHit.point.y + bottonLnegth - 0.1f - offset.y,
                transform.position.z);

            //速度方向
            Vector2 speedDirection = (forwardHit.point - bottonHit.point).normalized;
            //坡度
            velocity = speedDirection * AxisAbs;


        }
        else
        {
            velocity.y -= gravity * Time.deltaTime * moveSpeed;
        }


        //判斷是否撞牆
        RaycastHit2D rf = GetForwardHit();
        if (rf)
        {
            velocity.x = 0;
            transform.position = new Vector3(
            rf.point.x + (axis.x < 0 ? forwardLength - 0.1f : -forwardLength + 0.1f) - offset.x,
            transform.position.y,
            transform.position.z);
        }
    }

    private void GetBottonRaycastHit(out RaycastHit2D bottonHit, out RaycastHit2D forwardHit)
    {
        //前射線位置
        Vector3 forward = transform.position + offset + Forward * bottonForwardOffset;

        //發射射線
        bottonHit = Physics2D.Raycast(transform.position + offset, Vector2.down, bottonLnegth, LayerMask.GetMask("Ground"));
        forwardHit = Physics2D.Raycast(forward, Vector2.down, bottonLnegth, LayerMask.GetMask("Ground"));
    }

    public RaycastHit2D GetForwardHit()
    {
        float line = forwardLength + AxisAbs * Time.deltaTime;
        //射線
        RaycastHit2D rf = Physics2D.Raycast(transform.position + offset, Forward, line, LayerMask.GetMask("Ground"));

        Debug.DrawRay(transform.position + offset, Forward * line, Color.green, 0.1f);

        return rf;
    }
    
}


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章