🟥给跳跃添加角色控制器

添加角色控制器Character Controller后,人物不会因动画问题而导致Y轴变化,人物会始终位于水平地面上

但任务可能因为和墙体碰撞,而无法翻越,我们需在特定时刻禁用角色控制器

思路1:判断当前是否处于Vault状态,是则禁用

思路2:思路一缺陷在于Vault未跳跃或跳跃结束后的部分动画的Character Controller处于禁用状态,它的Y轴位置还是会发生变化,或处于地面以下。通过编辑动画,给动画添加Curve(Curve全程与动画match),在特定的起跳和落地两个区域添加关键帧,将这块区域的Curve值设置的与其他区域不同,通过判断Curve值是否位于这个区间,来禁用启用Character Controller

    我们可以在Animator—Parameters里另外添加和Curve同名的参数,系统会自动识别这个参数与Curve名相同,而产生关联,Curve值运行时便在这里显示,同时我们通过判断这个值的大小,来启用或关闭角色控制器

unity eventsystem 控制人物 unity角色控制器添加到模型_项目开发

代码:

AvatarTarget.Root:角色的根节点

using UnityEngine;

public class Player : MonoBehaviour {

    private Animator anim;
    private int speedRotateID = Animator.StringToHash("SpeedRotate");
    private int speedZID = Animator.StringToHash("SpeedZ");
    int vaultID = Animator.StringToHash("Vault");
    private Vector3 matchTarget;  //用来匹配目标点

    private CharacterController characterController;
    private int controllerID = Animator.StringToHash("Controller");

    void Start () {
        anim = GetComponent<Animator>();
        characterController = GetComponent<CharacterController>();
	}
	
	void Update () {

        anim.SetFloat(speedZID, Input.GetAxis("Vertical") * 4.5f);
        anim.SetFloat(speedRotateID, Input.GetAxis("Horizontal")*126);

        if (anim.GetFloat(speedZID) > 3 && anim.GetCurrentAnimatorStateInfo(0).IsName("LocalMotion"))
        {
            RaycastHit hit;
            if(Physics.Raycast(transform.position+Vector3.up*0.3f,transform.forward,out hit, 4.5f))
            {
                if (hit.collider.tag == "Obstacle")
                {
                    if (hit.distance > 3)
                    {
                        Vector3 point = hit.point;
                        point.y = hit.transform.position.y+ hit.collider.bounds.size.y+0.09f;  //普通的cube在水平面时默认中心高度y=0.5,但此模型水平中心高度为0(和底边一致)!,所以是它最低点位置+Y轴的大小
                        matchTarget =point;

                        anim.SetBool(vaultID, true);
                    }
                    
                }
            }
            else anim.SetBool(vaultID, false);
        }

        if (anim.GetCurrentAnimatorStateInfo(0).IsName("Vault"))
        {
            anim.MatchTarget(matchTarget, Quaternion.identity, AvatarTarget.LeftHand, new MatchTargetWeightMask(Vector3.one, 0), 0.32f, 0.4f);
        }

        //if (anim.GetFloat(controllerID) > 0.5f)
        //{
        //    characterController.enabled = false;
        //}
        //else
        //{
        //    characterController.enabled = true;
        //}
        characterController.enabled = anim.GetFloat(controllerID) < 0.5f;
    }
}

🟧 添加滑动动画,并给滑动添加角色控制器和MatchTarget()

在滑动动画,我们会发现角色脚部运动不正常,我们可以重新配置下骨骼,修改脚部骨骼角度,并添加Curve、Parameter

    pose—Enforce T-Pose

unity eventsystem 控制人物 unity角色控制器添加到模型_游戏引擎_02

代码:

思路:在1.5m高度检测前方是否有障碍物,此位置既不会检测到墙,且又能检测到栏杆

1️⃣ 如图所示,修改Parameters名字

unity eventsystem 控制人物 unity角色控制器添加到模型_关键帧_03

2️⃣ 代码

    注意:Character Controller激活关闭方式要分开判断,避免第二个表达式将第一个表达式覆盖

    MatchTarget目的用于匹配滑动后滑到射线碰撞点前方两米处,避免滑行中由于滑行距离不足造成的干涉现象

using UnityEngine;

public class Player : MonoBehaviour {

    private Animator anim;
    private int speedRotateID = Animator.StringToHash("SpeedRotate");
    private int speedZID = Animator.StringToHash("SpeedZ");
    int vaultID = Animator.StringToHash("Vault");
    private Vector3 matchTarget;

    private CharacterController characterController;
    private int vaultCurveID = Animator.StringToHash("vaultCurve");
    private int sliderID = Animator.StringToHash("Slider");
    private int sliderCurveID = Animator.StringToHash("sliderCurve");

    void Start () {
        anim = GetComponent<Animator>();
        characterController = GetComponent<CharacterController>();
	}
	
	void Update () {

        anim.SetFloat(speedZID, Input.GetAxis("Vertical") * 4.5f);
        anim.SetFloat(speedRotateID, Input.GetAxis("Horizontal")*126);

        ProcessVault();
        ProcessSlider();
    }

    private void ProcessVault()
    {
        if (anim.GetFloat(speedZID) > 3 && anim.GetCurrentAnimatorStateInfo(0).IsName("LocalMotion"))
        {
            RaycastHit hit;
            if (Physics.Raycast(transform.position + Vector3.up * 0.3f, transform.forward, out hit, 4.5f))
            {
                if (hit.collider.tag == "Obstacle")
                {
                    if (hit.distance > 3)
                    {
                        Vector3 point = hit.point;
                        point.y = hit.transform.position.y + hit.collider.bounds.size.y + 0.09f;  //普通的cube在水平面时默认中心高度y=0.5,但此模型水平中心高度为0(和底边一致)!,所以是它最低点位置+Y轴的大小
                        matchTarget = point;

                        anim.SetBool(vaultID, true);
                    }

                }
            }
            else anim.SetBool(vaultID, false);
        }

        characterController.enabled = anim.GetFloat(vaultCurveID) < 0.5f;

        if (anim.GetCurrentAnimatorStateInfo(0).IsName("Vault"))
        {
            anim.MatchTarget(matchTarget, Quaternion.identity, AvatarTarget.LeftHand, new MatchTargetWeightMask(Vector3.one, 0), 0.32f, 0.4f);
        }

        //if (anim.GetFloat(controllerID) > 0.5f)
        //{
        //    characterController.enabled = false;
        //}
        //else
        //{
        //    characterController.enabled = true;
        //}
        
    }
    private void ProcessSlider()
    {
        if (anim.GetFloat(speedZID) > 3 && anim.GetCurrentAnimatorStateInfo(0).IsName("LocalMotion"))
        {
            RaycastHit hit;
            if (Physics.Raycast(transform.position + Vector3.up * 1.5f, transform.forward, out hit, 5f))
            {
                if (hit.collider.tag == "Obstacle")
                {
                    if (hit.distance <2.8)
                    {
                        Vector3 point = hit.point;
                        point.y = 0 ; 
                        matchTarget = point+transform.forward*2;
                        anim.SetBool(sliderID, true);
                    }
                }
            }
        }
        else anim.SetBool(sliderID, false);

        if (anim.GetCurrentAnimatorStateInfo(0).IsName("Slider"))
        {
            anim.MatchTarget(matchTarget, Quaternion.identity, AvatarTarget.Root, new MatchTargetWeightMask(new Vector3(1,0,1), 0), 0.38f, 0.67f);
        }

        if (anim.GetCurrentAnimatorStateInfo(0).IsName("Slider" ))
        {
            characterController.enabled = anim.GetFloat(sliderCurveID) < 0.5f;
        }
    }
}

动画展示:

unity eventsystem 控制人物 unity角色控制器添加到模型_项目开发_04