转载请注明出处: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. shell的变量处理

    shell的变量处理 一.删除 删除(删除某一段) # 从前向后删除 % 从后向前删除 删除(删除某一部分) $(var:nu1:nu2) nu1表示开始位置 nu2表示删除长度 示例如下 file= ...

  2. Restful、Jersey和JAX-RS

     一:MVC与SpringMVC MVC:是一种思想,是一种设计模式 SpringMVC:是一个框架,实现了MVC这种思想. 之前:写JSP页面,比较繁琐.eg:在页面显示用户列表,我们会在JSP页面 ...

  3. 数据分区------《Designing Data-Intensive Applications》读书笔记9

    进入到第六章了,我们要开始聊聊分布式系统之中的核心问题:数据分区.分布式系统通常是通过大规模的数据节点来处理单机没有办法处理的海量数据集,因此,可以将一个大型数据集可以分布在多个磁盘上,查询负载可以分 ...

  4. Oracle 12cR1 RAC 在VMware Workstation上安装(中)—图形界面安装

    Oracle 12cR1 RAC 在VMware Workstation上安装(中)—图形界面安装 1.1  图形界面安装 1.1.1  安装GRID 安装日志:/u01/app/oraInvento ...

  5. 让44.1版本的sketch打开更高版本的sketch文件

    我们都知道,sketch的有效license与版本挂钩.最近设计师又更新了sketch版本,导致她生成的源文件我都无法打开. 毕竟我不是使用sketch进行UI设计,仅用它来查看设计稿参数,再花99美 ...

  6. ZOJ [P2314] 无源汇点有上下界模版

    对于有上下界的网络流来说,我们可以分离出必要弧,然后将必要弧切开,两端分别连接源点和汇点,原图有可行解充要于源点或汇点满流. 这样求下来,只能求出可行流 #include <iostream&g ...

  7. 计算机基础之Windows10操作系统安装U盘制作

    1.第一步,下载Windows10--ISO镜像(Windows7类似),下载站点: https://msdn.itellyou.cn/(百度搜索msdn即可),个人认为这是最干净的操作系统镜像站点, ...

  8. MacbookPro管理员问题

    更改用户名重启之后,发现用户名还是用户名,管理员权限降成了普通用户. 参考这个帖子改的用户名: https://zhidao.baidu.com/question/259845860.html 找回管 ...

  9. tomcat管理授权:tomcat-users.xml

    ou are not authorized to view this page. If you have already configured the Manager application to a ...

  10. maven配置全局的jdk和配置局部的jdk

    配置全局的jdk需要修改maven的setting.xml文件 <profile> <id>jdk17</id> <activation> <ac ...