【Unity3D】Unity3D开发《我的世界》之三、创建一个Chunk
转载请注明出处: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的更多相关文章
- unity3d游戏开发学习之使用3dmax创建导弹模型
在着手研究Unity3D的游戏开发时,3D模型能够考虑从unity的assets store去获取,也能够从网上搜索下载,同一时候咱们也能够尝试下自己动手去做一些简单的模型. 这里就依据unity3d ...
- C#程序员的春天之从零开始学习unity3D游戏开发入门教程二(创建项目及基本面板介绍)
一项目创建: 创建项目是开发的第一步. 运行untiy之后如果是第一次运行会弹出 我们这里随便创建一个项目. 二Untiy面板介绍: 三代码编辑器的切换: 这里我安装了vs2012. 到这里开发环境基 ...
- Unity3D游戏开发从零单排(四) - 制作一个iOS游戏
提要 此篇是一个国外教程的翻译,尽管有点老,可是适合新手入门. 自己去写代码.debug,布置场景,能够收获到非常多.游戏邦上已经有前面两部分的译文,这里翻译的是游戏的最后一个部分. 欢迎回来 在第一 ...
- three.js-走进3d的奇妙世界一创建一个三维场景
一.git代码仓库地址 git clone https://github.com/josdirksen/learning-threejs-third 下载并解压 二.创建一个三维场景 如下图所示 ...
- 【Xamarin开发 Android 系列 8】 创建一个Json读取数据应用(上)
后续将内容贴上来...........
- Unity3D游戏开发之粒子系统实现具体解释
今天为大家分享的是Unity3D中的粒子系统.粒子系统通经常使用来表现烟雾.云等高级效果.是一个十分注重制作技巧的部分.今天我们将以一个气泡的演示实例来一起学习怎样在Unity3D中使用粒子系统 ...
- cocos2d-x游戏开发(二)之创建第一个项目
配置好开发环境之后,尝试创建一个cocos项目 (1)打开cocos2d-x安装目录,如D:\DIY\cocos2d-x-3.3 看到目录下有可执行文件 download-deps 以及 setup ...
- Revit API创建一个拷贝房间内对象布局命令
本课程演示创建一个拷贝房间内对象布局命令,完整演示步骤和代码.这个命令把选中房间内的对象复制到其它选中的一个或多个房间中,而且保持与源房间一致的相对位置.通过本讲座使听众知道创建一个二次开发程序很简单 ...
- Framework7 - 入门教程(安装、配置、创建一个H5应用)
1,Framework7介绍 (1)Framework7 是一个开源免费的框架.可以用来开发混合移动应用(原生和 HTML 混合)或者开发 iOS & Android 风格的 WEB APP. ...
随机推荐
- __getattr__动态获取接口
# -*- coding:utf-8 -*- #在看廖雪峰的python3.5教学时,看到面向对象高级编程_定义类 https://www.liaoxuefeng.com/wiki/001431608 ...
- curl访问nagios中Host Status Details For All Host Groups页面的方法
由于进入nagios要输入用户名与密码才能进入,故用curl模拟输入用户名与密码取得当前所有主机的报警信息: # curl -u nagiosadmin:password http://192.168 ...
- 01-Go命令与基础
什么是Go? Go是一门并发支持.垃圾回收的编译型系统编程语言,旨在创造一门具有在静态编译语言的高性能和动态的高效开之间拥有良好平衡点的一门编程语言. Go的主要特点有哪些? 类型安全和内存安全 以非 ...
- MVVM之旅(1)创建一个最简单的MVVM程序
这是MVVM之旅系列文章的第一篇,许多文章和书喜欢在开篇介绍某种技术的诞生背景和意义,但是我觉得对于程序员来说,一个能直接运行起来的程序或许能够更直观的让他们了解这种技术.在这篇文章里,我将带领大家一 ...
- SSD写入放大问题[转]
原文地址:http://blog.csdn.net/cywosp/article/details/29812433 之前在SSD(Solid State Drive)上设计并实现缓存系统用于存储数据块 ...
- [LeetCode] 679. 24 Game(回溯法)
传送门 Description You have 4 cards each containing a number from 1 to 9. You need to judge whether the ...
- 发生Windows无法访问远程共享的解决办法
发现问题 今天在访问远程时发生一个很郁闷的网络错误,如下图所示 问题修复步骤 这个问题可以采用以下步骤来修复 Step1.添加网络映射盘 添加网络映射时选择使用其他凭据登陆,这样就有机会让Window ...
- docker dead but pid file exists
CentOS 6安装docker 报docker dead but pid file exists 执行 yum install epel-release yum install docker-io ...
- Docker安装Nginx1.11.10+php7+MySQL
Docker安装php-fpm 1.编辑Dockerfile FROM php:7.1.3-fpm ADD sources.list /etc/apt/sources.list RUN cp /usr ...
- Docker Centos6 下建立 Docker 桥接网络
cd /etc/sysconfig/network-scripts/; cp ifcfg-eth0 ifcfg-br0 vi ifcfg-eth0 //增加BRIDGE=br0,删除IPADDR,N ...