🟥给跳跃添加角色控制器
添加角色控制器Character Controller后,人物不会因动画问题而导致Y轴变化,人物会始终位于水平地面上
但任务可能因为和墙体碰撞,而无法翻越,我们需在特定时刻禁用角色控制器
思路1:判断当前是否处于Vault状态,是则禁用
思路2:思路一缺陷在于Vault未跳跃或跳跃结束后的部分动画的Character Controller处于禁用状态,它的Y轴位置还是会发生变化,或处于地面以下。通过编辑动画,给动画添加Curve(Curve全程与动画match),在特定的起跳和落地两个区域添加关键帧,将这块区域的Curve值设置的与其他区域不同,通过判断Curve值是否位于这个区间,来禁用启用Character Controller
我们可以在Animator—Parameters里另外添加和Curve同名的参数,系统会自动识别这个参数与Curve名相同,而产生关联,Curve值运行时便在这里显示,同时我们通过判断这个值的大小,来启用或关闭角色控制器
代码:
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
代码:
思路:在1.5m高度检测前方是否有障碍物,此位置既不会检测到墙,且又能检测到栏杆
1️⃣ 如图所示,修改Parameters名字
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;
}
}
}
动画展示: