How to make a variable gameObject an actual VARIABLE?

A GameObject is supposedly a variable, but it’s actually a constant because in the inspector you can only put ONE object into it.
I’ve tried the following

public GameObject Enemy1 = GameObject.FindWithTag("Enemy1");
void Update(){
Enemy1 = GameObject.FindWithTag("Enemy1");

I have a battle system set up where there will only ever be MAX 3 Enemy gameObjects active at a time.
I have an “attack ability” on my main player that currently looks like

    public void SwipeAbility() {
    
        StartCoroutine ("Swipe");
    }

    private IEnumerator Swipe(){
        enemy1.EnemyHP -= enemy1.EnemyHP + SeikoATK;
        yield return null;
        battle.currentState = Battle.BattleStates.EnemyTurn;
    }

and it works only when I drag the enemy into inspector.

What exactly is the issue? You’re finding a GameObject with the name “Enemy1” and assigning it to Enemy1 variable. Why do you think it is constant? If you’re supposed to assign it in the inspector, then why overwrite it in Update?

And how is the attack ability related to getting a specific GameObject? It’s also using a different variable (“enemy1”), but you didn’t tell what that is.

nvm
didn’t know i could do this

        GameObject.FindWithTag ("Enemy 1").GetComponent<Enemy>().EnemyHP -= GameObject.FindWithTag ("Enemy 1").GetComponent<Enemy>().EnemyHP + SeikoATK;

Because Enemy1 is always going to be different depending on which battle the main character is. See the issue with dragging and dropping is that I can only grab one enemy out of one battle.
Say I have 5 battles, and 3 enemies each. I wanted to make 3 variable slots for enemies to be placed in so when the battle starts, and my player attacks, the button for attacking enemy 1 will work. But I want the 3 enemies to be placed automatically.

I fixed this by adding tags to “enemy1, enemy2, enemy3” to all the enemies and updating my attack function to find the active gameobject with that tag everytime he attacks. It’s kind of long and strung out but now it works.

You’re making this really complicated.

I’m not sure if I understood your problem correctly, but you should probably just make a list of GameObjects:

using System.Collections.Generic;

List<GameObject>() enemies = new List<GameObject>();

Then, whenever you’re deciding which enemies to spawn you add them to this list:

GameObject enemy1 = whatever;
GameObject enemy2 = whatever;

enemies.Add(enemy1);
enemies.Add(enemy2);

Alternatively, if you’re able to just drop the Enemies into the inspector, then making the list public will be all there is to it. You’ll have slots for multiple GameObjects then. You access them by using enemies[index] (index being 0 to 2).

1 Like

Arrays solved all my problems, thanks!

Just a nitpick, but what I used is a list, not an array. The difference is basically that a list can be resized dynamically while an array always has the number of elements you define it with.
So for your example you could dynamically have 1, 2, 3 or any number of enemies in that list, but with an array (GameObject[ ] enemies = new GameObject[3]) you will always have 3 enemies and would need to handle empty enemy slots individually.

seems a bit complex

seems a bit complex, where do you insert the

into your code? with the other variables or in the Start function or Awake?

Right now I have 1 battle script that applies to all my battles, 1 enemy script that applies to all my enemies. The hard part is dealing with battles where I have less than 3 enemies. Or when an enemy dies.
Right now when I kill the first enemy, then attack with my player, which starts EnemyTurn(), the script gets stuck at starting Enemy1Turn() so I have to somehow put an if function somewhere that skips that coroutine if the enemy[0] is dead.

public void EnemyTurn(){
      
            if (Enemies.Length == 1) {
                E1Turn = true;
                StartCoroutine ("Enemy1Turn");
            }
            if (Enemies.Length == 2) {
                E1Turn = true;
                E2Turn = true;
                StartCoroutine ("Enemy1Turn");
                StartCoroutine ("Enemy2Turn");
            }
            if (Enemies.Length == 3) {
                E1Turn = true;
                E2Turn = true;
                E3Turn = true;
                StartCoroutine ("Enemy1Turn");
                StartCoroutine ("Enemy2Turn");
                StartCoroutine ("Enemy3Turn");
            }
    }
    private IEnumerator Enemy1Turn(){
        yield return new WaitForSeconds (1f);
        enemy[0].GetComponent<Enemy>().EnemyAttack();
        yield return new WaitForSeconds (1f);
        E1Turn = false;
        yield return null;
        }
    private IEnumerator Enemy2Turn(){
        yield return new WaitForSeconds (2f);
        enemy[1].GetComponent<Enemy>().EnemyAttack();
        yield return new WaitForSeconds (1f);
        E2Turn = false;
        yield return null;
    }
    private IEnumerator Enemy3Turn(){
        yield return new WaitForSeconds (3f);
        enemy[2].GetComponent<Enemy>().EnemyAttack();
        yield return new WaitForSeconds (1f);
        E3Turn = false;
        yield return null;
    }

I usually declare the list at the start of my class and then define it in the Awake() function, but that’s up to you. You can also just put all of that directly in your class.

To do it like I do:

public List<GameObject> enemies;

void Awake()
{
     enemies = new List<GameObject>();
}

In the Editor you probably won’t even notice a difference between arrays and lists. It’s just that they handle slighlty differently in code.

With lists you normally want to loop through its elements if you plan to do something with multiple elements. I’ll tell you how I’d do it, but keep in mind that there are a lot of different ways to do this and none are strictly ‘better’.

Assuming the enemies are already in the list, I’d then write a generic ‘EnemyTurn’ method that handles the turn of the enemy you pass into it:

private IEnumerator EnemyTurn(Enemy enemy)
{
     //handle the turn
}

This gets called for each enemy in the list by another Coroutine, that waits for the previous one to finish:

foreach (GameObject enemy in enemies)
{
    StartCoroutine(EnemyTurn(enemy.GetComponent<Enemy>()));
    yield return new WaitForSeconds(2f);
}

I think you can also do something like

yield return (StartCoroutine(EnemyTurn()));

and it might wait until it’s done, but I’m not sure.

If an enemy dies you delete it from the list with enemies.Remove(), so it doesn’t get called by the foreach loop anymore. So now you can have as many enemies as you like, from 0 to 1000000 if you want, and don’t have to add additional ones manually.

To give the enemies different attacking times (which seems to be like you want to do that) or actually any other different behavior, you just give the enemies different variable values and get them in the EnemyTurn() Coroutine:

IEnumerator EnemyTurn(Enemy enemy)
{
     float attackWait = enemy.GetAttackWait();
     yield return new WaitForSeconds(attackWait);
}

By the way - you should call Coroutines with their method instead of the name (EnemyTurn() instead of “EnemyTurn”). It’s a bit more efficient.

I made a whole new battle script and implemented the functions you showed me. It’s working much better now…
One question about the enemies.Remove()… Where would I put this in the script? I tried in a IEnumerator Enemy Dies() and I kept getting a no overload method takes 0 arguments error.

As of now the enemy script is telling the enemy that when it dies to destroy itself. I think this was the reason the battle kept freezing when the first player died. I changed it to SetActive(false) and I get an error in the console but this time it doesn’t freeze the battle and I’m able to kill the other enemy.

So thanks a bunch, you got me through a major sticking point.

Ok what I don’t get is that when I use the Remove() function, I am getting an error, collection was modified, Enumeration may not execute.

This script runs fine and I can attack my players, the game will wait for them to take turns attacking, then allows me to select my attack and enemy again. But, when an enemy dies, he is “Remove()'d” from the list and/or SetActive(false) (I tried a bunch of different combinations of setting it to false, keeping it in the list, destroying it etc.)

No matter what I do it seems the EnemyTurnCycle is still trying to access that Enemy GameObject and start his Turn again. I know I can get past it by not removing it from the list, but the error in my console is kind of bugging me.

    public IEnumerator CheckEnemy(){
        foreach (GameObject enemy in enemies) {
            if (enemy.GetComponent<Enemy> ().EnemyHP <= 0) {
                enemy.SetActive (false);
                enemy.GetComponent<Enemy> ().EnemyHPBar.SetActive (false);
                enemies.Remove (enemy);
            }
        }
yield return StartCoroutine(EnemyTurnCycle();
    }
    public IEnumerator EnemyTurnCycle(){
        foreach (GameObject enemy in enemies) {
            if (enemy.GetComponent<Enemy> ().EnemyHP > 0) {
                yield return StartCoroutine (EnemyTurn (enemy.GetComponent<Enemy> ()));
            }
        }
        yield return new WaitForSeconds (1f);
        SeikoAbilities.SetActive (true);
    }

    public IEnumerator EnemyTurn(Enemy enemy){
        yield return new WaitForSeconds (1f);
        enemy.EnemyAttack ();
        yield return null;
    }

Oh, you need to pass over the GameObject you want to remove from the list of course.

Are you using Visual Studio? I don’t know if MonoDevelop has something like IntelliSense. If you are having problems with a method it’s best to look through the available overloads first.
For example when you have a list called ‘list’ and then start writing the ‘.’ you’ll see all the available methods, right? There you can see ‘Remove’. Remove takes one argument though, which is the object of the type that the list consists of. You’ll see this once you start writing the ‘(’ after Remove:

So you have enemy1, enemy2 and enemy3 in that list. You can just call Remove(enemy1) then and it will be removed from the list. Alternatively lists have a method called RemoveAt(int index). That one takes a number from 0 upwards and removes the item it finds at that position. If you know the position you can also use that.

You might want to make sure the enemy is in the list though, just in case something goes wrong:

GameObject enemy; //assign this with the enemy that dies

if (enemy != null)
   enemies.Remove(enemy);

The point of making the list is so I don’t have to attach every single enemy onto my battle.
Let me show you my inspector…

When the enemy is removed, the size of the “enemies” list turns to 1, and the skeleton_001 and/or skeleton_002 (depending on which died first) is setactive.false

Also I didn’t use your new List();
because right now I’m dragging and dropping enemies into the List, not creating enemies in my script. So when I put that in Awake and the battle started, my enemy list disappeared.

Idk… it works now, who cares about error messages, they’re so redundant xD