您当前的位置: 首页 > 

CloudHu1989

暂无认证

  • 7浏览

    0关注

    89博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

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

CloudHu1989 发布时间:2019-08-27 20:30:06 ,浏览量:7

基于Unity2019最新ECS架构开发MMO游戏笔记21
    • 准备工作
    • 鼠标触碰六边形单元
      • 鼠标点击位置
  • 更新计划
    • 作者的话
  • 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深似海啊,掉进坑里面了,没办法啊,自己挖坑埋自己,于是只能自己填坑累了。MMO的项目也还没有填完,这个地图坑,恐怕一时半会儿也搞不定,慢慢来吧。 Anyway,如果你对ECS版的海克斯无限地图感兴趣,不妨参与进来,欢迎Star/Folk源码,如果有大佬可以贡献点代码,那就太好了,具体操作如下: 0下载Unity编辑器(2019.1.12f1 or 更新的版本),if(已经下载了)continue; 1克隆源码:git clone https://github.com/cloudhu/HexMapMadeInUnity2019ECS.git --recurse or 点击ECSHexMap下载Zip压缩包 if(已经下载了)continue; 2如果下载的是压缩包,需要先将压缩包解压。然后将HexMapMadeInUnity2019ECS添加到Unity Hub项目中; 3用Unity Hub打开的开源项目:HexMapMadeInUnity2019ECS,等待Unity进行编译工作; 4打开项目后,启动场景在Scenes目录下,打开AdvancedHexMap场景。

鼠标触碰六边形单元

AdvancedHexMap场景是优化重构的场景,点击运行后,如下图所示:

六边形地图 我们得到了计算出来的六边形地图,下面我们需要使用鼠标来点击六边形单元,使其改变颜色。

鼠标点击位置

我们在Common文件夹中新建一个C#脚本,命名为:HexMapEditor,在这个脚本中实现鼠标点击地图,把屏幕坐标转化成射线,再把射线触碰到的点转化成世界坐标:

  using UnityEngine;
using UnityEngine.EventSystems;

public class HexMapEditor : MonoBehaviour {

    public Color[] colors;

    private Color activeColor;

    void Awake()
    {
        SelectColor(0);
    }

    void Update()
    {
        if (
            Input.GetMouseButton(0) &&
            !EventSystem.current.IsPointerOverGameObject()
        )
        {
            HandleInput();
        }
    }

    void HandleInput()
    {
        Ray inputRay = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast(inputRay, out hit))
        {
            MainWorld.Instance.ColorCell(hit.point, activeColor);
        }
    }

    public void SelectColor(int index)
    {
        activeColor = colors[index];
    }
}

这段代码算是Unity入门的基本操作了,不需要多余的注释,但是要解释一下,本来应该一开始就实现的简单功能,一直拖到现在才做。那是因为ECS没法使用MeshCollider,所以不能响应射线,进而无法确定鼠标点击的单元所在。 现在把六边形放到OOP中来渲染,就可以获取到鼠标点击的六边形单元的所在位置了,应该是通过鼠标点击的位置来判断点击了哪一个六边形单元。我们需要一个坐标系来进行转换,新建一个C#脚本,命名为HexCoordinates:

using UnityEngine;

[System.Serializable]
public struct HexCoordinates {

    [SerializeField]
    private int x, z;

    public int X {
        get {
            return x;
        }
    }

    public int Z {
        get {
            return z;
        }
    }

    public int Y {
        get {
            return -X - Z;
        }
    }

    public HexCoordinates(int x, int z)
    {
        this.x = x;
        this.z = z;
    }

    public static HexCoordinates FromOffsetCoordinates(int x, int z)
    {
        return new HexCoordinates(x - z / 2, z);
    }

    public static HexCoordinates FromPosition(Vector3 position)
    {
        float x = position.x / (HexMetrics.InnerRadius * 2f);
        float y = -x;

        float offset = position.z / (HexMetrics.OuterRadius * 3f);
        x -= offset;
        y -= offset;

        int iX = Mathf.RoundToInt(x);
        int iY = Mathf.RoundToInt(y);
        int iZ = Mathf.RoundToInt(-x - y);

        if (iX + iY + iZ != 0)
        {
            float dX = Mathf.Abs(x - iX);
            float dY = Mathf.Abs(y - iY);
            float dZ = Mathf.Abs(-x - y - iZ);

            if (dX > dY && dX > dZ)
            {
                iX = -iY - iZ;
            }
            else if (dZ > dY)
            {
                iZ = -iX - iY;
            }
        }

        return new HexCoordinates(iX, iZ);
    }

    public override string ToString()
    {
        return "(" +
               X.ToString() + ", " + Y.ToString() + ", " + Z.ToString() + ")";
    }

    public string ToStringOnSeparateLines()
    {
        return X.ToString() + "\n" + Y.ToString() + "\n" + Z.ToString();
    }
}

没有什么特别的,就是一些坐标转换的计算,下面需要新建一个系统来更新单元的颜色UpdateCellSystem:

/// 
/// 更新六边形单元系统
/// 
//[DisableAutoCreation]
[UpdateInGroup(typeof(SimulationSystemGroup))]
public class UpdateCellSystem : JobComponentSystem {

    BeginSimulationEntityCommandBufferSystem m_EntityCommandBufferSystem;

    protected override void OnCreate()
    {
        m_EntityCommandBufferSystem = World.GetOrCreateSystem();
    }

    /// 
    /// 循环创建六边形单元,使其生成对应长宽的阵列
    /// 
    struct CalculateJob : IJobForEachWithEntity {
        public EntityCommandBuffer.Concurrent CommandBuffer;
        [BurstCompile]
        public void Execute(Entity entity, int index, ref Cell cellData, [ReadOnly]ref UpdateData updata)
        {
            //0.获取单元索引,Execute的index顺序混乱
            int cellIndex = cellData.Index;
            int updateIndex = updata.CellIndex;
            int width = updata.Width;

            //1.判断并更新自身单元颜色以及相邻单元颜色
            if (cellIndex updateIndex - width-2)
            {
                Color color = updata.NewColor;
                //更新相邻单元的颜色
                if (cellData.NEIndex == updateIndex) cellData.NE = color;
                if (cellData.EIndex == updateIndex) cellData.E = color;
                if (cellData.SEIndex == updateIndex) cellData.SE = color;
                if (cellData.SWIndex == updateIndex) cellData.SW = color;
                if (cellData.WIndex == updateIndex) cellData.W = color;
                if (cellData.NWIndex == updateIndex) cellData.NW = color;
                if (cellIndex == updateIndex)//更新自身单元的颜色
                {
                    cellData.Color = color;
                }
            }

            //2.remove UpdateTag after Update,therefor NewDataTag need to be added
            CommandBuffer.RemoveComponent(index, entity);
            CommandBuffer.AddComponent(index, entity);

        }

    }

    /// 
    /// 如果有新地图,则启动任务
    /// 
    /// 依赖
    /// 任务句柄
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var job = new CalculateJob
        {
            CommandBuffer = m_EntityCommandBufferSystem.CreateCommandBuffer().ToConcurrent(),

        }.Schedule(this, inputDeps);
        m_EntityCommandBufferSystem.AddJobHandleForProducer(job);

        return job;

    }
}

这个系统的功能就是更新六边形单元的颜色数据,为此我新增了一个组件来保存更新的数据:

/// 
/// 单元的更新数据
/// 
public struct UpdateData : IComponentData
{
    /// 
    /// 单元的索引
    /// 
    public int CellIndex;
    /// 
    /// 新的颜色
    /// 
    public Color NewColor;
    /// 
    /// 宽度
    /// 
    public int Width;
}

宽度实际上是为了缩小更新的范围,毕竟和单元相邻的只有六个,那么我们就可以根据宽度来缩小范围了,超出范围不更新。

接下来只需把数据传递给实体即可,当我们点击单元的时候,调用了MainWorld的公共方法:

    /// 
    /// 染色指定位置的六边形单元
    /// 
    /// 位置
    /// 颜色
    public void ColorCell(Vector3 position, Color color)
    {
        position = transform.InverseTransformPoint(position);
        HexCoordinates coordinates = HexCoordinates.FromPosition(position);
        int index = coordinates.X + coordinates.Z * MapWidth + coordinates.Z / 2;
        if (index==m_PrevClickCell && color==m_PrevSelect)
        {//避免玩家重复操作
            return;
        }

        m_PrevClickCell = index;
        m_PrevSelect = color;
        StartCoroutine(UpdateCellColor(index,color));
    }

    /// 
    /// 更新单元的颜色
    /// 
    /// 单元索引
    /// 颜色
    /// 
    IEnumerator UpdateCellColor(int cellIndex,Color color)
    {
        yield return null;
        NativeArray entities = m_EntityManager.GetAllEntities();
        if (entities.Length             
关注
打赏
1664096582
查看更多评论
0.0667s