Domina el uso del motor de física de Unity 2D para que el intercambio tipo Candy Crush funcione solo entre piezas adyacentes. Aquí se muestra cómo desactivar autocolisiones, lanzar un raycast en cuatro direcciones, detectar vecinos con RaycastHit2D y validar si un swipe es válido. Todo con código limpio y fácil de extender.
¿Cómo configurar Physics 2D para evitar autocolisiones en Unity?
Para que el raycast no detecte el propio caramelo, debes desactivar la opción que hace que las consultas empiecen dentro del collider.
Ruta de menú: Edit > Project Settings > Physics 2D.
Opción clave: desactivar "Queries Start In Colliders".
Efecto inmediato: el raycast ignora el propio objeto y encuentra el vecino real.
Error común: si no lo desactivas, el rayo choca con el caramelo actual y el intercambio se intentará consigo mismo.
¿Cómo detectar vecinos con RaycastHit2D y Vector2?
La detección se basa en lanzar un raycast desde la posición del caramelo en una dirección dada. Si hay colisión, se toma el GameObject del collider impactado. Luego, se repite para las cuatro direcciones con un bucle foreach y una lista List<GameObject>.
¿Qué hace getNeighbor con RaycastHit2D?
Entrada: una dirección Vector2.
Acción: lanzar Physics2D.Raycast desde transform.position hacia esa dirección.
Salida: el GameObject vecino si hay colisión; si no, null.
// Dentro de la clase CandyprivateGameObjectGetNeighbor(Vector2 direction){RaycastHit2D hit = Physics2D.Raycast(transform.position, direction);if(hit.collider !=null){return hit.collider.gameObject;// vecino encontrado}returnnull;// no hay vecino en esa dirección}
¿Cómo reunir todos con getAllNeighbors y foreach?
Recorre las cuatro direcciones del arreglo adjacentDirections.
Llama a GetNeighbor por cada dirección.
Agrega los vecinos no nulos a una lista.
privateList<GameObject>GetAllNeighbors(){List<GameObject> neighbors =newList<GameObject>();foreach(Vector2 direction in adjacentDirections){GameObject neighbor =GetNeighbor(direction);if(neighbor !=null){ neighbors.Add(neighbor);}}return neighbors;}
Puntos clave:
Origen del rayo: transform.position.
Solo chocará con objetos que tengan collider.
En bordes, algunas direcciones no devolverán vecino.
¿Cuándo permitir el swipe entre caramelos adyacentes?
La condición es simple y robusta: el intercambio solo se permite si el caramelo previamente seleccionado está dentro de la lista de vecinos del caramelo actual. Así se garantiza que solo se mueven piezas vecinas.
¿Cómo validar con canSwipe y la lista de vecinos?
Comprueba si GetAllNeighbors contiene previousSelected.gameObject.
Devuelve true si se puede intercambiar; en caso contrario, false.
¿Cómo integrar la validación en la selección y el swap?
En el flujo donde antes hacías el intercambio directo en el else, inserta una verificación con CanSwipe.
Si es válido: ejecuta SwipeSprite y deselecciona el anterior.
Si no es válido: deselecciona el anterior y deja seleccionado el nuevo para feedback visual.
// Fragmento ilustrativo del manejo de selecciónif(previousSelected ==null){SelectCandy();}else{if(CanSwipe()){SwipeSprite(); previousSelected.DeselectCandy();}else{ previousSelected.DeselectCandy();SelectCandy();}}
Ideas a retener:
Desactiva Queries Start In Colliders para evitar autocolisión y falsos positivos.
Usa RaycastHit2D para encontrar el primer vecino en una dirección.
Centraliza la lógica en CanSwipe para decidir si el intercambio procede.
Vecinos adyacentes: cuatro direcciones guardadas en adjacentDirections como Vector2.
Selección y feedback: solo un caramelo seleccionado a la vez; si no son vecinos, no hay intercambio.
¿Quieres que revise tu implementación de GetNeighbor o CanSwipe y te sugiera mejoras puntuales? Deja tu fragmento de código en un comentario y lo vemos juntos.
Configuración de Física en Unity para Movimiento de Caramelos
O dios mio, hay una Opcion asi para desactivar al propio objeto como detectable en el raycasting.
a mi me tocaba hacer malabares para que funcionara hasta me toco hacer usar capas siendo lasegunda el objeto que va detectar.
<--(x_x)>¬
Yikes al fin mi primer error
No me hace el swipe que debe hacer en este capitulo pero el del capitulo anterior si me sirvió aquí va el código (esta igualito)
Usando el Debug.Log para me di cuenta que la función booleana CanSwipe (la ultima) da false lo que significa que nunca entra a la parte del codigo donde el swpa ocurre pero... ¿porqué?
Cuando sepa que pasa lo publicare por si a alguien mas le ocurrio
<using System.Collections;using System.Collections.Generic;using UnityEngine;publicclassCandy:MonoBehaviour{privatestaticColor selectedColor =newColor(0.5f,0.5f,0.5f,1.0f);privatestaticCandy previousSelected =null;privateSpriteRenderer spriteRenderer;//variable privada como componente de un objeto deben ser iniciazliados en el awakeprivate bool isSelected =false;public int id;privateVector2[] adjacentDirections =newVector2[]//los vectores se pueden inicializar si se colocan corchets y unas llaves adnetro los elementos{Vector2.up,//enumerados , variables que ya existen de la libreriaVector2.down,Vector2.left,Vector2.right};privatevoidAwake(){ spriteRenderer =GetComponent<SpriteRenderer>();}privatevoidSelectCandy(){ isSelected =true; spriteRenderer.color= selectedColor; previousSelected = gameObject.GetComponent<Candy>();}privatevoidDeselectCandy(){ isSelected =false; spriteRenderer.color=Color.white; previousSelected =null;}privatevoidOnMouseDown(){if(spriteRenderer.sprite==null||BoardManager.sharedInstance.isShifting){return;}if(isSelected){DeselectCandy();}else{if(previousSelected ==null){SelectCandy();}else{/*SwapSprite(previousSelected);
previousSelected.DeselectCandy();*/Debug.Log(CanSwipe());if(CanSwipe()){SwapSprite(previousSelected); previousSelected.DeselectCandy();}else{ previousSelected.DeselectCandy();SelectCandy();Debug.Log("no puedo cambiar");}}}}publicvoidSwapSprite(Candy newCandy){if(spriteRenderer.sprite== newCandy.GetComponent<SpriteRenderer>().sprite){return;}Sprite oldCandy = newCandy.spriteRenderer.sprite; newCandy.spriteRenderer.sprite=this.spriteRenderer.sprite;this.spriteRenderer.sprite= oldCandy; int tempId = newCandy.id; newCandy.id=this.id;this.id= tempId;Debug.Log("puedo cambiar");}privateGameObjectGetNeighbor(Vector2 direction){RaycastHit2D hit =Physics2D.Raycast(this.transform.position, direction);if(hit.collider!=null){return hit.collider.gameObject;}else{returnnull;}}privateList<GameObject>GetAllNeighbors(){List<GameObject> neighbors =newList<GameObject>();foreach(Vector2 direction in adjacentDirections){ neighbors.Add(GetNeighbor(direction));}return neighbors;}private bool CanSwipe(){returnGetAllNeighbors().Contains(previousSelected.gameObject);}}>