#9 Object in a Random position of the Labyrinth

By GameDevTraum

Introduction

In this article we are going to see a strategy to place an object in a random labyrinth position using the Random.Range method and the sword pedestal prefab created in the second article of the project, click here to download the files and see how to configure the prefabs.

Go to the project’s Main Page

Antes de comenzar te invito a ver el siguiente video.

Descripción del problema

We need the pedestal with the incrusted sword observed in figure 1 to appear anywhere in the labyrinth.

In video 4 of the series we solved a similar problem to place the character in one of the doors at the start of the game. Here is the article if you want to take a look.

pedestal con espada instanciado aleatoriamente en un laberinto utilizando random.range unity
Fig. 1: El pedestal con la espada aparece en una posición aleatoria del escenario.
miniatura video 4 del juego del laberinto, colocar personaje aleatoriamente en escenario, unity
Fig. 2: En el video y artículo 4 colocamos al personaje aleatoriamente en una de las puertas.

In video 4 we use empty GameObjects to designate specific positions in which to place the character’s prefab.

In this case we are looking for a more complex solution, first we are going to choose one of the pieces that compose the labyrinth. The piece we choose will be a duplicate of one of the pieces shown in figure 3.

piezas simples que forman un laberinto para un prototipo en unity
Fig. 3: These are the pieces of the labyrinth in which you can walk.

It is not enough just to choose the piece, we need to obtain a position within it and this position must be within a region in which you can walk. We see these regions in figure 4.

piezas simples que forman un laberinto para un prototipo en unity
Fig. 4: In these regions we will be able to place the pedestal.

When analyzing the problem we see that we will have to make several random decisions and it would be desirable that the solution we propose is independent of the number of pieces that the labyrinth has. In other words, if we add more pieces from figure 4, they are automatically added to the selection system.

Strategy: Define Segments

Given one of the pieces in figure 4, we are able to draw one or two internal lines where the player can circulate within the piece.

Let’s consider the ends of these two lines. Figure 6 identifies four points that would be the ends of these two segments.

These points could be represented with Empty GameObjects as children of the piece.

encrucijada de laberinto con texturas
Fig. 5: Crossroads of the labyrinth.

encrucijada de laberinto con texturas, puntos extremos marcados
Fig. 6: At the ends of the piece we place 4 points.

In figure 7 we see these two segments drawn.

Then we could place the pedestal with the sword at any point of one of the segments.

encrucijada de laberinto con texturas, puntos extremos forman dos segmentos
Fig. 7: These points form two segments, horizontal and vertical.

First of all, let’s choose one of the two segments, let’s suppose segment B (figure 8) formed by empty GameObjects B1 and B2.

Then we’ll take a random point between the two empty GameObjects, figure 9.

encrucijada de laberinto con texturas, uno de los segmentos se eligen al azar con random.range
Fig. 8: Suppose we choose segment B.

encrucijada de laberinto con texturas, se elige un punto aleatorio del segmento con random.range
Fig. 9: We choose a random point of segment B.

Finally at that chosen point we will place the pedestal.

encrucijada de laberinto con texturas, posicion final donde se coloca el pedestal utilizando Random.Range
Fig. 10: At the chosen point, place the pedestal with the sword.

In the case of the pieces of the corridor and dead end that only have one direction, we will make coincide the points A and B, that way we will have two coincident segments, then we will be able to use the same solution that for the rest of the pieces.

pasillo de laberinto con texturas, tomaremos un punto aleatorio del segmento con random.range
Fig. 11: In the case of the corridor, we will make segments A and B coincide.

Implementation of the strategy

We have worked out a plan to place the pedestal somewhere inside one of the pieces of the labyrinth. Now based on this we are going to solve the problem.

First in the hierarchy I’m going to separate the obstruction pieces from the others, because these pieces are not going to be considered in our solution.

In the figure 1 we see selected the pieces that we are going to use.

prototipo de laberinto hecho en unity
Fig. 12: Select the labyrinth and separate the obstructing parts.

We need to find the references of these pieces in our code to be able to choose one of them. The simplest way to do this is to use a Tag.

crear tag en unity, spawnpiece para las piezas en las que podremos colocar el objeto de manera aleatoria usando random.range
Fig. 13: We create the SpawnPiece tag for the pieces that can contain the labyrinth.

I’m going to create a Tag called “SpawnPiece” and assign it to all the pieces selected in figure 12.

se seleccionan todas las piezas que pueden contener al pedestal
Fig. 14: We select all the parts that can contain the pedestal.

asignacion de tag a uno o mas gameobjects en unity
Fig. 15: The selected pieces are assigned the SpawnPiece tag.

Next we create the Script “LabyrinthPiece” (labyrinth piece) that will be assigned to all the pieces selected in figure 12.

creacion de script en unity que se encargara de colocar un objeto en una posicion aleatoria del laberinto usando random.range
Fig. 16: We create a new script called LabyrinthPiece, which we will assign to the parts that can contain the pedestal.

In the Script first we will define four GameObjects that will be points A1, A2, B1 and B2. We declare them as serializable fields so that they appear in the inspector and we can assign them manually.

definicion de campos serializados en script c sharp, unity
Fig. 17: We define four GameObject type fields to contain the points of each piece.

We select any piece type Crossroads and assign the Script LabyrinthPiece. In figure 19 we see that in the inspector appear the fields for GameObjects.

prototipo de laberinto hecho en unity
Fig. 18: In the hierarchy we select the crossroads.

ventana inspector de un gameobject perteneciente a un prototipo de laberinto hecho en unity
Fig. 19: We assign the Script LabyrinthPiece to the crossroads.

Next we’re going to create the four empty GameObjects that we’ll call A1, A2, B1 and B2. In figure 20 we see created the first point. Note that it is defined as the son of an Empty GameObject called Spawn, which in turn is the son of the crossroads piece.

encrucijada de laberinto con texturas
Fig. 20: We created four empty GameObjects as sons of this piece.

We are going to position these four objects according to figure 6, at the ends of the imaginary segments that represent the walkable area inside the piece.

encrucijada de laberinto con texturas
Fig. 21: Using the orthographic perspective, we position the empty GameObjects, one at each end.

encrucijada de laberinto con texturas
Fig. 22: Using the orthographic view, we position the empty GameObjects, one at each end.

We assign these objects to their respective fields in the inspector, within the LabyrinthPiece component.

Finally we apply the changes. This is very important because we are applying the changes on the Prefab of the crossroads, that is to say that all the crossroads of the labyrinth will now have their own objects A1, A2, B1 and B2 and will have assigned the component LabyrinthPiece, with their own points loaded in the fields.

ventana inspector de un gameobject perteneciente a un prototipo de laberinto hecho en unity
Fig. 23: We assign the empty GameObjects to the spaces in the inspector.

aplicar cambios en inspector para extender la configuracion a los demas prefabricados
Fig. 24: We apply the changes so that all the crossroads of the scenario have the same configuration.

We can check that by checking every crossroads in the hierarchy and checking that it has these points and the Script assigned to it.

ventana jerarquia en unity, gameobjects que representas piezas de un laberinto
Fig. 25: When applying the changes, all the labyrinth crossroads become empty objects and the Script LabyrinthPiece.

What follows is to repeat the process for the other pieces. In figure 26 we see the T-shaped piece, this case is similar to the bifurcation only that one of the imaginary segments will have its end in the center of the piece.

prototipo de laberinto hecho en unity, piezas de laberinto en la jerarquia
Fig. 26: Empty objects for the bifurcation piece.

In the corridor piece we create only points A1 and A2. In figure 28 we see that these points are also assigned in fields B1 and B2 respectively.

prototipo de laberinto hecho en unity, piezas de laberinto en la jerarquia
Fig. 27: Empty objects for the aisle piece.

ventana inspector de un gameobject perteneciente a un prototipo de laberinto hecho en unity
Fig. 28: For the aisle part we assign points A1 and A2 also to fields B1 and B2.

In the corner piece, figure 29, the imaginary segments will have two coincident points, we could create only three Empty GameObjects and one of them assign it for example to A2 and B1, but we chose to create the four points.

prototipo de laberinto hecho en unity, piezas de laberinto en la jerarquia
Fig. 29: Empty objects for the corner piece.

The case of the dead-end piece is the same as that of the aisle only with less distance.

In figure 31 we see that in points B1 and B2 we repeat points A.

prototipo de laberinto hecho en unity, piezas de laberinto en la jerarquia
Fig. 30: Empty objects for the dead-end piece

ventana inspector de un gameobject perteneciente a un prototipo de laberinto hecho en unity
Fig. 31: For the dead end piece we assign points A1 and A2 also to fields B1 and B2.

Method for choosing a random position of a piece – Random.Range

In the Script LabyrinthPiece we are going to create a public method that will return a Vector3 that will indicate a random position of the piece.

The first instruction will be to declare a Vector3 called position which will be the one we return to at the end of the execution.

Let us remember that they are two imaginary segments formed one by points A1 and A2, another by points B1 and B2. So then let’s do an if to choose one segment or the other.

In the if argument we use Random.Value to generate a random number between 0-1 and check if this value is less than 0.5f. This means that we will have a 50% chance of choosing segment A and another 50% of choosing segment B.

To choose a random point of the imaginary segment formed by the points we use the Vector3.Lerp method, which will make a linear interpolation between two Vectors3 that we indicate.

The method receives three arguments, the first two are the Vector3 between which it will be interpolated and the third parameter is the interpolation point we are interested in.

To exemplify the interpolation function consider the following: if the third value of the Lerp method is 0 we will have a Vector3 equal to the first parameter indicated. If it is worth 1 we will have a Vector3 equal to the second parameter indicated. And if it is worth 0.5f we will have a Vector3 that will be located exactly in the central point between the two Vectors3 that we indicate as parameters.

In this way we use Random.Range to generate a Vector3 that will be in some position between the points indicated in the first two parameters of the Lerp method and we assign that vector to the Vector3 position that we had defined at the beginning.

In an if region we use the position of points A1 and A2. In the other if region we do exactly the same but with points B1 and B2.

Finally we return the Vector3 position.

All this explained is summarized in the 7 lines of the GetRandomPosition method in figure 32.

metodo publico en script c sharp para colocar objeto en posicion aleatoria usando Random.Range
Fig. 32: The GetRandomPosition method will deliver a random position inside the part.

Now, this we did was for the Script LabyrinthPiece that is assigned to each piece of the labyrinth.

In the GameControl Script we are going to create a method that will place the pedestal in a random position in the labyrinth and make use of the public method of LabyrinthPiece.

We begin by defining the fields shown in figure 33 below the comment “//Video 9”. These are the fields and variables that we will use to solve the problem.

definicion de campos serializados en script c sharp, unity
Fig. 33: In the GameControl Script we define a GameObjects array for the labyrinth pieces.

In the GameControl component in the inspector (assigned to GameObject Control), fill in the fields. In SpawnPieceTag type “SpawnPiece”.

ventana inspector de un gameobject, el campo spawn piece tag pertenece a todas las piezas en las que podremos colocar el objeto de manera aleatoria usando Random.Range
Fig. 34: We write the name of the tag that we assign in the pieces of the labyrinth, in this case “SpawnPiece”.

In ObjectToFind we will assign the Prefab of the pedestal with the sword, which is the object to find.

prefabs en unity, pedestal con espada y reloj de pendulo, estos objetos seran colocados en una posicion aleatoria del laberinto usando Random.Range
Fig. 35: We looked for the prefab of the pedestal with the sword we made in video 2 of the series.

In the minimum distance for the moment we write the value 75. Then we will see what this variable is used for.

ventana inspector de un gameobject perteneciente a un prototipo de laberinto hecho en unity
Fig. 36: Assign the Prefab of the pedestal to the ObjectToFind field.

It is not necessary that the LabyrinthPieces array appears in the inspector so I am going to remove the [SerializeField] selected in figure 37.

campos serializados, privados y publicos en un script c sharp para un prototipo de juego de laberinto en unity
Fig. 37: It is not necessary for the GameObject array of labyrinth pieces to be visible in the inspector.
campos serializados, privados y publicos en un script c sharp para un prototipo de juego de laberinto en unity
Fig. 38: It is not necessary for the GameObject array of labyrinth pieces to be visible in the inspector.
ventana inspector de un gameobject perteneciente a un prototipo de laberinto hecho en unity
Fig. 39: When removing the SerializeField line, the field does not appear in the inspector as it is private.

In the StartGame method we are going to find all the GameObjects of the hierarchy that have the Tag that we indicate in the inspector. Last instruction of the startGame method in figure 40.

metodo start de script c sharp en unity para un prototipo de juego de laberinto
Fig. 40: In the GameControl Start method we find all GameObjects with the indicated Tag.

Then we declare the PlaceObjectToFind method (figure 41) and call this method from the StartGame method (figure 42).

este metodo se encargara de colocar un objeto en una posicion aleatoria del laberinto usando Random.Range
Fig. 41: We define a private method that will be in charge of placing the pedestal in some part of the labyrinth.

metodo start game de script c sharp en unity para un prototipo de juego de laberinto
Fig. 42: We make the call from the StartGame method, i.e. when the game starts the pedestal is placed on the stage.

PlaceObjectToFind Method

What we will do with this method is to choose a random piece from the labyrinth, making sure that piece is far enough away from the player using the “minDistance” variable that we assigned 70 in the inspector. If the selected piece does not meet this requirement we will choose another piece again. This is taken care of by the While loop shown in figure 43.

Once we find a part that meets the requirements, we will place the pedestal at a random point inside. For this we use a version of the Instantiate method, which receives three parameters: The first is the object to be found stored in the “objectToFind” field, the second is the position that we will receive automatically from the labyrinth piece executing the GetRandomPosition method of the LabyrinthPiece component assigned to it. The third parameter is the rotation, here we will indicate: Quaternion.identity (a rotation identity).

este metodo se encargara de colocar un objeto en una posicion aleatoria del laberinto usando Random.Range
Fig. 43: Instructions for the PlaceObjectToFind method.

It is important that we save the reference of this new object that we have created, we will do it in “objectToFindInstance” (last instruction in figure 43). This way when the game is over we can destroy this object manually.

In the EndGame method we destroy the instance of the object to be found, figure 44.

la instancia del objeto que fue colocado en una posicion aleatoria usando Random.Range
Fig. 44: In the EndGame method, the pedestal instance is destroyed.

When you enter the game mode and press the Start button, everything seems to be working correctly. The pedestal with the sword appears in a random labyrinth position inside one of the pieces.

prototipo de laberinto hecho en unity, pedestal con espada. el objeto es colocado en una posicion aleatoria del laberinto usando Random.Range
Fig. 45: When entering the game mode the pedestal is placed on one of the pieces of the stage.

prototipo de laberinto hecho en unity, el objeto es colocado en una posicion aleatoria del laberinto usando Random.Range
Fig. 46: We can see from the editor window which part it is.

Place object in other regions

There are regions of the labyrinth that are outside the pieces, for example the one highlighted in figure 47. We might be interested in placing the object in a position belonging to this area.

How could we reuse what we have done?

prototipo de laberinto hecho en unity. en una posicion aleatoria de estas areas colocaremos el objeto usando Random.Range
Fig. 47: This region can be a place where we would like the pedestal to appear.

To begin with we created an Empty GameObject and called it SpawnArea and placed it between the pieces of the labyrinth.

jerarquia de un prototipo de juego de laberinto hecho en unity
Fig. 48: Create an Empty GameObject and call it SpawnArea, this will allow us to place the pedestal in other parts of the labyrinth.

jerarquia de un prototipo de juego de laberinto hecho en unity
Fig. 49: The four points in the region are children of GameObject SpawnArea.

Then we created four empty GameObjects to represent points A1, A2, B1 and B2. We place these objects at the ends of the two imaginary segments of the area highlighted in green in Figure 49.

prototipo de laberinto hecho en unity. en una posicion aleatoria de estas areas colocaremos el objeto usando Random.Range
Fig. 50: We are going to create 4 empty GameObjects to use in the LabyrinthPiece script.

Then we create a Prefab along with the other pieces of the labyrinth (figure 51), because we may reuse this object by changing the internal points.

prefabs en unity, piezas para crear un laberinto
Fig. 51: We took the GameObject SpawnArea and created a Prefab for reuse.

Then we assign the component LabyrinthPiece and place the internal points in the respective fields.

ventana inspector de un gameobject perteneciente a un prototipo de laberinto hecho en unity
Fig. 52: Asignamos el Script LabyrinthPiece al GameObject SpawnArea.

Do not forget to assign the Tag SpawnPiece in the inspector, otherwise these areas will not be considered when choosing a position for the pedestal. In my case, as can be seen in figure 53, I had not assigned it and I spent several minutes testing for the pedestal to appear in these areas.

ventana inspector de un gameobject perteneciente a un prototipo de laberinto hecho en unity, asignar tags
Fig. 53: We must remember to assign the SpawnPiece tag to the GameObject SpawnArea. Apply changes.

Final Details

When I tried the game, I noticed that the door was quite large in relation to the character, so I made it a little smaller and applied the changes.

prototipo de laberinto hecho en unity, puertas de entrada-salida
Fig. 54: The doors were too big in relation to the character.

Another problem I detected was that there were pieces with the Tag SpawnPiece whose inner region was inaccessible to the player. In figure 55 you see one of these pieces, if the pedestal appears here, the player won’t be able to find it.

The solution to this is to select this piece and remove the Tag SpawnPiece, this way the piece will not be considered.

prototipo de laberinto hecho en unity
Fig. 55: This piece is out of reach of the character, the pedestal does not have to appear here.

ventana inspector de un gameobject perteneciente a un prototipo de laberinto hecho en unity, asignar tags
Fig. 56: Select that piece and in the tag field select: “Untagged”. We do not apply the changes.

Conclusion

In this article we managed to place the pedestal in a random position within the labyrinth.

To do this we had to analyze the type of pieces that make up the labyrinth, establish certain rules and propose a strategy to solve our problem.

We used object-oriented programming thinking to create a flexible solution that fits all types of parts.

As usually happens, there is no single way to solve a problem. Another way to address this situation is to make a Navmesh Bake and take a point within these regions.