Estoy haciendo un juego de rompecabezas y la mecánica básica del rompecabezas parece funcionar bien.

Sin embargo, después de codificar la mecánica principal, intenté implementar una función de aleatorización para que el rompecabezas no estuviera resuelto cuando lo cargó por primera vez. Hice lo que tenía más sentido en mi mente, y fue darle a cada quad un nombre único, y luego los hice cambiar de posición con el comando transform.positions.

Cuando intenté ejecutar esta función, todo lo que obtuve fue un error CS0103 que me dice que los nombres únicos asignados no existían en el contexto actual, lo que tiene sentido ya que los quads primero se crearán cuando se ejecute el script.

Solo quiero saber si esto es realmente posible con mi código:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Puzzle : MonoBehaviour {

    public Texture2D image;

    //Amount of blocks per line(can be changed inside of unity)
    public int blocksPerLine = 4;

    Block emptyBlock;

    //The function that calls the CreatePuzzle function
    void Start()
    {
        CreatePuzzle();
        Randomizer();
    }

    //CreatePuzzle function
    void CreatePuzzle()
    {
        Texture2D[,] imageSlices = ImageSlicer.GetSlices(image, blocksPerLine);
        //Everytime y is equal to 0 and less than the blocksPerLine it will postfix y, meaning it will create another block until it is at its limit.
        for (int y = 0; y < blocksPerLine; y++)
        {
            for(int x = 0; x < blocksPerLine; x++)
            {
                //This creates the gameobject as the primitive type: Quad.
                GameObject blockObject = GameObject.CreatePrimitive(PrimitiveType.Quad);
                blockObject.name = "Piece" + x;
                blockObject.transform.position = -Vector2.one * (blocksPerLine - 1) * .5f + new Vector2(x, y);
                blockObject.transform.parent = transform;

                Block block = blockObject.AddComponent<Block>();
                block.OnBlockPressed += PlayerMoveBlockInput;
                block.Init(new Vector2Int(x, y), imageSlices[x, y]);

                //Creates an empty block, so that you're able to slide the puzzle around
                if(y == 0 && x == blocksPerLine - 1)
                {
                    blockObject.SetActive(false);
                    emptyBlock = block;
                }
            }
        }
        //Sets the sixe of the block to match the Camera view
        Camera.main.orthographicSize = blocksPerLine * .55f;
    }


    void Randomizer()
    {
        Piece0.transform.positions = Piece1.transform.positions;
    }

    //The function that makes the player available to move the blocks
    void PlayerMoveBlockInput(Block blockToMove)
    {
        if ((blockToMove.coord - emptyBlock.coord).sqrMagnitude == 1)
        {
            Vector2Int targetCoord = emptyBlock.coord;
            emptyBlock.coord = blockToMove.coord;
            blockToMove.coord = targetCoord;

            Vector2 targetPosition = emptyBlock.transform.position;
            emptyBlock.transform.position = blockToMove.transform.position;
            blockToMove.transform.position = targetPosition;
        }
    }
}
0
vax 16 oct. 2018 a las 15:06

2 respuestas

La mejor respuesta

El problema

Se lanza la excepción porque está intentando hacer referencia a los bloques creados de forma incorrecta. Cuando ejecutas:

Piece0.transform.positions = Piece1.transform.positions;

Lo que le está diciendo a su programa que haga es encontrar una variable llamada Piece0, encontrar una variable llamada Piece1 y hacer cosas con su valor transform.positions [sic]. Pero aquí está el problema: En realidad, no tiene ninguna variable llamada Piece0 o Piece1. ¡Así que el compilador no puede resolver esas referencias!

La solución

Lo que sí tiene son GameObjects que colocó en la escena, con el atributo name establecido en "Piece0" y "Piece1". (Esto no es lo mismo que crear variables con esos nombres). Por lo tanto, para obtener referencias a esos objetos, antes de poder acceder a sus miembros, deberá adoptar un enfoque diferente. Algo como GameObject.Find(). Según su descripción:

Encuentra un GameObject por su nombre y lo devuelve.

Entonces usa GameObject.Find() para recuperar referencias a los dos GameObjects, luego reasigna sus posiciones. Su código actual tampoco hace realmente un cambio; más bien, simplemente coloca un objeto en el mismo lugar que el otro, por lo que la lógica debe mejorarse. Y necesitará alguna variable intermedia para almacenar la posición original de uno de los objetos, antes de asignarlo al otro objeto.

Así es como se verían todos los cambios en la práctica:

void Randomizer()
{
    // Get the references
    GameObject piece0 = GameObject.Find("Piece0");
    GameObject piece1 = GameObject.Find("Piece1");

    // Store the position so it isn't lost
    Vector3 piece0OriginalPos = piece0.transform.position;

    // Switch the positions
    piece0.transform.position = piece1.transform.position;
    piece1.transform.position = piece0OriginalPos;
}
0
Serlite 16 oct. 2018 a las 15:39

Guarde todos los bloques que cree en una lista.

Para hacer eso, declare una Lista en la parte superior:

private List<GameObject> blocks;

E inicialícelo en createPuzzle() así:

blocks = new List<GameObject>();

Entonces llénelo así:

blocks.Add(block);

Luego, en la función de aleatorización, baraja la lista, por ejemplo, con un método como este:

private static Random rng = new Random();  

public static void Shuffle<T>(this IList<T> list)  
{  
    int n = list.Count;  
    while (n > 1) {  
        n--;  
        int k = rng.Next(n + 1);  
        T value = list[k];  
        list[k] = list[n];  
        list[n] = value;  
    }  
}

Sugerido por @grenade en esta respuesta

Una vez que se baraja la lista, tome los elementos uno por uno y asigne posiciones en orden, como lo hace ahora en la creación de objetos

0
KYL3R 16 oct. 2018 a las 13:53