#10 Place collectables on Unity randomly.

By GameDevTraum

Introduction

In this article we are going to see a way to place collectable objects in Unity, these objects will be watches that when collecting them will add time in the countdown.

We are going to use the prefabricated clock that we configured 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 the video on which this article is based.



Objective description

We must establish rules and make an exhaustive description of the behavior of the clocks, for example how they will interact with the character, how they will appear on the stage, etc. The better the description, the easier it will be to create a solution using programming.

What we are looking for is that a certain number of clocks appear in the labyrinth. We must make sure that these clocks do not appear inside the walls and do not overlap each other.

To achieve this I am going to reuse the solution from the previous article to place the pedestal in a random position, in the solution I made each piece of the labyrinth know its own geometry and give us a position of its interior if we ask for it.

reloj colocado aleatoriamente en unity
Fig. 1: The clocks will appear in random positions in the labyrinth.

In addition, I am going to make sure that each piece of the labyrinth can contain only one watch, in this way we do not run the risk of placing two superimposed watches.

To achieve this we must keep a record of the pieces of the labyrinth that have a clock inside, so when we are going to place a new clock, those pieces will not be considered in the selection.

When the character takes a watch, a certain number of seconds will be added to the countdown.

Every time a watch is destroyed, a new one appears on the stage, so that all the time we will have the same number of watches on the stage.



Resolution

Previous steps

We start by selecting GameObject Control from the hierarchy and assigning it the tag “GameController”. Then select the GameObject FPSController and assign the tag Player.

asignacion de tags en unity desde la ventana inspector
Fig. 2: Select the control object and assign the tag “GameController”.

asignacion de tags en unity desde la ventana inspector
Fig. 3: Select the player’s prefab and assign the tag “Player”.

Next we are going to create a new Script called Clock, which we will later assign to the prefabricated clock.

creacion de scripts c# en unity
Fig. 4: We create a new script with the name “Clock”.

This script will model the behavior of clocks.

Fields of Clock Script

We are going to define a serialized String that we will call “playerTag”, this variable will contain simply the name of the tag that we have assigned in the player.

Then we will define a float type variable to indicate the life time in seconds of the clock on the stage.

We define a GameObject called labyrinthPiece with “get” and “set” as shown in Figure 5, this way it will be a parameter that can be read and written. I usually like to define public methods to access variables, but this is a very practical way to do it.

Finally we define a GameControl type object because the clock needs to inform the control what is happening.

script c# en unity para controlar la aparicion aleatoria de relojes en el escenario
Fig. 5: We define these fields for use in the solution.



Methods of Clock Script

First we define the SetLifeTime public method with float parameter that we will use so that the GameControl object assigns a certain life time to the clock.

Then there’s the SelfDestruction method that will run when the clock’s life time runs out or the character grabs it.

The Collect method will run when the character picks up the clock, alerts GameControl and then self-destruct.

Finally the OnTriggerEnter method to detect the character, when this happens we will execute the Collect method.

script c# en unity para controlar la aparicion aleatoria de relojes en el escenario
Fig. 6: We define these methods to use in the solution,

Fields in GameControl Script

Let’s go to the GameControl Script and define four serialized fields.

ClockPrefab will contain the prefab of the clock that we will place on the stage. The nClocks integer will indicate how many clocks should be placed on the stage. The float clockLifetime will be the average lifetime of a clock on the stage. Finally the whole timePerClock will indicate how many seconds are added to the Timer after the character takes a clock.

campos de la clase gamecontrol que se usaran para resolver el problema
Fig. 7: In GameControl we are going to define some fields to solve the problem.



GameControl Script Methods Statement

The PlaceAllTheClocks method will make sure that it is possible to set the indicated number of clocks and then run the PlaceAClock method as many times as clocks need to be set.

ClockDestroyed will be executed by a clock that has just self-destruct, that way the GameControl Script will be able to re-consider the maze piece where the clock was and place a new clock in a random position.

ClockCollected will be executed by a clock when the character comes into contact with it, this way we can add time to the timer.

Finally, the DestroyAll method will destroy everything that must be destroyed at the end of a game (pedestal, clocks, character, etc.).

metodos de la clase gamecontrol que se usaran para resolver el problema
Fig. 8: We define some methods in GameControl.

Clock Method Instructions

To start in the Clock Start method we find the reference of the GameControl object, then we invoke the selfDestruction method in a time that is the result of the sum of the life time of the clock with a random value between 0 and 1. In this way we achieve that the clocks are not destroyed in the same frame.

metodo start del reloj, encontrar referencias, invocar autodestruccion
Fig. 9: In the Clock Start method we are going to find the reference of the GameControl component and invoke self-destruction.

In the SelfDestruction method, we inform the GameControl object that a clock has been destroyed and pass the maze piece assigned to the clock as a parameter, so that the GameControl object can remove it from the exclusion list. Then we run the Destroy method with parameter “gameObject” so that the object destroys itself.

metodo auto destruccion del reloj
Fig. 10: In SelfDestruction we are going to tell the Control that the clock was destroyed and then run the Destroy method.

In the Collect method we are first going to cancel the pending invocation of the selfDestruction method. Then we inform the GameControl object that a clock was collected. Finally we run the selfDestruction method.

metodo collect para un reloj que aparece en el escenario aleatoriamente
Fig. 11: The Collect method informs the event control and then self-destruct.

In the OnTriggerEnter method we are going to ask if the tag of the Collider that touched the clock is the one of the player and if this is true we are going to execute the Collect method.

metodo ontriggerenter para un reloj que aparece en el escenario aleatoriamente
Fig. 12: If the Collider that comes into contact with the clock has the tag “Player” we will execute the Collect method.



GameControl Methods Instructions

PlaceAllTheClocks Methods

In the PlaceAllTheClocks method of GameControl we will read the amount of labyrinth pieces we have available to place the clocks. If it turns out that more clocks must be placed than the number of labyrinth pieces, let’s make nClocks equal to the number of labyrinth pieces minus one, this way we’ll prevent the program from entering into an infinite loop.

Then we will do a loop executing the PlaceAClock method to place a clock on the stage.

metodo place all the clocks para colocar todos los relojes aleatoriamente en el escenario
Fig. 13: In the PlaceAllTheClocks method we will put all the clocks on the stage.

StartGame Method

In the StartGame method we are going to create the GameObjects List object (line 182 of figure 14).

To create the object is very important, so much so that in a computer exam in which it was necessary to write code on paper, I forgot to place the new instruction to create the object and I subtracted almost 40 points from 100. When I went to defend my exam they considered that they had exaggerated a little given that it had been my only error and they forgave me.

metodo start de gamecontrol, juego del laberinto en unity
Fig. 14: In GameControl Start we create the list object and then execute the PlaceAllTheClocks method.

PlaceAClock Method

Returning to the subject of methods, in PlaceAClock we have to randomly choose a piece from the labyrinth assuring us that the piece no longer contains a clock, once we get it, we ask for a random position of its interior. To solve this it is necessary to have solved the exercise of the previous article, in which we created the Script Labyrinth Piece.

The algorithm for placing the watch part can be seen in figure 15.

metodo place a clock para colocar un reloj aleatoriamente en el escenario
Fig. 15: The PlaceAClock method will choose a piece from the labyrinth and place the watch.



ClockDestroyed Method

In the ClockDestroyed method we are going to remove from the list the piece of the labyrinth that is passed to us as a parameter and then execute the PlaceAClock method to place a new clock in the scenario.

metodo clock destroyed de gamecontrol para informar que un reloj ha sido destruido
Fig. 16: In Clock Destroyed we removed the maze piece containing the watch from the list and placed a new watch.

AddSeconds Method of Timer

We need to define a public method within Timer that allows us to add a number of seconds to the timer.

At the end of the script we make the declaration of the AddSeconds method, we will complete it at the end.

metodo add seconds de timer para agregar segundos a la cuenta regresiva
Fig. 17: In the Timer Script we are going to add a public method to add seconds to the timer.

Returning to the GameControl Script, in the ClockCollected method we make the call to the AddSeconds method of timer, passing as parameter the whole timePerClock.

metodo clock collected de game control para informar que se ha recogido un reloj y se debe sumar segundos al timer
Fig. 18: In the Clock Collected method we are going to add seconds to the countdown.

Now let’s go to the EndGame method, where the Destroy lines are, let’s run the DestroyAll method and cut the two Destroy instructions that we had previously placed in other items.

metodo end game para realizar todas las acciones al finalizar una partida
Fig. 19: Let’s go to the EndGame method, cut the destruction instructions and execute the DestroyAll method.

We paste those two instructions into the DestroyAll method and then find all the clocks on stage and destroy them using a foreach loop.

metodo destroy all para destruir todo lo que sea necesario al finalizar una partida en el juego del laberinto
Fig. 20: In DestroyAll we paste the previously cut instructions and remove all clocks from the stage.



Setup Clock Prefab

Now let’s select the prefabricated clock from the project folder and drag it to the scenario to set it up.

prefab de elemento colectable en unity, los relojes aparecen aleatoriamente en el escenario
Fig. 21: Select the prefab of the clock.

We create the Clock tag and assign it to the GameObject of the clock.

creacion de tags en unity
Fig. 22: We created a new Tag called Clock.

asignacion de tags en unity desde la ventana inspector
Fig. 23: We assign the Clock tag to the prefab of the clock.

ventana inspector de un reloj que aparece en el escenario aleatoriamente en el juego del laberinto
Fig. 24: Clock with the assigned tag.

Then drag the Script Clock to its components or use the AddComponent button. Then we introduce “Player” in the tag field (figure 25).

gameobject colectable en unity
Fig. 25: Assign the Script Clock to the prefab of the clock and enter the player’s Tag.

Let’s go to GameObject Control and enter the new parameters that we had previously defined.

ventana inspector del game object control que se encarga de control el juego del laberinto, parametros para colocar elementos colectables en unity
Fig. 26: Select the Control object and set the new parameters in the inspector.

Now let’s complete the AddSeconds method of the Timer Script that we had pending.

Inside we will simply increase the seconds, adjust the minutes and execute the WriteTimer method to update the values.

metodo add seconds de timer para agregar segundos a la cuenta regresiva
Fig. 27: Back to the Timer Script and complete the AddSeconds method.

Programming error

At this point an error appeared in the console saying that there is no version of the PlaceAClock method that does not have parameters.

error visualizado en consola de unity
Fig. 28: An error occurs saying that there is no method definition that does not contain parameters.

I go to line 184 of the GameControl Script where the error appeared, in effect we see in figure 29 that the execution of the PlaceAClock method is done without parameters.

correccion de bug en script game control del juego del laberinto en unity
Fig. 29: I go to the instruction where the error is located.

I define the integer nPieces with the value of the amount of elements in the labyrinth parts list and enter this integer as a method parameter.

correccion de bug en script game control del juego del laberinto en unity
Fig. 30: I define an integer with the number of labyrinth pieces and pass it as parameter to the method.



Bug correction

When I test the game, the clocks didn’t seem to have appeared on the stage. When I paused the simulation and searched the hierarchy, I discovered that they had appeared but were upside down, as shown in figure 31.

bug en el que los elementos colectables en unity aparecen mirando boca abajo en el juego del laberinto
Fig. 31: When running the game we find a bug, the clocks appear face down.

To correct this bug I go to the method where a clock is placed on the stage and I look for the instruction in which I make the Instantiate, instruction 173 in figure 32.

Instead of giving the new GameObject the rotation of the Quaternion identity I’m going to give it the rotation that is defined in the prefab of the clock, which when placed on the stage appears correctly oriented.

solucion del bug en el que los relojes aparecen mirando boca abajo en el juego del laberinto
Fig. 32: I go to the point where I place the clocks on the stage.

solucion del bug en el que los relojes aparecen mirando boca abajo en el juego del laberinto
Fig. 33: Instead of using Quaternion.Identity I use the rotation defined in Prefab.

Final Details and Test

When I tried the game, I noticed that there were few watches and that they gave up very little time when I picked them up.

In order to balance the elements correctly it is necessary to make several tests and see what works best, in addition this can form part of a system of difficulty in which a high difficulty implies less frequent clocks that deliver little time when picking them up.

ventana inspector del game object control que se encarga de control el juego del laberinto, parametros para colocar elementos colectables en unity
Fig. 34: I adjust the values in GameControl to give me more time to collect them.

In figure 35 we have two clocks in front of nostors and the timer marks approximately one minute fifty, the figure 36 was taken moments after grabbing the two watches, we see that the Timer now marks a little more than two minutes ten. This concludes the problem.

escena del juego del laberinto en el que hay dos relojes para recoger y obtener tiempo
Fig. 35: In the scene two clocks are observed in front of the character and the time indicates 1:47.

escena del juego del laberinto
Fig. 36: After picking up both watches, the time is 2:13.



Conclusion

In this article we have seen how to randomly place collectables in Unity, these collectables were the clocks that when collecting them had to add time to the countdown.

The object GameControl is in charge of placing them on the stage randomly using the solution created in the previous article to place the pedestal randomly in a piece of the labyrinth.

To solve the problem we have created a Script that will model the behavior of the clock and will exchange messages with other Scripts to report events such as a self-destruction or that the character has picked up the clock.

The effect of collectable element we do it simply executing appropriate actions when the character passes over them.