WinterJamSnowman/Assets/Scripts/LemonGenericLib/Generic Classes/Grid/GridClass.cs

454 lines
16 KiB
C#

using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;
using System;
using UnityEngine.UI;
using TMPro;
//things to do (for the authur only):
//check if this class is very reusable
//implement a proper way to destroy grids
//add more debug features and controls
//test this in 3D (using x and z axis)
//remove all mentions of real names in this class and replace them with screen names
//this is Wong Sui Bin's grid class, I advise not to touch this if you are not the authur of this class
//this is a class that greats a 2D grid in the game world that maps with real coordinates
//requires text mesh pro to work
//so far used for
//level editors
//tile based building system
//grid reresentation of positions of game objects
namespace Lemon.GenericLib.Generics
{
public class Grid<TGridObject>
{
public event EventHandler<OnGridObjectChangedArgs> OnGridObjectChanged;
public class OnGridObjectChangedArgs : EventArgs
{
public int x, y;
}
public int width;
public int height;
private float cellSize;
private Vector3 originPosition;
private TGridObject[,] gridArray;
private TextMeshPro[,] gridText;
private Color textColour;
private bool showText;
private int textSize;
private bool spawnText;
private bool debugLine;
#region constructors
//arrangement of y is up and x is right
//grid constructor for custom data types
public Grid(int width, int height, float cellSize, Vector3 originPosition, Transform parent, Func<Grid<TGridObject>, int, int, TGridObject> createGridObject, bool spawnText = true, bool debugLine = true)
{
this.width = width;
this.height = height;
this.cellSize = cellSize;
this.gridArray = new TGridObject[width, height];
this.gridText = new TextMeshPro[width, height];
this.originPosition = originPosition;
this.textColour = Color.white;
this.showText = true;
this.textSize = 5;
this.spawnText = spawnText;
this.debugLine = debugLine;
//construct grid object
for (int x = 0; x < gridArray.GetLength(0); x++)
{
for (int y = 0; y < gridArray.GetLength(1); y++)
{
gridArray[x, y] = createGridObject(this, x, y);
}
}
//set debug visuals
for (int x = 0; x < gridArray.GetLength(0); x++)
{
for (int y = 0; y < gridArray.GetLength(1); y++)
{
if (debugLine)
{
Debug.DrawLine(GetWorldPosition(x, y), GetWorldPosition(x, y + 1), Color.white, 100f);
Debug.DrawLine(GetWorldPosition(x, y), GetWorldPosition(x + 1, y), Color.white, 100f);
}
if (spawnText)
gridText[x, y] = CreateGridText(gridArray[x, y]?.ToString(), parent, x, y, GetWorldPositionCenter(x, y), textSize, textColour, TMPro.TextAlignmentOptions.CenterGeoAligned);
//disable text if configured to not show
gridText[x, y]?.gameObject.SetActive(showText);
}
}
if (debugLine)
{
Debug.DrawLine(GetWorldPosition(0, height), GetWorldPosition(width, height), Color.white, 100f);
Debug.DrawLine(GetWorldPosition(width, 0), GetWorldPosition(width, height), Color.white, 100f);
}
//assign object changed even for debug visuals
OnGridObjectChanged += (object sender, OnGridObjectChangedArgs eventArgs) =>
{
gridText[eventArgs.x, eventArgs.y].text = gridArray[eventArgs.x, eventArgs.y].ToString();
};
}
//grid constructor for non custom data type
public Grid(int width, int height, float cellSize, Vector3 originPosition, Transform parent, bool spawnText = true, bool debugLine = true)
{
this.width = width;
this.height = height;
this.cellSize = cellSize;
this.gridArray = new TGridObject[width, height];
this.gridText = new TextMeshPro[width, height];
this.originPosition = originPosition;
this.textColour = Color.white;
this.showText = true;
this.textSize = 5;
this.spawnText = spawnText;
this.debugLine = debugLine;
//set debug visuals
for (int x = 0; x < gridArray.GetLength(0); x++)
{
for (int y = 0; y < gridArray.GetLength(1); y++)
{
if (debugLine)
{
Debug.DrawLine(GetWorldPosition(x, y), GetWorldPosition(x, y + 1), Color.white, 100f);
Debug.DrawLine(GetWorldPosition(x, y), GetWorldPosition(x + 1, y), Color.white, 100f);
}
if (spawnText)
gridText[x, y] = CreateGridText(gridArray[x, y]?.ToString(), parent, x, y, GetWorldPositionCenter(x, y), textSize, textColour, TMPro.TextAlignmentOptions.CenterGeoAligned);
//disable text if configured to not show
gridText[x, y]?.gameObject.SetActive(showText);
}
}
if (debugLine)
{
Debug.DrawLine(GetWorldPosition(0, height), GetWorldPosition(width, height), Color.white, 100f);
Debug.DrawLine(GetWorldPosition(width, 0), GetWorldPosition(width, height), Color.white, 100f);
}
//assign object changed even for debug visuals
OnGridObjectChanged += (object sender, OnGridObjectChangedArgs eventArgs) =>
{
gridText[eventArgs.x, eventArgs.y].text = gridArray[eventArgs.x, eventArgs.y].ToString();
};
}
#endregion
#region position hash
//the hash index for the grid, start from 0 in the bottom left cell and counting right and upwards
//hash the ridArray position into hash index
public int positionHash(int x, int y)
{
return y * width + x;
}
//hash the index back to gridArray position
public void positionHash(int index, out int x, out int y)
{
y = index / width;
x = index % width;
}
#endregion
#region Get Positions
//get world posisiton of a corner of a cell
private Vector3 GetWorldPosition(int x, int y)
{
return new Vector3(x, y) * cellSize + originPosition;
}
//get world positon of the center of a cell
public Vector3 GetWorldPositionCenter(int x, int y)
{
return GetWorldPosition(x, y) + new Vector3(cellSize, cellSize) * .5f;
}
//convert a vector 3 position to the center of a grid
public Vector3 GetWorldPositionCenter(Vector3 worldPosition)
{
int x, y;
GetXY(worldPosition, out x, out y);
return GetWorldPositionCenter(x, y);
}
//get x and y of grid from world posistion
public void GetXY(Vector3 worldPosition, out int x, out int y)
{
x = Mathf.FloorToInt((worldPosition - originPosition).x / cellSize);
y = Mathf.FloorToInt((worldPosition - originPosition).y / cellSize);
}
public Vector2Int GetXYVector2Int(Vector3 worldPosition)
{
return new Vector2Int(Mathf.FloorToInt((worldPosition - originPosition).x / cellSize), Mathf.FloorToInt((worldPosition - originPosition).y / cellSize));
}
#endregion
#region Grid Object Manipulation
//set value from x and y of the array
public void SetGridObject(int x, int y, TGridObject gridObject)
{
if (x >= 0 && x < width && y >= 0 && y < height)
{
gridArray[x, y] = gridObject;
TriggerGridObjectChanged(x, y);
}
}
//set value from world position
public void SetGridObject(Vector3 worldPosition, TGridObject gridObject)
{
int x, y;
GetXY(worldPosition, out x, out y);
SetGridObject(x, y, gridObject);
}
//get value from x and y of the array
public TGridObject GetGridObject(int x, int y)
{
if (x >= 0 && x < width && y >= 0 && y < height)
{
return gridArray[x, y];
}
else
{
return default(TGridObject);
}
}
public TGridObject GetGridObject(Vector2Int pos)
{
return GetGridObject(pos.x, pos.y);
}
//get value from world position
public TGridObject GetGridObject(Vector3 worldPosition)
{
int x, y;
GetXY(worldPosition, out x, out y);
return GetGridObject(x, y);
}
#endregion
#region get grid object patterns
List<TGridObject> returnObjList = new List<TGridObject>();
public bool CheckWithinGrid(Vector2Int pos) {
if (pos.x >= 0 && pos.x < width) {
if (pos.y >= 0 && pos.y < height)
return true;
}
return false;
}
public bool CheckWithinGrid(Vector3 worldPosition)
{
if (worldPosition.x >= originPosition.x && worldPosition.x < originPosition.x + (cellSize*width))
{
if (worldPosition.y >= originPosition.y && worldPosition.y < originPosition.y + (cellSize * height))
return true;
}
return false;
}
public List<TGridObject> GetAdjacent(Vector2Int originPos, int range = 1, bool includeSelf = false)
{
returnObjList.Clear();
List<Vector2Int> dirList = new List<Vector2Int>() { Vector2Int.up, Vector2Int.down, Vector2Int.left, Vector2Int.right };
if (CheckWithinGrid(originPos) && includeSelf) returnObjList.Add(GetGridObject(originPos));
foreach (var item in dirList)
{
if (CheckWithinGrid(originPos+(item*range))) returnObjList.Add(GetGridObject(originPos+ (item * range)));
}
return returnObjList;
}
public List<TGridObject> GetSurrounding(Vector2Int originPos,int range = 1, bool includeSelf = false)
{
returnObjList.Clear();
List<Vector2Int> dirList = new List<Vector2Int>() { new Vector2Int(-1,1), new Vector2Int(1, 1), new Vector2Int(-1, -1), new Vector2Int(1, -1) };
returnObjList.AddRange(GetAdjacent(originPos, range, includeSelf));
foreach (var item in dirList)
{
if (CheckWithinGrid(originPos + (item * range))) returnObjList.Add(GetGridObject(originPos + (item * range)));
}
return returnObjList;
}
public List<TGridObject> GetArea(Vector2Int originPos, int range = 1, bool includeSelf = false)
{
returnObjList.Clear();
Vector2Int temp;
for (int x = originPos.x- range; x <= originPos.x+range; x++)
{
for (int y = originPos.y - range; y <= originPos.y + range; y++)
{
temp = new Vector2Int(x, y);
if (CheckWithinGrid(temp)) {
if (temp == originPos && !includeSelf) continue;
returnObjList.Add(GetGridObject(temp));
}
}
}
return returnObjList;
}
public List<TGridObject> GetRow(int y)
{
returnObjList.Clear();
for (int x = 0; x < width; x++)
{
returnObjList.Add(gridArray[x, y]);
}
return returnObjList;
}
public List<TGridObject> GetColumn(int x)
{
returnObjList.Clear();
for (int y = 0; y < height; y++)
{
returnObjList.Add(gridArray[x, y]);
}
return returnObjList;
}
#endregion
#region entire grid manipulation
public TGridObject[,] OutputArray()
{
return gridArray;
}
public void DoAllGridObjects(Action<int,int,TGridObject> function) {
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
function(x,y,gridArray[x, y]);
}
}
}
public void DestroyGrid()
{
if (!spawnText) return;
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (gridText[x, y] != null)
{
MonoBehaviour.Destroy(gridText[x, y].gameObject);
}
}
}
}
#endregion
#region DebugText
//trigger the update debug text event using gridArray Position
public void TriggerGridObjectChanged(int x, int y)
{
if (OnGridObjectChanged != null && spawnText) OnGridObjectChanged(this, new OnGridObjectChangedArgs { x = x, y = y });
}
//trigger the update debug text event using world Position
public void TriggerGridObjectChanged(Vector3 position)
{
int x, y;
GetXY(position, out x, out y);
if (OnGridObjectChanged != null && spawnText) OnGridObjectChanged(this, new OnGridObjectChangedArgs { x = x, y = y });
}
//configure debug text option
//so far no use for this
public void configureDebugText(bool show, Color textColour, int size)
{
this.showText = show;
this.textColour = textColour;
this.textSize = size;
if (!spawnText) return;
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
//disable text if configured to not show
gridText[x, y]?.gameObject.SetActive(showText);
if (gridText[x, y] != null)
{
gridText[x, y].color = this.textColour;
gridText[x, y].fontSize = this.textSize;
}
}
}
}
//get if the text is shown
public bool getShowText()
{
return showText;
}
// Create Text in the Grid
private TextMeshPro CreateGridText(string text, Transform parent = null, int x = 0, int y = 0, Vector3 localPosition = default(Vector3), int fontSize = 40, Color? color = null, TextAlignmentOptions textAlignment = TextAlignmentOptions.Left, int sortingOrder = 5000)
{
if (color == null) color = Color.white;
return CreateGridText(parent, text, $"Grid_Text_[{x}, {y}]", localPosition, fontSize, (Color)color, textAlignment, sortingOrder);
}
// Create Text in the Grid
private TextMeshPro CreateGridText(Transform parent, string text, string objectName, Vector3 localPosition, int fontSize, Color color, TextAlignmentOptions textAlignment, int sortingOrder)
{
GameObject gameObject = new GameObject(objectName, typeof(TextMeshPro));
Transform transform = gameObject.transform;
transform.SetParent(parent, false);
transform.localPosition = localPosition;
TextMeshPro textMesh = gameObject.GetComponent<TextMeshPro>();
textMesh.alignment = textAlignment;
//textMesh.anchor = AnchorPositions.Center;
textMesh.text = text;
textMesh.fontSize = fontSize;
textMesh.color = color;
textMesh.GetComponent<MeshRenderer>().sortingOrder = sortingOrder;
return textMesh;
}
#endregion
}
}