如何设置我的协程来逐个轮到每个角色?

问题描述 投票:0回答:1

我正在尝试在 Unity 中建立一个基于回合的战斗系统,我可以逐步完成角色的每个回合,并在玩家的回合中获得一些输入。我听说协程和 IEnumerators 可以帮助我做到这一点,但显然我没有完全掌握它是如何工作的,因为目前,我可以看到玩家角色的 currentHealth 一直下降到 0,甚至在它到达第一回合之前。

我希望有人能帮助我理解我哪里出了问题。这是我的 CombatSystem 课程:

public enum CombatState { START, ENEMYTURN, PLAYERTURN, WON, LOST }

public class CombatSystem : MonoBehaviour
{
    public List<Character> currentEnemiesInCombat;
    public CombatState state;
    
    private GameController controller;

    private void Awake()
    {
        controller = GetComponent<GameController>();
    }

    //Need to create an action response similar to openhiddenroom that will 
    //activate combat upon coming across revealed enemies, then CombatSystem 
    //will handle what happens below
    public void StartCombat(PlayerCharacter playerCharacter, List<Character> triggeredEnemies)
    {
        //Bring up enemy illustrations, possibly resize canvas
        //Roll initiative for each character
        state = CombatState.START;
        controller.ResizeDisplayTextForCombat();
        controller.LogStringWithReturn("CombatSystem.cs DEBUG: YOU ARE IN COMBAT NOW");
        SetUpCharacterInitiatives(playerCharacter, triggeredEnemies);
        StartCoroutine(TurnBasedCombatRoutine(playerCharacter, triggeredEnemies));
    }

    //THIS NEEDS REWORKING FROM HERE DOWN-------------------------------------------------------------------------------------------------
    //------------------------------------------------------------------------------------------------------------------------------------
    IEnumerator TurnBasedCombatRoutine(PlayerCharacter playerCharacter, List<Character> triggeredEnemies)
    {
        var allCharactersInBattle = triggeredEnemies.Concat(new[] { playerCharacter }).OrderByDescending(c => c.initiative);

        //Keep the fight going while the player is above 0 hp and enemies still remain
        while (playerCharacter.currentHealth > 0 && triggeredEnemies.Count > 0)
        {
            foreach (Character character in allCharactersInBattle)
            {
                //Check if the character and playercharacter are still alive
                //because we don't want enemies taking more turnns if the player is
                //already dead
                if (character.currentHealth > 0 && playerCharacter.currentHealth > 0)
                {
                    controller.LogStringWithReturn("CombatSystem.cs DEBUG: " + character.charName + "'s turn");

                    //Perform actions for the current character's turn
                    //yield if it is the player's turn
                    if (character != controller.playerCharacter)
                    {
                        state = CombatState.ENEMYTURN;
                        PerformEnemyAction(character);
                    }
                    else
                    {
                        //NEED TO FIGURE OUT HOW TO MAKE THIS YIELD UNTIL A VALID USER INPUT
                        state = CombatState.PLAYERTURN;
                        controller.LogStringWithReturn("It is your turn...");
                        yield return null;
                    }

                    // Check for defeated enemies after each turn
                    triggeredEnemies.RemoveAll(enemy => isDead(enemy));

                    // Break out of the loop if all enemies are defeated
                    if (triggeredEnemies.Count == 0)
                    {
                        state = CombatState.WON;
                        controller.LogStringWithReturn("You have defeated all of your foes. It is safe to move forward.");
                        break;
                    }
                }
            }
        }

        EndCombat();
    }

    //Roll initiatives for all characters, including player
    //Make sure they don't have the same initiative amounts
    void SetUpCharacterInitiatives(PlayerCharacter playerCharacter, List<Character> CharactersInBattle)
    {
        System.Random rnd = new System.Random();
        HashSet<int> usedInitiatives = new HashSet<int>();

        // Set up playerCharacter as the first item in the CharactersInBattle List
        playerCharacter.initiative = rnd.Next(1, 21);
        usedInitiatives.Add(playerCharacter.initiative);

        // Set up initiatives for other characters
        for (int i = 0; i < CharactersInBattle.Count; i++)
        {
            int initiative;
            do
            {
                initiative = rnd.Next(1, 21);
            } while (usedInitiatives.Contains(initiative));

            CharactersInBattle[i].initiative = initiative;
            usedInitiatives.Add(initiative);
        }
    }

    void PerformEnemyAction(Character currentEnemy)
    {
        System.Random rnd = new System.Random();
        int attackRoll = rnd.Next(1, 21);
        int damageRoll = rnd.Next(1, currentEnemy.maxDamage);

        //if currentcharacter != playerCharacter, then make an attack roll
        //against playerCharacter's ArmorClass
        if (attackRoll > controller.playerCharacter.armorClass)
        {
            controller.playerCharacter.currentHealth = controller.playerCharacter.currentHealth - damageRoll;
            controller.LogStringWithReturn($"{currentEnemy.charName} hits you {"[" + attackRoll + "]"} for {damageRoll} damage!");
            //Check if player died from that hit
            if(isDead(controller.playerCharacter))
            {
                state = CombatState.LOST;
                controller.LogStringWithReturn("The Eternal Night washes over you... \nYOU ARE DEAD\nGAME OVER");
                GameOver();
            }
        }
        else if (attackRoll < controller.playerCharacter.armorClass)
        {
            controller.LogStringWithReturn($"{currentEnemy.charName} misses its attack!");
        }
        else
        {
            controller.LogStringWithReturn($"You narrowly dodge {currentEnemy.charName}'s attack!");
        }
    }

    bool isDead(Character character)
    {
        if (character.currentHealth <= 0)
        {
            return true;
        }
        else 
        {   
            return false; 
        }
    }

    void GameOver()
    {
        //Need to have something happen if you lose combat
        //Disable text input or allow a restart command
        //Maybe send them to a game over room???
    }

    void EndCombat()
    {
        controller.ResizeDisplayTextPostCombat();
    }
}

我认为将 PerformEnemyAction 更改为协程也可能有所帮助。我尝试过,但最终又重新使用它,因为错误仍然存在。

unity-game-engine coroutine enumeration
1个回答
0
投票

对我来说,你似乎永远不会碰到 else 语句,因为 if 语句正在询问是否字符!= Controller.playerCharacter。您的类型不匹配,因为 foreach 循环仅循环角色对象,而玩家角色的类型为“PlayerCharacter”。

C# 是一种强类型语言,您应该重新设计当前的方法,尝试声明具有列表/数组属性的无类型变量,否则您将陷入问题的世界。

有很多方法可以做到这一点,你可以创建一个堆栈并从中弹出对象,或者你也可以创建一个事件系统等。我个人会在玩家角色和角色类上都给出一个 'CombatOrder' int 变量。然后这两个类都可以有一个名为“AssignInitialCombatOrder(int order)”和“ProgressCombatOrder()”的方法,这样你就可以计算战斗状态变化时的顺序,将它们赋予每个对象,然后让它们的更新循环不执行任何操作,除非战斗顺序是 0 或其他什么,然后它重置回战斗顺序结束。不需要疯狂的产量或协程等

© www.soinside.com 2019 - 2024. All rights reserved.