Skip to content

Instantly share code, notes, and snippets.

@nothke
Created November 6, 2016 16:05
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nothke/7caa58a7b76e87691825d58de3dc9f84 to your computer and use it in GitHub Desktop.
Save nothke/7caa58a7b76e87691825d58de3dc9f84 to your computer and use it in GitHub Desktop.
///
/// Placas objects dynamically around the player
/// by Nothke
///
/// unlicensed, aka do whatever you want with it
///
using UnityEngine;
using System.Collections.Generic;
public class PoolPlacer : MonoBehaviour
{
[Tooltip("Prefab to pool, it needs to have a renderer to work")]
public GameObject prefab;
[Tooltip("Track position of this transform")]
public Transform player;
[Tooltip("Grid dimension, there will be n*n pooled objects")]
public int chunksGridSize = 10;
[Tooltip("Separation width between 2 chunks")]
public float chunkScale = 1;
[Tooltip("LayerMask to raycast against when placing objects")]
public LayerMask raycastLayerMask;
public bool debug;
public class Chunk
{
public int x;
public int y;
public Renderer renderer;
public bool visible
{
get { return renderer.enabled; }
set { renderer.enabled = value; }
}
public bool IsWithinRange(Range range)
{
return range.IsWithin(x, y);
}
}
public struct Coord
{
public int x, y;
public Coord(int x, int y)
{
this.x = x;
this.y = y;
}
}
public struct Range
{
public int x, y, w, h;
public int endX { get { return x + w; } }
public int endY { get { return y + h; } }
public Range(int x, int y, int w, int h)
{
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
public bool IsWithin(int pX, int pY)
{
return pX >= x
&& pX < x + w
&& pY >= y
&& pY < y + h;
}
public override string ToString()
{
return x + ", " + y + ", " + w + ", " + h;
}
}
Range curRange;
Range prevRange;
Chunk[] chunks;
int curX = 0;
int curY = 0;
float halfWidth;
void Start()
{
if (chunkScale == 0) chunkScale = 1;
halfWidth = (chunksGridSize * chunkScale) / 2;
if (!prefab)
{
Debug.LogError("No prefab assigned");
enabled = false;
return;
}
InitChunks();
}
void InitChunks()
{
chunks = new Chunk[chunksGridSize * chunksGridSize];
for (int i = 0; i < chunks.Length; i++)
{
chunks[i] = new Chunk();
GameObject go = Instantiate(prefab);
go.name = "DetailChunk_" + i;
chunks[i].renderer = go.GetComponent<Renderer>();
chunks[i].visible = false;
}
}
void RefreshChunks()
{
Queue<Coord> redoCoords = new Queue<Coord>();
// First, find all coords that need to be redone
for (int y = curRange.y; y < curRange.endY; y++)
for (int x = curRange.x; x < curRange.endX; x++)
{
// if a new point is not within the old range, queue it for regen
if (!prevRange.IsWithin(x, y))
redoCoords.Enqueue(new Coord(x, y));
}
if (debug)
foreach (var redoCoord in redoCoords)
Debug.DrawRay(new Vector3(redoCoord.x * chunkScale - halfWidth, 0, redoCoord.y * chunkScale - halfWidth), Vector3.up, Color.white, 0.2f);
foreach (var chunk in chunks)
{
// if chunk is not within the new range, reassign it's coords to one that needs to be redone
if (!chunk.IsWithinRange(curRange))
{
// if there are no coords in queue, hide the chunk
if (redoCoords.Count == 0)
{
chunk.visible = false;
continue;
}
Coord coord = redoCoords.Dequeue();
chunk.x = coord.x;
chunk.y = coord.y;
// show it and set new position
PlaceChunkRaycast(chunk);
}
}
}
// Without raycast, just at y = 0
void PlaceChunk(Chunk chunk)
{
chunk.visible = true;
chunk.renderer.transform.position = GetChunkPos(chunk.x, chunk.y);
}
void PlaceChunkRaycast(Chunk chunk)
{
Vector3 chunkPos = GetChunkPosRandomGridOffset(chunk.x, chunk.y);
RaycastHit hit;
if (Physics.Raycast(chunkPos + Vector3.up, Vector3.down, out hit, Mathf.Infinity, raycastLayerMask))
{
chunk.visible = true;
chunk.renderer.transform.position = hit.point;
}
else
{
chunk.visible = false;
}
}
Vector3 GetChunkPos(int x, int y)
{
return new Vector3(x * chunkScale - halfWidth, 0, y * chunkScale - halfWidth);
}
Vector3 GetChunkPosRandomGridOffset(int x, int y)
{
Random.InitState((x + y * chunksGridSize) * 323 % 3414);
float X = (x + Random.value) * chunkScale - halfWidth;
float Y = (y + Random.value) * chunkScale - halfWidth;
return new Vector3(X, 0, Y);
}
void Check()
{
int x = Mathf.RoundToInt(player.position.x / chunkScale);
int y = Mathf.RoundToInt(player.position.z / chunkScale);
if (x != curX || y != curY)
{
curRange = new Range(x, y, chunksGridSize, chunksGridSize);
Debug.Log(curRange);
RefreshChunks();
prevRange = curRange;
}
curX = x;
curY = y;
}
void Update()
{
Check();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment