转载请注明出处:http://www.cnblogs.com/shamoyuu/p/unity_minecraft_05.html

一、导入Unity3D自带的第一人称角色控制器

直接导入就行,我们用FPSController。

二、为Map添加创建Chunk和判断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;
} //生成Chunk
public void CreateChunk(Vector3i pos)
{
if (spawningChunk) return; StartCoroutine(SpawnChunk(pos));
} private IEnumerator SpawnChunk(Vector3i pos)
{
spawningChunk = true;
Instantiate(chunkPrefab, pos, Quaternion.identity);
yield return null;
spawningChunk = false;
} //通过Chunk的坐标来判断它是否存在
public bool ChunkExists(Vector3i worldPosition)
{
return this.ChunkExists(worldPosition.x, worldPosition.y, worldPosition.z);
}
//通过Chunk的坐标来判断它是否存在
public bool ChunkExists(int x, int y, int z)
{
return chunks.ContainsKey(new Vector3i(x, y, z));
}
}
}

上上一章用来测试的Start方法也删掉了,我们下面会通过玩家的位置来生成

三、添加草方块

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); Block grass = new Block(, "Grass", , , , , , );
blocks.Add(grass.id, grass);
} public static Block GetBlock(byte id)
{
return blocks.ContainsKey(id) ? blocks[id] : null;
}
}

四、修改Chunk

using Soultia.Util;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
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.ChunkExists(position))
{
Debug.Log("此方块已存在" + position);
Destroy(this);
}
else
{
Map.instance.chunks.Add(position, this.gameObject);
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++)
{
if (y == Chunk.height - )
{
if (Random.Range(, ) == )
{
blocks[x, y, z] = ;
}
}
else
{
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;
} //此坐标方块是否透明,Chunk中的局部坐标
public bool IsBlockTransparent(int x, int y, int z)
{
if (x >= width || y >= height || z >= width || x < || y < || z < )
{
return true;
}
else
{
//如果当前方块的id是0,那的确是透明的
return this.blocks[x, y, z] == ;
}
} //前面
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));
}
}
}

我们修改了Chunk的CreateMap方法,让它在最顶部有一定几率生成草方块,便于我们直观地看到无限地形的生成。

然后修改了Start方法,判断了它是否存在,如果不存在,就把它添加到Map的chunks里,如果已经存在了,就销毁它。

五、添加PlayerController

这个对象就是用来检测玩家周围一定范围内的Chunk是否已经生成,如果没有生成就会生成它。

using Soultia.Util;
using Soultia.Voxel;
using UnityEngine; public class PlayerController : MonoBehaviour
{
//视线范围
public int viewRange = ; void Update()
{
for (float x = transform.position.x - Chunk.width * ; x < transform.position.x + Chunk.width * ; x += Chunk.width)
{
for (float z = transform.position.z - Chunk.width * ; z < transform.position.z + Chunk.width * ; z += Chunk.width)
{
int xx = Chunk.width * Mathf.FloorToInt(x / Chunk.width);
int zz = Chunk.width * Mathf.FloorToInt(z / Chunk.width);
if (!Map.instance.ChunkExists(xx, , zz))
{
Map.instance.CreateChunk(new Vector3i(xx, , zz));
}
}
}
}
}

然后把它拖给玩家

到这里就已经可以生成无限地形了,按住Shift一直跑,已经可以当成小小的跑酷游戏玩了~

【Unity3D】Unity3D开发《我的世界》之五、创建无限地形(视频)的更多相关文章

  1. unity3d游戏开发学习之使用3dmax创建导弹模型

    在着手研究Unity3D的游戏开发时,3D模型能够考虑从unity的assets store去获取,也能够从网上搜索下载,同一时候咱们也能够尝试下自己动手去做一些简单的模型. 这里就依据unity3d ...

  2. C#程序员的春天之从零开始学习unity3D游戏开发入门教程二(创建项目及基本面板介绍)

    一项目创建: 创建项目是开发的第一步. 运行untiy之后如果是第一次运行会弹出 我们这里随便创建一个项目. 二Untiy面板介绍: 三代码编辑器的切换: 这里我安装了vs2012. 到这里开发环境基 ...

  3. Unity3D游戏开发初探—1.跨平台的游戏引擎让.NET程序员新生

    一.Unity3D平台简介 Unity是由Unity Technologies开发的一个让轻松创建诸如三维视频游戏.建筑可视化.实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的 ...

  4. [Unity3D]Unity3D游戏开发《反对》说到游戏(上)——目标跟踪

    朋友,大家好.我是秦培,欢迎关注我的博客.我的博客地址blog.csdn.net/qinyuanpei. 首先博主要自我反省,过了这么久才来更新博客,这段时间主要是在忙着写期末的作业,所以博主基本上没 ...

  5. [Unity3D]Unity3D游戏开发之跑酷游戏项目解说

    大家好,我是秦元培.我參加了CSDN2014博客之星的评选,欢迎大家为我投票,同一时候希望在新的一年里大家能继续支持我的博客. 大家晚上好.我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.c ...

  6. [Unity3D]Unity3D游戏开发之伤害数值显示

    大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是blog.csdn.net/qinyuanpei.众所周知,在RPG游戏策划中最为重要的一个环节是数值策划.数值策划是一个关于游戏平衡方面的概念 ...

  7. Unity3D游戏开发初探—2.初步了解3D模型基础

    一.什么是3D模型? 1.1 3D模型概述 简而言之,3D模型就是三维的.立体的模型,D是英文Dimensions的缩写. 3D模型也可以说是用3Ds MAX建造的立体模型,包括各种建筑.人物.植被. ...

  8. 从一点儿不会开始——Unity3D游戏开发学习(一)

    一些废话 我是一个windows phone.windows 8的忠实粉丝,也是一个开发者,开发数个windows phone应用和两个windows 8应用.对开发游戏一直抱有强烈兴趣和愿望,但奈何 ...

  9. Unity3D游戏开发之连续滚动背景

    Unity3D游戏开发之连续滚动背景 原文  http://blog.csdn.net/qinyuanpei/article/details/22983421 在诸如天天跑酷等2D游戏中,因为游戏须要 ...

随机推荐

  1. Docker之镜像

    镜像(Images) 镜像是Docker的三大核心之一,类似于虚拟机,作用和虚拟机是一样的,唯独是组成部分会有些区别.简单的说如果我们想启动一个容器就必须要有镜像.docker运行容器前需要本地存在对 ...

  2. oracle pl/sql如何定义变量

    目的:如何在pl/sql中定义基本类型,引用类型,记录型变量? 以下plsql程序用的scott用户的dept,emp表. 定义基本类型的变量 set serveroutput on ; --使用基本 ...

  3. office2013使用空格符

    空格符:所有的空格都以点表示出来. 作用:挑出空格符后,可以很方便的看到所有的空格,分页符等,使页面更容易调整的工整(在写正规的word文档时,需要这样做). 用法:很简单,根据图片上操作的顺序即可调 ...

  4. spring之p命名空间注入

    <bean id="personId" class="com.itheima.f_xml.c_p.Person" p:pname="禹太璞&qu ...

  5. Call to undefined function mysql_connect()错误原因

    从PHP5.0开始就不推荐使用mysql_connect()函数,到了php7.0则直接废弃了该函数,替代的函数是: mysqli_connect();

  6. [eslint-plugin-vue] [vue/no-unused-vars] 'scope' is defined but never used.

    前言 今天在做项目的时候Visual Studio Code报了一个错 这个错的意思是声明了scope却没有使用它,这是vue的eslink插件检测的. 我想这个scope的属性不是自己的吗,咋是我声 ...

  7. HDU [P2819] swap

    二分图行列匹配+输出路径 经典题,当且仅当一行匹配一列的时候,符合题意. 本题的难点在于如何输出路径,我们发现这个移动的过程就是将所有匹配选择排序,在选择排序时输出路径即可 #include < ...

  8. BZOJ 2006: [NOI2010]超级钢琴 [ST表+堆 | 主席树]

    题意: 一个序列,求k个不相同的长度属于\([L,R]\)的区间使得和最大 前缀和,对于每个r找最小的a[l] 然后我yy了一个可持久化线段树做法...也许会T 实际上主席树就可以了,区间k小值 然后 ...

  9. ES6的Symbol

    let s = Symbol(); alert(typeof(s)); // Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比 ...

  10. Python数据结构之二——tuple(元组)

    Python版本:3.6.2  操作系统:Windows  作者:SmallWZQ 列表和元组是Python中最常见的内建序列.元组与列表一样,但是tuple一旦创建就不能修改.创建元组的语法非常简单 ...