The idea of this 2D game is as follows: Given a 800*600 image, a custom unity plugin splits into pieces (say, 4) in edit mode and then randomly shuffles pieces around the gameboard. In run-time user has to drag and drop pieces into its original position with mouse. If original image is assembled, the goal of the game is achieved and game setup is restarted (pieces shuffled again).
Play puzzle Unity game onlne
Download puzzle game Unity source code
Script that handles game logic
using UnityEngine;
using System.Collections;
using System.Linq;
using System.Collections.Generic;
using System;
public class DragIt : MonoBehaviour {
public GameObject winImage;
private Ray ray;
private RaycastHit hit;
private bool dragging=false;
private GameObject draggingObject = null;
private GameObject targetObject = null;
private Color savedColor;
// Update is called once per frame
void Update () {
if (Input.GetMouseButton(0)) {
ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit)) {
if (!dragging) {
dragging = true;
draggingObject = hit.transform.gameObject;
}
if (dragging) {
draggingObject.transform.position = new Vector3(hit.point.x,hit.point.y,200);
// calculate distance to all pieces, except current object
var pieces = Helper.IteratePieces().Where (p=>p!=draggingObject).ToList();
var distances = pieces.Select(p=>Vector3.Distance(p.transform.position,draggingObject.transform.position)).ToList();
var minDistance = distances.Min(); // get minimum distance
// check distance between current position and original position
PositionData storage = draggingObject.GetComponent();
var currentDistance = Vector3.Distance(storage.ShuffledPosition,draggingObject.transform.position);
GameObject targetObjectNew=null;
if (currentDistance < minDistance)
targetObjectNew = draggingObject;
else {
var index = distances.IndexOf(minDistance);
targetObjectNew = pieces[index];
}
//Debug.Log("Distances: " + Helper.ToString(distances) + ", target " + targetObjectNew.name + " min distance: " + minDistance + ", current distance: " + currentDistance);
// target object changed
if (targetObjectNew != targetObject) {
//Debug.Log("Distances: " + Helper.ToString(distances) + ", target " + targetObjectNew.name + " min distance: " + minDistance + "current distance: " + currentDistance);
Debug.Log("Switch to " + targetObjectNew.name );
if (targetObject!=null && targetObject != draggingObject ) {
targetObject.renderer.material.color = savedColor;
//Debug.Log("Restored color for " + targetObject.name + " to " + savedColor.ToString() );
}
if (targetObjectNew != draggingObject) {
// highlight target object
savedColor = targetObjectNew.renderer.material.color;
targetObjectNew.renderer.material.color = Color.green;
}
targetObject = targetObjectNew;
}
}
}
} else if (dragging) {
dragging = false; // dropping
PositionData storageSource = draggingObject.GetComponent();
// reset target object position, if it is the same object
if (targetObject==draggingObject)
draggingObject.transform.position = storageSource.ShuffledPosition;
else {
// restore color
if (targetObject!=null)
targetObject.renderer.material.color = savedColor;
PositionData storageTarget = targetObject.GetComponent();
// swap positions between source and target
targetObject.transform.position = storageSource.ShuffledPosition;
draggingObject.transform.position = storageTarget.ShuffledPosition;
// store new positions
storageSource.ShuffledPosition = draggingObject.transform.position;
storageTarget.ShuffledPosition = targetObject.transform.position;
// check win condition and notify by color blinking
if (!CheckWinCondition())
{
if (draggingObject.transform.position == storageSource.OriginalPosition)
StartCoroutine(Blink (Color.green, draggingObject));
else
StartCoroutine(Blink (Color.red, draggingObject));
if (targetObject.transform.position == storageTarget.OriginalPosition)
StartCoroutine( Blink (Color.green, targetObject));
else
StartCoroutine(Blink (Color.red, targetObject));
}
}
Debug.Log("Dropped " + targetObject.name );
targetObject = null;
draggingObject = null;
}
}
bool CheckWinCondition() {
bool win = Helper.IteratePieces().Select(o=>o.GetComponent()).All(d=>d.OriginalPosition==d.ShuffledPosition);
if (win) {
StartCoroutine(Win ());
}
return win;
}
///
/// Blink with the specified color for a given game object.
///
///
/// Color.
///
///
/// Go.
///
IEnumerator Blink(Color color, GameObject go) {
Color savedcolor = go.renderer.material.color;
for(int i=0;i<=2;i++) {
yield return new WaitForSeconds(.1f);
go.renderer.material.color = color;
yield return new WaitForSeconds(.25f);
go.renderer.material.color = savedcolor;
}
}
IEnumerator Win() {
Helper.IteratePieces().ToList().ForEach(o=>StartCoroutine(Blink (Color.green, o)));
yield return new WaitForSeconds(.2f);
if (winImage!=null)
winImage.SetActive(true);
else
Debug.LogError("WinImage not found");
// enjoy a "GAME WON" title for a moment and restart a game
yield return new WaitForSeconds(5f);
if (winImage!=null)
winImage.SetActive(false);
Helper.ShuffleGameObjects();
}
}
Unity plugin source code
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using System.IO;
using System;
using System.Linq;
public class CreatePuzzle : ScriptableWizard {
public Texture2D Picture;
public int N=2;
[MenuItem("GameObject/Create Other/Puzzle")]
static void CreateWizard()
{
ScriptableWizard.DisplayWizard("Create Puzzle",typeof(CreatePuzzle));
}
void OnWizardCreate()
{
int width = Picture.width;
int height = Picture.height;
Debug.Log("wh: " + width + "/" + height);
for(int x=1;x<=N;x++)
for(int y=1;y<=N;y++) {
int xPiece = Convert.ToInt32(width*x/(float)N - width/2f - width/2f/N);
int yPiece = Convert.ToInt32(height*y/(float)N - height/2f - height/2f/N);
string name = "piece_" + x + "_" + y;
//Debug.Log(name + ": " + xPiece + "/" + yPiece);
Rect uv = new Rect( (x-1)/(float)N,(y-1)/(float)N, 1/(float)N, 1/(float)N);
CreateMesh(name,xPiece,yPiece,uv, width/N, height/N);
}
Shuffle(); // shuffle pieces to make a puzzle
}
GameObject CreateMesh(string meshName, int x, int y, Rect uv, int width, int height) {
string assetFolder = "Assets";
int anchorX=Convert.ToInt32(width/2);
int anchorY=Convert.ToInt32(height/2);
//Create Vertices
Vector3[] Vertices = new Vector3[4];
//Create UVs
Vector2[] UVs = new Vector2[4];
//Two triangles of quad
int[] Triangles = new int[6];
//Assign vertices based on pivot
//Bottom-left
Vertices[0].x = -anchorX;
Vertices[0].y = -anchorY;
//Bottom-right
Vertices[1].x = Vertices[0].x+width;
Vertices[1].y = Vertices[0].y;
//Top-left
Vertices[2].x = Vertices[0].x;
Vertices[2].y = Vertices[0].y+height;
//Top-right
Vertices[3].x = Vertices[0].x+width;
Vertices[3].y = Vertices[0].y+height;
Vector2[] uvs2 = new Vector2[]
{
new Vector2(uv.x, uv.y), //Bottom-left
new Vector2(uv.x+uv.width, uv.y), //Bottom-right
new Vector2(uv.x, uv.y+uv.height), //Top-left
new Vector2(uv.x+uv.width, uv.y+uv.height), //Top-right
};
//Assign triangles
Triangles[0]=3;
Triangles[1]=1;
Triangles[2]=2;
Triangles[3]=2;
Triangles[4]=1;
Triangles[5]=0;
//Generate mesh
Mesh mesh = new Mesh();
mesh.name = meshName;
mesh.vertices = Vertices;
mesh.uv = uvs2;
mesh.triangles = Triangles;
mesh.RecalculateNormals();
//Create asset in database
AssetDatabase.CreateAsset(mesh, AssetDatabase.GenerateUniqueAssetPath(assetFolder + "/" + meshName) + ".asset");
AssetDatabase.SaveAssets();
//Create plane game object
GameObject piece = new GameObject(meshName);
MeshFilter meshFilter = (MeshFilter)piece.AddComponent(typeof(MeshFilter));
piece.AddComponent(typeof(MeshRenderer));
//Assign mesh to mesh filter
meshFilter.sharedMesh = mesh;
mesh.RecalculateBounds();
//Add a box collider component
piece.AddComponent(typeof(BoxCollider));
// place piece into designed coordinates
piece.transform.Translate(new Vector3(x,y,200));
// capture original position
piece.AddComponent();
PositionData storage = piece.GetComponent();
storage.OriginalPosition = piece.transform.position;
var tempMaterial = new Material(piece.renderer.material); //
tempMaterial.mainTexture = Picture;
piece.renderer.material = tempMaterial;
//plane.renderer.sharedMaterial.mainTexture = Picture;
piece.transform.parent = GameObject.Find("scene").transform;
return piece;
}
void Shuffle() {
Helper.ShuffleGameObjects();
}
}
Helper class
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System;
using System.Linq;
public static class Helper {
public static IEnumerable Shuffle(this IEnumerable enumerable)
{
var r = new System.Random();
return enumerable.OrderBy(x=>r.Next()).ToList();
}
public static IEnumerable IteratePieces() {
var parent = GameObject.Find("scene").transform;
for (int i = 0; i < parent.childCount; ++i)
{
var piece = parent.GetChild(i).gameObject;
yield return piece;
}
}
public static IEnumerable GetPositions(this IEnumerable enumerable) {
return enumerable.Select (o=>o.transform.position);
}
public static string ToString(this IEnumerable enumerable) {
return string.Join (" ",enumerable.Select (i=>i.ToString()).ToArray());
}
public static void ShuffleGameObjects() {
var objects = Helper.IteratePieces().ToList();
var positions = objects.GetPositions();
var positionsShuffed = positions.Shuffle().ToList();
for (int i = 0; i < objects.Count; i++) {
objects[i].transform.position = positionsShuffed[i];
PositionData storage = objects[i].GetComponent();
storage.ShuffledPosition = positionsShuffed[i];
}
}
}