您当前的位置: 首页 > 

CloudHu1989

暂无认证

  • 5浏览

    0关注

    89博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

用ECS做HexMap:刷子难题

CloudHu1989 发布时间:2019-09-02 14:46:52 ,浏览量:5

基于Unity2019最新ECS架构开发MMO游戏笔记24
    • 准备工作
    • 前言
    • 更大的地图
      • ECS专题目录
      • ECS更新计划
      • 作者的话

本篇效果

准备工作

如果大佬对ECS版的海克斯无限地图感兴趣,不妨参与进来,欢迎Star/Folk源码 0下载Unity编辑器(2019.1.12f1 or 更新的版本),if(已经下载了)continue; 1克隆:git clone https://github.com/cloudhu/HexMapMadeInUnity2019ECS.git --recurse 或下载Zip压缩包 2如果下载的是压缩包,需要先将压缩包解压。然后将HexMapMadeInUnity2019ECS添加到Unity Hub项目中; 3用Unity Hub打开的开源项目:HexMapMadeInUnity2019ECS,等待Unity进行编译工作; 4打开项目后,启动场景在Scenes目录下,打开AdvancedHexMap场景(优化重构的版本)。

前言

首先对地图做了扩展,由原来的一个Mesh,增加到Mesh矩阵。由于我们需要物理组件MeshCollider,这个之前已经提到过了,ECS还没有物理引擎支持,所以目前被迫选择混合开发,这很尴尬,预计明年会有改善。这段时间用ECS开发,我越加觉得面向对象太好用了,简直就是人性化开发。而ECS更偏机器一些,所以反人类,很多东西需要特别去设计数据,否则操作起来特别麻烦,这是我的亲身感受。本来Hex Map就有OOP的实现,无疑是更容易理解的版本,更好做自定义。 总之,我越用ECS,越想弃坑,这也是这两天拖更的原因,陷入深切的自我怀疑,究竟该不该脱坑? 不管怎样,混合开发并非长久之计,如果真的想要开发自己的游戏,这样做实在太冒险了。为了一点性能优势,难道就牺牲整个项目吗?真是食之无味,弃之可惜,过渡阶段的ECS就是这样的鸡肋。 吐槽就到这里吧,也许我根本不应该选择用ECS来做无限地图的开发。

更大的地图

前言中说了这么多,就是为了下面的OOP部分做铺垫,更大的地图,意味着不止一个Mesh(Unity中Mesh数组最大能存储65000个顶点),于是我开始改造OOP的结构,新建HexGrid脚本,用来管理地图块Chunk:

/// 
/// 地图网格,管理地图块
/// 
public class HexGrid : MonoBehaviour
{

    /// 
    /// 地图块的数量
    /// 
    public int chunkCountX = 4, chunkCountZ = 3;

    /// 
    /// 噪声采样纹理图
    /// 
    public Texture2D noiseSource;

    /// 
    /// 地图块预设
    /// 
    public HexGridChunk chunkPrefab;

    /// 
    /// 地图宽度(以六边形为基本单位)
    /// 
    private int cellCountX;

    /// 
    /// 地图长度(以六边形为基本单位)
    /// 
    private int cellCountZ;
    /// 
    /// 地图块数组
    /// 
    HexGridChunk[] chunks;

    #region Mono

    private void Awake()
    {
        cellCountX = chunkCountX * HexMetrics.chunkSizeX;
        cellCountZ = chunkCountZ * HexMetrics.chunkSizeZ;
        HexMetrics.noiseSource = noiseSource;
        CreateChunks();
    }

    void OnEnable()
    {
        HexMetrics.noiseSource = noiseSource;
    }

    // Start is called before the first frame update
    void Start()
    {
        MainWorld.Instance.SetupMap(cellCountX, cellCountZ,chunkCountX);
    }

    #endregion
    /// 
    /// 添加单元到地图块
    /// 
    /// 地图块编号
    /// 地图块索引
    /// 单元索引
    /// 单元
    public void AddCellToChunk(int chunkId,int chunkIndex,int cellIndex,Entity cell)
    {
        HexGridChunk chunk = chunks[chunkId];
        chunk.AddCell(chunkIndex,cellIndex, cell);
    }

    /// 
    /// 刷新地图块
    /// 
    /// 地图块编号
    public void Refresh(int chunkId)
    {
        HexGridChunk chunk = chunks[chunkId];
        chunk.Refresh();
    }

    /// 
    /// 更新地图块
    /// 
    /// 地图块编号
    /// 单元索引
    /// 颜色
    /// 海拔
    /// 是否受影响
    /// 刷子大小
    public void UpdateChunk( int chunkId, int cellIndex, Color color, int elevation,bool affected=false,int brushSize=0)
    {
        if (chunkId==int.MinValue)
        {
            return;
        }
        HexGridChunk chunk = chunks[chunkId];
        StartCoroutine(chunk.UpdateChunk(cellIndex,color,elevation,affected,brushSize));
    }

    /// 
    /// 创建地图块
    /// 
    void CreateChunks()
    {
        chunks = new HexGridChunk[chunkCountX * chunkCountZ];

        for (int z = 0, i = 0; z  int.MinValue )
            {
                if (GetChunkIndex(NEIndex) == int.MinValue)
                {
                    MainWorld.Instance.AffectedChunk(NEIndex, NEIndex, 0, false);
                }
            }
            if (neighborsIndexs.EIndex > int.MinValue && GetChunkIndex(neighborsIndexs.EIndex) == int.MinValue)
            {
                MainWorld.Instance.AffectedChunk(neighborsIndexs.EIndex, neighborsIndexs.EIndex, 0, false);
            }

            if (neighborsIndexs.SEIndex > int.MinValue && GetChunkIndex(neighborsIndexs.SEIndex) == int.MinValue)
            {
                MainWorld.Instance.AffectedChunk(neighborsIndexs.SEIndex, neighborsIndexs.SEIndex, 0, false);
            }

            if (neighborsIndexs.SWIndex > int.MinValue && GetChunkIndex(neighborsIndexs.SWIndex) == int.MinValue)
            {
                MainWorld.Instance.AffectedChunk(neighborsIndexs.SWIndex, neighborsIndexs.SWIndex, 0, false);
            }
            if (neighborsIndexs.WIndex > int.MinValue && GetChunkIndex(neighborsIndexs.WIndex) == int.MinValue)
            {
                MainWorld.Instance.AffectedChunk(neighborsIndexs.WIndex, neighborsIndexs.WIndex, 0, false);
            }

            if (neighborsIndexs.NWIndex > int.MinValue && GetChunkIndex(neighborsIndexs.NWIndex) == int.MinValue)
            {
                MainWorld.Instance.AffectedChunk(neighborsIndexs.NWIndex, neighborsIndexs.NWIndex, 0, false);
            }

            UpdateData data = new UpdateData
            {
                CellIndex = cellIndex,
                NewColor = color,
                Elevation = elevation,
                NEIndex=NEIndex,
                EIndex= neighborsIndexs.EIndex,
                SEIndex= neighborsIndexs.SEIndex,
                SWIndex= neighborsIndexs.SWIndex,
                WIndex= neighborsIndexs.WIndex,
                NWIndex= neighborsIndexs.NWIndex
            };
            for (int i = 0; i  int.MinValue && GetChunkIndex(cell.EIndex) == int.MinValue)
                    {
                        MainWorld.Instance.AffectedChunk(cell.EIndex, cellIndex, 0, true);
                        Debug.Log(cellIndex + "影响E:" + cell.EIndex);
                    }

                    if (cell.SEIndex > int.MinValue && GetChunkIndex(cell.SEIndex) == int.MinValue)
                    {
                        MainWorld.Instance.AffectedChunk(cell.SEIndex, cellIndex, 0, true);
                        Debug.Log(cellIndex + "影响SE:" + cell.SEIndex);
                    }

                    if (cell.SWIndex > int.MinValue && GetChunkIndex(cell.SWIndex) == int.MinValue)
                    {
                        MainWorld.Instance.AffectedChunk(cell.SWIndex, cellIndex, 0, true);
                        Debug.Log(cellIndex + "影响SW:" + cell.SWIndex);
                    }
                    if (cell.WIndex > int.MinValue && GetChunkIndex(cell.WIndex) == int.MinValue)
                    {
                        MainWorld.Instance.AffectedChunk(cell.WIndex, cellIndex,0, true);
                        Debug.Log(cellIndex + "影响W:" + cell.WIndex);
                    }

                    if (cell.NWIndex > int.MinValue && GetChunkIndex(cell.NWIndex) == int.MinValue)
                    {
                        MainWorld.Instance.AffectedChunk(cell.NWIndex, cellIndex, 0, true);
                        Debug.Log(cellIndex + "影响NW:" + cell.NWIndex);
                    }
                }
            }
        }
    }

    /// 
    /// 获取块内索引
    /// 
    /// 单元索引
    /// 
    private int GetChunkIndex(int cellIndex)
    {
        if (cellIndex!=int.MinValue)
        {
            for (int i = 0; i  int.MinValue) updateList.Add(updata.NEIndex);
            if (updata.EIndex > int.MinValue) updateList.Add(updata.EIndex);
            if (updata.SEIndex > int.MinValue) updateList.Add(updata.SEIndex);
            if (updata.SWIndex > int.MinValue) updateList.Add(updata.SWIndex);
            if (updata.WIndex > int.MinValue) updateList.Add(updata.WIndex);
            if (updata.NWIndex > int.MinValue) updateList.Add(updata.NWIndex);
            //1.判断并更新自身单元颜色以及相邻单元颜色
            Color color = updata.NewColor;

            //更新相邻单元的颜色
            if (updateList.Contains(neighborsIndex.NEIndex))
            {
                neighbors.NE = color;
                neighbors.NEElevation = updata.Elevation;
            }

            if (updateList.Contains(neighborsIndex.EIndex))
            {
                neighbors.E = color;
                neighbors.EElevation = updata.Elevation;
            }
            if (updateList.Contains(neighborsIndex.SEIndex)) {
                neighbors.SE = color;
                neighbors.SEElevation = updata.Elevation;
            }

            if (updateList.Contains(neighborsIndex.SWIndex))
            {
                neighbors.SW = color;
                neighbors.SWElevation = updata.Elevation;
            }

            if (updateList.Contains(neighborsIndex.WIndex) )
            {
                neighbors.W = color;
                neighbors.WElevation = updata.Elevation;
            }

            if (updateList.Contains(neighborsIndex.NWIndex) )
            {
                neighbors.NW = color;
                neighbors.NWElevation = updata.Elevation;
            }
            if (updateList.Contains(cellData.Index))//更新自身单元的颜色
            {
                cellData.Color = color;
                cellData.Position.y= updata.Elevation * HexMetrics.elevationStep;
                cellData.Elevation = updata.Elevation;
            }

            updateList.Dispose();
            //2.remove UpdateData after Update,therefor NewDataTag need to be added to active CellSystem
            CommandBuffer.RemoveComponent(index, entity);
            CommandBuffer.AddComponent(index, entity);
        }

    }

但是这些改进都是非常局限的,因为IComponentData无法储存数组,所以这里没有无限扩充更新的数量,仅限本单元和相邻的六个单元,也就是最多7个单元,如下图所示: 一次更新七个相邻单元 这就是刷子难题,无法像原版OOP实现那样可以使用更大的刷子,而且这个刷子还有Bug,例如在地图块边界的地方时,会造成桥的连接突起。总之ECS的数据很不好处理,也许是我没有设计好的缘故! 就是这样吧,bug越来越多,希望有大佬来帮忙解决!

ECS专题目录 ECS更新计划 作者的话

Alt

如果喜欢可以点赞支持一下,谢谢鼓励!如果有什么疑问可以给我留言,有错漏的地方请批评指证! 技术难题?加入开发者联盟:566189328(QQ付费群)提供有限技术探讨,以及,心灵鸡汤Orz! 当然,不需要技术探讨也欢迎加入进来,在这里劈柴、遛狗、聊天、撸猫!( ̄┰ ̄*)

关注
打赏
1664096582
查看更多评论
立即登录/注册

微信扫码登录

0.1123s