一种创新的正六边形定位(全过程详解)

生活便是这样,你越努力挣扎,越是越陷越深

提纲

■为了让您按需阅读,您可以点击这里来快进
■但我并不建议快进,因为这篇文章有它自己的逻辑

  • 若您不想阅读我的吐槽,您可以点击这里直接从正文看起
  • 若您连讲解都懒得看,就想白嫖代码,您可以点击这里直接白嫖
  • 若您啥也不想看,但是多余了5毛钱,您可以点击豪华的这里施舍一下

序言

■为什么会有这篇?

  • 近来闲来无事,于是想写个六边形的游戏地图玩一玩。一拍脑门说干就干,一会儿就画出了基本的地图形状。然后……我要怎么确定地图点击的是哪格呢?Emm…于是,再折腾了一会后,我打开了百度,开始了一波尝试搜(白)索(嫖)之旅。
    然鹅……

白嫖之旅

■在翻了一堆帖子后,我发现,我找到的博客都是这种画风的:

  • 只贴代码不给原理的
  • 用不了的
  • 复制下来一手报错的
  • 试图写原理但写的乱七八糟的(可能是我数学太渣)
  • ……

    于是乎, 在挣扎了半天后,作为一个高中在读的理工男,我瞬间决定: 自!己!写!

原理

在开始之前,我们应当知道:
■正方形地图与正六边形地图坐标系是不一样的:

  • 来一张嫖来的正方形方格示意图
    来源:https://blog.csdn.net/kun1234567/article/details/39058995
    (来源见水印,侵删)
  • 而正六边形呢?可以如下:
    来源:https://blog.csdn.net/kun1234567/article/details/39058995
    (来源见水印,侵删)
    也可以将X轴修改为弯曲的,而y轴不变。如下:

    (这张图看起来可能有些莫名其妙,但后面会详细解释)
    本帖采取的是第二种方式,即将x轴设为倾斜的折线,y轴保持直线不变。具体坐标请看图片上的标注,有利于加深理解。
    当然,这并非就这样死板。具体坐标轴的选取应当以您的具体需求为标准而作调整。

开始定位

完成坐标轴的搭建后,接下来就是本文讲述的重点,也是难点:如何完成(x,y)的实际坐标与正六边形坐标的转换?对于这个问题,其他帖子给出了自己的不同作法。具体的此处暂且不谈,但有一个共性, 借助矩形格完成坐标的辅助转换 。现在的问题就在于:这个矩形怎么画?
如果您看过其他帖子,你会发现,许多博客是这样画的:

(来源见水印,侵删)
这样带来了一个问题,即大格子中涉及到三个六边形,判断其到底归属于哪个并不好解决。
于是,在经历多次尝试后,我把格子画成了这样:
来源:Shen
(图片上p的不太标准,各位看官谅解)
边长1/2为宽(设为d)和高的1/2(设为h)画格子
这有什么用呢?您可以看到,这样细分后,每个长方形格子只涉及一个或两个对应的六边形,大大简化了计算。
为了方便的表示格子的位置,我们以这个格子再建一个辅助坐标系,用变量 lie1,hang1 分别表示矩形格子的横纵坐标,接下来,就可以愉快的玩耍啦!
由简单的几何分析可知,假设实际坐标为(x,y),则:

int lie1=x/d;
int hang1=y/h;

注:此处使用语言为Java,如果您使用其他语言,您可能需要进行相应的转换,以确保得到的结果为向下取整。如:JavaScript使用Math.floor(),python使用x//d等
接下来我们要干的事情,就是把这个坐标转化为正六边形地图的坐标,这里的重点是: 分类!

分类

(先声明两个变量,他们表示正六边形真正的坐标)

int lie=0,hang=0;

■在仔细观察所得到的方形区域后,我们发现所划分出来的矩形所在位置大概有这几种类型:

  1. 如图:

    这种情况非常简单,通过观察,发现它的横坐标恰好就是lie1/2,而纵坐标则为(hang1-1)/6
    那么如何确定我们点到的就是这里呢?这就需要在if里面进行判断。通过一定的观察(您可以对照图片),我们可以逐渐发现:这两列所代表的列数减一后,除以六所得的余数恰好为0或者1。故,综上,我们可以得到如下表达式:
if((lie1-1)%6==0||(lie1-1)%6==1){//23 78
         lie=lie1/3;
         hang=hang1/2;
      }

(此处的百分号在Java里表示求余。若您使用其他语言,请使用相应的求余运算)

2.第二种情况与第一种类似,如图:

在这种情况下每个小矩形所对应的正六边形也有且仅有一个。与第一种情况类似(请读者自行分析),我们可以得到如下表达式:

else if((lie1-4)%6==0||(lie1-4)%6==1){//4,5 10,11
         lie=lie1/3;
         hang=(hang1-1)/2;
      }

3.好了,现在我们把小方形只对应一个的解决了,那么接下来我们要处理的就是对应两个的了。
观察后发现,他们有这四种位置:

(图上每种颜色代表一种位置)
而进一步的观察可以发现,在这四种情况中,小矩形的斜线有两种走向:从左上到右下和从右上到左下。如图:

读到这里,你可能很好奇,图片上的斜线阴影,以及隔壁写的表达式是什么意思?实际上,为了判断您实际的点是在斜线左侧,还是在斜线右侧,此处我以小矩形的左上角为坐标原点建了一个局部坐标系,并利用简单的解析几何知识得到了在阴影部分所满足的表达式。这样对于每一个矩形我们都可以利用这样的相对位置来判断它是从属于哪一个正六边形,也就间接达到了目的。
相信有朋友就会问了:你现在建的系是把矩形左上角的坐标设为了(0,0),但是我点的点又不是(0,0),我要怎么得到这个相对坐标呢?我的回答是:减法与求余
以上图的橙色区域为例,通过观察与猜想我们可以发现,要得到这个相对坐标,它的相对x坐标就是将它的实际x坐标与6d相除所得到的余数,而y坐标就是与2h相处得到的余数。
我相信肯定还会有朋友问:你这个d和h是什么意思?呃……关于这个我不得不说,您看贴的时候不太仔细,在开始定位的时候我就解释过d和h是干嘛的。当然在这里我再来一遍: 正六边形边长1/2为宽(设为d),高的1/2设为h
于是对四个情况进行整体分析,我们就得到了以下这串代码:

else if(lie1%6==0){//0 6
         if(hang1%2==0){//左上
            if((x%(6*d))/d+(y%(2*h)/h)<=1){//左边
               lie=lie1/3-1;
               hang1=hang1/2-1;
            }else{
               lie=lie1/3;
               hang=hang1/2;
            }
         }else{//左下
            if((y-h)%(2*h)<h/d*(x%(6*d))){
               lie=lie1/3;
               hang=(hang1-1)/2;
            }else{
               lie=lie1/3+1;
               hang=(hang1-1)/2;
            }
         }
      }else{//3,9
         if(hang1%2==0){//偶数 |\|
            if(y%(2*h)<h/d*(x%(3*d))){
               lie=lie1/3;
               hang=(hang1-1)/2;
            }else{
               lie=lie1/3-1;
               hang=hang1/2;
            }
         }else{//奇数 |/|
            if((x%(3*d))/d+((y-h)%(2*h)/h)>1){
               lie=lie1/3;
               hang1=(hang1-1)/2;
            }else{
               lie=lie1/3-1;
               hang=(hang1-1)/2;
            }
         }
      }

我相信这时候你内心肯定是一句卧槽,怎么会写的这么长?但如果你认真去看,这就是对每一种情况的细分而已,其原理都是一模一样的。看似长,其实真正执行的并不多。
那么到这里我们就差不多啦~

全部代码

我相信肯定有人喜欢白嫖,那么这里就是了:

 public int[] getPointPosition(int x,int y){
      //传入地图真实点击坐标
      int[] position=new int[2];
      int lie1=(int)(x/d),hang1=(int)(y/h);
      int lie=0,hang=0;
      if((lie1-1)%6==0||(lie1-1)%6==1){//23 78
         lie=lie1/3;
         hang=hang1/2;
      }else if((lie1-4)%6==0||(lie1-4)%6==1){//4,5 10,11
         lie=lie1/3;
         hang=(hang1-1)/2;
      }else if(lie1%6==0){//0 6
         if(hang1%2==0){//左上
            if((x%(6*d))/d+(y%(2*h)/h)<=1){//左边
               lie=lie1/3-1;
               hang1=hang1/2-1;
            }else{
               lie=lie1/3;
               hang=hang1/2;
            }
         }else{//左下
            if((y-h)%(2*h)<h/d*(x%(6*d))){
               lie=lie1/3;
               hang=(hang1-1)/2;
            }else{
               lie=lie1/3+1;
               hang=(hang1-1)/2;
            }
         }
      }else{//3,9
         if(hang1%2==0){//偶数 |\|
            if(y%(2*h)<h/d*(x%(3*d))){
               lie=lie1/3;
               hang=(hang1-1)/2;
            }else{
               lie=lie1/3-1;
               hang=hang1/2;
            }
         }else{//奇数 |/|
            if((x%(3*d))/d+((y-h)%(2*h)/h)>1){
               lie=lie1/3;
               hang1=(hang1-1)/2;
            }else{
               lie=lie1/3-1;
               hang=(hang1-1)/2;
            }
         }
      }
      position[0]=lie;
      position[1]=hang;
      return position;
   }

尾声

*这篇博文陆陆续续写了两个星期,都是在我学习的间隙写的,所以写的难免有些粗糙,各位谅解。其中有些原理因为不好描述,所以讲的可以不是很到位。但如果您自己尝试按着我的思路推一推,您会发现我也讲完了该讲的。
*这是我第一次写博客,用的是手机端的markdown编辑器,所以不一定适配电脑的排版,也请您谅解。
*其实原本还打算给一个完整的项目例子的,包括怎么画图、怎么点击、怎么坐标之间相互转换……但是因为是从原来的项目提取出来,目前还都是bug。如果您期待,请评论回复。您的评论对我很重要。
*最后若您觉得这篇文章不错,欢迎扫一扫下面的赞赏码。或许您付不起233、付不起66、付不起23,但即使有五毛钱给我搞包辣条也挺好的。谢谢。

(欢迎土豪,另,微信不加人,谢谢合作)


Shen.Ying 最后完成于2019.11.2

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值