Skip to content

Instantly share code, notes, and snippets.

@twobob
Created April 11, 2015 15:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save twobob/c6e6c3f9a6b4f3b7eefb to your computer and use it in GitHub Desktop.
Save twobob/c6e6c3f9a6b4f3b7eefb to your computer and use it in GitHub Desktop.
Reads vector3's from a scriptable object to do the tracking
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class GridValue
{
// should we ever need it
public enum EntityType
{
None,
Tree,
Rock
}
public string id;
public Vector3 position;
public EntityType entityType;
public GameObject go;
}
public class Grid
{
public static Dictionary<string, Grid> grids = new Dictionary<string, Grid>();
private Dictionary<int, Dictionary<string, GridValue>> cells = new Dictionary<int, Dictionary<string, GridValue>>();
private Dictionary<string, GridValue> objectIndex = new Dictionary<string, GridValue>();
private Dictionary<string, int> cellsIndex = new Dictionary<string, int>();
public static float coordOffset = 5000f;
private int max;
private int cellSize = 0;
private float convFactor;
private int width;
private int cellCount;
private List<GridValue> result;
public Grid(int max, int cellSize)
{
this.max = max;
this.cellSize = cellSize;
this.convFactor = 1.0f / this.cellSize;
this.width = (int)(this.max / this.cellSize);
this.cellCount = this.width * this.width;
}
public int Count()
{
return objectIndex.Count;
}
public static Grid FindOrCreate(string id, int max, int cellSize)
{
if (!grids.ContainsKey(id))
{
grids[id] = new Grid(max, cellSize);
}
return grids[id];
}
private HashSet<int> __mcells;
public HashSet<int> CellsWithinBounds(float x, float z)
{
x += coordOffset;
z += coordOffset;
//HashSet<int>
__mcells = new HashSet<int>();
int offset = this.cellSize;
int startX = (int)(x - offset);
int startZ = (int)(z - offset);
int endX = (int)(x + offset);
int endZ = (int)(z + offset);
for (int rowNum = startX; rowNum <= endX; rowNum += offset)
{
for (int colNum = startZ; colNum <= endZ; colNum += offset)
{
if (rowNum >= 0 && colNum >= 0)
{
__mcells.Add(Hash(rowNum, colNum));
}
}
}
return __mcells;
}
private HashSet<int> __cells;
private ICollection<GridValue> __gridValues;
public List<GridValue> Neighbors(float x, float z, GridValue.EntityType entityType)
{
result = new List<GridValue>();
// ICollection<GridValue> gridValues;
__gridValues = null;
//HashSet<int>
__cells = CellsWithinBounds(x, z);
foreach (int cell in __cells)
{
__gridValues = GridValuesInCell(cell);
if (__gridValues == null)
{
continue;
}
foreach (GridValue gridValue in __gridValues)
{
if (gridValue != null)
{
if (entityType == GridValue.EntityType.None)
{
result.Add(gridValue);
}
else if (gridValue.entityType == entityType)
{
result.Add(gridValue);
}
}
}
}
return result;
}
public ICollection<GridValue> GridValuesInCell(int cell)
{
if (cells.ContainsKey(cell))
{
return cells[cell].Values;
}
else
{
return null;
}
}
public void Set(GridValue gridValue)
{
bool hasExisting = false;
int oldCellValue = -1;
string id = gridValue.id;
if (objectIndex.ContainsKey(id))
{
hasExisting = true;
oldCellValue = cellsIndex[id];
}
int cell = Hash(gridValue.position.x + coordOffset, gridValue.position.z + coordOffset);
Dictionary<string, GridValue> cellGridValues;
if (hasExisting && oldCellValue != cell)
{
if (cells.ContainsKey(oldCellValue))
{
cellGridValues = cells[oldCellValue];
if (cellGridValues.ContainsKey(id))
{
cellGridValues.Remove(id);
}
if (cellGridValues.Count == 0)
{
cells.Remove(oldCellValue);
}
}
}
cellsIndex[id] = cell;
objectIndex[id] = gridValue;
if (cells.ContainsKey(cell))
{
cellGridValues = cells[cell];
cellGridValues[id] = gridValue;
}
else
{
cellGridValues = new Dictionary<string, GridValue>();
cellGridValues[id] = gridValue;
cells[cell] = cellGridValues;
}
}
public int Hash(float x, float z)
{
return (int)((x * this.convFactor)) + (int)((z * this.convFactor)) * this.width;
}
}
public sealed class Objectpool2<T>
where T : new()
{
private readonly int size;
private readonly Queue<T> queue;
private int count = 0;
/// <summary>
/// Initializes a new instance of the ObjectPool class.
/// </summary>
/// <param name="size">The size of the object pool.</param>
public Objectpool2(int size)
{
this.size = size;
queue = new Queue<T>();
}
public int Count()
{
return queue.Count;
}
/// <summary>
/// Retrieves an item from the pool.
/// </summary>
/// <returns>The item retrieved from the pool.</returns>
public T Get()
{
if (queue.Count > 0)
{
T item = queue.Dequeue();
count--;
return item;
}
else
{
Debug.Log("Queue is empty count=" + count + " size=" + size);
return default(T);
}
}
/// <summary>
/// Places an item in the pool.
/// </summary>
/// <param name="item">The item to place to the pool.</param>
public void Put(T item)
{
queue.Enqueue(item);
count++;
}
}
public class GridTracking : MonoBehaviour
{
public int MaxCapacity = 5000;
public int ObjectPoolSize = 200;
public int SearchDistance = 20;
public static int __MaxCapacity = 5000;
public static int __ObjectPoolSize = 200;
public static int __SearchDistance = 20;
public static List<TreeLocation> treeList = new List<TreeLocation>(__MaxCapacity);
private Grid grid;
private Dictionary<string, GridValue> objectIndex = new Dictionary<string, GridValue>(5000 * 4);
private Objectpool2<GameObject> objectPool = new Objectpool2<GameObject>(200);
private int removeCheckCount;
Transform player;
void Start()
{
objectIndex = new Dictionary<string, GridValue>(MaxCapacity * 4);
objectPool = new Objectpool2<GameObject>(ObjectPoolSize);
__MaxCapacity = MaxCapacity;
__ObjectPoolSize = ObjectPoolSize;
__SearchDistance = SearchDistance;
if (GameManager.playerEntity == null)
{
player = GameObject.Find("TestController").transform;
}
else
{
player = GameManager.playerEntity.transform;
}
grid = Grid.FindOrCreate("terrain", MaxCapacity, SearchDistance);
StartRepeatingCheckForParticleCount();
//CreateGrid();
//CreatePool();
//InvokeRepeating("UpdateGrid", 0.005f, 1f);
}
private void StartRepeatingCheckForParticleCount()
{
InvokeRepeating("CreateGridsWhenParticlesPlaced", 0, 0.05F); // skim edges
}
private void CreateGridsWhenParticlesPlaced()
{
// Now actually create some stuff. Once the trees have finished placing. sigh.
if (!GameManager.TreesPlaced)
{
return;
}
CreateGrid();
CreatePool();
InvokeRepeating("UpdateGrid", 0.005f, 2f);
CancelInvoke("CreateGridsWhenParticlesPlaced");
Debug.Log("Cancelled CreateGridsWhenParticlesPlaced as complete ");
}
public static void SetMeshColliders(GameObject go, bool enabled)
{
MeshCollider col;
col = go.GetComponent<MeshCollider>() as MeshCollider;
if (col == null)
{
foreach (Transform s in go.transform)
{
col = s.GetComponent<MeshCollider>() as MeshCollider;
if (col != null)
{
col.enabled = enabled;
}
}
}
else
{
col.enabled = enabled;
}
}
public static void CreateGrid()
{
Grid grid = Grid.FindOrCreate("terrain", __MaxCapacity, __SearchDistance);
Object o = Resources.Load("TreeLocationDatabase", typeof(TreeLocationHolder));
TreeLocationHolder TreeLocationDB = (TreeLocationHolder)o;
//fill our list of stuff, with the content from the Holder in the asset
treeList = TreeLocationDB.content;
int treecount = 0;
TreeLocation tree;
for (int i = 0; i < treeList.Count; i++)
{
tree = treeList[i];
Vector3 tpos = tree.position;
GridValue gridValue = new GridValue();
gridValue.position = tpos;
gridValue.id = "tree_" + treecount;
gridValue.entityType = GridValue.EntityType.Tree;
grid.Set(gridValue);
treecount++;
}
Debug.Log("Grid size " + grid.Count());
}
void CreatePool()
{
GameObject trees = new GameObject();
trees.name = "trees";
for (int i = 0; i < __ObjectPoolSize; i++)
{
GameObject go = new GameObject();
go.transform.parent = trees.transform;
go.tag = "harvestable";
go.name = "Tree_";
go.transform.position = Vector3.zero;
Rigidbody rigid = go.AddComponent<Rigidbody>();
rigid.isKinematic = true;
CapsuleCollider col = go.AddComponent<CapsuleCollider>() as CapsuleCollider;
col.center = Vector3.zero;
col.height = 10f;
col.radius = 0.4f;
col.direction = 1;
col.enabled = false;
NavMeshObstacle nmo = go.AddComponent<NavMeshObstacle>() as NavMeshObstacle;
nmo.center = Vector3.zero;
nmo.shape = NavMeshObstacleShape.Capsule;
nmo.height = 10f;
nmo.radius = 0.4f;
nmo.enabled = false;
objectPool.Put(go);
}
}
// Update is called once per frame
void UpdateGrid()
{
List<GridValue> gridValues = grid.Neighbors(player.position.x, player.position.z, 0);
if (gridValues.Count > 0)
{
AddNew(gridValues);
if (removeCheckCount >= 1)
{
removeCheckCount = 0;
RemoveOld(gridValues);
//Debug.Log ("Gridvalues count " + gridValues.Count + " indexcount " + objectIndex.Count + " queuecount " + objectPool.Count ());
}
else
{
removeCheckCount++;
}
}
}
void AddNew(List<GridValue> gridValues)
{
int addCount = 0;
foreach (GridValue gridValue in gridValues)
{
if (AddCollider(gridValue))
{
addCount++;
}
if (addCount > 10)
{
return;
}
}
}
private GridValue _tempGridValueReference;
void RemoveOld(List<GridValue> gridValues)
{
HashSet<GridValue> toCheck = new HashSet<GridValue>(gridValues);
List<string> idsToRemove = new List<string>();
foreach (GridValue old in objectIndex.Values)
{
if (!toCheck.Contains(old))
{
idsToRemove.Add(old.id);
}
}
int removeCount = 0;
for (int i = 0; i < idsToRemove.Count; i++)
{
GridValue gridValue = objectIndex[idsToRemove[i]];
RemoveCollider(gridValue);
objectIndex.Remove(idsToRemove[i]);
removeCount++;
if (removeCount > 20)
{
break;
}
}
// debug
if (removeCount > 0)
{
Debug.Log(removeCount); // This creates a bit less garbage
}
}
void RemoveCollider(GridValue gridValue)
{
if (gridValue.entityType == GridValue.EntityType.Rock)
{
SetMeshColliders(gridValue.go, false);
}
else if (gridValue.entityType == GridValue.EntityType.Tree)
{
GameObject go = gridValue.go;
go.transform.position = Vector3.zero;
go.GetComponent<CapsuleCollider>().enabled = false;
go.GetComponent<NavMeshObstacle>().enabled = false;
objectPool.Put(go);
gridValue.go = null;
}
}
bool AddCollider(GridValue gridValue)
{
if (objectIndex.ContainsKey(gridValue.id))
{
return false;
}
if (gridValue.entityType == GridValue.EntityType.Rock)
{
SetMeshColliders(gridValue.go, true);
}
else if (gridValue.entityType == GridValue.EntityType.Tree)
{
GameObject go = objectPool.Get();
go.transform.position = gridValue.position;
go.GetComponent<CapsuleCollider>().enabled = true;
go.GetComponent<NavMeshObstacle>().enabled = true;
gridValue.go = go;
}
objectIndex[gridValue.id] = gridValue;
return true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment