动画之人物移动旋转
一、动画状态机控制
1、导入人物动画,调整为人形动画,做好前期准备工作(修改动画是否循环,动画范围,动画旋转锁定,Y轴,XZ轴锁定等)
2、添加Animator组件,创建动画状态机
3、打开动画状态机,把人物待机、走路、跑步、向左跑、向右跑的动画添加到状态机中。
4、添加参数Speed、ISRun、TurnRun/移动速度、是否跑步、转向速度
二、混合树Blend Tree
1、1D混合树控制一个方向运动
在Animator面板下创建BlendTree,双击BlendTree进入编辑界面
2、2D混合树通过两个变量控制,一个使用速度控制PosY,另一个是用角度控制PosX
三、功能代码
1.相机跟随移动
1 public Transform playerTran;//目标人物 2 Vector3 offset; 3 float smoothing=2; 4 private void Start() 5 { 6 offset = transform.position - playerTran.position;//相机位置与人的位置差 7 } 8 private void Update() 9 { 10 Vector3 targetPosition = playerTran.position + playerTran.TransformDirection(offset); 11 transform.position = Vector3.Lerp(transform.position, targetPosition, Time.deltaTime * smoothing); 12 transform.LookAt(playerTran.position); 13 }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class PlayerAnimationController : MonoBehaviour { public Animator playerAni; int speedID = Animator.StringToHash( "Speed" ); int IsRunID = Animator.StringToHash( "IsRun" ); int turnRun = Animator.StringToHash( "TurnRun" ); void Update() { playerAni.SetFloat(speedID, Input.GetAxis( "Vertical" )); //控制移动 playerAni.SetFloat(turnRun, Input.GetAxis( "Horizontal" )); //控制转向 if (Input.GetKeyDown(KeyCode.LeftShift)) //控制是否加速 { playerAni.SetBool(IsRunID, true ); } else if (Input.GetKeyUp(KeyCode.LeftShift)) { playerAni.SetBool(IsRunID, false ); } } } |
四、控制人物FQ
1、将修改完的FQ动画添加到状态机中,新添加变量SpeedX、SpeedY、IsOverTheWall
2、添加功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | int speedX = Animator.StringToHash( "SpeedX" ); //转向的速度<br> int speedY = Animator.StringToHash("SpeedY");//前进的速度<br> int IsOverTheWall = Animator.StringToHash("IsOverTheWall");//FQ bool isOverTheWall = false ; //是否FQ private Vector3 margetPosition=Vector3.zero; //FQ匹配的位置 //FQ void PlayerVault() { isOverTheWall = false ; playerAni.SetFloat(speedX, Input.GetAxis( "Horizontal" ) * 126); playerAni.SetFloat(speedY, Input.GetAxis( "Vertical" ) * 3.6f); if (playerAni.GetFloat(speedY) > 2 && playerAni.GetCurrentAnimatorStateInfo(0).IsName( "Locomotion" )) { RaycastHit hit; if (Physics.Raycast(transform.position + Vector3.up * 0.3f, transform.forward, out hit, 4)) { if (hit.collider.tag == "Obstacle" ) { if (hit.distance > 3) { Vector3 point = hit.point; point.y = hit.collider.transform.position.y + hit.collider.bounds.size.y + 0.1f; margetPosition = point; //目标位置 isOverTheWall = true ; } } } } playerAni.SetBool(IsOverTheWall, isOverTheWall); if (playerAni.GetCurrentAnimatorStateInfo(0).IsName( "Vault" ) && playerAni.IsInTransition(0) == false ) { //指定了匹配的位置、角度、身体的具体部位、权重、匹配开始时间和结束时间 playerAni.MatchTarget(margetPosition, Quaternion.identity, AvatarTarget.LeftHand, new MatchTargetWeightMask(Vector3.one, 0), 0.32f, 0.4f); } } |
3、给人物添加CharacterController,在动画状态机中新添加变量Collider来控制播放FQ动画是角色控制器与墙不发生碰撞
4、设置FQ动画下Curves,先确定两帧(开始FQ和结束FQ),编辑两个点大于0.5,并把设置的Collider变量放置在Curves上
5、效果展示
五、控制人物滑动
1、将修改完的滑动动画添加到状态机中,新添加变量Slider控制是否滑动
2、动画状态机连接情况如图
3、添加功能代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | int slider = Animator.StringToHash( "Slider" ); //滑动 int playcollider = Animator.StringToHash( "Collider" ); void Update() { PlayerSlider(); PlayerVault(); playerCharacterController.enabled = playerAni.GetFloat(playcollider) < 0.45f; } //滑动 void PlayerSlider() { bool IsSlider = false ; if (playerAni.GetFloat(speedY) > 2 && playerAni.GetCurrentAnimatorStateInfo(0).IsName( "Locomotion" )) { RaycastHit hit; if (Physics.Raycast(transform.position + Vector3.up * 1.5f, transform.forward, out hit, 3)) { if (hit.collider.tag == "Obstacle" ) { if (hit.distance > 2) { Vector3 point = hit.point; point.y = -10; margetPosition = point + transform.forward * 2; IsSlider = true ; } } } } playerAni.SetBool(slider, IsSlider); if (playerAni.GetCurrentAnimatorStateInfo(0).IsName( "Slide" ) && playerAni.IsInTransition(0) == false ) { Debug.Log( "滑动" ); playerAni.MatchTarget(margetPosition, Quaternion.identity, AvatarTarget.Root, new MatchTargetWeightMask( new Vector3(1, 0, 1), 0), 0.17f, 0.67f); } } |
4、同样也给滑动动画下Curves添加Collider,在播放动画时关闭角色控制器
5、效果展示
六、动画状态机Layers层设置
Weight:1(对其他层的动画的影响权重值,1:完全影响(如果Blending为Additive的话,那么会将所有层的Additive的Weight的值进行同时计算),0:完全不影响)
Mask:HandWaveLMask(下面有图,我们只要保留左手绿色即可)
Blending:Override(该层的动画将会覆盖其他的)
1、举木头动画切换
2、由于举木头动画与移动动画有影响,创骨骼遮罩在Projects——>Avatar Mask设置骨骼遮罩
3、通过IK设置左右手位置,与木头更加吻合
勾选上图中IK Pass,添加两个点是左右手位置
代码如下:
1 public Transform leftHandPos;//左手位置 2 public Transform rightHandPos;//右手位置 3 int holdLog = Animator.StringToHash("IsHoldLog");//是否举起 4 private void OnTriggerEnter(Collider other) 5 { 6 if (other.tag=="Wood") 7 { 8 Destroy(other.gameObject); 9 CreateWood(); 10 } 11 } 12 //显示木头 13 void CreateWood() 14 { 15 newWood.SetActive(true); 16 playerAni.SetBool(holdLog, true); 17 } 18 //通过IK动画设置举木头手的位置 19 private void OnAnimatorIK(int layerIndex) 20 { 21 float Weight = playerAni.GetBool(holdLog) ? 1 : 0; 22 if (layerIndex == 1) 23 { 24 //左手 25 playerAni.SetIKPosition(AvatarIKGoal.LeftHand, leftHandPos.position);//设置左手位置 26 playerAni.SetIKPositionWeight(AvatarIKGoal.LeftHand, Weight);//设置左手权重 27 playerAni.SetIKRotation(AvatarIKGoal.LeftHand, leftHandPos.rotation);//设置左手旋转 28 playerAni.SetIKRotationWeight(AvatarIKGoal.LeftHand, Weight); 29 //右手 30 playerAni.SetIKPosition(AvatarIKGoal.RightHand, rightHandPos.position); 31 playerAni.SetIKPositionWeight(AvatarIKGoal.RightHand, Weight); 32 playerAni.SetIKRotation(AvatarIKGoal.RightHand, rightHandPos.rotation); 33 playerAni.SetIKRotationWeight(AvatarIKGoal.RightHand, Weight); 34 35 } 36 }
4、效果如图
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?