You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为动态生成的六边形网格中的每个六边形命名并实现点击交互

嘿,我来帮你梳理这个六边形网格的问题!先给你明确几个核心点,再给你重构优化后的代码方案:

六边形网格设计与优化指南

一、先给你吃个定心丸:用Graphics绘制完全可行

你当前用Graphics类绘制六边形的方向是对的,但核心问题是你没有把每个六边形的信息持久化存储——现在你只是在Paint事件里临时生成顶点绘制,后续要点击、引用这些六边形根本找不到对应的对象。所以第一步是要把每个六边形变成可管理的对象。

二、解决“命名/引用”的关键:创建Hexagon类

要给每个六边形命名并方便后续操作,最好的方式是定义一个Hexagon类,把每个六边形的唯一标识(比如行列ID)、中心坐标、顶点、颜色状态都存进去。这样每个六边形都是一个独立的对象,想怎么引用就怎么引用。

比如这个基础的Hexagon类:

Public Class Hexagon
    ' 唯一命名标识,比如"Hex_2_3"代表第2行第3列的六边形
    Public Property Id As String
    ' 六边形中心坐标
    Public Property Center As PointF
    ' 六个顶点(用于绘制和点击检测)
    Public Property Vertices As PointF()
    ' 自定义样式属性
    Public Property FillBrush As Brush = Brushes.Green
    Public Property OutlinePen As Pen = Pens.Black

    ' 构造函数:传入中心坐标和半径,自动生成顶点
    Public Sub New(centerX As Single, centerY As Single, radius As Integer)
        Center = New PointF(centerX, centerY)
        Vertices = GenerateVertices(centerX, centerY, radius)
    End Sub

    ' 生成六边形顶点的工具方法(用弧度计算,更准确)
    Private Function GenerateVertices(centerX As Single, centerY As Single, radius As Integer) As PointF()
        Dim vertices(5) As PointF
        ' 六边形每个顶点间隔60度(π/3弧度)
        For angleIndex As Integer = 0 To 5
            Dim radian As Double = Math.PI / 3 * angleIndex
            vertices(angleIndex) = New PointF(
                centerX + radius * Math.Cos(radian),
                centerY + radius * Math.Sin(radian)
            )
        Next
        Return vertices
    End Function

    ' 判断点击点是否在六边形内部(用于点击检测)
    Public Function ContainsPoint(clickPoint As Point) As Boolean
        Dim isInside As Boolean = False
        Dim p1 As PointF = Vertices(0)
        ' 遍历所有边,用射线法判断点是否在多边形内
        For i As Integer = 1 To Vertices.Length
            Dim p2 As PointF = Vertices(i Mod Vertices.Length)
            If clickPoint.Y > Math.Min(p1.Y, p2.Y) AndAlso clickPoint.Y <= Math.Max(p1.Y, p2.Y) Then
                If clickPoint.X <= Math.Max(p1.X, p2.X) Then
                    Dim xIntersect As Double = (clickPoint.Y - p1.Y) * (p2.X - p1.X) / (p2.Y - p1.Y) + p1.X
                    If p1.X = p2.X OrElse clickPoint.X <= xIntersect Then
                        isInside = Not isInside
                    End If
                End If
            End If
            p1 = p2
        Next
        Return isInside
    End Function
End Class

三、重构网格生成逻辑:把所有六边形存起来

接下来在Form里定义一个集合来存储所有Hexagon对象,把生成网格的逻辑和绘制逻辑分开——Paint事件只负责绘制,数据都提前存在集合里,这样Paint触发时不会重复计算,效率更高。

Public Class Form1
    ' 存储所有六边形的集合
    Private _hexGrid As List(Of Hexagon) = New List(Of Hexagon)()
    ' 网格配置参数(改成常量,方便修改)
    Private Const HexRadius As Integer = 20
    Private Const GridSize As Integer = 10
    ' 六边形的间距计算(基于半径,避免硬编码数值)
    Private ReadOnly HexHorizontalSpacing As Single = HexRadius * 1.5F
    Private ReadOnly HexVerticalSpacing As Single = HexRadius * Math.Sqrt(3)

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ' 窗体加载时生成网格并存储所有六边形
        GenerateHexGrid()
    End Sub

    Private Sub GenerateHexGrid()
        _hexGrid.Clear()
        For row As Integer = 0 To GridSize - 1
            For col As Integer = 0 To GridSize - 1
                ' 计算中心坐标:奇数行要偏移半个垂直间距,实现交错排列
                Dim centerX As Single = HexRadius + col * HexHorizontalSpacing
                Dim centerY As Single = HexRadius + row * HexVerticalSpacing + If(row Mod 2 = 1, HexVerticalSpacing / 2, 0)
                ' 创建六边形对象,用行列号作为唯一ID
                Dim hex = New Hexagon(centerX, centerY, HexRadius)
                hex.Id = $"Hex_{row + 1}_{col + 1}" ' 比如第一行第一列就是Hex_1_1
                _hexGrid.Add(hex)
            Next
        Next
    End Sub

    Private Sub Form1_Paint(sender As Object, e As PaintEventArgs) Handles Me.Paint
        ' 遍历集合绘制每个六边形,简单高效
        For Each hex In _hexGrid
            e.Graphics.FillPolygon(hex.FillBrush, hex.Vertices)
            e.Graphics.DrawPolygon(hex.OutlinePen, hex.Vertices)
            ' 可选:绘制ID文本,方便调试查看命名
            e.Graphics.DrawString(hex.Id, Me.Font, Brushes.Black, hex.Center.X - 18, hex.Center.Y - 10)
        Next
    End Sub

四、实现点击六边形的功能

现在有了存储的Hexagon对象,就可以通过MouseClick事件来检测点击的是哪个六边形,然后执行对应的操作:

Private Sub Form1_MouseClick(sender As Object, e As MouseEventArgs) Handles Me.MouseClick
        ' 遍历所有六边形,判断点击点是否在内部
        For Each hex In _hexGrid
            If hex.ContainsPoint(e.Location) Then
                ' 这里写点击后的操作,比如弹窗提示、改变颜色
                MessageBox.Show($"你点击了:{hex.Id}")
                hex.FillBrush = Brushes.Yellow ' 点击后变色标记
                Me.Invalidate() ' 刷新界面显示变化
                Exit For ' 找到目标六边形后退出循环
            End If
        Next
    End Sub
End Class

五、给你的代码优化建议

  1. 去掉硬编码数值:你之前写的HexagonY +=34、HexagonY=36.75都是基于半径20的固定值,现在用HexVerticalSpacing = HexRadius * Math.Sqrt(3)动态计算,修改半径时不需要改其他代码,灵活性拉满。
  2. 分离数据与视图:把六边形的数据(顶点、ID、状态)和绘制逻辑分开,符合面向对象设计,后续要加拖动、缩放、状态修改等功能都非常方便。
  3. 复用计算逻辑:把生成顶点、点是否在多边形内的逻辑封装到Hexagon类里,避免重复写代码,也方便维护。
  4. 避免Paint事件里的重复计算:Paint事件会频繁触发(比如窗口拖动、最小化恢复),提前把所有六边形生成好存在集合里,每次Paint只需要遍历绘制,性能更好。

内容的提问来源于stack exchange,提问作者Elijah Adams

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:10:47