Nonogramz
A puzzle-based role playing game.
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
public class ImageToPuzzle : MonoBehaviour
{
    //Source image to create a nonogram of
    public GameObject image;
    //Width in pixels of the source image, feed only square images for this to work
    private int image_size;
    //Sprite component of image
    private Sprite mySprite;
    //2D matrix of size: image_size. Stores colour of each pixel in respective elements
    private Color[,] table;
    //Size of the puzzle to be created, should be specified in editor
    public int puzzle_size;
    //2D matrix of puzzle_size, holds colour of each tile of the nonogram
    private Color[,] puzzle;
    //Size of each block of the puzzle, shows the number of pixels to combine into one block per row/column
    private int block_size;
    //Prefab of square used to display generated nonogram on screen for testing purposes
    public GameObject tile;
    // Start is called before the first frame update
    void Start()
    {
    }
    //Samples the image pixel by pixel and stores the colour in a separate element in the table matrix
    void GenerateImageTable()
    {
        mySprite = image.GetComponent<SpriteRenderer>().sprite;
        image_size = (int)mySprite.rect.width;
        table = new Color[image_size, image_size];
        for (int i = 0; i < image_size; i++)
        {
            for (int j = 0; j < image_size; j++)
            {
                table[i, j] = mySprite.texture.GetPixel(j, image_size - i - 1);
            }
        }
    }
    //Cuts down the table matrix into desired puzzle size matrix
    void GeneratePuzzleTable()
    {
        block_size = image_size / puzzle_size;
        puzzle = new Color[puzzle_size, puzzle_size];
        GameObject tileDisplay = new GameObject("TileDisplay");
        for (int i = 0; i < puzzle_size; i++)
        {
            for (int j = 0; j < puzzle_size; j++)
            {
                puzzle[i, j] = Avg(i * block_size, j * block_size);
                GameObject x = Instantiate(tile, new Vector3(1 + j * (float)0.30, 1 - i * (float)0.30, 0), Quaternion.identity);
                x.transform.SetParent(tileDisplay.transform);
                x.GetComponent<Renderer>().material.color = puzzle[i, j];
            }
        }
    }
    //Creates the puzzle's text file from collected information
    void GeneratePuzzleText()
    {
        int count = 1;
        string filepath = Application.dataPath + "/Resources/Nonograms/Generated/" + puzzle_size.ToString() + "x" + puzzle_size.ToString() + image.name;
        if (!File.Exists(filepath + ".txt"))
        {
            using (StreamWriter sw = File.CreateText(filepath + ".txt"))
            {
                sw.WriteLine(puzzle_size.ToString());
                sw.WriteLine(getColNumberText());
                sw.WriteLine(puzzle_size.ToString());
                sw.WriteLine(getRowNumberText());
                sw.WriteLine(MapColors());
                sw.WriteLine(image.name);
            }
        }
        else
        {
            while (File.Exists(filepath + count.ToString() + ".txt"))
            {
                count++;
            }
            using (StreamWriter sw = File.CreateText(filepath + count.ToString() + ".txt"))
            {
                sw.WriteLine(puzzle_size.ToString());
                sw.WriteLine(getColNumberText());
                sw.WriteLine(puzzle_size.ToString());
                sw.WriteLine(getRowNumberText());
                sw.WriteLine(MapColors());
                sw.WriteLine(image.name);
            }
        }
    }
    //Generates Column Labels
    string getColNumberText()
    {
        string newString = "";
        for (int i = 0; i < puzzle_size; i++)
        {
            int count = 0;
            int commaCount = 0;
            if (i > 0)
            {
                newString += "|";
            }
            for (int j = 0; j < puzzle_size; j++)
            {
                if (puzzle[j, i].a == 1)
                {
                    count++;
                }
                else if (count > 0)
                {
                    if (commaCount > 0)
                    {
                        newString += "," + count;
                    }
                    else
                    {
                        newString += count;
                        commaCount++;
                    }
                    count = 0;
                }
            }
            if (count > 0)
            {
                if (commaCount > 0)
                {
                    newString += "," + count;
                }
                else
                {
                    newString += count;
                    commaCount++;
                }
                count = 0;
            }
            else
            {
                if (commaCount == 0)
                {
                    newString += "0";
                }
            }
        }
        return newString;
    }
    //Generates row labels
    string getRowNumberText()
    {
        string newString = "";
        for (int i = 0; i < puzzle_size; i++)
        {
            int count = 0;
            int commaCount = 0;
            if (i > 0)
            {
                newString += "|";
            }
            for (int j = 0; j < puzzle_size; j++)
            {
                if (puzzle[i, j].a == 1)
                {
                    count++;
                }
                else if (count > 0)
                {
                    if (commaCount > 0)
                    {
                        newString += "," + count;
                    }
                    else
                    {
                        newString += count;
                        commaCount++;
                    }
                    count = 0;
                }
            }
            if (count > 0)
            {
                if (commaCount > 0)
                {
                    newString += "," + count;
                }
                else
                {
                    newString += count;
                    commaCount++;
                }
                count = 0;
            }
            else
            {
                if (commaCount == 0)
                {
                    newString += "0";
                }
            }
        }
        //Debug.Log(newString);
        return newString;
    }
    //Creates a cut-down version of the Puzzle matrix according to the txt format
    string MapColors()
    {
        List<string> colors = new List<string>();
        int[,] newPuzzle = new int[puzzle_size, puzzle_size];
        string matrix_ = "";
        for (int i = 0; i < puzzle_size; i++)
        {
            for (int j = 0; j < puzzle_size; j++)
            {
                if (puzzle[i, j].a > 0)
                {
                    if (!colors.Contains(ColorUtility.ToHtmlStringRGB(puzzle[i, j])))
                    {
                        colors.Add(ColorUtility.ToHtmlStringRGB(puzzle[i, j]));
                    }
                    newPuzzle[i, j] = colors.IndexOf(ColorUtility.ToHtmlStringRGB(puzzle[i, j])) + 1;
                }
                else
                {
                    newPuzzle[i, j] = 0;
                }
                matrix_ += newPuzzle[i, j].ToString() + " ";
            }
            matrix_ += "\n";
        }
        string cols = colors.Count.ToString();
        foreach (string clr in colors)
        {
            cols += " " + clr;
        }
        matrix_ = cols + "\n" + matrix_;
        return matrix_;
    }
    //Returns the average colour value from a block_size x block_size group in the image table
    Color Avg(int row, int col)
    {
        Color sum = new Color();
        for (int i = row; i < row + block_size; i++)
        {
            for (int j = col; j < col + block_size; j++)
            {
                sum += table[i, j];
            }
        }
        return sum / (block_size * block_size);
    }
    // Update is called once per frame
    void Update()
    {
        GenerateImageTable();
        GeneratePuzzleTable();
        GeneratePuzzleText();
        Destroy(this); //The generation has to be done only once, so destroy this script after one loop
    }
}
								Nonogramz is an RPG where a player progresses through the story by solving nonogram puzzles. It is being developed on Unity3D for mobile and PC platforms. I primarily worked on the automation of the creator's engine, where the gamemakers can upload any image and the game converts it into a puzzle, which can later be used during actual gameplay. The code sample above shows how I've dealt with reading colors, pixels and more.
Nonogramz is the first Unity3D game that I worked on that was designed to be played (all others were for research/simulation purposes). I worked with a team of around 15 people, and closely followed the Agile methodolody through this project. I experienced how different teams interact, how the workflow progresses between the design team and the development team. I was leading new team members within days of joining the project thanks to my active contribution and ability to communicate well, and I am still one of two admins overseeing the development of this game.


