Skip to content

Instantly share code, notes, and snippets.

@jonbro
Created November 12, 2013 14:40
Show Gist options
  • Select an option

  • Save jonbro/7431874 to your computer and use it in GitHub Desktop.

Select an option

Save jonbro/7431874 to your computer and use it in GitHub Desktop.
3d tiling system
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class GenerateVoxelLevel : MonoBehaviour {
// not going to do octtrees, just totally straight forward arrays so I can get this done in 25 mins
bool[,,] worldMap;
bool[,,] filled;
int[] digPosition;
int worldSize = 40;
public GameObject worldBit;
public List<GameObject> tile;
public List<GameObject> light;
List<CombineInstance> meshSegments = new List<CombineInstance>();
public GameObject player;
// Use this for initialization
// 3d neighborhood
int[,] nDir = {
{0, 1, 0}, {0, -1, 0},
{1, 0, 0}, {-1, 0, 0},
{0,0,1}, {0,0,-1}
};
// rotation mappings for 3d neighborhood
Vector3[] rotationMapping = {
new Vector3 (270, 0, 0), new Vector3 (90, 0, 0),
new Vector3 (0, 90, 0), new Vector3 (0, 270, 0),
new Vector3 (0, 0, 0), new Vector3 (0, 180, 0)
};
void Start () {
digPosition = new int[3];
worldMap = new bool[worldSize, worldSize, worldSize];
filled = new bool[worldSize, worldSize, worldSize];
// fill in the world map
for (int x=0; x<worldSize; x++) {
for (int y=0; y<worldSize; y++) {
for (int z=0; z<worldSize; z++) {
// worldMap [x, y, z] = (Random.value < 0.9f);
worldMap [x, y, z] = true;
// just fill the walls
// if (x == 0 || y == 0 || z == 0 || x == worldSize - 1 || y == worldSize - 1 || z == worldSize - 1)
// worldMap [x, y, z] = true;
}
}
}
for (int i=0; i<4; i++) {
DigTunnel();
}
// build the world from tiles
int VertCount = 0;
for (int x=0; x<worldSize; x++) {
for (int y=0; y<worldSize; y++) {
for (int z=0; z<worldSize; z++) {
if (worldMap [x, y, z]) {
// check the 3d neighborhood
for (int i = 0; i < 6; i++) {
int _x = x + nDir [i, 0];
int _y = y + nDir [i, 1];
int _z = z + nDir [i, 2];
if (CheckOutsideBuilding(_x, _y, _z) || !worldMap[_x, _y, _z]) {
// add a tile at the location
Vector3 tileOffset = new Vector3 (nDir [i, 0], nDir [i, 1], nDir [i, 2]) * 0.5f;
CombineInstance thisMesh = new CombineInstance();
thisMesh.mesh = tile[Random.Range(0,tile.Count)].GetComponent<MeshFilter>().sharedMesh;
VertCount += thisMesh.mesh.vertexCount;
transform.position = new Vector3 (x, y, z) + tileOffset;
transform.rotation = Quaternion.Euler (rotationMapping [i]) * Quaternion.Euler(new Vector3(0,0,90)*Random.Range(0,4));
transform.localScale = Vector3.one * 0.5f;
thisMesh.transform = transform.localToWorldMatrix;
meshSegments.Add(thisMesh);
if (VertCount > 15 * 1000) {
CombineMeshes ();
VertCount = 0;
}
}
}
}
}
}
}
CombineMeshes ();
for (int i=0; i<8; i++) {
// find a random empty spot and add a light
int[] empty = FindEmpty();
if (empty [0] >= 0) {
GameObject g = (GameObject)Instantiate (light[Random.Range(0, light.Count)], new Vector3 (empty [0], empty [1], empty [2]), Quaternion.identity);
g.transform.parent = transform;
}
}
player.transform.position = Vector3.one * worldSize * 0.5f;
}
// necessary because we must combine meshes as we go
void CombineMeshes(){
transform.position = Vector3.zero;
transform.localRotation = Quaternion.identity;
transform.localScale = Vector3.one;
GameObject subMesh = new GameObject ();
subMesh.AddComponent<MeshFilter> ();
subMesh.AddComponent<MeshCollider> ();
subMesh.AddComponent<MeshRenderer> ();
subMesh.renderer.sharedMaterial = renderer.material;
subMesh.GetComponent<MeshFilter>().mesh = new Mesh();
subMesh.GetComponent<MeshFilter>().mesh.CombineMeshes(meshSegments.ToArray(), true);
subMesh.GetComponent<MeshCollider> ().sharedMesh = subMesh.GetComponent<MeshFilter> ().sharedMesh;
// subMesh.transform.parent = transform;
meshSegments.Clear ();
}
int[] FindEmpty(){
int bail = 0;
int[] empty = new int[3];
while (bail < 3000) {
empty [0] = Random.Range (0, worldSize);
empty [1] = Random.Range (0, worldSize);
empty [2] = Random.Range (0, worldSize);
if (!worldMap[empty [0], empty [1], empty [2]]) {
Debug.Log ("found empty");
return empty;
}
bail++;
}
empty [0] = -1;
return empty;
}
void DigTunnel(){
// dig some tunnels
int tunnelDigCount = 0;
digPosition[0] = worldSize/2;
digPosition[2] = worldSize/2;
digPosition[1] = worldSize/2;
while (tunnelDigCount < 40) {
DigInNeighborhood (digPosition [0], digPosition [1], digPosition [2]);
for (int i=0; i<3; i++) {
// add a random value to the dig position
digPosition [i] += Random.value > 0.5 ? -1 : 1;// (-1, 2);
// limit the position to the cube
digPosition [i] = Mathf.Max (0, Mathf.Min (worldSize - 1, digPosition [i]));
}
tunnelDigCount++;
}
}
void DigInNeighborhood(int _x, int _y, int _z){
for (int x=_x-1; x<_x+1; x++) {
for (int y=_y-1; y<_y+1; y++) {
for (int z=_z-1; z<_z+1; z++) {
if (x > 0 && x < worldSize && y > 0 && y < worldSize && z > 0 && z < worldSize)
worldMap [x, y, z] = false;
}
}
}
}
bool CheckOutsideBuilding(int x, int y, int z){
return x < 0 || x > worldSize - 1 || y < 0 || y > worldSize - 1 || z < 0 || z > worldSize - 1;
}
// if the area surrounding this has an empty, or is outside of the level, return true
bool checkNeighborhoodContainsEmpty(int _x, int _y, int _z){
for (int x=_x-1; x<=_x+1; x++) {
for (int y=_y-1; y<=_y+1; y++) {
for (int z=_z-1; z<=_z+1; z++) {
if (x < 0 || x >= worldSize-1 || y < 0 || y >= worldSize-1 || z < 0 || z >= worldSize-1)
return true;
if ((x != _x || y != _y || z != _z) && !worldMap [x, y, z])
return true;
}
}
}
return false;
}
// Update is called once per frame
void Update () {
// transform.rotation *= Quaternion.Euler (0, 0, 30 * Time.deltaTime);
}
}
@KraigWalker

Copy link
Copy Markdown

You might want to make int worldSize [line 10] public

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment