Created
May 17, 2016 21:31
-
-
Save tsaodown/778ebd98799459c702acf62f997a07dd to your computer and use it in GitHub Desktop.
DerpCraft - Building a MineCraft Clone in Unity - Textured Blocks | Full code available at https://git.io/vrlIk
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using Utilities; | |
public class Block { | |
// Enumerator representing the face of the block. | |
public enum FaceDirection { | |
North, | |
// +z | |
South, | |
// -z | |
East, | |
// +x | |
West, | |
// -x | |
Top, | |
// +y | |
Bottom | |
// -y | |
} | |
// Target texture is 16x16 of 16x16 textures. | |
static readonly float tileSize = 0.0625f; | |
protected virtual Vector2Int TextureTilePosition(FaceDirection direction) { | |
// Default is a hot pink box. | |
return new Vector2Int(0, 1); | |
} | |
// Calculates the UVs for a given direction on the block. | |
protected virtual Vector2[] FaceUVs(FaceDirection direction) { | |
Vector2Int texTile = TextureTilePosition(direction); | |
var uvs = new Vector2[4]; | |
// top-left, top-right, bottom-right, bottom-left | |
uvs[0] = texTile * tileSize + Vector2.up * tileSize; | |
uvs[1] = texTile * tileSize + Vector2.one * tileSize; | |
uvs[2] = texTile * tileSize + Vector2.right * tileSize; | |
uvs[3] = texTile * tileSize; | |
return uvs; | |
} | |
public virtual MeshData GetBlockData(Vector3Int position, | |
MeshData collectedMeshData) { | |
// Go through each face and get the vertices, indices, normals, and UVs. | |
collectedMeshData = FaceDataTop(position, collectedMeshData); | |
collectedMeshData = FaceDataBottom(position, collectedMeshData); | |
collectedMeshData = FaceDataNorth(position, collectedMeshData); | |
collectedMeshData = FaceDataSouth(position, collectedMeshData); | |
collectedMeshData = FaceDataEast(position, collectedMeshData); | |
collectedMeshData = FaceDataWest(position, collectedMeshData); | |
return collectedMeshData; | |
} | |
protected virtual MeshData FaceDataTop(Vector3Int position, | |
MeshData collectedMeshData) { | |
// top-left, top-right, bottom-right, bottom-left | |
collectedMeshData.AddVertex(new Vector3( | |
position.x - 0.5f, position.y + 0.5f, position.z + 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x + 0.5f, position.y + 0.5f, position.z + 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x + 0.5f, position.y + 0.5f, position.z - 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x - 0.5f, position.y + 0.5f, position.z - 0.5f)); | |
// Add tris from verts. | |
collectedMeshData.AddQuadTriangles(); | |
// Add UVs for face. | |
collectedMeshData.uvs.AddRange(FaceUVs(FaceDirection.Top)); | |
return collectedMeshData; | |
} | |
protected virtual MeshData FaceDataBottom(Vector3Int position, | |
MeshData collectedMeshData) { | |
// top-left, top-right, bottom-right, bottom-left | |
collectedMeshData.AddVertex(new Vector3( | |
position.x - 0.5f, position.y - 0.5f, position.z - 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x + 0.5f, position.y - 0.5f, position.z - 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x + 0.5f, position.y - 0.5f, position.z + 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x - 0.5f, position.y - 0.5f, position.z + 0.5f)); | |
// Add tris from verts. | |
collectedMeshData.AddQuadTriangles(); | |
// Add UVs for face. | |
collectedMeshData.uvs.AddRange(FaceUVs(FaceDirection.Bottom)); | |
return collectedMeshData; | |
} | |
protected virtual MeshData FaceDataNorth(Vector3Int position, | |
MeshData collectedMeshData) { | |
// top-left, top-right, bottom-right, bottom-left | |
collectedMeshData.AddVertex(new Vector3( | |
position.x + 0.5f, position.y + 0.5f, position.z + 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x - 0.5f, position.y + 0.5f, position.z + 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x - 0.5f, position.y - 0.5f, position.z + 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x + 0.5f, position.y - 0.5f, position.z + 0.5f)); | |
// Add tris from verts. | |
collectedMeshData.AddQuadTriangles(); | |
// Add UVs for face. | |
collectedMeshData.uvs.AddRange(FaceUVs(FaceDirection.North)); | |
return collectedMeshData; | |
} | |
protected virtual MeshData FaceDataSouth(Vector3Int position, | |
MeshData collectedMeshData) { | |
// top-left, top-right, bottom-right, bottom-left | |
collectedMeshData.AddVertex(new Vector3( | |
position.x - 0.5f, position.y + 0.5f, position.z - 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x + 0.5f, position.y + 0.5f, position.z - 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x + 0.5f, position.y - 0.5f, position.z - 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x - 0.5f, position.y - 0.5f, position.z - 0.5f)); | |
// Add tris from verts. | |
collectedMeshData.AddQuadTriangles(); | |
// Add UVs for face. | |
collectedMeshData.uvs.AddRange(FaceUVs(FaceDirection.South)); | |
return collectedMeshData; | |
} | |
protected virtual MeshData FaceDataEast(Vector3Int position, | |
MeshData collectedMeshData) { | |
// top-left, top-right, bottom-right, bottom-left | |
collectedMeshData.AddVertex(new Vector3( | |
position.x + 0.5f, position.y + 0.5f, position.z - 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x + 0.5f, position.y + 0.5f, position.z + 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x + 0.5f, position.y - 0.5f, position.z + 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x + 0.5f, position.y - 0.5f, position.z - 0.5f)); | |
// Add tris from verts. | |
collectedMeshData.AddQuadTriangles(); | |
// Add UVs for face. | |
collectedMeshData.uvs.AddRange(FaceUVs(FaceDirection.East)); | |
return collectedMeshData; | |
} | |
protected virtual MeshData FaceDataWest(Vector3Int position, | |
MeshData collectedMeshData) { | |
// top-left, top-right, bottom-right, bottom-left | |
collectedMeshData.AddVertex(new Vector3( | |
position.x - 0.5f, position.y + 0.5f, position.z + 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x - 0.5f, position.y + 0.5f, position.z - 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x - 0.5f, position.y - 0.5f, position.z - 0.5f)); | |
collectedMeshData.AddVertex(new Vector3( | |
position.x - 0.5f, position.y - 0.5f, position.z + 0.5f)); | |
// Add tris from verts. | |
collectedMeshData.AddQuadTriangles(); | |
// Add UVs for face. | |
collectedMeshData.uvs.AddRange(FaceUVs(FaceDirection.West)); | |
return collectedMeshData; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
[RequireComponent(typeof(MeshFilter))] | |
[RequireComponent(typeof(MeshRenderer))] | |
[RequireComponent(typeof(MeshCollider))] | |
public class BlockDisplay : MonoBehaviour { | |
Block block; | |
// Use this for initialization | |
void Start() { | |
// Create internal block, generate mesh data, calculate normals. | |
block = new GrassBlock(); | |
var meshData = new MeshData(); | |
meshData = block.GetBlockData(transform.position, meshData); | |
meshData.RecalculateNormals(); | |
// Set the mesh in the MeshFilter for display. | |
var filter = GetComponent<MeshFilter>(); | |
filter.mesh.vertices = meshData.verts.ToArray(); | |
filter.mesh.triangles = meshData.tris.ToArray(); | |
filter.mesh.normals = meshData.normals.ToArray(); | |
filter.mesh.uv = meshData.uvs.ToArray(); | |
// Set the mesh in the MeshCollider for collision. | |
var coll = GetComponent<MeshCollider>(); | |
var collMesh = new Mesh(); | |
collMesh.vertices = meshData.colliderVerts.ToArray(); | |
collMesh.triangles = meshData.colliderTris.ToArray(); | |
collMesh.normals = meshData.colliderNormals.ToArray(); | |
coll.sharedMesh = collMesh; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Utilities; | |
public class GrassBlock : Block { | |
protected override Vector2Int TextureTilePosition(FaceDirection direction) { | |
switch (direction) { | |
case FaceDirection.Top: | |
return new Vector2Int(0, 15); | |
case FaceDirection.Bottom: | |
return new Vector2Int(2, 15); | |
default: | |
return new Vector2Int(3, 15); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections.Generic; | |
public class MeshData { | |
// Lists to hold vertices, triangles, normals, and UV coordinates. | |
public List<Vector3> verts = new List<Vector3>(); | |
public List<int> tris = new List<int>(); | |
public List<Vector3> normals = new List<Vector3>(); | |
public List<Vector2> uvs = new List<Vector2>(); | |
// Separate lists for collider vertices, triangles, and normals. | |
public List<Vector3> colliderVerts = new List<Vector3>(); | |
public List<int> colliderTris = new List<int>(); | |
public List<Vector3> colliderNormals = new List<Vector3>(); | |
// Flag to use the same data for both mesh and collision. | |
public bool useRenderDataForCollision = true; | |
// Adds triangles to represent a quad made from the last 4 | |
// vertices that were added. | |
public void AddQuadTriangles() { | |
/** | |
* 0 1 | |
* ----- | |
* |\ | | |
* | \ | | |
* | \| | |
* ----- | |
* 3 2 | |
* Uses clockwise winding. | |
**/ | |
// Triangle for 0 -> 1 -> 2 | |
tris.Add(verts.Count - 4); | |
tris.Add(verts.Count - 3); | |
tris.Add(verts.Count - 2); | |
// Calculate face normal. | |
Vector3 v1 = verts[verts.Count - 3] - verts[verts.Count - 4]; | |
Vector3 v2 = verts[verts.Count - 2] - verts[verts.Count - 4]; | |
Vector3 normal = Vector3.Cross(v1, v2).normalized; | |
normals[normals.Count - 4] += normal; | |
normals[normals.Count - 3] += normal; | |
normals[normals.Count - 2] += normal; | |
// Add the same for collision if necessary. | |
if (useRenderDataForCollision) { | |
colliderTris.Add(colliderVerts.Count - 4); | |
colliderTris.Add(colliderVerts.Count - 3); | |
colliderTris.Add(colliderVerts.Count - 2); | |
colliderNormals[colliderNormals.Count - 4] += normal; | |
colliderNormals[colliderNormals.Count - 3] += normal; | |
colliderNormals[colliderNormals.Count - 2] += normal; | |
} | |
// Triangle for 0 -> 2 -> 3 | |
tris.Add(verts.Count - 4); | |
tris.Add(verts.Count - 2); | |
tris.Add(verts.Count - 1); | |
// Calculate face normal. | |
v1 = verts[verts.Count - 2] - verts[verts.Count - 4]; | |
v2 = verts[verts.Count - 1] - verts[verts.Count - 4]; | |
normal = Vector3.Cross(v1, v2).normalized; | |
normals[normals.Count - 4] += normal; | |
normals[normals.Count - 2] += normal; | |
normals[normals.Count - 1] += normal; | |
// Add the same for collision if necessary. | |
if (useRenderDataForCollision) { | |
colliderTris.Add(colliderVerts.Count - 4); | |
colliderTris.Add(colliderVerts.Count - 2); | |
colliderTris.Add(colliderVerts.Count - 1); | |
colliderNormals[colliderNormals.Count - 4] += normal; | |
colliderNormals[colliderNormals.Count - 2] += normal; | |
colliderNormals[colliderNormals.Count - 1] += normal; | |
} | |
} | |
// Adds vertex to mesh verts and optionally collider verts. | |
public void AddVertex(Vector3 v3) { | |
verts.Add(v3); | |
normals.Add(Vector3.zero); | |
if (useRenderDataForCollision) { | |
colliderVerts.Add(v3); | |
colliderNormals.Add(Vector3.zero); | |
} | |
} | |
// Adds triangle to mesh tris and optionally collider tris. | |
public void AddTriangle(int i) { | |
tris.Add(i); | |
if (useRenderDataForCollision) | |
colliderTris.Add(i - (verts.Count - colliderVerts.Count)); | |
} | |
public void RecalculateNormals() { | |
for (int i = 0; i < normals.Count; i++) { | |
// Since each vertex normal was the sum of the normals | |
// of every face touching this vertex, we need to normalize | |
// the result. | |
normals[i].Normalize(); | |
if (useRenderDataForCollision) | |
colliderNormals[i].Normalize(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment