View 2d array of 'Tile' class in inspector? [help requested]

Hi there,

I’m trying to create a tilemap/an array of ‘Tiles’ (a custom class). At the moment, I create an instance of my ‘Tile’ class for every position that a unit could move (in this example I have a 5x5 board; 25 tiles in total.

I want to do this so that I can store variables such as a string ‘tileName’, int ‘tileID’, or gameObject ‘unitOnTile’ – so that I can track information about which gameobject is sat on what tile at any one point. I imagine this will let me detect collisions efficiently, amongst other things.

I have a script ‘TileMaster’ which is attached to the camera. My custom ‘Tile’ class is kept within TileMaster.cs. However, when inspecting the camera during runtime I can’t see anything related to my custom class ‘Tile’ – making it impossible to debug or check if things are working properly.

Ideally I would be able to see each instance of my ‘Tile’ class along with their individual variables (tileName, tileInt, tilePosition, etc) in the Unity inspector so that I could check things are working during runtime.

Unfortunately, when inspecting the camera all I can see is my TileMaster script (and a couple of test variables I set up within the TileMaster class in order to check they would display in the inspector. I can’t see anything relating to my custom ‘tile’ class.

Screenshot of my inspector and the TileMaster script here:
http://imgur.com/a/ErCFG

Also my TileMaster script here:

[System.Serializable]
public class TileMaster : MonoBehaviour
{
	public Tile [,] map;
	private static int worldX = 5, worldY=5;

	public int TestInt;
	public string TestString;

	void Awake()
	{
		Debug.Log("TileMaster Awake.");
		//CREATE TILES
		map = new Tile[worldX, worldY];

		for (int x = 0; x < worldX; x++)
		{
			for (int y = 0; y < worldY; y++)
			{
				map[x,y] = new Tile("aTile", 0, new Vector2(x,y));
			}
		}
		//ASSIGN STUFF
		foreach(Tile currentTile in map)
		{
			if ((currentTile.tilePosition.x == worldX - 1 || currentTile.tilePosition.x == 0) ||
				(currentTile.tilePosition.y == worldY - 1 || currentTile.tilePosition.y == 0))
			{
				currentTile.tileName = "Wall";
				currentTile.tileID = 1;
			}
			else
			{
				currentTile.tileName = "Floor";
				currentTile.tileID = 0;
			}
			Debug.Log(currentTile.tileName);
			Debug.Log(currentTile.tileID);
			Debug.Log(currentTile.tilePosition);
		}
	}
}
	
[System.Serializable]
public class Tile
{
	public GameObject containedObject;

	public string tileName;
	public int tileID;
	public Vector2 tilePosition;

	public Tile(string aName, int anID, Vector2 aPosition)
	{
		tileName = aName;
		tileID = anID;
		tilePosition = aPosition;

		Debug.Log("Hello, I'm a new tile.");
	}
}

It’s probably worth noting that the [System.Serializable] stuff was part of an attempt to make it viewable in the inspector (obviously it didn’t work). Also I should note that whilst in this example my class ‘Tile’ is located in the TileMaster.cs script, it also works fine as its own separate script Tile.cs. Either way though, I can’t see to view any instance of Tile in the inspector.

I’m aware that 2d arrays aren’t viewable in the inspector (for example even if I had a 2d array of integers contained within TileMaster.cs I wouldn’t be able to see it in the inspector – whereas if it was simply a one-dimensional array or a list I would be able to see it. It’s just the fact that my Tile map is both a 2d array AND an array of custom classes that i’m completely lost…

If anyone can point me in the right direction I’d really really appreciate it.

Put Tile Class outside of the tilemaster class brackets.

edit - Because think about it, you can’t serialize a script ITSELF to be shown in the inspector like a class, and as you have it right now, Tile is a subclass of Tilemaster. That is why it works when you make it a seperate class.

In this and every instance of tiles I’ve ever implemented, you don’t need it inside of your controller class.

edit 2- just for the sake of full summation, to get your script to work this way, (which I wouldn’t do) you’d just have to attach it to a controller script onto your Camera and call it like regular. You’ll have fully-viewable variables if you keep the scripts exactly as they are (I believe, I haven’t syntax checked anything).

@letitslide Thanks for that! I think i’ve managed to implement it correctly…

This is what I have at the moment (TileMaster.cs)

public class TileMaster : MonoBehaviour
{
	public Dictionary<Tile, GameObject> tileDictionary;
	private static int worldX = 5, worldY=5;

	public static TileMaster instance = null;
	void OnEnable()
	{
		if(instance == null)
		{
			instance = this;
		}
		else if
			(instance != this)
		{
			Destroy(gameObject);
		}
	}

	void Awake()
	{
		tileDictionary = new Dictionary<Tile, GameObject>();

		for (int x = 0; x < worldX; x++)
		{
			for (int y = 0; y < worldY; y++)
			{
				Tile tile_In_Memory = new Tile("aTile", 0, new Vector2(x,y)); 
				GameObject tile_Game_Object = new GameObject("tileObject");
				tile_Game_Object.transform.position = tile_In_Memory.tilePosition;

				tileDictionary.Add(tile_In_Memory, tile_Game_Object);

				Debug.Log(tileDictionary.Count);
				Debug.Log(tile_In_Memory.tileName);
				Debug.Log(tile_In_Memory.tilePosition);
			}
		}
	}
	GameObject ReturnTileGameObject(Tile tile_In_Memory)
	{
		GameObject tile_Game_Object = tileDictionary[tile_In_Memory];

		return tile_Game_Object;
	}
}

And Tile.cs

public class Tile 
{
	public GameObject containedObject;

	public string tileName;
	public int tileID;
	public Vector2 tilePosition;

	public Tile(string aName, int anID, Vector2 aPosition)
	{
		tileName = aName;
		tileID = anID;
		tilePosition = aPosition;

		Debug.Log("Hello, I'm a new tile.");
	}
}

Nothing is throwing up any errors. As it should (i think?): 25 instances of the Tile class (tile_In_Memory) and 25 gameObjects (tile_Game_Object) are created by the loops, which are then added (as a pair?) to the dictionary (tileDictionary). This is all great.

I also have my player (PlayerController.cs)

public class PlayerController : MonoBehaviour {

	public Vector2 position;
	public float xPos;
	public float yPos;

	void Update() 
	{
		position = transform.position;
		xPos = transform.position.x;
		yPos = transform.position.y;

		if (Input.GetKeyDown(KeyCode.W))
		{
			Vector3 position = this.transform.position;
			position.y++;
			transform.position = position;
		}
		if (Input.GetKeyDown(KeyCode.D))
		{
			Vector3 position = this.transform.position;
			position.x++;
			transform.position = position;
		}
		if (Input.GetKeyDown(KeyCode.S))
		{
			Vector3 position = this.transform.position;
			position.y--;
			transform.position = position;
		}
		if (Input.GetKeyDown(KeyCode.A))
		{
			Vector3 position = this.transform.position;
			position.x--;
			transform.position = position;
		}
	}
}

I just can’t seem to get my head around how I can access the tiles north, south, etc adjacent to the player to check whether the player can move into that space. Or for that matter – how I could keep track of the players position (and the position of other units) using the tiles and dictionary that i’ve just set up so that players and other units will also block each other’s movement.

In the past I’ve done this by using a list of all units in the scene. When the player (or another object) wanted to move, it was check every object’s (in the Unit list) position against the position of the tile I wanted to move into – but this slowed the game down far too much once I had many objects checking to move (and hence also a longer Unit list). Obviously there should be a simpler way of doing this; which is why I imagined I’d be able to keep a reference of whatever object was in a specific Tile at any one point, so I could check directly:

if (tile north of the player = full/occupied)
{
don't move;
}
else if (tile north of the player = empty)
{
move into it;
}

Hopefully all of that makes sense. Not sure if I just fundamentally misunderstand what i’m trying to do or if i’m completely over thinking it.