转载请注明出处: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. win8设置自动关机

    运行中输入 shutdown -S -T 3600 表示1个小时后关机,单位是秒

  2. ABP框架源码学习之修改默认数据库表前缀或表名称

    ABP框架源码学习之修改默认数据库表前缀或表名称 1,源码 namespace Abp.Zero.EntityFramework { /// <summary> /// Extension ...

  3. spring之AspectJ基于注解 AOP编程

    一.前言 使用注解代替之前在spring配置文件中配置目标类.切面类和aop配置. 二.注意 需要注意的是,需要在spring配置文件中引入如下,如果不添加,切面类中的@Aspect注解将不起作用 & ...

  4. 16_Python闭包

    一.什么是闭包 什么是闭包:内函数对外函数非全局变量的引用,并且外函数的返回值是内函数的引用(地址). def wrapper(): name = 'zhangsan' def inner(): na ...

  5. Git:warning: LF will be replaced by CRLF

    windows中的换行符为 CRLF, 而在linux下的换行符为LF,所以在执行add . 时出现提示,解决办法: $ rm -rf .git // 删除.git $ git config --gl ...

  6. phantomjs集成到scrapy中,并禁用图片,切换UA

    phantomjs是一个没有界面的浏览器,支持各种web标准,提供DOM 处理, CSS 选择器, JSON, Canvas, 和 SVG,对于爬取一些经过js渲染的页面非常有用.但是phantomj ...

  7. HTML中的表单

    1.HTML中表单元素的基本概念 HTML表单是HTML元素中较为复杂的部分,表单往往和脚本,动态页面,数据处理等功能相结合,因此是制作动态网站很重要的内容. 表单一般用来收集用户的输入信息 2.表单 ...

  8. iOS-Runtime之关于页面跳转的捷径【Runtime获取当前ViewController】

    写在前面 在我们操作页面跳转时,如果当前的类不是UIViewcontroller(下面用VC表示),你会不会写一个代理,或者block给VC传递信息,然后在VC里面进行 ///假如targetVc是将 ...

  9. 济南清北学堂游记 Day 7.

    完结撒花? 最后的模拟赛与讲题.大家似乎仍然乐在其中,虽然看评测结果,仍然是满地红. 其实到了最后已经是没有多少可写的了..... 一个精彩的地方是晚上填表的时候,群里炸锅了已经.到处都在刷CCL老师 ...

  10. SDN第三次作业

    作业链接 阅读文章:http://www.sdnlab.com/19777.html 阅读<重构网络>第一二章 列举openflow1.0的12元组? 入端口 源MAC地址 目的MAC地址 ...