《乡村铁路》开发日志1-地块
前言
一看时间,距离第0篇发布已经过了超过一个月了😆,最近忙于享受福报,每天回到家就很晚了,完全没时间推动进度orz。
总之现在的功能还十分简陋,每篇就只写一个模块吧,这次是地块。
地块
其实这一段时间我倒是啥折腾弄出了个Wiki页面,有关地块的在这里。这个Wiki是用GitBook生成的,功能目前也完全能满足我的需求,之后每做一个系统啥的就(可能)会在Wiki上及时更新文档说明。一方面是仪式感(误),另一方面就是这个项目就算是能做出来,周期也会相当长,到后面我怕自己都忘记最初的一些设定了,也算是个笔记吧~
基本概念
在默认情况下,整个地图的尺寸为100米x100米的矩形,其中每个地块的尺寸为1米x1米,也就是说整个地图一共10,000个地块。当然现在100x100的尺寸也暂定的,初期所有的测试都是基于这个尺寸的地图,到时候根据实际情况(比如建筑摆上去之后规模不合适)可以调整。
地块目前有三种状态:室外地板,墙壁,室内地板。也就是说我们的墙都是厚度1米的厚墙233,也是为了方便建设和管理。
墙壁支持多方向,也就是根据上下左右四个方向有没有墙壁来更换当前的墙壁模型,这个东西用文字不太好描述,直接看图就很清楚了:
- 左图:单独的墙壁
- 中图:一个房间
- 右图:更复杂的连接关系
可以看到,不同形状的墙壁组合会调用不同的模型。
地块的内容就只有这些,其他的所有东西(角色,设施,植物等)都作为单独的实体独立运行。
实现细节
虽然ECS的设计原则有一点就是不要在系统中保存状态,但是为了方便根据坐标获取到对应的实体,我还是用了一个很Magic的写法:
1 | public class TileMapSystem : ComponentSystem |
然后在OnCreateManager
方法里创建这个持久化存储的数组:AllTileEntity = new NativeArray<Entity>(mapWidth * mapHeight, Allocator.Persistent);
。然后记得在OnDestroyManager
方法里销毁就行了。
这里插一段,其实本来(也应该)是要把这个
NativeArray
放到单例组件里的,不过好像IComponentData
里不能存NativeArray
类型,如果想实现数组的话需要用到DynamicBuffer
,这个还没去研究,之后有空了还是要把这块重构掉的。
其中地块实体在游戏过程中只有这些,不会增减,于是就可以通过坐标(用x+y*Width
对应一个整数)直接取到每一个实体了。
目前实现的功能只有点击鼠标左键造墙和点击鼠标右键拆墙。
造墙拆墙
TileMapSystem
的OnUpdate
方法中做了两件事情:处理鼠标点击事件和更新地块的模型。
因为设定是点击鼠标左键的时候在鼠标指向的地块造一堵墙,这就需要射线检测了,当时UnityECS的物理系统还没出来(现在有了,之后需要重构掉),就用Hybrid的方式,在场景中创建了10000个带有Collider
组件的GameObject
,在另外一个叫做ClickableSystem
的系统中处理所有的点击事件,传递到TileMapSystem
中进行处理。
找到这一帧鼠标点击的地块,如果存在且该地块的状态是室外
的话,则将这个地块的状态设置为墙壁
,并创建一个TileChangeRequest
的组件,添加到地块实体中,用于下一步处理。
模型更新
这一步遍历所有含有TileChangeRequest
组件的地块实体,并把对应的模型更换掉,如果这个地块变成了墙或者从墙变成了其他类型,那么则需要更新这个地块上下左右相邻的一共5个地块的状态,原因就是上面提到的多方向墙壁模型。
另外,地图的边缘一圈地块模型是除了地面之外的完成5个面,除了这一圈,里面所有的地块都是没有地平线以下的部分的,地板类型的地块其实就是一个上表面。而墙壁和草地在边缘的模型是不一样的,草地是一整块,而墙壁是普通的墙壁外加一个通用地基组合出来的。所以就还需要在模型变化的时候同时对地基进行特殊处理。
下期预告
目前的进度很慢,下一篇(不知道啥时候了233)打算记录一下摄像机相关的东西。