Genera un tablero visual atractivo en segundos: con C# en Unity, basta con tres cambios para que cada casilla muestre un caramelo aleatorio en lugar de un cuadrado. Aquí verás cómo usar lista de sprites, Random.Range, SpriteRenderer y un identificador único (ID) provisional para dejarlo todo listo antes de optimizar.
¿Cómo generar caramelos aleatorios con C# en Unity?
Para reemplazar el cuadrado por caramelos, la clave es intervenir justo antes de añadir el nuevo caramelo al array/colección. La idea: tomar un sprite de los prefabs de forma aleatoria, asignarlo al SpriteRenderer del objeto y establecer un ID temporal. El proceso es directo y evita cambios estructurales.
Usar instanciación del caramelo y, antes de agregarlo al array, configurar su gráfico.
Elegir uno aleatorio de la lista de sprites usando Random.Range con distribución uniforme por defecto.
Reemplazar la imagen del cuadrado por el sprite seleccionado.
Asignar un ID provisional hardcodeado para cada caramelo.
¿Qué líneas cambian para seleccionar un sprite de prefabs?
Selecciona un sprite al azar del array/lista configurada de sprites/prefabs.
// 1) Elegir un sprite aleatorio de la listaSprite newSprite = sprites[UnityEngine.Random.Range(0, sprites.Count)];// 2) Asignarlo al SpriteRenderer del nuevo caramelonewCandy.GetComponent<SpriteRenderer>().sprite = newSprite;
Notas clave: Random.Range(0, n) para enteros devuelve un valor entre 0 (incluido) y n (excluido). Si tienes 6 caramelos, los índices irán de 0 a 5.
¿Cómo asignar un identificador único provisional?
Configura un ID temporal para diferenciar caramelos. Evita usar 0 si está reservado en colecciones; utiliza -1 como valor “no válido”.
// 3) Asignar un ID provisional (hardcodeado)newCandy.GetComponent<Candy>().Id =-1;
Este DNI/ID se calculará automáticamente más adelante. De momento se deja hardcodeado para avanzar rápido.
¿Dónde insertar el código en el word manager?
El cambio debe colocarse entre el renombrado del nuevo caramelo y su inserción en la colección de caramelos. Así te aseguras de que, cuando se agregue al array, ya tenga su SpriteRenderer configurado y su ID establecido.
Insertar las 3 líneas tras la instanciación de newCandy.
Hacerlo antes de añadir el objeto al array/lista del tablero.
Mantener el resto del flujo sin cambios.
Ejemplo de ubicación en pseudocódigo:
var newCandy =Instantiate(currentCandy, position, rotation);// aquí: configurar gráfico e ID antes de añadirlo a la colecciónSprite newSprite = sprites[UnityEngine.Random.Range(0, sprites.Count)];newCandy.GetComponent<SpriteRenderer>().sprite = newSprite;newCandy.GetComponent<Candy>().Id =-1;candies[row, col]= newCandy;// ahora sí, se agrega a la colección
Conceptos trabajados aquí: instanciación, colección/array, SpriteRenderer, lista de sprites, Random.Range, ID único. Además, se menciona la distribución uniforme como base; más adelante se verán otras distribuciones de probabilidad.
¿Qué validar en el editor y qué problemas aleatorios surgen?
Antes de ejecutar, verifica el guardado del script en el editor. El icono de círculo indica cambios sin guardar; al usar Ctrl+S (Windows) o Command+S (Mac) desaparece y se muestra una equis. Luego presiona play para ver el tablero con caramelos.
Guardar siempre el script antes de compilar y dar a play.
Confirmar que aparecen caramelos y no cuadrados.
Notar la aleatoriedad de cada partida: el orden cambia en cada ejecución.
Durante la prueba se detectan efectos de la aleatoriedad inicial:
Aparición de tripletes o cadenas largas al empezar.
Ejemplo: varias sandías alineadas o una fila con seis caracolas consecutivas.
Estos casos se corregirán con lógica adicional que usará el ID y validaciones de vecinos (arriba, abajo, izquierda, derecha) para evitar coincidencias iniciales.
¿Quieres que profundicemos en cómo calcular el ID automáticamente y prevenir tripletes desde el inicio? Deja tu duda o comentario y lo abordamos en el siguiente paso.
Aqui hay que hacer un parentesis, si estuviste haciendo los objetos animaciones, vas a necesitar hacer un par de cambios para que esto funcione como deberia, y para que a futuro tengas todo ya preparado.
Para empezar, a las animaciones debes crearles un estado de Idle (o inerte), crear un parametro bool para que transicione a la animacion que corresponde.
Luego ya que lo que estamos pasando es una animacion, en vez de un sprite debemos crear prefabs que contengan un componente animator enlazado a la animacion que creamos, cambiamos de List<Sprite> a List<GameObject> y luego el procedimiento es bastante intuitivo, dejo aqui mi codigo para quien pueda interesarle.
Todo en programación se puede hacer en una linea de código. Sin embargo, no es recomendable ya que esto dificulta la legibilidad del código. Es mejor escribir código que sea fácil de leer. Esto facilitara la tarea de mantener el código en el futuro.
Para generar un número aleatorio podemos utilizar la función Random.Range() con el mínimo y el máximo de elementos de nuestra lista de posibles resultados, por ejemplo, del 0 al 6.
Sin embargo, para acceder a todos los elementos de nuestra lista debemos empezar desde 0: lista[0] es el primer elemento, lista[1] el segundo y así hasta lista[5] que es el sexto y último elemento.
Por lo tanto, cuando tenemos una lista de 6 elementos, debemos iterar desde la posición list.Count - 1 que en nuestro ejemplo es 5 6 - 1. Así que, la función Random.Range necesita el número mínimo más la cantidad de elementos de nuestra lista, pero, en realidad, va a devolver alguno de los posibles resultados comenzado desde 0:
Tienes un error al momento de asignar el id, está asignando null. El error lo tienes en esta linea ** (y < 0 && idx == candies[x, y - 1].GetComponent<Candy>().id));**
En vez de colocar el signo < , debes colocar este: >.
De esta forma: ** (y > 0 && idx == candies[x, y - 1].GetComponent<Candy>().id));**
Este error puede ser porque estas asignando un elemento que estaba en la escena, pero eliminaste, en vez de asignar un prefab.
Da clic en el BoardManager y arrastra el elemento desde la pestaña Project y no de la escena.
Yo, estableci el ID del caramelo de la siguiente forma:
publicclassCandy:MonoBehavior{//en las declaraciones publicas #region PublicDefinitions//coloque el ID del candy como estatico inicializado en 0publicstatic int id =0; #endregion
//todo el codigo igual al curso}
y cuando asigno el id en el codigo de BoardManager:
publicclassBoardManager:MonoBehavior{//todo el codigo igual al cursopublicCreateInitialBoard(Vector2 offset){//todo el codigo igual al curso, sin embargoCandy.id++;//incrementa el id "static"}}
Con esto se genera el ID automaticamente y se almacena para cada uno.
PD: no se si esta del todo correcto, pero es un autoincrementable, quizas al ser publico traiga problemas de seguridad luego, pero, de momento me parecio una buena idea para efectos practicos.
tengo una duda al ejecutar el juego el candy[0][0] cambia de sprite constantemente en pantalla pero en el inspector figura siempre el mismo sprite asignado
alguien tiene alguna idea de a que se debe este bug?
Hola lo solucione, era un typo, se repetia constantemente el loop de asigancion del candy
Hola, tengo una duda, al ejecutar mi juego cambia el Sprite de manera aleatoria, sin embargo me sale un error y no despliega en el arreglo los otros Sprites, siendo este el mensaje de error en la consola: NullReferenceException: Object reference not set to an instance of an object
BoardManager.CreateInitialBoard (UnityEngine.Vector2 offset) (at Assets/Scripts/BoardManager.cs:51)
BoardManager.Start () (at Assets/Scripts/BoardManager.cs:27)
<code>using System.Collections;using System.Collections.Generic;using UnityEngine;publicclassBoardManager:MonoBehaviour{publicstaticBoardManager sharedInstance;publicList<Sprite> prefabs =newList<Sprite>();publicGameObject currentCandy;public int xSize, ySize;privateGameObject[,] candies;public bool isShifting {get; set;}// Start is called before the first frame updatevoidStart(){if(sharedInstance ==null){ sharedInstance =this;}else{Destroy(gameObject);}Vector2 offset = currentCandy.GetComponent<BoxCollider2D>().size;CreateInitialBoard(offset);}privatevoidCreateInitialBoard(Vector2 offset){ candies =newGameObject[xSize, ySize]; float startX =this.transform.position.x; float startY =this.transform.position.y;for(int x =0; x < xSize; x++){for(int y =0; y < ySize; y++){GameObject newCandy =Instantiate( currentCandy,newVector3(startX +(offset.x*x), startY +(offset.y*y),0), currentCandy.transform.rotation); newCandy.name= string.Format("Candy[{0}{1}]", x ,y);Sprite sprite = prefabs[Random.Range(0,prefabs.Count)]; newCandy.GetComponent<SpriteRenderer>().sprite= sprite; newCandy.GetComponent<Candy>().id=-1; candies[x,y]= newCandy;}}}// Update is called once per framevoidUpdate(){}}
(at Assets/Scripts/BoardManager.cs:67)BoardManager.Start()(at Assets/Scripts/BoardManager.cs:33)
Mi BaordManager:
using System.Collections;using System.Collections.Generic;using UnityEngine;publicclassBoardManager:MonoBehaviour{publicstaticBoardManager sharedInstance;publicList<Sprite> prefabs =newList<Sprite>();publicGameObject currentCandy;public int xSize, ySize;privateGameObject[,] candies;public bool isShifting {get;set;}[SerializeField]private float paddingX =0.4f, paddingY =0.4f;// Start is called before the first frame updatevoidStart(){if(sharedInstance ==null){ sharedInstance =this;}else{Destroy(this.gameObject);}Vector2 offset =newVector2(currentCandy.GetComponent<BoxCollider2D>().size.x+ paddingX, currentCandy.GetComponent<BoxCollider2D>().size.y+ paddingY);CreateInitialBoard(offset);}privatevoidCreateInitialBoard(Vector2 offset){ candies =newGameObject[xSize,ySize]; float startX =this.transform.position.x; float startY =this.transform.position.y; int idx =-1;for(int x =0; x < xSize; x++){for(int y =0; y < ySize; y++){GameObject newCandy =Instantiate( currentCandy,newVector3(startX +(offset.x* x), startY +(offset.y* y),0), currentCandy.transform.rotation); newCandy.name= string.Format("Candy[{0}][{1}]", x, y);do{ idx =Random.Range(0, prefabs.Count);}while((x>0&& idx == candies[x -1, y].GetComponent<Candy>().id)||(y <0&& idx == candies[x, y -1].GetComponent<Candy>().id));Sprite sprite = prefabs[Random.Range(0, prefabs.Count)]; newCandy.GetComponent<SpriteRenderer>().sprite= sprite; newCandy.GetComponent<Candy>().id= idx; candies[x,y]= newCandy;}}}// Update is called once per framevoidUpdate(){}}