实现主角翻越障碍物的动画
准备工作:首先在动画状态机中做好两种动画状态的切换(行走-翻越/奔跑-翻越)如下
这里使用了动画混合树
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerBehavior : MonoBehaviour
{
public GameObject woolGo;
public Transform leftHandIKPos;
public Transform rightHandIKPos;
Animator anim;
//int speedID = Animator.StringToHash("Speed");
//int isSpwwdUpID = Animator.StringToHash("IsSpeedUp");
//int horizontalID = Animator.StringToHash("Horizontal");
int speedRotate = Animator.StringToHash("SpeedRotate");//PosX(Angle Speed(Deg))旋转角度
int speedZ = Animator.StringToHash("SpeedZ");//PosY速度(最大速度为4.2)
int vaultID = Animator.StringToHash("Vault");//翻越状态
int slideID = Animator.StringToHash("Slide");
int colliderID = Animator.StringToHash("Collider");
int isHoldLogID = Animator.StringToHash("IsHoldLog");
private Vector3 matchTarget = Vector3.zero;//目标匹配位置
private CharacterController cc;
void Start()
{
anim = this.GetComponent<Animator>();
cc = this.GetComponent<CharacterController>();
}
void Update()
{
anim.SetFloat(speedZ, Input.GetAxis("Vertical") * 4.2f);
anim.SetFloat(speedRotate, Input.GetAxis("Horizontal") * 126);
ProcessVault();
ProcessSlide();
cc.enabled = anim.GetFloat(colliderID) < 0.5f;
//anim.SetFloat(speedID, Input.GetAxis("Vertical")*4.1f);
//anim.SetFloat(horizontalID, Input.GetAxis("Horizontal"));
//if (Input.GetKeyDown(KeyCode.LeftShift))
//{
// anim.SetBool(isSpwwdUpID, true);
//}
//if (Input.GetKeyUp(KeyCode.LeftShift))
//{
// anim.SetBool(isSpwwdUpID, false);
//}
}
private void ProcessSlide()
{
bool isSlide = false;
if (anim.GetFloat(speedZ) >= 3 && anim.GetCurrentAnimatorStateInfo(0).IsName("Locomotion"))
{
RaycastHit hitInfo;
if (Physics.Raycast(transform.position + Vector3.up * 1.5f, transform.forward, out hitInfo, 3f))
{
if (hitInfo.collider.tag == "Obstacle")
{
if (hitInfo.distance > 2f)
{
Vector3 point = hitInfo.point;//射线碰撞的点
point.y = 0;
matchTarget = point + transform.forward * 2;
isSlide = true;
}
}
}
}
anim.SetBool(slideID, isSlide);
if (anim.GetCurrentAnimatorStateInfo(0).IsName("Slide") && anim.IsInTransition(0) == false)
{
//匹配目标位置、旋转、和那个位置匹配、匹配目标权重遮罩、 (0.32f, 0.42f|开始匹配位置时间,这个时间是一个差值运算从前一个时间开始(开始起跳)到后一个时间结束(撑到目标位置)完成差值运算,)
anim.MatchTarget(matchTarget, Quaternion.identity, AvatarTarget.Root, new MatchTargetWeightMask(new Vector3(1, 0, 1), 0), 0.17f, 0.67f);
}
}
void ProcessVault()
{
bool isVault = false;
//当速度大于3时检测翻越(并且当前状态是在Locomotion状态期间)
if (anim.GetFloat(speedZ) >= 3 && anim.GetCurrentAnimatorStateInfo(0).IsName("Locomotion"))
{
RaycastHit hitInfo;
//通过射线检测来判断主角前方是否有障碍物
if (Physics.Raycast(transform.position + Vector3.up * 0.3f, transform.forward, out hitInfo, 4.5f))
{
if (hitInfo.collider.tag == "Obstacle")
{
//射线检测距离大于3的时候去检测
if (hitInfo.distance > 3f)
{
Vector3 point = hitInfo.point;//射线碰撞的点
point.y = hitInfo.collider.transform.position.y + hitInfo.collider.bounds.size.y + 0.05f;//取得射线碰到的点的高度+碰到物体的高度+(位置的高度偏差)
matchTarget = point;
isVault = true;//翻越
}
}
}
}
anim.SetBool(vaultID, isVault);//动画状态机设置翻越状态
//设置匹配目标时限制在(当前状态为翻越状态并且动画融合为false)
if (anim.GetCurrentAnimatorStateInfo(0).IsName("Vault") && anim.IsInTransition(0) == false)
{
//匹配目标位置、旋转、和那个位置匹配、匹配目标权重遮罩、 (0.32f, 0.42f|开始匹配位置时间,这个时间是一个差值运算从前一个时间开始(开始起跳)到后一个时间结束(撑到目标位置)完成差值运算,)
anim.MatchTarget(matchTarget, Quaternion.identity, AvatarTarget.LeftHand, new MatchTargetWeightMask(Vector3.one, 0), 0.32f, 0.42f);
}
}
private void OnTriggerEnter(Collider other)
{
if (other.tag == "Log")
{
Destroy(other.gameObject);
CarryWool();
}
}
void CarryWool()
{
woolGo.SetActive(true);
anim.SetBool(isHoldLogID, true);
}
//每帧都在调用
private void OnAnimatorIK(int layerIndex)
{
if (layerIndex == 1)
{
int weight = anim.GetBool(isHoldLogID) ? 1 : 0;//设置权重
anim.SetIKPosition(AvatarIKGoal.LeftHand, leftHandIKPos.position);
anim.SetIKRotation(AvatarIKGoal.LeftHand, leftHandIKPos.rotation);
anim.SetIKPositionWeight(AvatarIKGoal.LeftHand, weight);
anim.SetIKRotationWeight(AvatarIKGoal.LeftHand, weight);
anim.SetIKPosition(AvatarIKGoal.RightHand, rightHandIKPos.position);
anim.SetIKRotation(AvatarIKGoal.RightHand, rightHandIKPos.rotation);
anim.SetIKPositionWeight(AvatarIKGoal.RightHand, weight);
anim.SetIKRotationWeight(AvatarIKGoal.RightHand, weight);
}
}
}