基于Unity2019最新ECS架构开发MMO游戏笔记23

  • 准备工作
  • 失败品
    • 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场景(优化重构的版本)。

失败品

昨天度过了非常纠结的一天,先展示下失败的作品吧:

失败在哪里呢?理想中的效果应该是这样的:

需求是要实现不规则化,同时应该保留一定的规律性,比如六边形单元不再是规则的六条边,而是不规则的多边形,但是整个表面是相对平整的。失败的本质原因是ECS无法使用Texture2D相关方法,例如Texture2D.GetPixelBilinear(),大概是因为Texture2D是OOP架构下的对象,或者是Texture2D只能在主线程上运行,总之是无法使用。

这使得原本应该在CellSystem中进行的噪声干扰,不得不延迟到OOP世界中来做,这样的后果就是无法准确的进行特定干扰。
虽然是失败的案例,还是从头进行代码Review吧,首先需要噪声,这里原版教程已经提供,直接使用:

图的设置也必须按照原版教程的设置:

然后通过代码来进行噪声采样,写在HexMetrics脚本里:

    #region 噪声干扰/// <summary>/// 海拔干扰度/// </summary>public const float elevationPerturbStrength = 1.5f;/// <summary>/// 噪源/// </summary>public static Texture2D noiseSource;/// <summary>/// 噪声缩放/// </summary>public const float noiseScale = 0.003f;/// <summary>/// 噪声采样/// </summary>/// <param name="position">顶点位置</param>/// <returns>双线性过滤</returns>public static Vector4 SampleNoise(Vector3 position){return noiseSource.GetPixelBilinear(position.x * noiseScale, position.z * noiseScale);}public const float cellPerturbStrength = 3f;#endregion

这样我们就可以在OOP中调用了,直接对每一个顶点进行干扰,而不是像原作者那样只对特定区域的顶点进行干扰。
这是很尴尬的事情,但是先这样做吧,代码写在MainWorld脚本里

    /// <summary>/// 噪声干扰/// </summary>/// <param name="position">顶点位置</param>/// <returns>被干扰的位置</returns>Vector3 Perturb(Vector3 position){Vector4 sample = HexMetrics.SampleNoise(position);position.x += (sample.x * 2f - 1f) * HexMetrics.cellPerturbStrength;position.y += (sample.y * 2f - 1f) * HexMetrics.cellPerturbStrength;position.z += (sample.z * 2f - 1f) * HexMetrics.cellPerturbStrength;return position;}

接下来对获取到的每一个顶点进行暴力的、统一的干扰,生成怪异的、完全不规则的地图:

Vector3 vertex = Perturb(vertexBuffer[j]);

顶点是从动态缓存中获取的,这个在之前的ECS博文中讲过了。
我也想过使用柏林噪声,然后在Job中使用,这样就没有Texture2D的限制了,而且还利用了多线程的优势。
所以在原作者那里直接Copy了一份柏林噪声的算法:

    #region Perlin Noise/// <summary>/// Perlin哈希表/// </summary>private readonly static int[] hash = {151,160,137, 91, 90, 15,131, 13,201, 95, 96, 53,194,233,  7,225,140, 36,103, 30, 69,142,  8, 99, 37,240, 21, 10, 23,190,  6,148,247,120,234, 75,  0, 26,197, 62, 94,252,219,203,117, 35, 11, 32,57,177, 33, 88,237,149, 56, 87,174, 20,125,136,171,168, 68,175,74,165, 71,134,139, 48, 27,166, 77,146,158,231, 83,111,229,122,60,211,133,230,220,105, 92, 41, 55, 46,245, 40,244,102,143, 54,65, 25, 63,161,  1,216, 80, 73,209, 76,132,187,208, 89, 18,169,200,196,135,130,116,188,159, 86,164,100,109,198,173,186,  3, 64,52,217,226,250,124,123,  5,202, 38,147,118,126,255, 82, 85,212,207,206, 59,227, 47, 16, 58, 17,182,189, 28, 42,223,183,170,213,119,248,152,  2, 44,154,163, 70,221,153,101,155,167, 43,172,  9,129, 22, 39,253, 19, 98,108,110, 79,113,224,232,178,185,112,104,218,246, 97,228,251, 34,242,193,238,210,144, 12,191,179,162,241,81, 51,145,235,249, 14,239,107, 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121, 50, 45,127,  4,150,254,138,236,205, 93,222,114, 67, 29, 24, 72,243,141,128,195, 78, 66,215, 61,156,180};/// <summary>/// 哈希遮罩/// </summary>private const int hashMask = 255;#region Perlin1Dpublic static float Perlin1D(Vector3 point, float frequency){point *= frequency;int i0 = Mathf.FloorToInt(point.x);float t0 = point.x - i0;float t1 = t0 - 1f;i0 &= hashMask;int i1 = i0 + 1;float g0 = gradients1D[hash[i0] & gradientsMask1D];float g1 = gradients1D[hash[i1] & gradientsMask1D];float v0 = g0 * t0;float v1 = g1 * t1;float t = Smooth(t0);return Mathf.Lerp(v0, v1, t) * 2f;}private static float[] gradients1D = {1f, -1f};private const int gradientsMask1D = 1;#endregion#region Perlin2Dpublic static float Perlin2D(Vector3 point, float frequency){point *= frequency;int ix0 = Mathf.FloorToInt(point.x);int iy0 = Mathf.FloorToInt(point.y);float tx0 = point.x - ix0;float ty0 = point.y - iy0;float tx1 = tx0 - 1f;float ty1 = ty0 - 1f;ix0 &= hashMask;iy0 &= hashMask;int ix1 = ix0 + 1;int iy1 = iy0 + 1;int h0 = hash[ix0];int h1 = hash[ix1];Vector2 g00 = gradients2D[hash[h0 + iy0] & gradientsMask2D];Vector2 g10 = gradients2D[hash[h1 + iy0] & gradientsMask2D];Vector2 g01 = gradients2D[hash[h0 + iy1] & gradientsMask2D];Vector2 g11 = gradients2D[hash[h1 + iy1] & gradientsMask2D];float v00 = Dot(g00, tx0, ty0);float v10 = Dot(g10, tx1, ty0);float v01 = Dot(g01, tx0, ty1);float v11 = Dot(g11, tx1, ty1);float tx = Smooth(tx0);float ty = Smooth(ty0);return Mathf.Lerp(Mathf.Lerp(v00, v10, tx),Mathf.Lerp(v01, v11, tx),ty) * sqr2;}private readonly static Vector2[] gradients2D = {new Vector2( 1f, 0f),new Vector2(-1f, 0f),new Vector2( 0f, 1f),new Vector2( 0f,-1f),new Vector2( 1f, 1f).normalized,new Vector2(-1f, 1f).normalized,new Vector2( 1f,-1f).normalized,new Vector2(-1f,-1f).normalized};private const int gradientsMask2D = 7;private static float sqr2 = Mathf.Sqrt(2f);#endregion#region Perlin3Dprivate static Vector3[] gradients3D = {new Vector3( 1f, 1f, 0f),new Vector3(-1f, 1f, 0f),new Vector3( 1f,-1f, 0f),new Vector3(-1f,-1f, 0f),new Vector3( 1f, 0f, 1f),new Vector3(-1f, 0f, 1f),new Vector3( 1f, 0f,-1f),new Vector3(-1f, 0f,-1f),new Vector3( 0f, 1f, 1f),new Vector3( 0f,-1f, 1f),new Vector3( 0f, 1f,-1f),new Vector3( 0f,-1f,-1f),new Vector3( 1f, 1f, 0f),new Vector3(-1f, 1f, 0f),new Vector3( 0f,-1f, 1f),new Vector3( 0f,-1f,-1f)};private const int gradientsMask3D = 15;public static float Perlin3D(Vector3 point, float frequency){point *= frequency;int ix0 = Mathf.FloorToInt(point.x);int iy0 = Mathf.FloorToInt(point.y);int iz0 = Mathf.FloorToInt(point.z);float tx0 = point.x - ix0;float ty0 = point.y - iy0;float tz0 = point.z - iz0;float tx1 = tx0 - 1f;float ty1 = ty0 - 1f;float tz1 = tz0 - 1f;ix0 &= hashMask;iy0 &= hashMask;iz0 &= hashMask;int ix1 = ix0 + 1;int iy1 = iy0 + 1;int iz1 = iz0 + 1;int h0 = hash[ix0];int h1 = hash[ix1];int h00 = hash[h0 + iy0];int h10 = hash[h1 + iy0];int h01 = hash[h0 + iy1];int h11 = hash[h1 + iy1];Vector3 g000 = gradients3D[hash[h00 + iz0] & gradientsMask3D];Vector3 g100 = gradients3D[hash[h10 + iz0] & gradientsMask3D];Vector3 g010 = gradients3D[hash[h01 + iz0] & gradientsMask3D];Vector3 g110 = gradients3D[hash[h11 + iz0] & gradientsMask3D];Vector3 g001 = gradients3D[hash[h00 + iz1] & gradientsMask3D];Vector3 g101 = gradients3D[hash[h10 + iz1] & gradientsMask3D];Vector3 g011 = gradients3D[hash[h01 + iz1] & gradientsMask3D];Vector3 g111 = gradients3D[hash[h11 + iz1] & gradientsMask3D];float v000 = Dot(g000, tx0, ty0, tz0);float v100 = Dot(g100, tx1, ty0, tz0);float v010 = Dot(g010, tx0, ty1, tz0);float v110 = Dot(g110, tx1, ty1, tz0);float v001 = Dot(g001, tx0, ty0, tz1);float v101 = Dot(g101, tx1, ty0, tz1);float v011 = Dot(g011, tx0, ty1, tz1);float v111 = Dot(g111, tx1, ty1, tz1);float tx = Smooth(tx0);float ty = Smooth(ty0);float tz = Smooth(tz0);return Mathf.Lerp(Mathf.Lerp(Mathf.Lerp(v000, v100, tx), Mathf.Lerp(v010, v110, tx), ty),Mathf.Lerp(Mathf.Lerp(v001, v101, tx), Mathf.Lerp(v011, v111, tx), ty),tz);}#endregionprivate static float Dot(Vector3 g, float x, float y, float z){return g.x * x + g.y * y + g.z * z;}private static float Dot(Vector2 g, float x, float y){return g.x * x + g.y * y;}/// <summary>/// 平滑/// </summary>/// <param name="t">参数</param>/// <returns></returns>private static float Smooth(float t){return t * t * t * (t * (t * 6f - 15f) + 10f);}#endregion

这个计算相当复杂,我数学不太好,抱着能用就行的心态这么做了,把方法直接放到CellSystem中:

    //噪声频率private float frequency = 5f;//高度private const float height = 0.2f;/// <summary>/// 噪声干扰/// </summary>/// <param name="position">顶点位置</param>/// <returns>被干扰的位置</returns>Vector3 Perturb(Vector3 position){position.x += (height * HexMetrics.Perlin3D(position, frequency)* 2f - 1f) * HexMetrics.cellPerturbStrength;position.y += (height * HexMetrics.Perlin3D(position, frequency)* 2f - 1f) * HexMetrics.cellPerturbStrength;position.z += (height * HexMetrics.Perlin3D(position, frequency)* 2f - 1f) * HexMetrics.cellPerturbStrength;return position;}

然后再在对应的位置进行干扰,而不是全局干扰,结果仍然不理想。这是因为这个算法得出的是散列的噪点,而不像那张噪声图一样,虽然都是Perlin的算法,却成为一个整体。可以利用UV去找到对应的点,从而进行干扰。
这里对每一个点单独进行干扰,必然会造成混乱的结果,并非我们的需求。
那么就还原一张噪声贴图,再利用贴图上的点来进行干扰?
这是可行的,但是同样也是复杂的,考虑到自己的数学水平,还是放弃这么做了。如果有数学比较好的大佬,可以对这部分进行优化,这个噪声的bug先保留在项目中,我也新建了一个Issue,等待诸君来解决。

同样也在官方论坛发帖,希望官方有对应的解决方案,这里留一个官方DOTS论坛的超链接给大家,有问题可以去发帖。
这一篇暂时先这样,后续如果有改进方案,再来更新,下面是分界线:
——————————————————二〇一九年八月三十日 10:39:33——————————————————————

ECS专题目录

ECS更新计划

作者的话

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

用ECS做HexMap:不规则化相关推荐

  1. 用ECS做HexMap:鼠标点击六边形单元涂色

    基于Unity2019最新ECS架构开发MMO游戏笔记21 准备工作 鼠标触碰六边形单元 鼠标点击位置 更新计划 作者的话 ECS系列目录 ECS官方示例1:ForEach ECS官方案例2:IJob ...

  2. 用ECS做HexMap:利用RenderMesh为六边形涂色

    基于Unity2019最新ECS架构开发MMO游戏笔记18 为六边形涂色 链接相邻单元 颜色混合 更新计划 作者的话 ECS系列目录 ECS官方示例1:ForEach ECS官方案例2:IJobFor ...

  3. 用ECS做HexMap:自动生成地图系统

    基于Unity2019最新ECS架构开发MMO游戏笔记16 自动生成地图系统 AutoCreateMapSystem 神奇的六边形 六边形实体 创建者和创建六边形单元系统 更新计划 作者的话 ECS系 ...

  4. 用ECS做HexMap:六边形单元的颜色混合

    基于Unity2019最新ECS架构开发MMO游戏笔记19 颜色混合 颜色平均化 区域混合 三角化混合区域 边界连接桥 填充间隙 边界合并 更新计划 作者的话 ECS系列目录 ECS官方示例1:For ...

  5. 用ECS做HexMap:重构地图系统

    基于Unity2019最新ECS架构开发MMO游戏笔记20 概述 概念 原型Archetypes 内存块 实体查询EntityQuery 任务Jobs 系统组织 优化地图系统 主世界 六边形单元生成系 ...

  6. 用ECS做HexMap:高地与阶梯

    基于Unity2019最新ECS架构开发MMO游戏笔记22 准备工作 高地与阶梯连接 桥面倾斜连接 阶梯状的桥连接 阶梯合理化 制作三角补丁 ECS专题目录 ECS更新计划 作者的话 准备工作 如果大 ...

  7. 陈一舟:雷军马云在通讯领域也做不过马化腾 为何我就该挨骂

    雷帝网 雷建平 11月16日报道 曾经辉煌的人人网"卖身"后,一度消失在大众视野的人人公司CEO陈一舟变得有话说. 陈一舟今日接受王峰十问时自我评价了一下,称感觉其管理能力和智商在 ...

  8. PPT 下载 | 神策数据孙超赟:数据驱动,做可“视”化运营

    在以"矩·变"为主题的神策 2019 数据驱动大会现场,神策数据业务咨询师孙超赟发表了名为<数据驱动,做可"视"化运营>的主题演讲.主要内容如下: ...

  9. 亚信安全:坚持自主可控 做中国本土化专业化的安全公司

    自去年亚信科技收购趋势科技中国以来,成立半年后的亚信安全专注于哪些安全方向的研究?想要做什么?都做了什么?在近日亚信安全2016战略媒体吹风会上,亚信安全为我们做了详细的解答. 不是趋势科技也不是过去 ...

最新文章

  1. Vivado2018如何与Notepad++关联?
  2. 网易创新企业大会圆满收官  看新技术和新商业如何撞出花火
  3. HDU-4081 Qin Shi Huang's Road 8ystem(次小生成树)
  4. php 抽象类 静态方法吗,php中的抽象类和静态方法是什么
  5. python selenium自动化面试_18年selenium3+python3+unittest自动化测试教程(上)
  6. NeurIPS 2020 | 基于“单目标域样本”的领域自适应方法
  7. 7-5-无向图生成树-图-第7章-《数据结构》课本源码-严蔚敏吴伟民版
  8. ApacheCN JavaScript 译文集(二) 20211123 更新
  9. Web渗/透/攻/击实战(1)—成功渗/透台湾某净化设备公司官网
  10. LeetCode 28. 实现strStr()
  11. Linux运维 第三阶段 (九)NFS
  12. 信号识别 matlab库,EEG信号MATLAB分析平台设计 ——模式识别部分
  13. List集合去重方式及效率对比
  14. .NET Standard(1)——.NET Standard是什么
  15. iOS10 关于推送
  16. CryEngine3SDK尝鲜
  17. android 8.0+后台Service限制
  18. win7利用pycharm代码连接夜神模拟器运行appium,被杀进程怎么办
  19. 华为鸿蒙到底是不是安卓系统套了个壳?
  20. 同比、环比、YTD、MTD

热门文章

  1. 有效APP地推的必要性
  2. 解决xampp安装后遇到的常见问题
  3. r语言中trifit怎么用_用R语言分析我的fitbit计步数据
  4. 人脸验证与识别——从模型训练到项目部署
  5. No qualifying bean of type [com.*.*.dao.InfoDao] found for :错误!
  6. 摩尔斯密码 php代码,摩尔斯密码 - 一个工具箱 - 好用的在线工具都在这里!
  7. SQL Server 2012 唯一约束(定义唯一约束、删除唯一约束)
  8. 基于动力学模型的无人驾驶车辆MPC轨迹跟踪算法及carsim+matlab联合仿真学习笔记
  9. SmartGit软甲提交代码时报: Commit: Not all refs have been pushed.的解决方法
  10. Vantiq平台的优势