【Unity】六边形地图格子基础

37 篇文章 3 订阅
17 篇文章 1 订阅

        这里的六边形地图,指SLG游戏中的大世界地图,用六边形的网格进行平铺。例如文明,以及部分SLG手游。

        这里介绍六边形地图格子的基本概念,以及具体数学实现代码。

1、基本概念

        这里强制规定,所有的格子都是尖头朝上(Z轴正向)。

        地图的起点在左下角(保证一个轴的值为正)。

1.1、 格子坐标

        六边形的坐标,在逻辑计算中采用立体坐标,坐标的三个值之和为0 (X+Y+Z =0 )。坐标定义可以设置为 XYZ,也可以设置为 QRS ,如下图所示:

        坐标的三个值分别代表六边形的三个朝向的偏移值。

        之所以使用立体坐标,因为其对称性好,而且简便公式是最多的。其缺点是在视图上不够直观,不能直观地反应出平面位置。所以,在针对玩家进行显示时,建议不要直接使用立体坐标,而是转换成平面坐标(XY)。

1.2、 格子尺寸

        关于格子有以下几个基本概念:

        内径:中心到边的距离(OB);

        外径:中心到角的距离(OA);

        显然,这里边长就等于外径 (AC = OA)。

        经过简单的三角函数计算,内径和外径的比例如下:

        /// <summary>
        /// 内径/外径  3^0.5 / 4 
        /// </summary>
        public const float InnerToOutter = 0.8660254037844386f;

        /// <summary>
        /// 外径/内径  4 / 3^0.5 
        /// </summary>
        public const float OutterToInner = 1.154700538379252f;

        建议保证内径为整数,这样在做寻路计算时,走的格子距离就是整数。

1.3、 格子方向

        对于六边形格子,是只能通过边跨越到另一个格子,且尖头朝上。因此方向就是:正左、正右、左上、右上、左下、右下六个方向。在坐标中的转换如下:   

        public static readonly int3[] HexDirOffset = new int3[]
            {
                new int3(0,0,0),//原点
                new int3(1,-1,0),//正右
                new int3(0,-1,1),//右上
                new int3(-1,0,1),//左上
                new int3(-1,1,0),//正左
                new int3(0,1,-1),//左下
                new int3(1,0,-1),//右下
            };

     

2、 格子平铺

        六边形的格子平铺也是尽量铺成矩形,这样方便设计地图。但这样的平铺,在计算边界时就要注意在X轴上要加上垂直偏移。        

        如上图所示,在平铺,左下为【0,0,0】的情况下,在X轴上会出现负数,但是Z轴确定是正数,Y轴是负数。

        提供给玩家显示的时候,改为平面坐标(XY),转换方式下文会提到。

        结果如下图所示:

         平铺坐标的显示范围就能和配置的范围相同了,如上图水平、垂直都是10*10。但是视图坐标只用来显示用,逻辑计算还是用立体坐标计算。

3、 坐标转换

3.1、 六边形坐标转换为Unity坐标

        ColumnLength : 列间距(内径的2倍);

        HexTileInnerRadius : 内径 ;

        RowLength : 行间距(外径的1.5f倍) ;

        /// <summary>
        /// 把六边形坐标转换成Unity坐标
        /// (Y 轴为0);
        /// </summary>
        /// <param name="hexCoordinate">六边形地块</param>
        /// <returns>Unity世界坐标,六边形的中心点</returns>
        public static float3 HexCoordineToUnityPos_V3(this HexGrid hexCoordinate)
        {
            float x = (hexCoordinate.x * ColumnLength) + (hexCoordinate.z * HexTileInnerRadius);
            float z = hexCoordinate.z * RowLength;
            return new float3(x, 0, z);
        }

3.2、 Unity坐标转换为六边形坐标

        使用三角函数进行计算:

         代码如下:

        HexTileOuterRadius : 外径

        /// <summary>
        /// 将世界坐标(UnityPos) 转换到目标 HexGrid 下标
        /// (这个方法不是很精确)
        /// </summary>
        /// <param name="unityX">世界坐标</param>
        /// <param name="unityZ">世界坐标</param>
        /// <returns>所属的 HexTile 坐标</returns>
        public static HexGrid UnityPosToHexGrid(float unityX, float unityZ)
        {
            float q = (Sqrt_3 / 3.0f * unityX) - (1.0f / 3.0f * unityZ);
            float r = 2.0f / 3.0f * unityZ;

            return new HexGrid(HexRound(q / HexTileOuterRadius, r / HexTileOuterRadius));
        }
                
        /// <summary>
        /// 坐标规整;
        /// </summary>
        private static Vector2Int HexRound(float q, float r)
        {
            float s = -q - r;
            int rq = Mathf.RoundToInt(q);
            int rr = Mathf.RoundToInt(r);
            int rs = Mathf.RoundToInt(s);
            float q_diff = Mathf.Abs(rq - q);
            float r_diff = Mathf.Abs(rr - r);
            float s_diff = Mathf.Abs(rs - s);
            if (q_diff > r_diff && q_diff > s_diff)
                rq = -rr - rs;
            else if (r_diff > s_diff)
                rr = -rq - rs;
            return new Vector2Int(rq, rr);
        }

        关于坐标规整:

        因为在计算时,通过三角函数会有小数的问题,将其四舍五入成整数,不总是能得到最近的格子坐标,且保证三个数之和为0 。

        因此需要对坐标进行规整计算。

3.3、 立体坐标和视图坐标的转换

        因为存在多种视图坐标和立体坐标的表示方法,因此坐标转换一定是要配套使用。

        但,一般在一个游戏中,会限定同一种表示方法。因此可以直接用如下代码进行计算。

3.3.1、 立体坐标转换为视图坐标

        x轴减去y轴的值,并出2即可:

        /// <summary>
        /// 转换成视图坐标
        /// </summary>
        public static Vector2Int ToViewCoordiante(this Vector3Int logicCoordiante)
        {
            int x = (logicCoordiante.x - logicCoordiante.y) / 2;
            int y = logicCoordiante.z;
            return new Vector2Int(x, y);
        }

3.3.2、 视图坐标转换为立体坐标

        /// <summary>
        /// 转换成立体坐标
        /// </summary>
        public static HexGrid ToCubeCoordiante(this Vector2Int viewGrid)
        {
            int z = viewGrid.y;
            int x = viewGrid.x - viewGrid.y / 2;
            return new HexGrid(x, z);
        }

  • 15
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值