转载请注明出处:http://www.cnblogs.com/shamoyuu/p/unity_minecraft_03.html

一、引入int类型的Vector3

我们都知道Unity3D里Vector3的xyz都是float类型的,但是我们的每一个Block的坐标都应该是int类型,这样在进行转换和存储的时候会有一定的消耗,所以我们先自己写一个xyz都是int类型的Vector3i类,它拥有所有Vector3的属性和方法,只是xyz都换成了int。

using UnityEngine;
using System; namespace Soultia.Util
{
[Serializable]
public struct Vector3i
{
public static readonly Vector3i zero = new Vector3i(, , );
public static readonly Vector3i one = new Vector3i(, , );
public static readonly Vector3i forward = new Vector3i(, , );
public static readonly Vector3i back = new Vector3i(, , -);
public static readonly Vector3i up = new Vector3i(, , );
public static readonly Vector3i down = new Vector3i(, -, );
public static readonly Vector3i left = new Vector3i(-, , );
public static readonly Vector3i right = new Vector3i(, , ); public static readonly Vector3i[] directions = new Vector3i[] {
forward, back,
right, left,
up, down
}; public int x, y, z; public Vector3i(int x, int y, int z)
{
this.x = x;
this.y = y;
this.z = z;
} public Vector3i(int x, int y)
{
this.x = x;
this.y = y;
this.z = ;
} public Vector3i(Vector3 pos)
{
this.x = Mathf.FloorToInt(pos.x);
this.y = Mathf.FloorToInt(pos.y);
this.z = Mathf.FloorToInt(pos.z);
} ///////////////////////////////////////////////////////////////
public static Vector3i Mul(Vector3i a, Vector3i b)
{
return new Vector3i(a.x * b.x, a.y * b.y, a.z * b.z);
} public static Vector3i Div(Vector3i a, Vector3i b)
{
return new Vector3i(a.x / b.x, a.y / b.y, a.z / b.z);
} public static Vector3i Min(Vector3i a, Vector3i b)
{
return Process(a, b, Mathf.Min);
} public static Vector3i Max(Vector3i a, Vector3i b)
{
return Process(a, b, Mathf.Max);
} public static Vector3i Abs(Vector3i a)
{
return Process(a, Mathf.Abs);
} public static Vector3i Floor(Vector3 v)
{
return v.ProcessTo3i(Mathf.FloorToInt);
} public static Vector3i Ceil(Vector3 v)
{
return v.ProcessTo3i(Mathf.CeilToInt);
} public static Vector3i Round(Vector3 v)
{
return v.ProcessTo3i(Mathf.RoundToInt);
} public static Vector3i Process(Vector3i v, Func<int, int> func)
{
v.x = func(v.x);
v.y = func(v.y);
v.z = func(v.z);
return v;
} public static Vector3i Process(Vector3i a, Vector3i b, Func<int, int, int> func)
{
a.x = func(a.x, b.x);
a.y = func(a.y, b.y);
a.z = func(a.z, b.z);
return a;
} ////////////////////////////////////////////////////////
public static Vector3i operator -(Vector3i a)
{
return new Vector3i(-a.x, -a.y, -a.z);
} public static Vector3i operator -(Vector3i a, Vector3i b)
{
return new Vector3i(a.x - b.x, a.y - b.y, a.z - b.z);
} public static Vector3i operator +(Vector3i a, Vector3i b)
{
return new Vector3i(a.x + b.x, a.y + b.y, a.z + b.z);
} public static Vector3i operator *(Vector3i v, int factor)
{
return new Vector3i(v.x * factor, v.y * factor, v.z * factor);
} public static Vector3i operator /(Vector3i v, int factor)
{
return new Vector3i(v.x / factor, v.y / factor, v.z / factor);
} public static Vector3i operator *(Vector3i a, Vector3i b)
{
return Mul(a, b);
} public static Vector3i operator /(Vector3i a, Vector3i b)
{
return Div(a, b);
} ////////////////////////////////////////////////////////
public static bool operator ==(Vector3i a, Vector3i b)
{
return a.x == b.x &&
a.y == b.y &&
a.z == b.z;
} public static bool operator !=(Vector3i a, Vector3i b)
{
return a.x != b.x ||
a.y != b.y ||
a.z != b.z;
} public static implicit operator Vector3(Vector3i v)
{
return new Vector3(v.x, v.y, v.z);
} ////////////////////////////////////////////////////////
public override bool Equals(object other)
{
if (other is Vector3i == false) return false;
Vector3i vector = (Vector3i)other;
return x == vector.x &&
y == vector.y &&
z == vector.z;
} public override int GetHashCode()
{
return x.GetHashCode() ^ y.GetHashCode() << ^ z.GetHashCode() >> ;
} public override string ToString()
{
return string.Format("Vector3i({0} {1} {2})", x, y, z);
}
}
}

以及Vector3的扩展工具类

using UnityEngine;
using System.Collections;
using System; namespace Soultia.Util
{
public static class Vector3Utils
{
public static Vector3 Mul(this Vector3 a, Vector3 b)
{
a.x *= b.x;
a.y *= b.y;
a.z *= b.z;
return a;
} public static Vector3 Div(this Vector3 a, Vector3 b)
{
a.x /= b.x;
a.y /= b.y;
a.z /= b.z;
return a;
} public static Vector3 Process(this Vector3 v, Func<float, float> func)
{
v.x = func(v.x);
v.y = func(v.y);
v.z = func(v.z);
return v;
} public static Vector3i ProcessTo3i(this Vector3 v, Func<float, int> func)
{
Vector3i vi;
vi.x = func(v.x);
vi.y = func(v.y);
vi.z = func(v.z);
return vi;
}
}
}

这两个类如果感兴趣的童鞋可以看一下具体都是怎么写的,不理解也没事,只要把它当成Vector3来用就好。

需要注意的是,虽然Unity3D 2017版已经加入了int类型的Vector类Vector3Int,但是过于简陋,我们还是自己写了。

二、Chunk类

using Soultia.Util;
using System.Collections;
using System.Collections.Generic;
using UnityEngine; namespace Soultia.Voxel
{
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
[RequireComponent(typeof(MeshCollider))]
public class Chunk : MonoBehaviour
{
public static int width = ;
public static int height = ; public byte[,,] blocks;
public Vector3i position; private Mesh mesh; //面需要的点
private List<Vector3> vertices = new List<Vector3>();
//生成三边面时用到的vertices的index
private List<int> triangles = new List<int>(); //当前Chunk是否正在生成中
private bool isWorking = false; void Start()
{
position = new Vector3i(this.transform.position);
if (Map.instance.chunks.ContainsKey(position))
{
Destroy(this);
}
else
{
this.name = "(" + position.x + "," + position.y + "," + position.z + ")";
StartFunction();
}
} void StartFunction()
{
mesh = new Mesh();
mesh.name = "Chunk"; StartCoroutine(CreateMap());
} IEnumerator CreateMap()
{
while (isWorking)
{
yield return null;
}
isWorking = true;
blocks = new byte[width, height, width];
for (int x = ; x < Chunk.width; x++)
{
for (int y = ; y < Chunk.height; y++)
{
for (int z = ; z < Chunk.width; z++)
{
blocks[x, y, z] = ;
}
}
} StartCoroutine(CreateMesh());
} IEnumerator CreateMesh()
{
vertices.Clear();
triangles.Clear(); //把所有面的点和面的索引添加进去
for (int x = ; x < Chunk.width; x++)
{
for (int y = ; y < Chunk.height; y++)
{
for (int z = ; z < Chunk.width; z++)
{
if (IsBlockTransparent(x + , y, z))
{
AddFrontFace(x, y, z);
}
if (IsBlockTransparent(x - , y, z))
{
AddBackFace(x, y, z);
}
if (IsBlockTransparent(x, y, z + ))
{
AddRightFace(x, y, z);
}
if (IsBlockTransparent(x, y, z - ))
{
AddLeftFace(x, y, z);
}
if (IsBlockTransparent(x, y + , z))
{
AddTopFace(x, y, z);
}
if (IsBlockTransparent(x, y - , z))
{
AddBottomFace(x, y, z);
}
}
}
} //为点和index赋值
mesh.vertices = vertices.ToArray();
mesh.triangles = triangles.ToArray(); //重新计算顶点和法线
mesh.RecalculateBounds();
mesh.RecalculateNormals(); //将生成好的面赋值给组件
this.GetComponent<MeshFilter>().mesh = mesh;
this.GetComponent<MeshCollider>().sharedMesh = mesh; yield return null;
isWorking = false;
} public static bool IsBlockTransparent(int x, int y, int z)
{
if (x >= width || y >= height || z >= width || x < || y < || z < )
{
return true;
}
return false;
} //前面
void AddFrontFace(int x, int y, int z)
{
//第一个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //第二个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //添加4个点
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + z));
} //背面
void AddBackFace(int x, int y, int z)
{
//第一个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //第二个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //添加4个点
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + z));
} //右面
void AddRightFace(int x, int y, int z)
{
//第一个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //第二个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //添加4个点
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + z));
} //左面
void AddLeftFace(int x, int y, int z)
{
//第一个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //第二个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //添加4个点
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + z));
} //上面
void AddTopFace(int x, int y, int z)
{
//第一个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //第二个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //添加4个点
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + z));
} //下面
void AddBottomFace(int x, int y, int z)
{
//第一个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //第二个三角面
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count);
triangles.Add( + vertices.Count); //添加4个点
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3(- + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + z));
vertices.Add(new Vector3( + x, + y, + z));
}
}
}

最上面我们新建了一个变量blocks用来存储block的信息,为了节约内存,我们只存储block的id。blocks是一个byte类型的数组, byte是无符号8位整数,值是0~255。(做软件的时候我永远都不会考虑一个数值应该存储在什么类型的变量中,这就是做游戏跟做软件的区别吧。做软件就像是在写语文作业,做游戏就像是在写数学作业)

我们首先调用CreateMap生成了一个Chunk中Block的信息,这里虽然都设置了它们是id为1的方块,但是并没有用到,这个我们下一章再讲。

然后就是用CreateMesh方法来创建我们的地形网格。

在创建之前,我们写了一个简单的方法IsBlockTransparent来判断这个坐标的Block是不是透明的。因为如果是透明的,那跟它相邻的面就需要绘制出来

比如我们在绘制前面这个面的时候

if (IsBlockTransparent(x + , y, z))
{
AddFrontFace(x, y, z);
}

我们先判断了它x+1的位置上的方块是否透明,才决定是否绘制它。

这里我们只是简单地判断了一下它的坐标,后面我们会写更详细的判断。

三、Map类

我们的Block并不是存储在Map对象中,而是存储在Chunk里,Chunk对象存储在Map中。

我们的Map对象只干两件事

1、存储Chunk

2、生成Chunk

using Soultia.Util;
using System.Collections;
using System.Collections.Generic;
using UnityEngine; namespace Soultia.Voxel
{
public class Map : MonoBehaviour
{
public static Map instance; public static GameObject chunkPrefab; public Dictionary<Vector3i, GameObject> chunks = new Dictionary<Vector3i, GameObject>(); //当前是否正在生成Chunk
private bool spawningChunk = false; void Awake()
{
instance = this;
chunkPrefab = Resources.Load("Prefab/Chunk") as GameObject;
} void Start()
{
StartCoroutine(SpawnChunk(new Vector3i(, , )));
} void Update()
{ } public IEnumerator SpawnChunk(Vector3i pos)
{
spawningChunk = true;
Instantiate(chunkPrefab, pos, Quaternion.identity);
yield return ;
spawningChunk = false;
}
}
}

代码很简单,首先生成了一个Map对象的单例,然后

因为现在只是为了简单地生成一个Chunk,所以SpawnChunk方法是写在了Start里,我们下下一章继续完善。

【Unity3D】Unity3D开发《我的世界》之三、创建一个Chunk的更多相关文章

  1. unity3d游戏开发学习之使用3dmax创建导弹模型

    在着手研究Unity3D的游戏开发时,3D模型能够考虑从unity的assets store去获取,也能够从网上搜索下载,同一时候咱们也能够尝试下自己动手去做一些简单的模型. 这里就依据unity3d ...

  2. C#程序员的春天之从零开始学习unity3D游戏开发入门教程二(创建项目及基本面板介绍)

    一项目创建: 创建项目是开发的第一步. 运行untiy之后如果是第一次运行会弹出 我们这里随便创建一个项目. 二Untiy面板介绍: 三代码编辑器的切换: 这里我安装了vs2012. 到这里开发环境基 ...

  3. Unity3D游戏开发从零单排(四) - 制作一个iOS游戏

    提要 此篇是一个国外教程的翻译,尽管有点老,可是适合新手入门. 自己去写代码.debug,布置场景,能够收获到非常多.游戏邦上已经有前面两部分的译文,这里翻译的是游戏的最后一个部分. 欢迎回来 在第一 ...

  4. three.js-走进3d的奇妙世界一创建一个三维场景

      一.git代码仓库地址 git clone https://github.com/josdirksen/learning-threejs-third  下载并解压 二.创建一个三维场景 如下图所示 ...

  5. 【Xamarin开发 Android 系列 8】 创建一个Json读取数据应用(上)

    后续将内容贴上来...........

  6. Unity3D游戏开发之粒子系统实现具体解释

     今天为大家分享的是Unity3D中的粒子系统.粒子系统通经常使用来表现烟雾.云等高级效果.是一个十分注重制作技巧的部分.今天我们将以一个气泡的演示实例来一起学习怎样在Unity3D中使用粒子系统 ...

  7. cocos2d-x游戏开发(二)之创建第一个项目

    配置好开发环境之后,尝试创建一个cocos项目 (1)打开cocos2d-x安装目录,如D:\DIY\cocos2d-x-3.3 看到目录下有可执行文件 download-deps 以及 setup ...

  8. Revit API创建一个拷贝房间内对象布局命令

    本课程演示创建一个拷贝房间内对象布局命令,完整演示步骤和代码.这个命令把选中房间内的对象复制到其它选中的一个或多个房间中,而且保持与源房间一致的相对位置.通过本讲座使听众知道创建一个二次开发程序很简单 ...

  9. Framework7 - 入门教程(安装、配置、创建一个H5应用)

    1,Framework7介绍 (1)Framework7 是一个开源免费的框架.可以用来开发混合移动应用(原生和 HTML 混合)或者开发 iOS & Android 风格的 WEB APP. ...

随机推荐

  1. Markdown内嵌Html语言

    概述 Markdown是内嵌Html语言的,这使得我们可以在Markdown文档里面实现很多有趣的东西.现在记录在此,供自己以后参考,相信对其他人也有用. 介绍 Markdown的语法只有一个目标:作 ...

  2. WindowXp-Windows7-Windows运行命令(转)

    Win7里面按 Win+R 呼出运行界面,一下是它的一些常用命令: 1.cleanmgr: 打开磁盘清理工具 2.compmgmt.msc: 计算机管理 3.conf: 启动系统配置实用程序 4.ch ...

  3. ELK安装

    一.概念 1.核心组成 ELK由Elasticsearch.Logstash和Kibana三部分组件组成: Elasticsearch是个开源分布式搜索引擎,它的特点有:分布式,零配置,自动发现,索引 ...

  4. Date对象和正则对象

    1.Date对象 创建 var date1 = new Date(); var date2 = new Date(12983798123);//填一个毫秒值,应该是距离1970年1月1日.....多少 ...

  5. 在VMware中安装ubuntu

    如果想在自己设置它的属性,比如时区,语言支持之类的,在开机时按[enter], 否则就是默认的设置了,英文语言,而且还不好调.

  6. flex基本概念

    基本使用 任何一个容器都可以指定为flex布局 .box { display: flex; } 行内元素也可以使用flex .box { display: inline-flex; } Webkit内 ...

  7. C# Sap Rfc 连接代码实例

    根据不同的需求,安装不同位数的 Rfc SDK 1.构造 Sap Adress Information,且继承 IDestinationConfiguration public class SapAd ...

  8. Go笔记-map

    [概念]     1- map 是引用类型的     2- 声明方式         var map1 map[keytype]valuetype             例如:var map1 ma ...

  9. 在eclipse的配置文件里指定jdk路径

    在eclipse的配置文件里指定jdk路径,只需在eclipse的配置文件里增加-vm参数即可. 打开eclipse目录下的eclipse.ini配置文件,增加-vm配置,需要注意的是该参数要加在-v ...

  10. Codeforces 250 E. The Child and Binary Tree [多项式开根 生成函数]

    CF Round250 E. The Child and Binary Tree 题意:n种权值集合C, 求点权值和为1...m的二叉树的个数, 形态不同的二叉树不同. 也就是说:不带标号,孩子有序 ...