2.5D的45度菱形格子地图大概长这样

 注意0点在左下角,w代表列,h代表行,我这个格子是长60高30的格子,黑色是不能走的部分,白色是可以走的。

可以看出来虽然一个格子高30但是因为格子的排列方式是交错排列的,所以实际上高度每增加15就增加了一行,列数则是固定的每60增加一列,但是因为靠边的部分是交错的,所以还要考虑奇数行的位置偏差。

目前有三种思路,第一种是根据计算出这些格子的每个点的坐标,存到字典里,然后根据坐标点判断点是否在多边形范围内,这个其实是个笨方法,但是也最简单,地图格子如果比较多的话可能性能不太好。

另一种算法是根据函数曲线判断点是在曲线的左边还是右边,地图是由很多格子组成的,这些格子是由线段组成的,这些线段都可以看作函数曲线,0点就是十字坐标轴的0点,所有的同一个方向的曲线都可以通过标准式+偏移值来表达出来

还有一种算法是将地图看成长方形格子,每个长方形格子内都包含中间一个菱形以及四个角的直角三角形,中间菱形格子的行列跟四个角的直角三角形的行列关系是固定的,而中间菱形格子的行列可以直接通过坐标和地图格子的长宽算出来,将长方形当成是由左上,右上,右下,左下四个区域组成的,每个区域其实都是由两个直角三角形组成的,通过坐标直接能算出来是在哪个长方形格子内,然后通过小数点判断是在哪个区域内,然后将坐标点和区域内三角形的锐角点连线,求出这条线和锐角点相邻直角边的角度,通过角度可判断行数的变化情况,这种算法可能会更好点

这里选择了第二种算法,因为我是写完了第二种才想到第三种的,没时间写第三种了,具体代码如下,需要的参数只有地图的格子的长宽,以及地图的总列数

 /// <summary>
        /// 获取行列,45度格子
        /// 地图是由很多格子组成的,这些格子是由线段组成的,这些线段都可以看作函数曲线
        /// 所有的同一个方向的曲线都可以通过标准式+偏移值来表达出来
        /// --------------------------------------------------------------------------------------------------
        /// 其实还有另一种算法,将地图看成长方形格子,每个长方形格子内都包含中间一个菱形以及四个角的直角三角形
        /// 中间菱形格子的行列跟四个角的直角三角形的行列关系是固定的,而中间菱形格子的行列可以直接通过坐标和地图格子的长宽算出来
        /// 将长方形当成是由左上,右上,右下,左下四个区域组成的,每个区域其实都是由两个直角三角形组成的
        /// 通过坐标直接能算出来是在哪个长方形格子内,然后通过小数点判断是在哪个区域内,然后将坐标点和区域内三角形的锐角点连线
        /// 求出这条线和锐角点相邻直角边的角度,通过角度可判断行数的变化情况,这种算法可能会更好点
        /// </summary>
        /// <param name="point"></param>
        /// <returns>x为列,y为行</returns>
        private Vector2Int GetRowsAndColumn(Vector3 point) 
        {
            float pointx = point.x;
            //高度除15,得到真实的行数
            var rowsFloat = point.y / CurMap.gridEdgeHeight;
            //高度除30,得到格子的行数,这里格子指的是将地图看作是长60宽30的长方形格子组成的,通过小数部分可以判断位于格子的上半部分还是下半部分
            var rowsLongFloat = point.y / (CurMap.gridEdgeHeight * 2);
            //得到列数,包含小数
            var columnFloat = point.x / CurMap.gridEdgeWeight;
            //先计算行数的整数
            var rows = Mathf.FloorToInt(rowsFloat);
            //计算格子的行数的整数
            var rowsLong = Mathf.FloorToInt(rowsLongFloat);
            //再计算列数的整数
            var column = Mathf.FloorToInt(columnFloat);
            //行的小数部分
            var rowsLongDecimal = rowsLongFloat - rowsLong;
            //列的小数部分
            var columnDecimal = columnFloat - column;
            //Y轴的偏移值
            float offsetY;
            //偏移值公式的偏移系数
            int n;
            //把地图格子当成长60宽30的长方形,格子的左上和右下区域会在这里进行判定。
            //行数变化趋势为右上的格子行数比较高,左上的格子行数比较高,根据点在函数曲线的左右两边判断行数
            if ((rowsLongDecimal >= 0.5f && columnDecimal < 0.5f) || (rowsLongDecimal <= 0.5f && columnDecimal > 0.5f))
            {
                //标准式y = 0.5x + 行高 先求出标准函数曲线上对应x的高度y,这个式子是根据地图格子的边长和角度推导出来的,
                //即第0个菱形格子的左上边符合的函数曲线
                var y = point.x / 2 + CurMap.gridEdgeWeight;
                //然后再计算Y轴的偏移,就是实际的坐标相对于标准式的高度的偏差,判断偏移公式的系数z,
                //这里用FloorToInt是因为一个格子的左上和右下区域,其小数部分不会超过10导致整数部分发生变化(这是通过不断断点测试发现的,具体原理不明),
                //小数区域在0-1.0之间不会超过1.0,所以可以放心的直接向下取整
                n = Mathf.FloorToInt((y - point.y) / (CurMap.gridEdgeWeight / 2));
                //偏移式 标准式变形后+上偏移值得到的 x =  2y - 列宽 / 2 + (偏移系数 - 1) * 列宽 这个式子是通过不断测试验证出来的,没有什么道理
                offsetY = point.y * 2 - CurMap.gridEdgeWeight / 2 + (n - 1) * CurMap.gridEdgeWeight;
                //判断X点在曲线的左边还是右边,因为是在左上和右下区域,行数变化趋势为左上的格子行数比较高,所以在右边的话行数要-1
                if (point.x > offsetY)
                {
                    rows--;
                }
            }
            //格子的左下和右上区域会在这里进行判定
            else if ((rowsLongDecimal > 0.5f && columnDecimal >= 0.5f) || (rowsLongDecimal < 0.5f && columnDecimal <= 0.5f))
            {
                //标准式y = 行高 - 0.5x + 列数 * 1列的长度的一半 先求出标准函数曲线上对应x的高度y,这个式子是根据地图格子的边长和角度推导出来的,
                //即第0行最后一个菱形格子的右上边符合的函数曲线
                var y = CurMap.gridEdgeHeight - point.x / 2 + CurMap.column * (CurMap.gridEdgeWeight / 2);
                //然后再计算Y轴的偏移,就是实际的坐标y相对于标准式的高度y的偏差,判断偏移公式的系数z
                //这里用RoundToInt四舍五入的方式是因为高度偏差除以CurMap.gridEdgeHeight * 2后得到的其实是两行的百分比,每0.5就相当于一行了,所以要四舍五入
                //而且因为左下和右上区域的下方公式,其小数部分会跨过1.0导致整数部分发生变化(这是通过不断断点测试发现的,具体原理不明),所以要用四舍五入的方式
                n = Mathf.RoundToInt(Math.Abs((y - point.y) / (CurMap.gridEdgeHeight * 2)));
                if (y - point.y < 0)
                {
                    n = n * -1;
                }
                //偏移式 标准式变形后+上偏移值得到的 x = 行高 - 2y + (列数 - 偏移系数) * 列宽,这个式子是通过不断测试验证出来的,没有什么道理
                offsetY = CurMap.gridEdgeWeight / 2 - point.y * 2 + (CurMap.column - n) * CurMap.gridEdgeWeight;
                //判断X点在曲线的左边还是右边,因为是在左下和右上区域,行数变化趋势为右上的格子行数比较高,所以在左边的话行数要-1
                if (point.x < offsetY)
                {
                    rows--;
                }
            }
            //奇数行的X要减去30,因为地图数据的格子是+了30,角色走在格子上,所以角色的坐标就是格子的坐标,所以角色的X坐标-30,就相当于把地图的格子左移了30
            //简单来说就是奇变偶不变!
            pointx -= (rows % 2) * (CurMap.gridEdgeWeight / 2);
            column = Mathf.FloorToInt(pointx / CurMap.gridEdgeWeight);
            return new Vector2Int(column, rows);
        }

第三种算法貌似之前就有大佬实现了,而且比我想的更加简洁,不过是C++的代码,但是原理很清晰,这里转发下连接

【瓦片地图】瓦片地图坐标转换_2.5d游戏地图-CSDN博客

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐