Find Cubes Same Colour in Stack

Hello,

I have a stack of cubes that are initiated on start up - the cube colours are selected randomly from an array of Materials (4 max).

I want to be able to have an array on every cube that lists if the cube next to it, is the same colour, and if the cube next to that is the same colour, etc.

When the cubes are initiated, they fire a raycast from every face to find the colours from the cubes that surround it. So I’m able to reference the colours that surround a cube.

But I also want to reference the same coloured cubes that surround neighbouring same coloured cubes. So if I select a cube at random, I can see a list of all the cubes that are the same colour as well as neighbouring cubes.

This script is attached to every cube that is initiated:

var FaceUp 	: Color;
var CubeUp : GameObject;

var FaceDown 	: Color;
var CubeDown : GameObject;

var FaceLeft : Color;
var CubeLeft : GameObject;

var FaceRight : Color;
var CubeRight : GameObject;

var FaceFront : Color;
var CubeFront : GameObject;

var FaceRear : Color;
var CubeRear : GameObject;

var ThisColour : Color;



function Start(){

	ThisColour = renderer.material.color;

	while(true){

		RaycastFire();
		yield WaitForSeconds(0.5);
	}
}


var showDebug : boolean = false;
function RaycastFire(){

	/*
	Fires raycast to only Down, Right and Rear. Becasue the neighbouring cubes will fire 
	a raycast at us, we might as well push the Up, Left and Forward colour to us too,
	to save us from firing 3 extra rays - Thus, we get the cube colours that surround us
	*/

	var ray = transform.position;
	var hitDown : RaycastHit;
	var hitRight : RaycastHit;
	var hitRear : RaycastHit;

	if (Physics.Raycast (ray, -Vector3.up, hitDown, 100.0)) {

		var hitColorDown = hitDown.collider.gameObject.renderer.material.color;
		
		if(showDebug)
			Debug.DrawLine (ray, hitDown.point, hitColorDown, 0.25);

		FaceDown = hitColorDown;
		CubeDown = hitDown.collider.gameObject;

		hitDown.collider.gameObject.GetComponent(JengaBlock).ColourAbove(renderer.material.color, gameObject);

	}


	if (Physics.Raycast (ray, -Vector3.left, hitRight, 100.0)) {

		var hitColorRight = hitRight.collider.gameObject.renderer.material.color;
			
		if(showDebug)
			Debug.DrawLine (ray, hitRight.point, hitColorRight, 0.25);

		FaceRight = hitColorRight;
		CubeRight = hitRight.collider.gameObject;

		hitRight.collider.gameObject.GetComponent(JengaBlock).ColourLeft(renderer.material.color, gameObject);

	}


	if (Physics.Raycast (ray, -Vector3.forward, hitRear, 100.0)) {

		var hitColorRear = hitRear.collider.gameObject.renderer.material.color;
			
		if(showDebug)
			Debug.DrawLine (ray, hitRear.point, hitColorRear, 0.25);

		FaceRear = hitColorRight;
		CubeRear = hitRear.collider.gameObject;

		hitRear.collider.gameObject.GetComponent(JengaBlock).ColourFront(renderer.material.color, gameObject);

	}

	CheckForSame();

}

function ColourAbove(above : Color, obj : GameObject){

	FaceUp = above;
	CubeUp = obj;
}
function ColourLeft(left : Color, obj : GameObject){

	FaceLeft = left;
	CubeLeft = obj;
}
function ColourFront(front : Color, obj : GameObject){

	FaceFront = front;
	CubeFront = obj;
}

So now I have referenced the cubes around us, I now need to check if any of these are the same colour, if they’re not - then we don’t need to worry.

So I understand that I can do something like this:

if(FaceUp == ThisColour)
    //The cube above us is the same colour

But now I want to check if that ‘FaceUp’ cube has any neighbours that are the same colour too (except us, as we already know we are the same colour).

Example:

Okay, so above is a 2D example of what I’m trying to achieve. The square 0 is the cube I have clicked on, I am able to find the squares next to it that are the same (1 and 2) by using ‘if(FaceUp == ThisColour)’ etc.

What I want to do now, is then ‘search’ squares 1 and 2, and see if there are any next to them. As you can see, square 2 has one square next to it (excluding square 0), so I want to add this square to the array. And so on.

Updated Answer
Here’s a class that looks for it’s neighbors, determines whether they are of the correct class (ColoredCube in this case), and then puts them in a list.

They are then compared to the current cube’s color, and if the comparison is successful, a reference is created via a List.

So each cube will know about it’s neighbors who are of the same color.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

public class ColoredSquare : MonoBehaviour
{

	public Color squareColor;
	public List<ColoredSquare> contiguous = new List<ColoredSquare>();
	private float range;

	void Start()
	{
		range = (transform.localScale.x + transform.localScale.y);

		List<ColoredSquare> neighbors = GameObject.FindObjectsOfType(typeof(ColoredSquare)).Select(c => c as ColoredSquare).Where(t => Vector3.Distance(t.transform.position, transform.position) < range && t.transform != transform).ToList();
		Debug.Log(neighbors.Count);
		foreach (ColoredSquare c in neighbors)
		{
			if (c.HasSharedColor(squareColor))
				contiguous.Add(c);
		}
	}

	public bool HasSharedColor(Color c)
	{
		return squareColor == c;
	}
}

Previous Semi-Answer

Looks like a job for Linq!

So, this may be overkill for your problem in the short term, but if you use Linq (and you should definitely use it sparingly, as it can be very slow) to sort objects, it can save you a lot of legwork.

this is one line of code which will retrieve all gameObjects in your scene that use the color red as their main color:

using UnityEngine;
using System.Linq;

void Start()
{
    GameObject[] objects = GameObject.FindObjectsOfType(typeof(GameObject)).Select(g => g as GameObject).Where(g => g.GetComponent<Renderer>() != null && g.GetComponent<Renderer>().material.GetColor("_Color") == Color.red).ToArray();
}

Linq is really confusing if it’s the first time you’re looking at it.
Let’s break down that above statement:

GameObject[] objects = GameObject.FindObjectsOfType(typeof(GameObject))...

you may recognize this. I’m creating an array of objects and assigning them the output of the FindObjectsOfType function. The FindObjects function will search for anything of type GameObject. This is obviously going to find everything, including things you don’t want.

so, we begin with the Linq sorting voodoo:

GameObject[] objects = GameObject.FindObjectsOfType(typeof(GameObject)).Select(g => g as GameObject)

so, beginning with the Linq function .Select(), we are telling Linq to grab the output of the FindObjectsOfType() function. We will start feeding the Linq function commands to specify what exactly we should be selecting.

so this line:

.Select(g => g as GameObject)

is telling the select command
“hey Linq, you’re going to be passed a System.Object, and I need it to be cast to something specific”. In this case I’ve created the temporary variable named “g”. the => notation specifies that the generic variable I’ve created should be passed to the second argument, which is “g as GameObject”, casting System.Object “g” to type GameObject. We know this will succeed, because FindGameObjects was already told to find GameObjects only.

next up we have:

.Select(g => g as GameObject).Where(g => g.GetComponent<Renderer>() != null 

the next step in our call, “.Where()” tells Linq “hey there buddy, you just selected a whole bunch of stuff. But I only want some of it. Let me tell you what it is I want”.

So, using the same notation, a temporary variable is created (“g” again in this case, although I only did this to signify that I’m still dealing with a GameObject datatype. It could be any variable name) and passed on for checking.
In this case, we are again saying we have a GameObject, and we need to make sure that there is a Renderer on that GameObject, so we use the familiar GetComponent off of the gameObject to make sure we are selecting the proper objects.

moving on, we’ve got:

).Where(g => g.GetComponent<Renderer>() != null && g.GetComponent<Renderer>().material.GetColor("_Color") == Color.red)

which you may recognize as a statement saying “if this gameObject has a renderer, AND it matches the color I’m looking for”. All of these will return the results for the “.Where()” function.

the last bit on the end of the function

 == Color.red).ToArray();

just tells Linq to put all that into an array.

So, to sum up, the flow is like this:

GameObject objects = GameObject.FindObjectsOfType(typeof(GameObject))

Dear Unity: Find me some Objects and put them in an array

.Select(g => g as GameObject)

Dear Linq: Select the results of FindObjectsOfType, and cast them to GameObjects

.Where(

Dear Linq: I’m about to tell you which selected objects are valid for my search

.Where(g => g.GetComponent() != null && g.GetComponent().material.GetColor(“_Color”) == Color.red)

Dear Linq: My selected GameObjects should have a renderer, and that renderer should have a material with the color Red on it.

.ToArray();

Dear Linq: Put all successful matches into an array.

phew
that was a lot to deal with. So, as I said, it might be a bit overkill for what you need, but this sort of work can be really useful in sorting large selections of objects.

There are some good tutorials about Linq out there, and I’d recommend you check them out.
Linq should be used sparingly, however, as it tends to be slow. This is not something you should do on every Update() loop.

This would be ideal to use at the start of your game to make lists of blocks of each color. It could also be used to arrange each block according to it’s neighbors. You’d want to do it once and then have each block store the results.

I started with this tutorial, and then by harrowing trial and error. I’m no master of Linq by any means, but I find it to be quite useful on a regular basis. Check it out.

You can do a (recursive) depth-first search. Breadth-first works too… doesn’t really matter in this case. Is it really recursive if it calls the same function on different objects? I digress.

What you’ll need is something like the following (I hardly ever do UnityScript, not sure on the syntax):

//CubeScript.js

function StartSearch() : List.<GameObject> {
    var foundList : List.<GameObject> = new List.<GameObject>();
    return PerformDFS(foundList,thisColor);
}

//objects of the same type can access their private members, at least in C#.
private function PerformDFS(List.<GameObject> foundGOs, Color colorToSearch) : List.<GameObject>{

    //quick-outs to prevent infinite loops and wrong colors being added.
    if(foundGOs.Contains(this.gameObject)) return foundGOs;

    if(thisColor != colorToSearch) return foundGOs;

    foundGOs.Add(this.gameObject);

    //the actual searching, each neighboring cube is accessed in its turn    
    foundGOs = CubeUp.GetComponent(CubeScript).PerformDFS(foundGOs, colorToSearch);

    foundGOs = CubeDown.GetComponent(CubeScript).PerformDFS(foundGOs, colorToSearch);

    //etc.

    return foundGOs;
}

This could be made slightly faster if you remembered the script instead of the GameObject of each cube. There would be no need for the GetComponent => faster code.