- Home /
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. :)
Any help and comments for achieving this is appreciated! Thanks:)
Answer by robertbu · Sep 25, 2014 at 03:12 AM
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.
Thanks! That makes sense. I'll try it out as soon as i come home.
Does scaling the orthographic size mean that I have to scale additional elements (mainly GUI, such as score, buttons, etc.) as well?
'orthographicSize' impacts what that specific camera sees. It won't impact GUI elements. It will impact any interface elements seen by the camera (if any). The usual way around world objects as UI elements is to use two cameras. One camera is dedicated to UI, and the second camera is dedicated to the game.
Using your code as a reference, I changed my sprites to quads. But I kept varying the size of the gameObjects and left the ortographicSize unchanged, as I didn't want to change the respective code (after days of working on it, I kinda feel attached to it ;) ).
And it works like a charm! Thank you! :)
A section of your code I had to change was the aspect ratio of the original image. Using your code, the resulting image was distorted, as the TextureImporter resizes the original image to the nearest power of two and makes it square by stretching the lower dimension. Apparently, the only way to the obtain the original size is a class reflection workaround. But maybe that's what you intended to do.
As you said, your visualization of the game may be different from what I wanted: Depending on the aspect ratio of the image, take the 'central part' (may be the whole if it fits the number of columns and rows) of the image and slize it into square pieces. But still, your answer led me to the solution. Thanks!
I'm glad you have it working. The class reflection workaround you reference is similar to code I was looking at to get this information. I've never dealt with this specific problem, but I did search to make sure there was a solution.
But I don't believe a solution is really not necessary. That is, textures in the project you control, so there are simpler, but more labor intensive solutions if necessary. For example, you could encode the aspect in the title, and pull it out at runtime, or have a dictionary you build that maps title to aspect, or stick to some standard aspects and segregate them into different folders, etc.
As for user images, most programmers use the WWW interface to pull in user images...either from local files or from the net. I believe the texture size of these files will be the actual size, so you can pull the aspect directly from the image. So you will only have to solve aspect issues for the images you put in the project (untested). In fact you could put your images in the Strea$$anonymous$$gAssets folder and use a common mechanism to pull in both standard images and user images.
Your answer
Follow this Question
Related Questions
Drag and slide object by touch 1 Answer
How do I check that no collisions have a tag? 1 Answer
How can I compare two tilemaps by color? 1 Answer
3D Tile Merging 0 Answers
Puzzle open door ? 1 Answer