#10 Place collectables on Unity randomly.
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.
Before we begin I invite you to watch the video on which this article is based.
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.
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.
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.
Next we are going to create a new Script called Clock, which we will later assign to the prefabricated 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.
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.
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.
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.).
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.
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.
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.
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.
GameControl Methods Instructions
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.
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.
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.
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.
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.
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.
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.
We paste those two instructions into the DestroyAll method and then find all the clocks on stage and destroy them using a foreach loop.
Setup Clock Prefab
Now let’s select the prefabricated clock from the project folder and drag it to the scenario to set it up.
We create the Clock tag and assign it to the GameObject of the clock.
Then drag the Script Clock to its components or use the AddComponent button. Then we introduce “Player” in the tag field (figure 25).
Let’s go to GameObject Control and enter the new parameters that we had previously defined.
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.
At this point an error appeared in the console saying that there is no version of the PlaceAClock method that does not have 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.
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.
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.
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.
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.
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.
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.