如何修复玩家受击后转向攻击者并原地奔跑的问题
问题修复方案
问题分析
当前出现两个异常行为:
- 玩家站立状态被攻击时,会原地播放奔跑动画(即使没有移动指令)
- 玩家会意外转向攻击他的敌方玩家(推测是无移动目标时的旋转逻辑误判导致)
修复步骤
1. 优化动画控制逻辑(解决原地奔跑)
放弃直接调用animator.Play()的方式,改用Animator参数控制动画过渡,避免状态冲突:
- 在你的Animator控制器中添加一个布尔参数
IsRunning - 设置
IDLE到WALK的过渡条件为IsRunning=true,反向过渡为IsRunning=false
2. 修正代码中的动画与移动逻辑
修改PlayerController中的相关函数,移除冗余变量并优化状态判断:
using System.Collections; using UnityEngine; using UnityEngine.UI; using UnityEngine.AI; using UnityEngine.InputSystem; public class PlayerController : MonoBehaviour { const string IDLE = "m_idle_A"; const string WALK = "m_run"; const string DEATH = "m_death_A"; CustomAction input; NavMeshAgent agent; Animator animator; [Header("Movement")] [SerializeField] ParticleSystem clickEffect; [SerializeField] LayerMask clickableLayers; [Header("Speed Boost")] [SerializeField] float speedBoost = 2.0f; [SerializeField] float maxBoostDuration = 7.0f; private float initialSpeed; private bool isBoosting; private float boostDuration; [Header("Health")] [SerializeField] int maxHealth = 2; private int currentHealth; [SerializeField] Text hpText; [Header("Fail Screen UI")] [SerializeField] RawImage FailImage; [SerializeField] Text YouDieAboutText; [SerializeField] Text YouDieText; [SerializeField] Text YouDieLVLAboutText; [SerializeField] Text YouDieLVLText; [SerializeField] Text DieScoreMoney; [SerializeField] Button MenuButton; [Header("Win Screen UI")] [SerializeField] RawImage WinImage; [SerializeField] Text YouWinAboutText; [SerializeField] Text YouWinText; [SerializeField] Text YouWinLVLAboutText; [SerializeField] Text YouWinLVLText; [SerializeField] Text WinScoreMoney; [SerializeField] Button MenuWinButton; [SerializeField] float lookRotationSpeed = 8.0f; void Awake() { agent = GetComponent<NavMeshAgent>(); animator = GetComponent<Animator>(); // Initialize custom actions input = new CustomAction(); AssignInputs(); } void AssignInputs() { input.Main.Move.performed += ctx => ClickToMove(); } void ClickToMove() { RaycastHit hit; if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 100, clickableLayers)) { agent.destination = hit.point; if (clickEffect != null) { Instantiate(clickEffect, hit.point + new Vector3(0, 0.1f, 0), clickEffect.transform.rotation); } } } void OnEnable() { input.Enable(); } void OnDisable() { input.Disable(); } void Start() { currentHealth = maxHealth; UpdateHPText(); FailImage.gameObject.SetActive(false); MenuButton.gameObject.SetActive(false); YouDieAboutText.gameObject.SetActive(false); YouDieText.gameObject.SetActive(false); YouDieLVLAboutText.gameObject.SetActive(false); YouDieLVLText.gameObject.SetActive(false); DieScoreMoney.gameObject.SetActive(false); initialSpeed = agent.speed; isBoosting = false; boostDuration = 0f; int randomDieScore = Random.Range(10, 15); DieScoreMoney.text = randomDieScore.ToString(); int randomYouDieLVL = Random.Range(37, 53); YouDieLVLText.text = randomYouDieLVL.ToString(); WinImage.gameObject.SetActive(false); MenuWinButton.gameObject.SetActive(false); YouWinAboutText.gameObject.SetActive(false); YouWinText.gameObject.SetActive(false); YouWinLVLAboutText.gameObject.SetActive(false); YouWinLVLText.gameObject.SetActive(false); WinScoreMoney.gameObject.SetActive(false); int randomWinScore = Random.Range(40, 75); WinScoreMoney.text = randomWinScore.ToString(); int randomYouWinLVL = Random.Range(37, 53); YouWinLVLText.text = randomYouWinLVL.ToString(); } void Update() { FaceTarget(); SetAnimations(); if (isBoosting) { boostDuration -= Time.deltaTime; if (boostDuration <= 0f) { isBoosting = false; SetSpeed(initialSpeed); } } if (currentHealth <= 0) { animator.Play(DEATH); this.enabled = false; } } void FaceTarget() { Vector3 direction = agent.destination - transform.position; // 只有当目标位置与当前位置有明显差异时,才执行旋转 if (direction.magnitude > 0.1f) { Quaternion lookRotation = Quaternion.LookRotation(new Vector3(direction.x, 0, direction.z)); transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Time.deltaTime * lookRotationSpeed); } } void SetAnimations() { // 只有当Agent正在移动(速度不为0)且有移动目标时,才标记为奔跑状态 bool isMoving = agent.velocity.magnitude > 0.1f && (agent.destination - transform.position).magnitude > 0.1f; animator.SetBool("IsRunning", isMoving); // 若未使用Animator参数,可保留以下兼容逻辑 // if (isMoving) // { // animator.Play(WALK); // } // else // { // animator.Play(IDLE); // } } void OnTriggerEnter(Collider other) { if (other.CompareTag("Hunter")) { if (currentHealth > 0) { currentHealth--; UpdateHPText(); ApplySpeedBoost(); } if (currentHealth == 0) { StartCoroutine(ShowObjectsAfterDelay(2f)); } } if (other.CompareTag("WinLine")) { WinImage.gameObject.SetActive(true); MenuWinButton.gameObject.SetActive(true); YouWinAboutText.gameObject.SetActive(true); YouWinText.gameObject.SetActive(true); YouWinLVLAboutText.gameObject.SetActive(true); YouWinLVLText.gameObject.SetActive(true); WinScoreMoney.gameObject.SetActive(true); this.enabled = false; } } IEnumerator ShowObjectsAfterDelay(float delay) { yield return new WaitForSeconds(delay); FailImage.gameObject.SetActive(true); MenuButton.gameObject.SetActive(true); YouDieAboutText.gameObject.SetActive(true); YouDieText.gameObject.SetActive(true); YouDieLVLAboutText.gameObject.SetActive(true); YouDieLVLText.gameObject.SetActive(true); DieScoreMoney.gameObject.SetActive(true); } public void ApplySpeedBoost() { isBoosting = true; boostDuration = maxBoostDuration; SetSpeed(initialSpeed + speedBoost); } private void SetSpeed(float speed) { agent.speed = speed; } private void UpdateHPText() { hpText.text = "HP: " + currentHealth; } }
3. 修复意外转向敌人的问题
修改后的FaceTarget函数增加了direction.magnitude > 0.1f的判断,只有当玩家有明确的移动目标(目标位置与当前位置距离足够远)时,才会转向目标。这样玩家站立被攻击时,不会因为误判而转向敌人。
关键修改点
- 移除冗余的
playerAnimator变量,合并为animator - 用Animator布尔参数
IsRunning控制动画过渡,避免直接播放动画导致的状态冲突 - 优化
SetAnimations中的移动判断逻辑,只有当玩家实际在向目标移动时,才触发奔跑动画 - 调整
FaceTarget的旋转条件,防止无移动目标时的意外旋转
内容的提问来源于stack exchange,提问作者Mr. Bait
相关产品推荐
相关产品推荐

