您当前的位置: 首页 > 

CloudHu1989

暂无认证

  • 4浏览

    0关注

    89博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

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

CloudHu1989 发布时间:2019-08-19 22:42:45 ,浏览量:4

基于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;
/// 
/// 六边形常量
/// 
public static class HexMetrics {

    /// 
    /// 总的顶点数,一个六边形有18个顶点
    /// 
    public static int totalVertices = 18;
    
    /// 
    /// 六边形外半径=六边形边长
    /// 
    public const float outerRadius = 10f;

    /// 
    /// 六边形内半径=0.8*外半径
    /// 
    public const float innerRadius = outerRadius * 0.866025404f;

    /// 
    /// 六边形的六个角组成的数组
    /// 
	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脚本:

/// 
/// E:六边形单元
/// 
[RequiresEntityConversion]
public class HexCellEntity : MonoBehaviour,IConvertGameObjectToEntity {

    /// 
    /// 三维坐标
    /// 
    public int X;
    public int Y;
    public int Z;

    /// 
    /// 颜色
    /// 
    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组件:

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

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


/// 
/// S:这里暂时只做旋转,后面会变色等
/// 
public class HexCellSystem : JobComponentSystem {
    EntityQuery m_Group;//查询到特定组件的实体,将其放入这个组中

    /// 
    /// 这里根据类型来查询到特定的实体
    /// 
    protected override void OnCreate()
    {
        ///typeof(Rotation)=带有Rotation组件的;ComponentType=对应HexCellData组件类型的
        /// ReadOnly=只读会加快获取实体的速度,ReadWrite=读写 则相对较慢
        m_Group = GetEntityQuery(typeof(Rotation), ComponentType.ReadOnly());
    }

    [BurstCompile]//同样使用Burst编译器来加速,区别是使用了块接口:IJobChunk
    struct RotationSpeedJob : IJobChunk {
        /// 
        /// 时间
        /// 
        public float DeltaTime;

        /// 
        /// 原型块组件类型=Rotation
        /// 
        public ArchetypeChunkComponentType RotationType;

        /// 
        /// 只读 原型块组件类型=HexCellData
        /// 
        [ReadOnly]
        public ArchetypeChunkComponentType RotationSpeedType;

        /// 
        /// 找出满足条件的实体来执行
        /// 
        /// 
        /// 块索引
        /// 第一个实体索引
        public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
        {
            var chunkRotations = chunk.GetNativeArray(RotationType);
            var chunkRotationSpeeds = chunk.GetNativeArray(RotationSpeedType);
            for (var i = 0; i             
关注
打赏
1664096582
查看更多评论
0.2299s