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

Unity中如何让重写的Attack函数等待协程完成后返回int值?

蓄力攻击协程等待问题解决方案

问题场景

弓箭武器的蓄力攻击实现中,Attack方法启动协程后立刻返回原始的attackMove值,无法等待蓄力完成后返回修改后的数值。同时Attack是重写的抽象方法,必须返回int类型,且仅有弓箭武器需要该蓄力逻辑;调用端的后续动画等逻辑必须依赖Attack的返回值才能执行。

原始代码

弓箭武器类(W_02_Bow)

public class W_02_Bow : WeaponDefault
{
    public override int Attack(int attackMove)
    {
        StartCoroutine(ChargeAttack(attackMove));
        return attackMove;
    }

    private IEnumerator ChargeAttack(int attackMove)
    {
        float chargeTime = 0f;

        while (chargeTime <= 5f && Input.GetMouseButton(0))
        {
            Debug.Log("Charging...");
            chargeTime += Time.deltaTime;
            yield return null;
        }

        if (chargeTime >= 5f)
            Debug.Log("Charged!");

        chargeTime = 0;
        
        yield return new WaitUntil(() => Input.GetMouseButtonUp(0));

        attackMove = attackMove % 3 + 1;
    }
}

调用端Attack方法

public void Attack()
{
    if (Input.GetMouseButton(0) && isLand)
    {
        if (IsCanAct((int)weaponType + 5) && !isAttack)
        {
            isAttack = true;
            playerNowStamina -= (int)weaponType + 5;

            playerAnim.SetInteger(_triggerNum, (int)AnimState.Attack);

            attackMove = weapons[(int)weaponState].GetComponent<WeaponDefault>().Attack(attackMove);

            playerAnim.SetInteger(_action, attackMove);

            playerAnim.SetTrigger(_trigger, () =>
            {
                isAttack = false;
                Variables.Instance?.WeaponVfx?[0]?.gameObject.SetActive(false);
            }, (float)weaponType * 0.2f + 0.6f
            );
        }
    }
}

问题分析

  • Unity协程是异步执行的,StartCoroutine调用后会立刻继续执行后续代码,导致Attack直接返回原始值,无法等待蓄力逻辑完成。
  • 抽象方法要求Attack同步返回int,不能直接将其改为异步方法(如async Task<int>),否则会破坏抽象类的契约。

解决方案

通过回调机制重构逻辑,既满足抽象类的约束,又能等待蓄力完成后执行后续依赖逻辑:

1. 扩展武器抽象类

在WeaponDefault中新增带回调的异步攻击方法,其他武器可直接使用默认实现:

public abstract class WeaponDefault : MonoBehaviour
{
    // 原同步攻击方法,保持抽象约束
    public abstract int Attack(int attackMove);

    // 新增异步攻击方法,带结果回调
    public virtual void AttackAsync(int attackMove, Action<int> onComplete)
    {
        // 默认同步返回原始值,兼容非蓄力武器
        onComplete?.Invoke(attackMove);
    }
}

2. 重构弓箭武器类

重写AttackAsync方法,在蓄力协程完成后通过回调传递最终的attackMove;原同步Attack方法可抛出异常避免误用:

public class W_02_Bow : WeaponDefault
{
    public override int Attack(int attackMove)
    {
        throw new InvalidOperationException("弓箭武器请使用AttackAsync方法执行蓄力攻击");
    }

    public override void AttackAsync(int attackMove, Action<int> onComplete)
    {
        StartCoroutine(ChargeAttack(attackMove, onComplete));
    }

    private IEnumerator ChargeAttack(int initialAttackMove, Action<int> onComplete)
    {
        float chargeTime = 0f;

        while (chargeTime <= 5f && Input.GetMouseButton(0))
        {
            Debug.Log("Charging...");
            chargeTime += Time.deltaTime;
            yield return null;
        }

        if (chargeTime >= 5f)
            Debug.Log("Charged!");

        chargeTime = 0;
        
        yield return new WaitUntil(() => Input.GetMouseButtonUp(0));

        int finalAttackMove = initialAttackMove % 3 + 1;
        // 蓄力完成后通过回调返回结果
        onComplete?.Invoke(finalAttackMove);
    }
}

3. 修改调用端逻辑

判断当前武器类型,对弓箭使用异步回调逻辑,其他武器保持原有同步逻辑:

public void Attack()
{
    if (Input.GetMouseButton(0) && isLand)
    {
        if (IsCanAct((int)weaponType + 5) && !isAttack)
        {
            isAttack = true;
            playerNowStamina -= (int)weaponType + 5;

            playerAnim.SetInteger(_triggerNum, (int)AnimState.Attack);

            WeaponDefault currentWeapon = weapons[(int)weaponState].GetComponent<WeaponDefault>();
            
            // 区分弓箭和其他武器
            if (currentWeapon is W_02_Bow bowWeapon)
            {
                bowWeapon.AttackAsync(attackMove, (finalAttackMove) =>
                {
                    attackMove = finalAttackMove;
                    // 蓄力完成后执行后续动画逻辑
                    playerAnim.SetInteger(_action, attackMove);

                    playerAnim.SetTrigger(_trigger, () =>
                    {
                        isAttack = false;
                        Variables.Instance?.WeaponVfx?[0]?.gameObject.SetActive(false);
                    }, (float)weaponType * 0.2f + 0.6f);
                });
            }
            else
            {
                // 非蓄力武器保持原有同步逻辑
                attackMove = currentWeapon.Attack(attackMove);
                playerAnim.SetInteger(_action, attackMove);

                playerAnim.SetTrigger(_trigger, () =>
                {
                    isAttack = false;
                    Variables.Instance?.WeaponVfx?[0]?.gameObject.SetActive(false);
                }, (float)weaponType * 0.2f + 0.6f);
            }
        }
    }
}

方案说明

  • 回调机制避免了主线程阻塞,同时满足了调用端必须等待蓄力完成后执行后续逻辑的需求。
  • 通过扩展抽象类的方式,保持了与其他武器的兼容性,无需修改非蓄力武器的代码。
  • 类型判断确保只有弓箭武器触发异步蓄力逻辑,符合业务场景要求。

内容的提问来源于stack exchange,提问作者EarthLEE

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 04:10:35