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

  • 自动生成地图系统
    • AutoCreateMapSystem
    • 神奇的六边形
    • 六边形实体
    • 创建者和创建六边形单元系统
  • 更新计划
    • 作者的话
  • ECS系列目录
    • ECS官方示例1:ForEach
    • ECS官方案例2:IJobForEach
    • ECS官方案例3:IJobChunk
    • ECS官方案例4:SubScene
    • ECS官方案例5:SpawnFromMonoBehaviour
    • ECS官方案例6:SpawnFromEntity
    • ECS官方案例7:SpawnAndRemove
    • ECS进阶:FixedTimestepWorkaround
    • ECS进阶:Boids
    • ECS进阶:场景切换器
    • ECS进阶:MegaCity0
    • ECS进阶:MegaCity1
    • UnityMMO资源整合&服务器部署
    • UnityMMO选人流程
    • UnityMMO主世界
    • UnityMMO网络同步
    • 用ECS做HexMap:自动生成地图系统
    • 用ECS做HexMap:利用RenderMesh绘制六边形
    • 用ECS做HexMap:利用RenderMesh为六边形涂色
    • 用ECS做HexMap:六边形单元的颜色混合
    • 用ECS做HexMap:重构地图系统
    • 用ECS做HexMap:鼠标点击六边形单元涂色

自动生成地图系统

ECS的世界由许许多多的系统来操控,在进入主世界的时候会创建这些系统,如下图所示:

CreateSystem
CreateSystem
CreateSystem
CreateSystem
MainWorld
PlayerInputSystem
CreateTargetPosFromUserInputSystem
MovementUpdateSystem
etc等等System

上一篇中PlayerInputSystem负责处理玩家的操作,与之对应的组件有UserCommand(用户命令),TargetPosition(目标位置)和MoveSpeed(移动速度)。原本想一起看看源码,加一点注释进去,算是走马观花,画蛇添足。不过,这样做实在没有太多营养价值,如果大家有兴趣,自行看下源码吧。这一篇想写一点创造性的东西,例如生动生成地图系统。

AutoCreateMapSystem

灵感来源于Unity Hex Map Tutorial,我觉得自动生成地图这件事情太适合ECS了,为什么?

  • 自动生成的地图涉及到大量的实体;
  • ECS的性能是为大世界而生,在其性能加持下,我们可以生成无限世界;
  • 逻辑解耦,分工明确。

不管怎样,都值得尝试一下。
说下我的大概需求:

  1. 自动生成地图,利用各种System来制定地图的规则,使其尽量贴近自然;
  2. 无限地图,玩家离地图边缘一定距离后,预判玩家行走线路并在其方向上动态扩展;
  3. 将地图数据保存到服务器,与其他玩家进行同步;
  4. 动态加载和动态裁剪,以最小的资源做出最大的地图;
  5. 地图与玩家互动,可破坏,可创建,所有操作进行网络同步。

神奇的六边形

我觉得像MegaCity那样的大地图,太吃资源,如果把地图的所有一切都转换成数据。然后再通过数据来驱动无限地图,这样也许很有意思,但是也不是随机生成所有一切,要利用算法来尽量还原大自然的规则。
大概就是这样,我们先从最简单的开始,一步一步实现我们的需求。就先从六边形开始吧!
国外的大佬解释了六边形有多么神奇和好用,蜜蜂选择六边形来筑巢,足以说明这个东西道法自然,详情点上面的链接了解。

using UnityEngine;
/// <summary>
/// 六边形常量
/// </summary>
public static class HexMetrics {/// <summary>/// 总的顶点数,一个六边形有18个顶点/// </summary>public static int totalVertices = 18;/// <summary>/// 六边形外半径=六边形边长/// </summary>public const float outerRadius = 10f;/// <summary>/// 六边形内半径=0.8*外半径/// </summary>public const float innerRadius = outerRadius * 0.866025404f;/// <summary>/// 六边形的六个角组成的数组/// </summary>public readonly static Vector3[] corners = {new Vector3(0f, 0f, outerRadius),//最顶上那个角作为起点,顺时针画线new Vector3(innerRadius, 0f, 0.5f * outerRadius),//顺数第二个new Vector3(innerRadius, 0f, -0.5f * outerRadius),//顺数第三个new Vector3(0f, 0f, -outerRadius),//依次类推,坐标如下图所示new Vector3(-innerRadius, 0f, -0.5f * outerRadius),new Vector3(-innerRadius, 0f, 0.5f * outerRadius),new Vector3(0f, 0f, outerRadius)};
}


如图,红色虚线代表内半径,蓝色实线代表外半径,而其数值都是相对固定的常量,因此这里直接定义出来。
根据这些常量,设定圆心坐标为(0,0,0),我们以最上角最为起点,就可以得出六个角的顶点坐标了。

六边形实体

接下来创建六边形实体,如下图所示:

实际上就是个空对象,我本来要通过ConvertToEntity将其转化成实体的,但是出了一个红色警报,只好移除,保留E脚本:

/// <summary>
/// E:六边形单元
/// </summary>
[RequiresEntityConversion]
public class HexCellEntity : MonoBehaviour,IConvertGameObjectToEntity {/// <summary>/// 三维坐标/// </summary>public int X;public int Y;public int Z;/// <summary>/// 颜色/// </summary>public Color Color;public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem){//数据交给C保存dstManager.AddComponentData(entity, new HexCellData{X=this.X,Y=this.Y,Z=this.Z,color=Color,RadiansPerSecond= math.radians(DegreesPerSecond)});//添加父组件dstManager.AddComponent(entity, typeof(Parent));//添加相对父类的本地位置组件dstManager.AddComponent(entity, typeof(LocalToParent));}}

对应的C组件:

/// <summary>
/// C:保存六边形的坐标和颜色数据
/// </summary>
[Serializable]
public struct HexCellData : IComponentData
{public int X;public int Y;public int Z;public Color color;public float RadiansPerSecond;
}

暂时设定六边形的功能是旋转,后面再更改成变色:


/// <summary>
/// S:这里暂时只做旋转,后面会变色等
/// </summary>
public class HexCellSystem : JobComponentSystem {EntityQuery m_Group;//查询到特定组件的实体,将其放入这个组中/// <summary>/// 这里根据类型来查询到特定的实体/// </summary>protected override void OnCreate(){///typeof(Rotation)=带有Rotation组件的;ComponentType=对应HexCellData组件类型的/// ReadOnly=只读会加快获取实体的速度,ReadWrite=读写 则相对较慢m_Group = GetEntityQuery(typeof(Rotation), ComponentType.ReadOnly<HexCellData>());}[BurstCompile]//同样使用Burst编译器来加速,区别是使用了块接口:IJobChunkstruct RotationSpeedJob : IJobChunk {/// <summary>/// 时间/// </summary>public float DeltaTime;/// <summary>/// 原型块组件类型=Rotation/// </summary>public ArchetypeChunkComponentType<Rotation> RotationType;/// <summary>/// 只读 原型块组件类型=HexCellData/// </summary>[ReadOnly]public ArchetypeChunkComponentType<HexCellData> RotationSpeedType;/// <summary>/// 找出满足条件的实体来执行/// </summary>/// <param name="chunk"><原型块/param>/// <param name="chunkIndex">块索引</param>/// <param name="firstEntityIndex">第一个实体索引</param>public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex){var chunkRotations = chunk.GetNativeArray(RotationType);var chunkRotationSpeeds = chunk.GetNativeArray(RotationSpeedType);for (var i = 0; i < chunk.Count; i++){var rotation = chunkRotations[i];var rotationSpeed = chunkRotationSpeeds[i];chunkRotations[i] = new Rotation{Value = math.mul(math.normalize(rotation.Value),quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * DeltaTime))};}}}/// <summary>/// 这个方法在主线程上每帧运行/// </summary>/// <param name="inputDependencies">输入依赖</param>/// <returns></returns>protected override JobHandle OnUpdate(JobHandle inputDependencies){// Explicitly declare: 声明// - Read-Write access to Rotation 读写的方式访问旋转// - Read-Only access to HexCellData 只读的方式访问旋转速度var rotationType = GetArchetypeChunkComponentType<Rotation>();var rotationSpeedType = GetArchetypeChunkComponentType<HexCellData>(true);var job = new RotationSpeedJob(){RotationType = rotationType,RotationSpeedType = rotationSpeedType,DeltaTime = Time.deltaTime};return job.Schedule(m_Group, inputDependencies);}
}

如上代码是六边形单元的基本ECS写法,都是最基础的:

E C S
HexCellEntity HexCellData HexCellSystem

在游戏对象上添加上一个Mesh显示相应的组件就可以让其旋转起来了,其实很简单。

接下来我们把它做成一个预设,然后再大量生成,以后的大地图就建立在这个六边形单元的基础上。

创建者和创建六边形单元系统

接下来我们新建一个空游戏对象,命名为:MapCreater。为其添加ConvertToEntity脚本组件,使其转化为实体,新建一个C#脚本来描述这个实体,命名为CreaterEntity:

/// <summary>
/// E:创建者实体
/// </summary>
[RequiresEntityConversion]
public class CreaterEntity : MonoBehaviour, IDeclareReferencedPrefabs, IConvertGameObjectToEntity
{/// <summary>/// 六边形单元预设/// </summary>public GameObject HexCellPrefab;/// <summary>/// 地图宽度(以六边形为基本单位)/// </summary>public int MapWidth=6;/// <summary>/// 地图长度(以六边形为基本单位)/// </summary>public int MapHeight=6;/// <summary>/// 地图颜色/// </summary>public Color defaultColor = Color.white;public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem){HexMetrics.totalVertices = MapWidth * MapHeight * 18;dstManager.AddComponentData(entity, new MapData{Width=MapWidth,Height=MapHeight,Prefab = conversionSystem.GetPrimaryEntity(HexCellPrefab),Color=defaultColor,bIsNewMap=bCreatNewMap});}public void DeclareReferencedPrefabs(List<GameObject> referencedPrefabs){referencedPrefabs.Add(HexCellPrefab);}
}

数据交给C保存起来:

/// <summary>
/// C:保存创建者数据
/// </summary>
[Serializable]
public struct CreaterData : IComponentData {public int Width;public int Height;public Entity Prefab;public Color Color;
}

S:创建六边形单元系统

/// <summary>
/// 创建六边形单元系统
/// </summary>
public class CreateHexCellSystem : JobComponentSystem {BeginInitializationEntityCommandBufferSystem m_EntityCommandBufferSystem;/// <summary>/// 是否是新地图/// </summary>public bool bIfNewMap = true;protected override void OnCreate(){m_EntityCommandBufferSystem = World.GetOrCreateSystem<BeginInitializationEntityCommandBufferSystem>();}/// <summary>/// 循环创建六边形单元,使其生成对应长宽的阵列/// </summary>struct SpawnJob : IJobForEachWithEntity<CreaterData> {public EntityCommandBuffer.Concurrent CommandBuffer;[BurstCompile]public void Execute(Entity entity, int index, [ReadOnly]ref CreaterData  createrData){for (int z = 0; z < createrData.Height; z++){for (int x = 0; x < createrData.Width; x++){//1.实例化var instance = CommandBuffer.Instantiate(index, createrData.Prefab);//2.计算阵列坐标float _x = (x + z * 0.5f - z / 2) * (HexMetrics.innerRadius * 2f);float _z = z * (HexMetrics.outerRadius * 1.5f);//3.设置父组件CommandBuffer.SetComponent(index, instance, new Parent{Value = entity});//4.设置每个单元的数据CommandBuffer.SetComponent(index, instance, new HexCellData{X = x - z / 2,Y = 0,Z = z,color = createrData.Color,});//5.设置位置CommandBuffer.SetComponent(index, instance, new Translation{Value = new float3(_x, 0F, _z)});}}}}/// <summary>/// 如果有新地图,则启动任务/// </summary>/// <param name="inputDeps">依赖</param>/// <returns>任务句柄</returns>protected override JobHandle OnUpdate(JobHandle inputDeps){if (bIfNewMap){var job = new SpawnJob{CommandBuffer = m_EntityCommandBufferSystem.CreateCommandBuffer().ToConcurrent(),}.Schedule(this, inputDeps);m_EntityCommandBufferSystem.AddJobHandleForProducer(job);job.Complete();var mapSystem = World.GetOrCreateSystem<CreateHexMapSystem>();mapSystem.bIfNewMap = true;//新地图创建完成,关闭创建bIfNewMap = false;return job;}return inputDeps;}
}


如上图所示,我们创建6*6的单元矩阵,但是它们并没有旋转。我们通过Entity Debugger窗口可以看到对应的实体。
我发现Rotation的数据一直都是0,并没有发生旋转,但是代码并没有问题。到官方论坛反馈时,发现是Rotation的API变了!
ECS还处于过渡时期,所以API会经常变动,开发起来非常尴尬。
我发现以前的写法,在做升级之后,就不起作用了。不仅如此,很多物理组件无法使用。
因此这一篇到这里搁浅了,后面找到正确的API继续写。
已经把项目上传到Github,有兴趣的朋友可以看看:HexMapMadeInUnity2019ECS

更新计划

Mon 12Mon 19Mon 261. ForEach 2. IJobForEach 3. IJobChunk 4. SubScene 5. SpawnFromMonoBehaviour 6. SpawnFromEntity 7. SpawnAndRemove 休息修正更新计划参加表哥婚礼进阶:FixedTimestepWorkaround进阶:BoidExample初级:SceneSwitcher我是休息时间 资源整合 部署服务器 启动流程 登录流程 MegaCity 选人流程 游戏主世界 UnityMMO网络同步 我是休息时间 待计划 待计划 待计划 待计划 我是休息时间 待计划 待计划 待计划 待计划 待计划 我是休息时间 读取Excel自动生成Entity 读取Excel自动生成Component读取数据库自动生成Entity读取数据库自动生成ComponentESC LuaFrameWork Skynet DOTS 官方示例学习笔记-----休息-----基于ECS架构开发MMO学习笔记休息-----LuaFrameWork学习笔记-----休息-----基于Skynet架构开发服务器学习笔记制作代码自动生成工具总结基于Unity2019最新ECS架构开发MMO游戏笔记

作者的话

如果喜欢我的文章可以点赞支持一下,谢谢鼓励!如果有什么疑问可以给我留言,有错漏的地方请批评指证!
如果有技术难题需要讨论,可以加入开发者联盟:566189328(付费群)为您提供有限的技术支持,以及,心灵鸡汤!
当然,不需要技术支持也欢迎加入进来,随时可以请我喝咖啡、茶和果汁!( ̄┰ ̄*)

ECS系列目录

ECS官方示例1:ForEach

ECS官方案例2:IJobForEach

ECS官方案例3:IJobChunk

ECS官方案例4:SubScene

ECS官方案例5:SpawnFromMonoBehaviour

ECS官方案例6:SpawnFromEntity

ECS官方案例7:SpawnAndRemove

ECS进阶:FixedTimestepWorkaround

ECS进阶:Boids

ECS进阶:场景切换器

ECS进阶:MegaCity0

ECS进阶:MegaCity1

UnityMMO资源整合&服务器部署

UnityMMO选人流程

UnityMMO主世界

UnityMMO网络同步

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

用ECS做HexMap:利用RenderMesh绘制六边形

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

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

用ECS做HexMap:重构地图系统

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

用ECS做HexMap:自动生成地图系统相关推荐

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

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

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

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

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

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

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

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

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

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

  6. 用ECS做HexMap:不规则化

    基于Unity2019最新ECS架构开发MMO游戏笔记23 准备工作 失败品 ECS专题目录 ECS更新计划 作者的话 准备工作 如果大佬对ECS版的海克斯无限地图感兴趣,不妨参与进来,欢迎Star/ ...

  7. php自动生成网站地图txt,织梦网站地图生成插件+发布文章后自动生成地图

    织梦网站地图生成插件,可同时在网站根目录生成5种地图文件. 下载地址(根据自己网站编码选择安装) 百度网盘 提取码: 3bjg 安装好后的生成菜单在模块一栏里面 如果你想把生成网站地图这个菜单放在生成 ...

  8. 爬虫第7课课后练习-用扇贝网做一个自动生成错词本

    第一步:分析需求,明确目标 扇贝网:https://www.shanbay.com/已经有一个测单词量的功能, 我们要做的就是把这个功能复制下来,并且做点改良,搞一个网页版没有的功能 ---- 自动生 ...

  9. 技术贴:如何简单地做游戏随机生成地图

    转自:http://www.gamelook.com.cn/2015/12/239245 Gamelook报道/对于大多数的游戏来说,内容的消耗都是开发商非常棘手的问题,而随机生成地图的做法则大大增加 ...

最新文章

  1. 计算机网络原理课程描述,计算机网络原理
  2. 这些个JAVA开源工具(那是相当地多啊)
  3. eclipse为什么导入不了awt_为什么选择javafx?
  4. 运动会成绩管理java代码_基于jsp的运动会成绩管理-JavaEE实现运动会成绩管理 - java项目源码...
  5. 在JSP页面中使用Ajax主题时的引入
  6. php 赋予最高权限,为PHP执行赋予root权限(一)
  7. 一种table超出高度自动出滚动条的解决方案
  8. 人工智能 量子力学 时间简史 山海经 三体
  9. Docker安装ik分词器
  10. 迅为RK3399开发板嵌入式linux开发指南
  11. 蓝桥杯等差素数列-python
  12. win7 开WiFi共享
  13. 不用社保也可以办理深圳居住证(全程网上办理) 解决提交后一直暂存状态
  14. 工商管理硕士(MBA)提前面试案例与技巧
  15. 基于Docker的PaaS平台建设实践
  16. Python 中创建 PostgreSQL 数据库连接池
  17. A Comparative Analysis of Deep Learning Approaches for Network Intrusion Detection Systems (N-IDSs)
  18. 图片怎么格式转换成jpg?介绍几种思路
  19. ipip.net IP位置信誉信息爬取
  20. ZZULIOJ 1148: 组合三位数之一

热门文章

  1. 2022年PMP报考条件是什么?
  2. 文献引文分析利器HistCite使用教程(附精简易用免安装Pro版本下载)
  3. CentOS7 开启telnet端口
  4. windowsserver可以ping通ip但telnet端口失败问题排查处理
  5. 微信红包封面开放平台使用指南
  6. Python实现股票行情接收V013
  7. 手机文件合并工具 android,手机视频合并工具 用安卓手机怎样把多个视频片段合并成一个视频...
  8. python语音识别预处理_自然语言处理之数据预处理
  9. 记录一下AD画简单PCB的大致流程
  10. 录音怎么转成文字?教你3个录音转文字方法,简单高效