#9 Object in a Random position of the Labyrinth

Updated information about this project

This article belongs to a series that consist on making a first person game about finding objects inside a maze. It’s one of my very first series from the channel. Now I reworked this project and you can download it to import in your own Unity project. Some day I will make a new series about this project, subscribe to my channel to see all the fresh content about Blender, Unity and programming.

Follow me on itch.io and download the source code of this project



YOU CAN TRY THIS GAME HERE, IT MAY TAKE A LITTLE WHILE TO LOAD
🔻

MOVEMENT: WASD CAMERA LOOK: MOUSE



Introduction of the old article

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

Before we begin I invite you to watch this 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.

Fig. 1: El pedestal con la espada aparece en una posición aleatoria del escenario.
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.

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.

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.

Fig. 5: Crossroads of the labyrinth.

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.

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.

Fig. 8: Suppose we choose segment B.

Fig. 9: We choose a random point of segment B.

Finally at that chosen point we will place the pedestal.

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.

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.

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.

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.

Fig. 14: We select all the parts that can contain the pedestal.

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.

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.

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.

Fig. 18: In the hierarchy we select the crossroads.

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.

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.

Fig. 21: Using the orthographic perspective, we position the empty GameObjects, one at each end.

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.

Fig. 23: We assign the empty GameObjects to the spaces in the inspector.

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.

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.

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.

Fig. 27: Empty objects for the aisle piece.

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.

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.

Fig. 30: Empty objects for the dead-end piece

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.

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.

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”.

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.

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.

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.

Fig. 37: It is not necessary for the GameObject array of labyrinth pieces to be visible in the inspector.
Fig. 38: It is not necessary for the GameObject array of labyrinth pieces to be visible in the inspector.
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.

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).

Fig. 41: We define a private method that will be in charge of placing the pedestal in some part of the labyrinth.

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).

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.

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.

Fig. 45: When entering the game mode the pedestal is placed on one of the pieces of the stage.

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?

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.

Fig. 48: Create an Empty GameObject and call it SpawnArea, this will allow us to place the pedestal in other parts of the labyrinth.

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.

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.

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.

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.

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.

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.

Fig. 55: This piece is out of reach of the character, the pedestal does not have to appear here.

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.

Exit mobile version
Secured By miniOrange