【Unity3D】Unity3D开发《我的世界》之四、创建一个Block
转载请注明出处:http://www.cnblogs.com/shamoyuu/p/unity_minecraft_04.html
一、新建Block类
我们的Block类用来存储跟Block相关的信息,例如name,id,贴图坐标等等
using UnityEngine; /// <summary>
/// 方块的方向
/// </summary>
public enum BlockDirection : byte
{
Front = ,
Back = ,
Left = ,
Right = ,
Top = ,
Bottom =
} /// <summary>
/// 方块对象,存储方块的所有信息
/// </summary>
public class Block
{
//方块的ID
public byte id; //方块的名字
public string name; //方块的图标,并不会采用在游戏中动态生成的做法
public Texture icon; //方向(指的是前面所面朝的方向)
public BlockDirection direction = BlockDirection.Front; //前面贴图的坐标
public byte textureFrontX;
public byte textureFrontY; //后面贴图的坐标
public byte textureBackX;
public byte textureBackY; //右面贴图的坐标
public byte textureRightX;
public byte textureRightY; //左面贴图的坐标
public byte textureLeftX;
public byte textureLeftY; //上面贴图的坐标
public byte textureTopX;
public byte textureTopY; //下面贴图的坐标
public byte textureBottomX;
public byte textureBottomY; //都是A面的方块
public Block(byte id, string name, byte textureX, byte textureY)
: this(id, name, textureX, textureY, textureX, textureY, textureX, textureY, textureX, textureY)
{
} //上面是A,其他面是B的方块
public Block(byte id, string name, byte textureX, byte textureY, byte textureTopX, byte textureTopY)
: this(id, name, textureX, textureY, textureX, textureY, textureX, textureY, textureX, textureY, textureTopX, textureTopY, textureX, textureY)
{
} //上面是A,下面是B,其他面是C的方块
public Block(byte id, string name, byte textureX, byte textureY, byte textureTopX, byte textureTopY, byte textureBottomX, byte textureBottomY)
: this(id, name, textureX, textureY, textureX, textureY, textureX, textureY, textureX, textureY, textureTopX, textureTopY, textureBottomX, textureBottomY)
{
} //上面是A,下面是B,前面是C,其他面是D的方块
public Block(byte id, string name, byte textureFrontX, byte textureFrontY, byte textureX, byte textureY, byte textureTopX, byte textureTopY, byte textureBottomX, byte textureBottomY)
: this(id, name, textureFrontX, textureFrontY, textureX, textureY, textureX, textureY, textureX, textureY, textureTopX, textureTopY, textureBottomX, textureBottomY)
{
} //上下左右前后面都不一样的方块
public Block(byte id, string name, byte textureFrontX, byte textureFrontY, byte textureBackX, byte textureBackY, byte textureRightX, byte textureRightY,
byte textureLeftX, byte textureLeftY, byte textureTopX, byte textureTopY, byte textureBottomX, byte textureBottomY)
{
this.id = id;
this.name = name; this.textureFrontX = textureFrontX;
this.textureFrontY = textureFrontY; this.textureBackX = textureBackX;
this.textureBackY = textureBackY; this.textureRightX = textureRightX;
this.textureRightY = textureRightY; this.textureLeftX = textureLeftX;
this.textureLeftY = textureLeftY; this.textureTopX = textureTopX;
this.textureTopY = textureTopY; this.textureBottomX = textureBottomX;
this.textureBottomY = textureBottomY;
}
}
最上面我们定义了方块方向,不过我们暂时不会用到,先放着。
然后我们定义了方块的基础信息,还有它各个面上的贴图。
重载了多个构造函数来生成方块,都是A面的那个应该比较常用。
贴图的坐标是0~1的float,我们把一张贴图划分成了32×32的(如下图),每一个瓦片宽高都是1/32,Block的贴图坐标就是这么算出来的。

图是高清图,右键保存下来,拖给Chunk的预制体
二、新建BlockList类
BlockList类,用来管理我们所有的Block,我们先简单地用它生成一个土块。
using System.Collections.Generic;
using UnityEngine; /// <summary>
/// 存储所有的Block对象的信息
/// </summary>
public class BlockList : MonoBehaviour
{
public static Dictionary<byte, Block> blocks = new Dictionary<byte, Block>(); void Awake()
{
Block dirt = new Block(, "Dirt", , );
blocks.Add(dirt.id, dirt);
} public static Block GetBlock(byte id)
{
return blocks[id];
}
}
这里Block dirt = new Block(1, "Dirt", 2, 31)中的2,31是以左下角为起点开始数的,左下角的贴图是0,0,所以2,31是我们最顶部第3个瓦片
三、修改Chunk,为Block添加UV坐标
我们新建一个private List<Vector2> uv = new List<Vector2>(),用来存储uv点的信息,它和vertices的用法是一样的,我们也是从左下角开始,逆时针循环。

我们按照这个顺序添加点就可以了
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>();
//所有的uv信息
private List<Vector2> uv = new List<Vector2>();
//uv贴图每行每列的宽度(0~1),这里我的贴图是32×32的,所以是1/32
public static float textureOffset = / 32f;
//让UV稍微缩小一点,避免出现它旁边的贴图
public static float shrinkSize = 0.001f; //当前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++)
{
//获取当前坐标的Block对象
Block block = BlockList.GetBlock(this.blocks[x, y, z]);
if (block == null) continue; if (IsBlockTransparent(x + , y, z))
{
AddFrontFace(x, y, z, block);
}
if (IsBlockTransparent(x - , y, z))
{
AddBackFace(x, y, z, block);
}
if (IsBlockTransparent(x, y, z + ))
{
AddRightFace(x, y, z, block);
}
if (IsBlockTransparent(x, y, z - ))
{
AddLeftFace(x, y, z, block);
}
if (IsBlockTransparent(x, y + , z))
{
AddTopFace(x, y, z, block);
}
if (IsBlockTransparent(x, y - , z))
{
AddBottomFace(x, y, z, block);
}
}
}
} //为点和index赋值
mesh.vertices = vertices.ToArray();
mesh.triangles = triangles.ToArray();
mesh.uv = uv.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, Block block)
{
//第一个三角面
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)); //添加UV坐标点,跟上面4个点循环的顺序一致
uv.Add(new Vector2(block.textureFrontX * textureOffset, block.textureFrontY * textureOffset) + new Vector2(shrinkSize, shrinkSize));
uv.Add(new Vector2(block.textureFrontX * textureOffset + textureOffset, block.textureFrontY * textureOffset) + new Vector2(-shrinkSize, shrinkSize));
uv.Add(new Vector2(block.textureFrontX * textureOffset + textureOffset, block.textureFrontY * textureOffset + textureOffset) + new Vector2(-shrinkSize, -shrinkSize));
uv.Add(new Vector2(block.textureFrontX * textureOffset, block.textureFrontY * textureOffset + textureOffset) + new Vector2(shrinkSize, -shrinkSize));
} //背面
void AddBackFace(int x, int y, int z, Block block)
{
//第一个三角面
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)); //添加UV坐标点,跟上面4个点循环的顺序一致
uv.Add(new Vector2(block.textureBackX * textureOffset, block.textureBackY * textureOffset) + new Vector2(shrinkSize, shrinkSize));
uv.Add(new Vector2(block.textureBackX * textureOffset + textureOffset, block.textureBackY * textureOffset) + new Vector2(-shrinkSize, shrinkSize));
uv.Add(new Vector2(block.textureBackX * textureOffset + textureOffset, block.textureBackY * textureOffset + textureOffset) + new Vector2(-shrinkSize, -shrinkSize));
uv.Add(new Vector2(block.textureBackX * textureOffset, block.textureBackY * textureOffset + textureOffset) + new Vector2(shrinkSize, -shrinkSize));
} //右面
void AddRightFace(int x, int y, int z, Block block)
{
//第一个三角面
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)); //添加UV坐标点,跟上面4个点循环的顺序一致
uv.Add(new Vector2(block.textureRightX * textureOffset, block.textureRightY * textureOffset) + new Vector2(shrinkSize, shrinkSize));
uv.Add(new Vector2(block.textureRightX * textureOffset + textureOffset, block.textureRightY * textureOffset) + new Vector2(-shrinkSize, shrinkSize));
uv.Add(new Vector2(block.textureRightX * textureOffset + textureOffset, block.textureRightY * textureOffset + textureOffset) + new Vector2(-shrinkSize, -shrinkSize));
uv.Add(new Vector2(block.textureRightX * textureOffset, block.textureRightY * textureOffset + textureOffset) + new Vector2(shrinkSize, -shrinkSize));
} //左面
void AddLeftFace(int x, int y, int z, Block block)
{
//第一个三角面
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)); //添加UV坐标点,跟上面4个点循环的顺序一致
uv.Add(new Vector2(block.textureLeftX * textureOffset, block.textureLeftY * textureOffset) + new Vector2(shrinkSize, shrinkSize));
uv.Add(new Vector2(block.textureLeftX * textureOffset + textureOffset, block.textureLeftY * textureOffset) + new Vector2(-shrinkSize, shrinkSize));
uv.Add(new Vector2(block.textureLeftX * textureOffset + textureOffset, block.textureLeftY * textureOffset + textureOffset) + new Vector2(-shrinkSize, -shrinkSize));
uv.Add(new Vector2(block.textureLeftX * textureOffset, block.textureLeftY * textureOffset + textureOffset) + new Vector2(shrinkSize, -shrinkSize));
} //上面
void AddTopFace(int x, int y, int z, Block block)
{
//第一个三角面
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)); //添加UV坐标点,跟上面4个点循环的顺序一致
uv.Add(new Vector2(block.textureTopX * textureOffset, block.textureTopY * textureOffset) + new Vector2(shrinkSize, shrinkSize));
uv.Add(new Vector2(block.textureTopX * textureOffset + textureOffset, block.textureTopY * textureOffset) + new Vector2(-shrinkSize, shrinkSize));
uv.Add(new Vector2(block.textureTopX * textureOffset + textureOffset, block.textureTopY * textureOffset + textureOffset) + new Vector2(-shrinkSize, -shrinkSize));
uv.Add(new Vector2(block.textureTopX * textureOffset, block.textureTopY * textureOffset + textureOffset) + new Vector2(shrinkSize, -shrinkSize));
} //下面
void AddBottomFace(int x, int y, int z, Block block)
{
//第一个三角面
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)); //添加UV坐标点,跟上面4个点循环的顺序一致
uv.Add(new Vector2(block.textureBottomX * textureOffset, block.textureBottomY * textureOffset) + new Vector2(shrinkSize, shrinkSize));
uv.Add(new Vector2(block.textureBottomX * textureOffset + textureOffset, block.textureBottomY * textureOffset) + new Vector2(-shrinkSize, shrinkSize));
uv.Add(new Vector2(block.textureBottomX * textureOffset + textureOffset, block.textureBottomY * textureOffset + textureOffset) + new Vector2(-shrinkSize, -shrinkSize));
uv.Add(new Vector2(block.textureBottomX * textureOffset, block.textureBottomY * textureOffset + textureOffset) + new Vector2(shrinkSize, -shrinkSize));
}
}
}
在CreateMesh方法中我们获取到了当前的Block对象,然后在绘制面的时候,根据Block的信息绘制了它对应的贴图。
需要注意的是,每一个uv点,我们都加了一个shrinkSize的偏差值,这是因为如果紧贴着边缘获取瓦片的话,当前的瓦片可能多取到旁边瓦片的1像素,形成锯齿

我们添加了这个偏差值以后就好了


我们加了偏差值以后,是少取了这个瓦片外面的一圈,这样即使某些情况下多取一像素也没事。
【Unity3D】Unity3D开发《我的世界》之四、创建一个Block的更多相关文章
- 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. ...
随机推荐
- 【转】Matlab中的括号()[] {}
Matlab中经常会用到括号去引用某Array或者是cell的内容,但三者有什么具体区别呢?] []
- Redis进阶实践之九 独立封装的RedisClient客户端工具类
一.引言 今天开始有关Redis学习的第九篇文章了,以后肯定会大量系统使用Redis作为缓存介质,为了更好的更好的Redis,自己写了两个工具类,但是这两个工具类,没有提供一致的接口,是为了使用的独立 ...
- java indexof、BigDecimal、字符串替换
自我总结,有什么需要改正弥补的地方,请指出,感激不尽! 本次总结了indexof的用法,BigDecimal的乘法.移位运算,Decimal的格式化输出,字符串替换 上代码: 测试类 Test.jav ...
- Web安全之CSP
内容安全策略(Content-Security-Policy,简称CSP) 概念: 内容安全策略(CSP)是一种web应用技术用于帮助缓解大部分类型的内容注入攻击,包括XSS攻击和数据注入等,这些攻击 ...
- vs2012编译在win7 32位电脑和win xp电脑上运行的win32程序遇到的问题记录
一.win7 32位电脑: vs2012编译的64位程序是没有问题的.但编译的32位程序在别的电脑(虚拟机模拟)出错: 感觉很无语,vs这么牛逼的东西,在设计时候都不考虑这些吗? 在自己电脑C:\Wi ...
- MySQL死锁案例分析与解决方案
MySQL死锁案例分析与解决方案 现象: 数据库查询: SQL语句分析: mysql. 并发delete同一行记录,偶发死锁. delete from x_table where id=? ...
- ImageMagick命令行工具
[ convert | identify | mogrify | composite | montage | compare | display | animate | import |conjure ...
- 关于C#连接Oracle数据库 尝试加载Oracle客户端时引发BadImageFormatException 如果在安装32位Oracle客户端组件的情况下以64位模式运行,将出现此问题
这个问题已经困扰了我快一个月了,各种百度,各种博客,可是,一个个都试过了,什么下载32位客户端,配置环境变量什么的,纯属扯犊子,开发环境win10 64位 oracle 11g r2 64位,这 ...
- 文件锁FileLock
1.文件锁的定义 FileLock是文件锁,进程锁,用于进程间并发,控制不同程序(JVM)对同一文件的并发访问. FileLock是java 1.4 版本后出现的一个类,它可以通过对一个可写文件(w) ...
- NIO内存映射
磁盘的IO因为速度较慢,可能成为系统运行的瓶颈.所以磁盘的IO在操作系统级实现了提前读,延迟写的机制来提升IO的性能. 提前读就是一次读取需求的数据的同时多读接下来的一段数据至OS缓冲区中,延迟写就是 ...