转载请注明出处: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. java_多线程4种实现方式

    为了34月份回学校春招,不得不复习一下线程的四种实现方式,希望春招时能找到更好的公司,加油! 1.继承Thread类 class MyThread extends Thread{ private in ...

  2. KVM详情

    KVM介绍 Kernel-based Virtual Machine的简称,是一个开源的系统虚拟化模块,自Linux 2.6.20之后集成在Linux的各个主要发行版本中.它使用Linux自身的调度器 ...

  3. zend framework框架学习走起——从零开始,点击记录-安装

    zend framework第一步,先来安装下zend framework框架. 先介绍下,我这边的php配置环境,为图省事,我是安装wampserver来搭载php环境的,,php+Apache+m ...

  4. Nodejs的运行原理-模块篇

    前言 使用Nodejs,就不可避免地引用第三方模块,它们有些是Nodejs自带的(例:http,net...),有些是发布在npm上的(例:mssql,elasticsearch...) 本篇章聚焦3 ...

  5. mac 上node.js环境的安装与测试

    如果大家之前做过web服务器的人都知道,nginx+lua与现在流行的Node.js都是可以做web服务器的,前者在程序的写法和配置上要比后者麻烦,但用起来都是差不多.在这里建议大家如果对lua脚本语 ...

  6. Vue.js源码——事件机制

    写在前面 因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出.文章的原地址:https://github.com/an ...

  7. XAMPP禁止目录浏览的方法

    XAMPP是目前比较流行Web服务器套件,集成了Apache.MySQL.PHP.PERL.FTP等各种软件包.但是细心的人可以发现,XAMPP安装完成后,默认是可以目录浏览的,这有些不安全.如果需要 ...

  8. mongodb的TTL索引介绍(超时索引)

    TTL索引是mongodb新支持的用于延时自动删除记录的一种索引.它仅包含一个字段,该字段值需要是Date()类型,并且不支持复合索引.可以指定某条记录在延时固定时间后自动删除.数据自动超时删除主要用 ...

  9. yii2 查询构建器

    Query Builder $rows = (new \yii\db\Query()) ->select(['dyn_id', 'dyn_name']) ->from('zs_dynast ...

  10. PHP秒杀系统全方位设计(一)

    秒杀系统特点人多商品少时间短流量高外挂机器[黄牛和非黄牛] 技术分析瞬间高并发的处理能力多层次的分布式处理能力人机交互与对抗[12306验证码图片] 技术选型分析Linux+Nginx+PHP+Mys ...