欢迎您来到有条有理(上海)道具有限公司网站!
有条有理(上海)道具有限公司
7X24小时服务热线:

155-8880-8889

热门关键词:上海雕塑 上海3D打印 美陈制作 广告道具 浮雕制作 道具制作 橱窗展示

联系我们

有条有理(上海)道具有限公司

联系人:韦经理
邮 箱:1156743471@qq.com
电 话:155-8880-8889
地 址:上海市青浦区纪鹤公路5348号北3层

您的位置:首页 >> 新闻资讯 >> 常见问题

图形界面大世界地图聚合

发布时间:2022-06-18 14:18:12浏览:6967点赞:

那次由Meta42撰写的《300行标识符同时实现Minecraft(我的当今世界)大当今世界地图聚合》受了大批雅雷,并赢得了创举

书名镜像:https://zhuanlan.zhihu.com/p/28730967

我先对并科学研究了该文中的标识符,作出了一点儿改良——在当今世界地图上点选会剪除两个魔方。那个新机能这类更动十分小,但可能将正好是听众们萌妹以期的机能,因此把方式撷取给他们,Jaunpur,他们亲自动手作出Minecraft为期不远 ?

除此之外,为的是合乎本时评才华出众的特征,责任编辑分两部份讲,第三部份带着萌新们尽量看懂Meta42天神加进的标识符可视化控制技术,第三部份做标识符预测并更动,让当今世界地图能被掘出坑来。剑客们能间接翻阅第三部份。

1、用标识符创建3D数学模型的基本上原理

他们极难透过一则十分钟的该文专业委员会流程3D可视化的方式。但我想透过单纯的相片和表明,让他们认知许多基本上的基本上概念,进而约莫看懂Minecraft的大当今世界地图聚合的大体关键步骤。

如何用脚本画出两个3D立方体呢?

1、确定某个顶点的位置,由于边长固定,其他顶点位置很容易确定。

3、画三角形,两个正方形面由两个三角形构成,如图:

对应工程中的标识符为:

voidBuildFace(BlockTypetypeid,Vector3corner,Vector3up,Vector3right,boolreversed,List<Vector3>verts,List<Vector2>uvs,List<int>tris){// index是之前画过的所有的顶点的总数,因为还有其它面的存在intindex=verts.Count;// up和right分别是向上和向右的单位方向向量,正方形很容易确定四个顶点的位置verts.Add(corner);verts.Add(corner+up);verts.Add(corner+up+right);verts.Add(corner+right);// UV坐标和贴图有关, 他们先不管它Vector2uvWidth=newVector2(0.25f,0.25f);…………省略UV的部份…………// 顶点顺序不同,就朝向不同的方向(3D的面只能从两个方向看,另两个方向看不见)// 比如立方体和上面朝上,下面朝下。上面只能从上面看见,下面要从下面才能看见// 如果两个三角形的顶点0 1 2是顺时针,1 0 2就是逆时针。if(reversed){tris.Add(index+0);tris.Add(index+1);tris.Add(index+2);tris.Add(index+2);tris.Add(index+3);tris.Add(index+0);}else{tris.Add(index+1);tris.Add(index+0);tris.Add(index+2);tris.Add(index+3);tris.Add(index+2);tris.Add(index+0);}// 以上标识符把顶点都加入verts列表,把顶点序号都放入了tris列表。之后由其他函数// 把所有的顶点和序号信息都传给渲染器}

我给这段标识符加上了详细的注释,能看到,要点就是顶点和顶点顺序,以下表明顶点顺序存在的意义:

比如那个正方形,有两个三角形,理论上来说一共有6个顶点。但由于3D数学模型有大批顶点是公共的,因此渲染的时候都是尽量复用顶点提高效率。上图只需要4个顶点既可,但要再用两个列表指定顺序,比如0、1、2、2、3、0,就能画出两个三角面了。那么如果逆时针画1、0、2、3、2、0,也能画出来。但那个面的方向是反的。

什么是面的正反方向呢?他们只要在Unity里随便拉两个Plane平面,然后从高处往下和低处往上看,自然就明白了。

从低处往上看,Plane消失了,只能看到选中的轮廓。这表明3D的面是有正反之分的。

明白了BuildFace函数在干嘛,再看BuildBlock函数就单纯了,因为BuildBlock函数就是调用了6次BuildFace函数,分别画立方体的上下左右前后六个面而已。但在画面之前,先判断了该面是不是能跳过不画:

if(CheckNeedBuildFace(x-1,y,z))BuildFace(typeid,newVector3(x,y,z),Vector3.up,Vector3.forward,false,verts,uvs,tris);

然后呢,BuildChunk来调用BuildBlock聚合每两个魔方,那个流程大部份是固定的写法,我也写了详细注释。

publicvoidBuildChunk(){// 新建两个Mesh,也就是网格chunkMesh=newMesh();// 创建三个List,存放所有的顶点、UV、序号。数量可能将十分多List<Vector3>verts=newList<Vector3>();List<Vector2>uvs=newList<Vector2>();List<int>tris=newList<int>();//遍历chunk, 聚合其中的每两个Blockfor(intx=0;x<width;x++){for(inty=0;y<height;y++){for(intz=0;z<width;z++){BuildBlock(x,y,z,verts,uvs,tris);}}}// 把List里面的所有信息送到网格里面chunkMesh.vertices=verts.ToArray();chunkMesh.uv=uvs.ToArray();chunkMesh.triangles=tris.ToArray();//固定写法,网格刷新并赋值给相应的Unity组件meshFilter和meshColliderchunkMesh.RecalculateBounds();chunkMesh.RecalculateNormals();meshFilter.mesh=chunkMesh;meshCollider.sharedMesh=chunkMesh;}

到此为止就差不多啦,作为暂时不想深入可视化细节的听众,以上解说加上注释,已经足够表明用标识符来组装网格的整体思路了。如果你透过阅读以上一段内容对Mesh的构建和使用有了一点儿感性的认识,我就算没白写这么多?

2、预测标识符结构,加入挖坑逻辑

其实Meta42给出的可视化方式,是完全支持Minecraft那样的挖坑效果的。同时实现挖坑不难,但预测标识符的过程必不可少。我花了不少时间搞清楚了原来的标识符逻辑,而最终的修改比想象中单纯的多。

首先,整个Unity工程的核心脚责任编辑件只有两个,就是Chunk.cs。Chunk类代表了Chunk对象,也就是20 * 20魔方组成的两个大格子。他们的标识符全都是针对某个Chunk这类进行操作的,但注意,只有个别static字段和方式,是全局唯一的,它们是:

publicstaticList<Chunk>chunks=newList<Chunk>();publicstaticChunkGetChunk(Vector3wPos)

chunks列表整个当今世界只有两个,用于存放所有Chunk。而某两个Chunk中的所有魔方Block,则是存储在那个字段里:

BlockType[,,]map;

单纯地说,整个当今世界是由一系列Chunk组成,每个Chunk里有个20*20*20的三维数组,存放着每两个格子Block的情况。他们只要修改BlockType[,,] map那个数组里面某两个格子map[i,j,k] = None,它就变成了空(也就是被剪除了),同理,只要设置某个坐标为Grass,它就变成了草地,就是这么单纯。

注:修改完map的数据后,只要调用BuildChunk() 方式即可生效。因此挖坑方式很单纯:

publicboolDig(intx,inty,intz){map[x,y,z]=BlockType.None;BuildChunk();returntrue;}

有了那个Dig函数,理论上你就能间接指定x y z挖坑了。现在还缺一步鼠标点选操作。

3、鼠标操作挖坑

他们不在原来的Player.cs脚本里乱改了,新建两个HitBlock.cs来处理鼠标点选。该脚本挂在Player的GameObject上。

publicclassHitBlock:MonoBehaviour{voidUpdate(){if(Input.GetMouseButtonDown(0)){// 3D游戏点选屏幕都是发射射线来做,不再赘述Rayray=Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHithitt=newRaycastHit();Physics.Raycast(ray,outhitt,1000);if(hitt.transform!=null){// 根据3D空间中的点坐标,赢得对应的大Chunk对象,Chunk里已经提供了该方式Debug.Log("hitt.point="+hitt.point);Chunkchunk=Chunk.GetChunk(hitt.point);// chunk.transform.position 其实是整块Chunk底面最角上一点儿的坐标,也就是该Chunk的原点// hitt.point是当今世界坐标,减去chunk的起点,就是对该chunk来说的局部坐标Vector3v=hitt.point-chunk.transform.position;// Unity能间接获取碰撞点的法线// 根据法线方向就知道了你点选的是侧面还是上面。那个地方坐标换算需要仔细考虑if(Mathf.Abs(hitt.normal.y)>0.01){Debug.Log("上表面");chunk.Dig((int)v.x,(int)v.y-1,(int)v.z);}else{Debug.Log("侧面");chunk.Dig((int)v.x,(int)v.y,(int)v.z);}// 注:我没有同时实现底面,其实确实存在从下往上挖的情况,再说吧 XD}}}}

那个脚本的要点:

1、发射射线,赢得点选的点的3D坐标。

2、用Chunk.GetChunk()函数能查找到点选的点是对应哪个Chunk。注:Chunk.GetChunk函数是遍历所有Chunk查找的,显然没有必要、能优化,不过对责任编辑来说无所谓。

4、根据是上面还是侧面,能算出到底玩家想挖哪两个魔方。调用之前写的Chunk.Dig方式即可。

完成 ≖‿≖✧~~看看效果:

总结

责任编辑初衷是修改和扩展他们喜爱的两个游戏原型,但写完发现大部份篇幅都是对上一则内容的讲解和预测,不过这样也挺好,合乎咱们时评的定位。

其实修改和维护别人写的标识符,也是一项很常见的工作。改良别人的标识符其实不比他们从头撰写单纯,因此这项工作也不太受人欢迎,但相信我,这项工作十分重要?。

本篇中3D数学模型聚合的控制技术属于高级控制技术,但其实难度并不很大,希望此文能引起游戏开发爱好者们的兴趣。 (•̀ᴗ•́) ̑̑


对游戏开发感兴趣的同学,欢迎围观他们:【Levelplay皮皮关游戏开发教育】 ,会定期更新各种教程干货,更有别具一格的线下小班教育。

他们的游戏开发控制技术交流群:610475807

在线客服
服务热线

服务热线

155-8880-8889

微信咨询
有条有理(上海)道具有限公司
返回顶部