Skip to content

Instantly share code, notes, and snippets.

@tsaodown
Created May 17, 2016 21:31
Show Gist options
  • Save tsaodown/778ebd98799459c702acf62f997a07dd to your computer and use it in GitHub Desktop.
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
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;
}
}
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;
}
}
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);
}
}
}
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