0

I'm making a puzzle game, and the basic puzzle mechanics seem to work fine.

However, after coding the core mechanics, I tried to implement a randomizer function so the puzzle wasn't already solved when you first loaded it up. I did the thing that made most sense in my mind, and that was to give each quad a unique name, and there after made them switch positions with the transform.positions command.

When trying to execute this function all I got was an CS0103 error that tells me that the unique assigned names didn't exist in the current context, which makes sense since the quads first will be created when the script is executed.

I just want to know if this is actually possible with my code:

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;
        }
    }
}
Serlite
  • 12,130
  • 5
  • 38
  • 49
vax
  • 21
  • 2
  • Move your imageSlices to class variable and then you can access them inside Randomizer() in some kind of for each loop. – maximelian1986 Oct 16 '18 at 12:29
  • @maximelian1986 I have already declared the ImageSlices as a class variable inside of another script: https://gyazo.com/391b80b4d5f075acf9f69f23f94c5e9a I'm just not sure how the ImageSlicer would help in context of randomizing the puzzle pieces. Could you please elaborate on that:)) – vax Oct 16 '18 at 12:46
  • What exactly is `Piece0` and `Piece1`? I'm not seeing those variables/classes defined anywhere in your code. – Serlite Oct 16 '18 at 14:13
  • @Serlite They're defined by this command: blockObject.name = "Piece" + x; This command changes the name of the Qauds to Piece0 and Piece1. – vax Oct 16 '18 at 14:31
  • @vax Can you explain what you're trying to do in your randomizer? At the moment, even if it worked correctly, you would only be setting the position of Piece0 to the position of Piece1. Basically, you'd be placing exactly one object at the position of another object, which...well, doesn't really sound like randomization logic. – Serlite Oct 16 '18 at 14:47
  • @Serlite It isn't a randomizer per se. You know those sliding puzzles, I also believe they are called the fifteen puzzle. Well when the game starts up the puzzle is already done. I want it to make it so it isn't complete and that you have to slide the puzzle pieces around until you get the full image. The puzzle will only be 2x2, so making to of the blocks switch position will make the puzzle work just fine for the concept it is made for. I hope it makes sense – vax Oct 16 '18 at 15:00

2 Answers2

0

Store all the blocks you create in a list.

To do that, declare a List at the top:

private List<GameObject> blocks;

And initialize it in createPuzzle() like this:

blocks = new List<GameObject>();

Then fill it like this:

blocks.Add(block);

Then in the randomize function, shuffle the list, for instance, with a method like this one:

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;  
    }  
}

Suggested by @grenade in this answer

Once the list is shuffle, take elements one by one and assign positions in order, like you do now on object creation

KYL3R
  • 3,877
  • 1
  • 12
  • 26
Basile Perrenoud
  • 4,039
  • 3
  • 29
  • 52
  • That's a great solution if i had a problem with the whole randomized puzzle pieces thing. The problem i am having is that i am unable to move the blocks around since the blocks first will be created after the CreatePuzzle function has been executed, and the function is only able to execute if there is no compile error in all of the scripts. Assigning a name to the blocks and then trying to make them switch positions doesn't work cause i will get an CS0103 error telling me that the blocks doesn't exist in the current context. – vax Oct 16 '18 at 13:59
  • I guess you are new at programming. This solution actually solves the problem because keeping the blocks in a list will allow you to access them later. Your problem is that you didn't save them in a variable, just let them in the scene. The solution you accepted will work but is less efficient (you need to find elements in the scene while here you keep a reference to them at hand in a list). And it requires you to code the switch manually. So it won[t be really random random – Basile Perrenoud Oct 17 '18 at 08:47
0

The Problem

The exception is thrown because you're trying to reference the created blocks incorrectly. When you execute:

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

What you're telling your program to do is to find a variable named Piece0, find a variable named Piece1, and do things with their transform.positions [sic] value. But here's the problem: You don't actually have any variables named Piece0 or Piece1. So the compiler can't resolve those references!

The Solution

What you do have are GameObjects that you placed into the scene, with the attribute name set to "Piece0" and "Piece1". (This is not the same as creating variables with those names.) So to get references to those objects, before you can access their members, you'll need to take a different approach. Something like GameObject.Find(). As per its description, it:

Finds a GameObject by name and returns it.

So use GameObject.Find() to retrieve references to the two GameObjects, then reassign their positions. Your current code also doesn't really do a switch; rather, it just places one object in the same place as the other, so the logic needs to be improved. And you'll need some intermediate variable to store the original position of one of the objects, before assigning it to the other object.

Here's what all the changes might look like in practice:

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;
}
Serlite
  • 12,130
  • 5
  • 38
  • 49