Segmenting image (at runtime) for puzzle pieces

Hi,

I’m working on my first Unity project, a sliding puzzle (like this), and I’m struggling with splitting the texture (imported at runtime) into segments to fit the square pieces. Each tile is a default Sprite-GameObject.

My current code:

Texture2D image = Resources.Load<Texture2D> ("Images/katze");

//Calculating the tile sizes
float sizeX = image.width / numCols;
float sizeY = image.height / numRows;
//Take the smaller size (tiles are square)
float tileSize = Mathf.Min(sizeX, sizeY);
//I don't know the reason for this value, just played around :)
float pixelsToUnits = tileSize / 8;

//Get the topLeft point which acts as 'origin' for segmentingthe image
Vector2 midPoint = new Vector2(image.width / 2, image.height / 2);
Vector2 offset = new Vector2 (tileSize * numCols / 2, tileSize * numRows / 2);
Vector2 topLeft = midPoint - offset; //new Vector2 (tileSize * numRows / 2, tileSize * numCols / 2);

Vector2 pivot = new Vector2 (0.5f, 0.5f); // Center

for (int x = 0; x < numCols; x++) {
	for(int y = 0; y < numRows; y++) {
		//The segment of the texture for this tile
		Rect rect = new Rect(topLeft.x + x * tileSize, topLeft.y + y * tileSize, tileSize, tileSize);
		Sprite tileSprite = Sprite.Create(image, rect, pivot, pixelsToUnits);
		grid[x, y] = createTile(x, y, tileSprite); //Instantiating the prefab and assigning the sprite
	}
}

Using this code, the resulting sprites are blurry (because of the texture being resized to the nearest power of two?) and I cannot handle non-square images (because of the texture being resized to a square).

The reasons, why I do not simply use the Sprite Editor for segmenting the texture, are:

  • I want the players to use their own image for the puzzle
  • Number (and therefore size) of tiles is variable
  • Size of tiles may depend on aspect ratio

I tried using planes and their uv maps, but my scaling code does not work with planes. That’s why I ask before rebuilding it. :slight_smile:

Any help and comments for achieving this is appreciated! Thanks:)

While my visualization of the game may not match yours, here is my thinking on the problem:

  • Use Quads rather than Sprites
  • Use a common material for all the tiles
  • Make tiles 1 x 1
  • Change the fieldOfView or orthograpicSize rather than resize the tiles
  • Set the uvs on the tiles

The result is that all the tiles will batch and therefore draw in a single draw call. Here is some example code:

using UnityEngine;
using System.Collections;

    public class Example : MonoBehaviour {
	public Texture2D tex;  // Texture to cut up
	public Material mat;   // Material to use (texture is discarded)
	public int cols = 6;   // Number of tiles across
	public float aspect = 1.5f;  // Original aspect (height / width)

	void Start() {
		mat.mainTexture = tex;
		BuildPieces();
	}

	void BuildPieces() {
		int rows = Mathf.RoundToInt(cols * aspect);
		Vector3 offset = Vector3.zero;
		offset.x = -Mathf.RoundToInt ((float)cols / 2.0f - 0.5f);
		offset.y = -Mathf.RoundToInt ((float)rows / 2.0f - 0.5f);
		float startX = offset.x;
		float uvWidth = 1.0f / cols;
		float uvHeight = 1.0f / rows;


		for (int i = 0; i < rows; i++) {
			for (int j = 0; j < cols; j++) {
				GameObject go = GameObject.CreatePrimitive (PrimitiveType.Quad);
				Transform t = go.transform;
				t.position = offset;
				t.localScale = new Vector3(0.95f, 0.95f, 0.95f);
				go.renderer.material = mat;

				Mesh mesh = go.GetComponent<MeshFilter>().mesh;
				Vector2[] uvs = mesh.uv;
				uvs[0] = new Vector2(j * uvWidth, i * uvHeight);
				uvs[3] = new Vector2(j * uvWidth, (i + 1) * uvHeight);
				uvs[1] = new Vector2((j + 1) * uvWidth, (i + 1) * uvHeight);
				uvs[2] = new Vector2((j + 1) * uvWidth, i * uvHeight);
				mesh.uv = uvs;
				offset.x += 1.0f;
			}
			offset.y += 1.0f;
			offset.x = startX;
		}
	}
}

Note I’m requiring the aspect ratio of the original image to deal with the square image problem. You can get this information dynamically for an image you load from WWW, and there are ways to get this information using an editor script without the Unity editor. I’ve made the tiles (0.95 x 0.95) so they can be easily seen. It might be smart to give a frame or some sort of separation to hide issues mapping pixels to uvs.