Unity滑动动画无法停止问题求助:StartCoroutine异常排查
Unity角色控制器滑动功能问题排查与修复
问题描述
按下空格键触发滑动动画后出现两个异常:
- 滑动动画无法自动停止
- 滑动启动时角色速度没有变化
怀疑问题出在第137行的StartCoroutine()调用中,此前滑动功能为独立脚本,集成到PlayerController后出现该问题。
原代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class PlayerController : MonoBehaviour { public bool isSliding = false; public CapsuleCollider2D regularColl; public CapsuleCollider2D slideColl; SpriteRenderer rend; public float movementSpeed = 3.0f; Vector2 movement = new Vector2(); Rigidbody2D rigidbody2D; [SerializeField] float normalSpeed, runSpeed, crouchSpeed, slideSpeed; Animator animator; void Start() { rend = GetComponent<SpriteRenderer>(); animator = GetComponent<Animator>(); rigidbody2D = GetComponent<Rigidbody2D>(); } private void Update() { UpdateState(); if (Input.GetKeyDown(KeyCode.Space)) prefromSlide(); } private void FixedUpdate() { MoveCharacter(); } private void MoveCharacter() { movement.x = Input.GetAxisRaw("Horizontal"); movement.y = Input.GetAxisRaw("Vertical"); if (movement.x > 0) { rend.flipX = true; } else if (movement.x < 0) { rend.flipX = false; } rigidbody2D.velocity = movement.normalized * movementSpeed; Vector3 moveDir = (Vector3.forward * movement.y) + (Vector3.right * movement.x); } private void UpdateState() { //걷기 if (Mathf.Approximately(movement.x, 0) && Mathf.Approximately(movement.y, 0)) { animator.SetBool("isMove", false); } else { animator.SetBool("isMove", true); } //달리기 if (Input.GetKey(KeyCode.LeftShift)) { movementSpeed = runSpeed; animator.SetBool("isRun", true); } else if (Input.GetKey(KeyCode.LeftControl)) { movementSpeed = crouchSpeed; animator.SetBool("isCrouch", true); } else { movementSpeed = normalSpeed; animator.SetBool("isRun", false); movementSpeed = normalSpeed; animator.SetBool("isCrouch", false); } //대기상태 x방향 설정 if (movement.x > 0) { animator.SetFloat("horIdle", 1); animator.SetFloat("verIdle", 0); } else if (movement.x < 0) { animator.SetFloat("horIdle", -1); animator.SetFloat("verIdle", 0); } //대기상태 y방향 설정 if (movement.y > 0) { animator.SetFloat("verIdle", 1); animator.SetFloat("horIdle", 0); } else if (movement.y < 0) { animator.SetFloat("verIdle", -1); animator.SetFloat("horIdle", 0); } animator.SetFloat("horizontal", movement.x); animator.SetFloat("vertical", movement.y); } private void prefromSlide(){ isSliding = true; animator.SetBool ("isSlide", true); regularColl.enabled = false; slideColl.enabled = true; if (!rend.flipX) { GetComponent<Rigidbody>().AddForce (Vector2.right * slideSpeed); } else{ GetComponent<Rigidbody>().AddForce (Vector2.left * slideSpeed); } StartCoroutine (stopSlide()); } IEnumerator stopSlide(){ yield return new WaitForSeconds (0.8f); animator.SetBool ("isSlide", false); regularColl.enabled = true; slideColl.enabled = false; isSliding = false; } }
问题根源分析
- 滑动速度无效:代码错误调用
GetComponent<Rigidbody>()获取3D刚体,但角色使用的是Rigidbody2D,导致加力逻辑完全不执行;同时MoveCharacter()方法每次FixedUpdate都会重置速度,直接覆盖滑动加力效果。 - 滑动动画无法停止:
UpdateState()方法未判断isSliding状态,会在滑动过程中持续修改其他动画参数,干扰滑动动画的状态;且未限制重复触发滑动,连续按空格会多次启动协程,导致状态混乱。
修复方案
1. 修正刚体组件调用
将prefromSlide中的3D刚体调用改为已初始化的Rigidbody2D对象,同时添加重复触发限制:
private void prefromSlide(){ if (isSliding) return; // 防止重复触发 isSliding = true; animator.SetBool ("isSlide", true); regularColl.enabled = false; slideColl.enabled = true; // 使用已获取的Rigidbody2D组件 if (!rend.flipX) { rigidbody2D.AddForce(Vector2.right * slideSpeed); } else{ rigidbody2D.AddForce(Vector2.left * slideSpeed); } StartCoroutine (stopSlide()); }
2. 避免移动逻辑覆盖滑动速度
在MoveCharacter()中添加isSliding判断,滑动时不重置速度:
private void MoveCharacter() { movement.x = Input.GetAxisRaw("Horizontal"); movement.y = Input.GetAxisRaw("Vertical"); if (movement.x > 0) { rend.flipX = true; } else if (movement.x < 0) { rend.flipX = false; } // 滑动状态下不覆盖速度 if (!isSliding) { rigidbody2D.velocity = movement.normalized * movementSpeed; } Vector3 moveDir = (Vector3.forward * movement.y) + (Vector3.right * movement.x); }
3. 保护滑动动画状态
在UpdateState()开头添加isSliding判断,滑动时跳过其他动画状态处理:
private void UpdateState() { // 滑动状态下仅维护基础动画状态,跳过其他逻辑 if (isSliding) { animator.SetBool("isMove", false); animator.SetBool("isRun", false); animator.SetBool("isCrouch", false); return; } // 原有的行走、奔跑、待机方向等逻辑保持不变 // ...(此处保留原代码内容) }
补充说明
IEnumerator本质是迭代器,StartCoroutine会开启一个独立于主线程的协程流程,yield return会暂停协程执行,等待指定时间后继续执行后续代码,只要协程正常启动,stopSlide中的逻辑会在0.8秒后执行。- 建议将
prefromSlide重命名为PerformSlide,符合C# PascalCase命名规范。
内容的提问来源于stack exchange,提问作者Jagari
相关产品推荐
相关产品推荐

