#10 Colocar colectables en Unity aleatoriamente

Información actualizada sobre esta entrada

Este artícu­lo pertenece a una serie que con­siste en hac­er un juego sim­ple en primera per­sona acer­ca de encon­trar obje­tos den­tro de un laber­in­to. Es una de mis primeras series de cuan­do empecé el canal, aho­ra he mejo­ra­do mucho este proyec­to y puedes descar­gar el códi­go fuente para impor­tar­lo en tu pro­pio proyec­to de Uni­ty. Algún día vamos a hac­er un remake de esta serie, suscrí­bete a mi canal para estar al tan­to del nue­vo con­tenido sobre Blender, Uni­ty y pro­gra­mación.

Sígueme en itch.io y descarga el código fuente de este proyecto

PUEDES TESTEAR ESTE JUEGO AQUÍ. TAL VEZ TARDE UN POCO EN CARGAR
🔻

MOVEMENT: WASD CAMERA LOOK: MOUSE

Introducción al artículo original

En este artícu­lo vamos a ver una for­ma de colo­car obje­tos colec­tables en Uni­ty, es decir obje­tos que el per­son­aje puede recolec­tar y estos ten­drán un efec­to en él. Estos obje­tos serán relo­jes que al recoger­los nos sumarán tiem­po en la cuen­ta regresiva.

Vamos a uti­lizar el pre­fab­ri­ca­do del reloj que con­fig­u­ramos en el segun­do artícu­lo del proyec­to, clic aquí para descar­gar los archivos y ver cómo con­fig­u­rar los prefabs.

Página principal del proyecto

Vídeo relacionado a este artículo


Tam­bién te dejo este video en el que hablo sobre Gen­eración de Datos Aleato­rios en Uni­ty, está divi­di­do en dos partes, en la primera vemos cómo gener­ar val­ores enteros y reales den­tro de uno o más inter­va­l­os y en la segun­da parte vemos cómo gener­ar otras estruc­turas de datos como Vector2, Vector3, Quater­nion y Color. 



Descripción del objetivo

Debe­mos estable­cer reglas y hac­er una descrip­ción exhaus­ti­va del com­por­tamien­to de los relo­jes, por ejem­p­lo cómo será la inter­ac­ción con el per­son­aje, cómo apare­cerán en el esce­nario, etc. Cuan­to mejor sea la descrip­ción, más fácil será crear una solu­ción usan­do programación.

Lo que bus­camos es que en el laber­in­to aparez­ca un deter­mi­na­do número de relo­jes. Debe­mos ase­gu­rarnos de que estos relo­jes no aparez­can den­tro de los muros y no se super­pon­gan entre si.

Para lograr esto voy a reuti­lizar la solu­ción del artícu­lo ante­ri­or para colo­car el pedestal en una posi­ción aleato­ria, en la solu­ción hacía que cada pieza del laber­in­to conoz­ca su propia geometría y nos dé una posi­ción de su inte­ri­or si se lo pedimos.

reloj colocado aleatoriamente en unity
Fig. 1: Los relo­jes apare­cerán en posi­ciones aleato­rias del laberinto.

Además voy a hac­er que cada pieza del laber­in­to pue­da con­tener un sólo reloj, de esta for­ma no cor­re­mos el ries­go de colo­car dos relo­jes superpuestos. 

Para lograr esto debe­mos lle­var un reg­istro de las piezas del laber­in­to que tienen un reloj en su inte­ri­or, de esta man­era cuan­do vamos a colo­car un nue­vo reloj, esas piezas no serán con­sid­er­adas en la selección.

Cuan­do el per­son­aje tome un reloj se agre­gará una deter­mi­na­da can­ti­dad de segun­dos a la cuen­ta regresiva.

Cada vez que un reloj es destru­i­do aparece uno nue­vo en el esce­nario, de esa for­ma todo el tiem­po ten­dremos la mis­ma can­ti­dad de relo­jes en el escenario.



Resolución

Pasos previos

Comen­zamos selec­cio­nan­do el GameOb­ject Con­trol de la jer­ar­quía y asignán­dole el tag "Game­Con­troller". Luego selec­cionamos el GameOb­ject FPSCon­troller y le asig­namos el tag Player.

asignacion de tags en unity desde la ventana inspector
Fig. 2: Selec­cionamos el obje­to con­trol y asig­namos el tag "Game­Con­troller".

asignacion de tags en unity desde la ventana inspector
Fig. 3: Selec­cionamos el pre­fab del jugador y le asig­namos el tag "Play­er".

A con­tin­uación vamos a crear un nue­vo Script lla­ma­do Clock, el cual pos­te­ri­or­mente asignare­mos al pre­fab­ri­ca­do del reloj.

creacion de scripts c# en unity
Fig. 4: Creamos un nue­vo Script con nom­bre "Clock".

Este Script mod­e­lará el com­por­tamien­to de los relojes.

Campos de Script Clock

Vamos a definir un String seri­al­iza­do que lla­mare­mos "play­erTag", esta vari­able con­tendrá sim­ple­mente el nom­bre del tag que ten­emos asig­na­do en el jugador.

Luego definire­mos una vari­able tipo float para indicar el tiem­po de vida en segun­dos del reloj en el escenario.

Defin­i­mos un GameOb­ject lla­ma­do labyrinth­Piece con "get" y "set" como se indi­ca en la figu­ra 5, de esta for­ma será un parámetro que puede ser leí­do y escrito. Usual­mente me gus­ta definir méto­dos públi­cos para acced­er a las vari­ables, pero esta es una for­ma muy prác­ti­ca de hacerlo.

Por últi­mo defin­i­mos un obje­to tipo Game­Con­trol ya que el reloj nece­si­ta infor­mar al con­trol lo que está ocurriendo.

script c# en unity para controlar la aparicion aleatoria de relojes en el escenario
Fig. 5: Defin­i­mos estos cam­pos para uti­lizar en la solución.



Métodos de Script Clock

En primer lugar defin­i­mos el méto­do públi­co SetLife­Time con parámetro float que usare­mos para que el obje­to Game­Con­trol le asigne un deter­mi­na­do tiem­po de vida al reloj.

Luego ten­emos el méto­do Self­De­struc­tion que se eje­cu­tará cuan­do se acabe el tiem­po de vida del reloj o el per­son­aje lo agarre.

El méto­do Col­lect se eje­cu­tará cuan­do el per­son­aje tome el reloj, se encar­gará de avis­ar al Game­Con­trol y luego autodestruirse.

Por últi­mo el méto­do OnTrig­ger­Enter para detec­tar al per­son­aje, cuan­do esto ocurre vamos a eje­cu­tar el méto­do Collect.

script c# en unity para controlar la aparicion aleatoria de relojes en el escenario
Fig. 6: Defin­i­mos estos méto­dos para uti­lizar en la solución,

Campos en GameControl

Vamos al Script Game­Con­trol y defin­i­mos cua­tro cam­pos serializados.

Clock­Pre­fab con­tendrá el pre­fab del reloj que colo­care­mos en el esce­nario. El entero nClocks indi­cará cuán­tos relo­jes se deben colo­car en el esce­nario. El float clock­Life­time será el tiem­po de vida medio de un reloj en el esce­nario. Por últi­mo el entero timePer­Clock indi­cará cuán­tos segun­dos se añaden al Timer luego de que el per­son­aje toma un reloj.

campos de la clase gamecontrol que se usaran para resolver el problema
Fig. 7: En Game­Con­trol vamos a definir algunos cam­pos para resolver el problema.



Declaración de métodos del Script GameControl

El méto­do PlaceAllThe­Clocks se encar­gará de ase­gu­rarse de que es posi­ble pon­er la can­ti­dad de relo­jes indi­ca­da y luego eje­cu­tar el méto­do PlaceA­Clock tan­tas veces como relo­jes haya que colocar.

Clock­De­stroyed será eje­cu­ta­do por un reloj que aca­ba de autode­stru­irse, de esa for­ma el Script Game­Con­trol podrá volver a con­sid­er­ar la pieza del laber­in­to donde el reloj esta­ba y colo­car un nue­vo reloj en una posi­ción aleatoria.

Clock­Col­lect­ed será eje­cu­ta­do por un reloj cuan­do el per­son­aje entra en con­tac­to con él, de esta for­ma podremos agre­gar tiem­po al temporizador.

Por últi­mo el méto­do Destroy­All se encar­gará de destru­ir todo lo que deba ser destru­i­do al finalizar una par­ti­da (pedestal, relo­jes, per­son­aje, etc).

metodos de la clase gamecontrol que se usaran para resolver el problema
Fig. 8: Defin­i­mos algunos méto­dos en GameControl.

Instrucciones de los métodos de Clock

Para empezar en el méto­do Start de Clock encon­tramos la ref­er­en­cia del obje­to Game­Con­trol, luego invo­camos el méto­do "self­De­struc­tion" en un tiem­po que es el resul­ta­do de la suma del tiem­po de vida del reloj con un val­or aleato­rio entre 0 y 1. De esta for­ma logramos que los relo­jes no se destruyan en el mis­mo frame.

metodo start del reloj, encontrar referencias, invocar autodestruccion
Fig. 9: En el méto­do Start de Clock vamos a encon­trar la ref­er­en­cia de la com­po­nente Game­Con­trol e invo­car la autodestrucción.

En el méto­do Self­De­struc­tion vamos a infor­mar al obje­to Game­Con­trol que un reloj fue destru­i­do y pasamos como parámetro la pieza del laber­in­to asig­na­da al reloj, de esa for­ma el obje­to Game­Con­trol podrá sacar­la de la lista de exclusión. Luego eje­cu­ta­mos el méto­do Destroy con parámetro "gameOb­ject" para que el obje­to se destruya a si mismo.

metodo auto destruccion del reloj
Fig. 10: En Self­De­struc­tion vamos a comu­nicar al Con­trol que el reloj fue destru­i­do y luego eje­cu­tar el méto­do Destroy.

En el méto­do Col­lect primero vamos a can­ce­lar la invoación pen­di­ente del méto­do self­De­struc­tion. Luego infor­mamos al obje­to Game­Con­trol que un reloj fue recolec­ta­do. Final­mente eje­cu­ta­mos el méto­do selfDestruction.

metodo collect para un reloj que aparece en el escenario aleatoriamente
Fig. 11: El méto­do Col­lect infor­ma al con­trol del even­to y luego se autodestruye.

En el méto­do OnTrig­ger­Enter vamos a pre­gun­tar si el tag del Col­lid­er que tocó el reloj es el del jugador y si esto es ver­dadero vamos a eje­cu­tar el méto­do Collect.

Para ver en detalle cómo fun­ciona el méto­do OnTrig­ger­Enter y OnTrig­ger­Enter te invi­to a ver uno de los videos de la serie fun­da­men­tal de Uni­ty que hice hace un tiem­po. Si pre­fieres infor­ma­ción más detal­la­da aquí está el artícu­lo de ese video.

metodo ontriggerenter para un reloj que aparece en el escenario aleatoriamente
Fig. 12: Si el Col­lid­er que entra en con­tac­to con el reloj tiene el tag "Play­er" eje­cutare­mos el méto­do Collect.



Instrucciones de los métodos de GameControl

Método PlaceAllTheClocks

En el méto­do PlaceAllThe­Clocks de Game­Con­trol vamos a leer la can­ti­dad de piezas de laber­in­to que ten­emos disponibles para colo­car los relo­jes. Si resul­ta que se deben pon­er más relo­jes que la can­ti­dad de piezas de laber­in­to, vamos a hac­er que nClocks sea igual al número de piezas de laber­in­to menos uno, de esta for­ma evitare­mos que el pro­gra­ma entre en un bucle infinito.

Luego hare­mos un loop eje­cu­tan­do el méto­do PlaceA­Clock para colo­car un reloj en el escenario.

metodo place all the clocks para colocar todos los relojes aleatoriamente en el escenario
Fig. 13: En el méto­do PlaceAllThe­Clocks pon­dremos todos los relo­jes en el escenario.

Método StartGame

En el méto­do StartGame vamos a crear el obje­to List de GameOb­jects (línea 182 de la figu­ra 14).

Crear los obje­to es muy impor­tante, tan­to así que en un exa­m­en de infor­máti­ca en el que había que escribir códi­go en papel, se me olvidó colo­car la instruc­ción new para crear el obje­to y me restaron casi 40 pun­tos de 100. Cuan­do fui a defend­er mi exa­m­en con­sid­er­aron que habían exager­a­do un poco dado que había sido mi úni­co error y me perdonaron.

metodo start de gamecontrol, juego del laberinto en unity
Fig. 14: En Start de Game­Con­trol creamos el obje­to lista y luego eje­cu­ta­mos el méto­do PlaceAllTheClocks.

Método PlaceAClock

Volvien­do al tema de los méto­dos, en PlaceA­Clock ten­emos que ele­gir aleato­ri­a­mente una pieza del laber­in­to ase­gurán­donos que la pieza no con­tiene ya un reloj, una vez que la con­seguimos, le ped­i­mos una posi­ción aleato­ria de su inte­ri­or. Para resolver esto es nece­sario haber resuel­to el ejer­ci­cio del artícu­lo ante­ri­or, en el que creamos el Script Labyrinth Piece.

El algo­rit­mo para colo­car la pieza del reloj se puede ver en la figu­ra 15.

metodo place a clock para colocar un reloj aleatoriamente en el escenario
Fig. 15: El méto­do PlaceA­Clock se encar­gará de ele­gir una pieza del laber­in­to y colo­car el reloj.



Método ClockDestroyed

En el méto­do Clock­De­stroyed vamos a remover de la lista la pieza del laber­in­to que nos pasan como parámetro y luego eje­cu­tar el méto­do PlaceA­Clock para colo­car un nue­vo reloj en el escenario.

metodo clock destroyed de gamecontrol para informar que un reloj ha sido destruido
Fig. 16: En Clock Destroyed elim­i­namos la pieza del laber­in­to que con­tenía el reloj de la lista y colo­camos un nue­vo reloj.

Método AddSeconds de Timer

Nece­si­ta­mos definir un méto­do públi­co den­tro de Timer que nos per­mi­ta añadir una can­ti­dad de segun­dos al temporizador.

Al final del Script hace­mos la declaración del méto­do AddSec­onds, lo com­pletare­mos al final.

metodo add seconds de timer para agregar segundos a la cuenta regresiva
Fig. 17: En el Script Timer vamos a agre­gar un méto­do públi­co para poder añadir segun­dos al timer.

Volvien­do al Script Game­Con­trol, en el méto­do Clock­Col­lect­ed hace­mos la lla­ma­da al méto­do AddSec­onds de timer, pasan­do como parámetro el entero timePerClock.

metodo clock collected de game control para informar que se ha recogido un reloj y se debe sumar segundos al timer
Fig. 18: En el méto­do Clock Col­lect­ed vamos a añadir segun­dos a la cuen­ta regresiva.

Aho­ra vamos al méto­do EndGame, en donde están las líneas Destroy vamos a eje­cu­tar el méto­do Destroy­All y cor­tar las dos instruc­ciones Destroy que habíamos colo­ca­do pre­vi­a­mente en otros artículos.

metodo end game para realizar todas las acciones al finalizar una partida
Fig. 19: Vamos al méto­do EndGame, cor­ta­mos las instruc­ciones de destruc­ción y eje­cu­ta­mos el méto­do DestroyAll.

Esas dos instruc­ciones las peg­amos en el méto­do Destroy­All y luego econ­tramos todos los relo­jes pre­sentes en el esce­nario y los destru­imos usan­do un bucle foreach.

metodo destroy all para destruir todo lo que sea necesario al finalizar una partida en el juego del laberinto
Fig. 20: En Destroy­All peg­amos las instruc­ciones cor­tadas ante­ri­or­mente y elim­i­namos todos los relo­jes del escenario.



Configurar prefabricado del reloj

Aho­ra vamos a selec­cionar el pre­fab­ri­ca­do del reloj de la car­pe­ta del proyec­to y arras­trar­lo al esce­nario para configurarlo.

prefab de elemento colectable en unity, los relojes aparecen aleatoriamente en el escenario
Fig. 21: Selec­cionamos el pre­fab del reloj.

Creamos el tag Clock y se lo asig­namos al GameOb­ject del reloj.

creacion de tags en unity
Fig. 22: Creamos un nue­vo Tag lla­ma­do Clock.

asignacion de tags en unity desde la ventana inspector
Fig. 23: Al pre­fab del reloj le asig­namos el tag Clock.

ventana inspector de un reloj que aparece en el escenario aleatoriamente en el juego del laberinto
Fig. 24: Reloj con el tag asignado.

Luego arras­tramos el Script Clock hacia sus com­po­nentes o uti­lizamos el botón Add­Com­po­nent. Luego intro­duci­mos "Play­er" en el cam­po del tag (figu­ra 25).

gameobject colectable en unity
Fig. 25: Asig­namos el Script Clock al pre­fab del reloj e intro­duci­mos el Tag del jugador.

Vamos al GameOb­ject Con­trol e ingre­samos los nuevos parámet­ros que habíamos definido previamente.

ventana inspector del game object control que se encarga de control el juego del laberinto, parametros para colocar elementos colectables en unity
Fig. 26: Selec­cionamos el obje­to Con­trol y en el inspec­tor seteamos los nuevos parámetros.

Aho­ra vamos a com­ple­tar el méto­do AddSec­onds del Script Timer que nos había queda­do pendiente.

Den­tro sim­ple­mente vamos a incre­men­tar los segun­dos, ajus­tar los min­u­tos y eje­cu­tar el méto­do Write­Timer para actu­alizar los valores.

metodo add seconds de timer para agregar segundos a la cuenta regresiva
Fig. 27: Volve­mos al Script Timer y com­ple­ta­mos el méto­do AddSeconds.

Error de programación

En este pun­to apare­ció un error en la con­so­la dicien­do que no existe una ver­sión del méto­do PlaceA­Clock que no lleve parámetros.

error visualizado en consola de unity
Fig. 28: Surge un error que dice que no hay una defini­ción del méto­do que no lleve parámetros.

Voy a la línea 184 del Script Game­Con­trol donde apare­ció el error, en efec­to vemos en la figu­ra 29 que la eje­cu­ción del méto­do PlaceA­Clock se hace sin parámetros.

correccion de bug en script game control del juego del laberinto en unity
Fig. 29: Voy a la instruc­ción en la que está el error.

Defi­no el entero nPieces con el val­or de la can­ti­dad de ele­men­tos en la lista de piezas del laber­in­to e ingre­so este entero como parámetro del método.

correccion de bug en script game control del juego del laberinto en unity
Fig. 30: Defi­no un entero con la can­ti­dad de piezas de laber­in­to y se lo paso como parámetro al método.



Corrección de Bug

Cuan­do probé el juego no parecían haber apare­ci­do los relo­jes en el esce­nario, al pausar la sim­u­lación y bus­car en la jer­ar­quía des­cubrí que habían apare­ci­do pero esta­ban boca aba­jo como se ve en la figu­ra 31.

bug en el que los elementos colectables en unity aparecen mirando boca abajo en el juego del laberinto
Fig. 31: Al cor­rer el juego encon­tramos un bug, los relo­jes apare­cen boca abajo.

Para cor­re­gir este bug voy al méto­do donde se colo­ca un reloj en el esce­nario y bus­co la instruc­ción en la que hago el Instan­ti­ate, instruc­ción 173 en la figu­ra 32.

En lugar de dar­le al nue­vo GameOb­ject la rotación del Quater­nion iden­ti­dad voy a dar­le la rotación que viene defini­da en el pre­fab del reloj, el cual cuan­do colo­camos en el esce­nario aparece cor­rec­ta­mente orientado.

solucion del bug en el que los relojes aparecen mirando boca abajo en el juego del laberinto
Fig. 32: Voy al pun­to donde colo­co los relo­jes en el escenario.

solucion del bug en el que los relojes aparecen mirando boca abajo en el juego del laberinto
Fig. 33: En lugar de usar Quaternion.Identity uti­li­zo la rotación que viene defini­da en el Prefab.

Últimos detalles y prueba

Al pro­bar el juego noté que había pocos relo­jes y que estos entre­ga­ban muy poco tiem­po al recogerlos.

Para bal­ancear cor­rec­ta­mente los ele­men­tos es nece­sario hac­er varias prue­bas y ver qué fun­ciona mejor, además esto puede for­mar parte de un sis­tema de difi­cul­tad en el cual una difi­cul­tad alta impli­ca relo­jes menos fre­cuentes que entre­gan poco tiem­po al recogerlos.

ventana inspector del game object control que se encarga de control el juego del laberinto, parametros para colocar elementos colectables en unity
Fig. 34: Ajus­to los val­ores en Game­Con­trol para que me den más tiem­po al recogerlos.

En la figu­ra 35 ten­emos dos relo­jes frente a nos­tors y el timer mar­ca aprox­i­mada­mente un min­u­to cin­cuen­ta, la figu­ra 36 fue toma­da momen­tos despues de agar­rar los dos relo­joes, vemos que el Timer aho­ra mar­ca un poco más de dos min­u­tos diez. Con esto damos por con­clu­i­do el problema.

escena del juego del laberinto en el que hay dos relojes para recoger y obtener tiempo
Fig. 35: En la esce­na se obser­van dos relo­jes frente al per­son­aje y el tiem­po indi­ca 1:47.

escena del juego del laberinto
Fig. 36: Luego de recoger ambos relo­jes, el tiem­po es 2:13.



Conclusión

En este artícu­lo hemos vis­to cómo colo­car colec­tables aleato­ri­a­mente en Uni­ty, estos colec­tables eran los relo­jes que al recoger­los debían sumar tiem­po a la cuen­ta regresiva.

El obje­to Game­Con­trol es el que se encar­ga de colo­car­los en el esce­nario aleato­ri­a­mente hacien­do uso de la solu­ción crea­da en el artícu­lo ante­ri­or para colo­car el pedestal aleato­ri­a­mente en una pieza del laberinto.

Para resolver el prob­le­ma hemos crea­do un Script que mod­e­lará el com­por­tamien­to del reloj y que inter­cam­biará men­sajes con los demás Scripts para infor­mar de even­tos como una autode­struc­ción o que el per­son­aje ha recogi­do el reloj.

El efec­to de ele­men­to colec­table lo hace­mos sim­ple­mente eje­cu­tan­do acciones apropi­adas cuan­do el per­son­aje pasa por enci­ma de ellos.

Deja un comentario

Tu dirección de correo electrónico no será publicada.

Scroll al inicio
Secured By miniOrange