六边形网格 (HexGrid)

Swift 5.6+ Swift Package Manager Compatible MIT License Documentation

Development in progress!

API might change without further notice until first major release 1.x.x.

HexGrid 库提供了一种简单直观的方式来处理六边形网格。它在后台处理所有的数学计算,因此您可以专注于更重要的事情。

该库旨在用于通用的后端。 因此,它不执行任何 UI 或渲染。 但是,它提供了渲染所需的计算。

功能

接下来是什么?

HexGrid 的实际应用

HexGrid 演示应用程序,使用 SpriteKit。(也可在 App Store 中获得。) HexGrid 演示应用程序,使用 SwiftUI。

入门指南

将 HexGrid 集成到您的项目中

将 HexGrid 作为依赖项添加到您的 Package.swift 文件中。

import PackageDescription

let package = Package(
name: "MyApp",
dependencies: [
...
// Add HexGrid package here
.package(url: "https://github.com/fananek/hex-grid.git", from: "0.4.11")
],
...
targets: [
        .target(name: "App", dependencies: [
            .product(name: "HexGrid", package: "hex-grid"),
            ...

将 HexGrid 包导入到您的代码中。

import HexGrid
...
// your code goes here

使用方法

创建网格

可以使用一组单元格坐标初始化网格,或者 HexGrid 可以为您生成一些标准形状的网格。

标准形状网格

例如

...
// create grid of hexagonal shape
var grid = HexGrid(shape: GridShape.hexagon(10))
// or rectangular shape
var grid = HexGrid(shape: GridShape.rectangle(8, 12))
// or triangular shape
var grid = HexGrid(shape: GridShape.triangle(6))

有关更多详细信息以及可用网格形状的完整列表,请参阅下面的 GridShape 部分。

自定义网格

示例

...
// create new HexGrid

let gridCells: Set<Cell> = try [
Cell(CubeCoordinates(x:  2,  y: -2,  z:  0)),
Cell(CubeCoordinates(x:  0,  y: -1,  z:  1)),
Cell(CubeCoordinates(x: -1,  y:  1,  z:  0)),
Cell(CubeCoordinates(x:  0,  y:  2,  z: -2))
]
var grid = HexGrid(cells: gridCells)
...

初始化器和绘图

请注意,假设您要绘制网格,您需要考虑是为每个单元格 (hexSize) 传递大小还是为整个网格 (pixelSize) 传递大小到初始化器。(参见下面的“绘制网格”。)

HexGrid <-> JSON

HexGrid 符合 swift Codable 协议,因此可以轻松地编码为 JSON 或从 JSON 解码。

示例

// encode (grid to JSON)
let grid = HexGrid(shape: GridShape.hexagon(5) )
let encoder = JSONEncoder()
let data = try encoder.encode(grid)
// decode (JSON to grid)
let decoder = JSONDecoder()
let grid = try decoder.decode(HexGrid.self, from: data)

网格操作示例

几乎所有函数都有两个变体。 一个使用 Cell,另一个使用 CubeCoordinates。 使用那些更好地满足您需求的。

获取坐标处的单元格

let cell = grid.cellAt(try CubeCoordinates(x: 1, y: 0, z: -1))

验证坐标

检查坐标是否有效(意味着它在网格的 cells 数组中具有相应的 Cell)。

// returns Bool
isValidCoordinates(try CubeCoordinates(x: 2, y: 4, z: -6))

获取被阻挡或未被阻挡的单元格

let blockedCells = grid.blockedCells()
// or
let nonBlockedCells = grid.nonBlockedCells()

获取单个相邻单元格

// get neighbor for a specific Cell
let neighbor = try grid.neighbor(
            for: someCell,
            at: Direction.Pointy.northEast.rawValue)

// get just neighbor coordinates
let neighborCoordinates = try grid.neighborCoordinates(
            for: someCoordinates,
            at: Direction.Pointy.northEast.rawValue)

获取所有相邻单元格

// get all neighbors for a specific Cell
let neighbors = try grid.neighbors(for: someCell)

// get only coordinates of all neighbors
let neighborsCoords = try grid.neighbors(for: someCoordinates)

获取从 A 到 B 的线段

// returns nil in case line doesn't exist
let line = try grid.line(from: originCell, to: targetCell)

获取环

// returns all cells making a ring from origin cell in radius
let ring = try grid.ring(from: originCell, in: 2)

获取填充环

// returns all cells making a filled ring from origin cell in radius
let ring = try grid.filledRing(from: originCell, in: 2)

查找可到达的单元格

// find all reachable cells (max. 4 steps away from origin)
let reachableCells = try grid.findReachable(from: origin, in: 4)

查找最短路径

// returns nil in case path doesn't exist at all
let path = try grid.findPath(from: originCell, to: targetCell)

计算视场 (FOV)

Cell 具有一个名为 isOpaque 的属性。 它的值可以是 truefalse。 基于此信息,可以计算所谓的 视场。 这意味着从网格上的特定位置可见的所有单元格,同时考虑到所有不透明的障碍物。

// set cell as opaque
obstacleCell.isOpaque = true 

为了获得视场,只需调用以下函数。

// find all hexes visible in radius 4 from origin cell
let visibleHexes = try grid.fieldOfView(from: originCell, in: 4)

默认情况下,一旦从原点单元格可以看到单元格的中心,该单元格就被认为是可见的。 如果您还想包括部分可见的单元格,请使用可选参数 includePartiallyVisible

// find all hexes even partially visible in radius 4 from origin cell
let visibleHexesIncludingPartials = try grid.fieldOfView(from: originCell, in: 4, includePartiallyVisible: true)

绘制网格

在内部,HexGrid 使用以下两种方法之一计算所有“像素坐标”

  1. 使用每个 Cell 的大小(存储在 hexSize 属性中)。

...或者...

  1. 使用整个网格的大小(存储在 pixelSize 属性中)。

请注意,hexSizepixelSize 属性在内部都存储为 HexSize 结构。 尽量不要对此感到困惑! HexSize 只是存储 widthheight 值的便捷方式。

您要使用的 HexGrid 初始化器的类型将取决于哪种方法最适合您的用例。 指定 hexSize 时,会自动为您计算 pixelSize;指定 pixelSize 时,也会自动设置 hexSize

虽然无法直接修改 hexSizepixelSize 属性(初始化后),但您可以随时使用 fitGrid(in size: HexSize) 函数设置网格的 pixelSize(并从中重新计算 hexSize)。 请注意,这也会重置 origin 属性。

原点属性

您可以将 HexGridorigin 属性视为 CubeCoordinate 0,0,0Cell 的中心点。

请注意,您可以在初始化时指定 origin,但仅在使用 cellSize 方法时。 指定 pixelSize 时,会自动为您设置原点,以便网格“适合”指定的宽度和高度。

如果您想更改网格的像素坐标,更改 origin 属性非常重要。 更改 origin 将修改所有像素计算函数的返回值。 您可以使用它来为网格应用偏移量,或稍后“重新居中”它。

角像素坐标

通常,在绘制六边形时,您需要每个 Cell 的多边形角的屏幕坐标。

let corners = grid.polygonCorners(for: someCell)

中心像素坐标

此函数返回 Cell 中心的 Point 结构(x: 和 y: 值)。

let screenCoords = grid.pixelCoordinates(for: someCell)

在屏幕坐标处查找单元格

// return cell for specified screen coordinates (or nil if such cell doesn't exists)
let cell = try grid.cellAt(point)

实现基础

有关详细信息,请参见完整的 文档

您应该知道的数据结构

六边形网格 (HexGrid)

表示网格本身,也是 HexGrid 库的入口点。

HexGrid 由一组 Cells 和其他一些属性定义。 它们共同构成网格设置。 换句话说,它将网格单元格放入有意义的上下文中。 因此,大多数可用操作都是直接在网格实例上调用的,因为它只有在这种上下文中(网格设置)才有意义。

属性

Cell

Cell 是网格的构建块。

属性

CubeCoordinates

HexGrid 库中最常用的坐标是立方体坐标系。 这种类型的坐标具有三个轴 x,y 和 z。 唯一的条件是其所有值的总和必须等于零。

// valid cube coordinates
CubeCoordinates(x: 1, y: 0, z: -1) -> sum = 0

// invalid cube coordinates
CubeCoordinates(x: 1, y: 1, z: -1) -> sum = 1 -> throws error

有关更多详细信息,请查看 Amit Patel 的解释

枚举

Orientation

选项

OffsetLayout

OffsetLayout 主要用于矩形形状的网格。 它有两个选项,但它们的含义可能因网格方向而异。

选项

根据六边形的方向,有四种偏移类型。 “row”类型与尖顶六边形一起使用,“column”类型与平顶六边形一起使用。

GridShape

可以将此枚举中的案例传递到 HexGrid 构造函数中,以生成各种形状和大小的网格。

选项

Rotation

选项

Direction

Direction 枚举是一致且人类可识别的方向命名。 使用 direction 枚举不仅更加方便,而且还有助于避免错误。 最好只是说 _“嘿,我在单元格 X 上,想去北方。”_ 而不是想 _“北方方向的索引到底是什么?”_,不是吗?

选项

实际上,每个网格方向都有一组单独的方向。 这是因为两个原因。 首先,某些方向仅对一种或另一种方向有效。 其次,方向原始值会根据方向而移动。

作者

另请参阅参与该项目的 贡献者 列表。

许可证

HexGrid 包中包含的所有代码均受 MIT 许可协议约束。