Dr. Lee Dr. Lee - 25 days ago 7
C# Question

Selecting random positions from array without duplicates

In my game, there are many enemies and have this script on it, where I am set their navmesh destination by randomly selecting an element from an array of positions. However, sometimes all enemies go at same position, so I am trying to skip previously generated array indices and find a new index. Any suggestions for this? I don't want to use the

goto
statement.

Here is code I have done so far:

void move_on_fired_positions()
{
navmesh.Resume ();
again:
new_position = Random.Range (0, firedpoints.Length);

if (prev_num != new_position) {
navmesh.SetDestination (firedpoints [new_position].position);
prev_num = new_position;
} else
{
goto again;
}
}

Answer Source

One thing you can try is keeping a dynamic list of available positions, rather than an array. (Don't worry - they're both serialized and modified the same way in the Unity editor interface.) This way, you can remove positions from the list as they are assigned to enemies, ensuring you never assign duplicates. Here's an idea of how that might look:

public List<Transform> firedpoints;

// Returns available firing position, if any
public Transform GetRandomFiringPosition()
{
    if (firedpoints.Count > 0)
    {
        // Get reference to transform, then remove it from list
        int newPositionIndex = Random.Range (0, firedpoints.Length);
        Transform returnedPosition = firedpoints[newPositionIndex];
        firedpoints.RemoveAt(newPositionIndex);
        return returnedPosition;
    }
    else
    {
        // Or you can specify some default transform
        return null;
    }
}

// Makes firing position available for use
public void RegisterFiringPosition(Transform newPosition)
{
    firedpoints.Add(newPosition);
}

This code should be in a script on a single object in the scene, which the enemies should have a reference to (or, you can change the code a little and make the class into a singleton, so its methods can be called without a direct reference). Whenever an enemy needs a new position, it can call GetRandomFiringPosition() and store the returned Transform in a variable for future reference.

You haven't determined what conditions make a position available for use again, but when you have, you can call RegisterFiringPosition() to get it back into the list. As a nice side effect, this also makes it possible to assign brand new positions to enemies, for example in response to player-triggered events.

Hope this helps! Let me know if you have any questions.