You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复玩家受击后转向攻击者并原地奔跑的问题

问题修复方案

问题分析

当前出现两个异常行为:

  1. 玩家站立状态被攻击时,会原地播放奔跑动画(即使没有移动指令)
  2. 玩家会意外转向攻击他的敌方玩家(推测是无移动目标时的旋转逻辑误判导致)

修复步骤

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 21:18:10