node.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. package astar6
  2. import "fmt"
  3. // Node A*寻路节点
  4. // 采用预计算策略,空间换时间,初始化时计算好所有坐标
  5. // 优化后的内存布局:将bool字段移到末尾,使用静态数组替代slice
  6. type Node struct {
  7. x, y int32 // 网格坐标(offset坐标系)
  8. // 预计算的坐标(空间换时间)
  9. pixelX, pixelY float64 // 像素坐标,用于视觉距离计算
  10. pixelXInt, pixelYInt int64 // 预计算的整数像素坐标(×1000),优化calLinearityPenalty
  11. cube [3]int32 // cube坐标(x,y,z),用于启发式距离计算
  12. // 邻居节点信息(优化:使用静态数组替代slice,内联数据)
  13. neighborNodes [6]*Node // 邻居节点指针
  14. neighborCosts [6]int32 // 邻居移动代价
  15. neighborCount int32 // 实际邻居数量(0-6)
  16. // bool字段移到末尾,减少内存填充
  17. isWalkable bool // 节点是否可行走
  18. isOdd bool // 是否奇数列,影响邻居关系和像素坐标
  19. }
  20. // NewNode 创建新节点并预计算各种坐标
  21. // 初始化时就计算好所有坐标,避免寻路时重复计算
  22. // 坐标系说明:
  23. // - offset坐标(x,y): 网格坐标,直接用于地图索引
  24. // - pixel坐标: 像素坐标,用于视觉距离计算(考虑奇偶列偏移)
  25. // - cube坐标(x,y,z): 三维立方体坐标,用于计算启发式距离,x+y+z=0
  26. func NewNode(x, y int32, isWalkable bool) *Node {
  27. n := &Node{
  28. x: x,
  29. y: y,
  30. isWalkable: isWalkable,
  31. isOdd: (x & 0x1) == 0x1, // 位运算判断奇偶
  32. }
  33. // 预计算像素坐标
  34. // odd-q坐标系:奇数列向上偏移半个六边形高度
  35. n.pixelX = float64(x) * HEX_WIDTH
  36. n.pixelY = float64(y) * HEX_HEIGHT
  37. if n.isOdd {
  38. n.pixelY -= HEX_HEIGHT / 2 // 奇数列Y坐标上移
  39. }
  40. // 优化:预计算整数像素坐标(×1000),避免运行时浮点运算
  41. n.pixelXInt = int64(n.pixelX * 1000)
  42. n.pixelYInt = int64(n.pixelY * 1000)
  43. // 预计算cube坐标(offset -> cube转换)
  44. // 公式参考: https://www.redblobgames.com/grids/hexagons/#conversions-offset
  45. n.cube[0] = x // cube.x = offset.x
  46. n.cube[2] = y - (x-(x&1))/2 // cube.z = offset.y - (offset.x - offset.x&1) / 2
  47. n.cube[1] = -n.cube[0] - n.cube[2] // cube.y = -cube.x - cube.z (保证x+y+z=0)
  48. return n
  49. }
  50. // AddNeighbor 添加邻居节点
  51. // 只添加可行走的邻居,距离为移动代价
  52. // 优化:使用静态数组,避免动态内存分配
  53. func (p *Node) addNeighbor(node *Node, distance int32) {
  54. if node != nil && node.isWalkable && p.neighborCount < 6 {
  55. p.neighborNodes[p.neighborCount] = node
  56. p.neighborCosts[p.neighborCount] = distance
  57. p.neighborCount++
  58. }
  59. }
  60. // X 获取节点X坐标
  61. func (p *Node) X() int32 { return p.x }
  62. // Y 获取节点Y坐标
  63. func (p *Node) Y() int32 { return p.y }
  64. // IsOdd 判断是否在奇数列
  65. func (p *Node) IsOdd() bool { return p.isOdd }
  66. // IsEven 判断是否在偶数列
  67. func (p *Node) IsEven() bool { return !p.isOdd }
  68. // SetWalkable 设置节点的可行走状态
  69. // 用于动态修改节点,例如在寻路过程中设置障碍
  70. func (p *Node) SetWalkable(walkable bool) {
  71. p.isWalkable = walkable
  72. }
  73. // PixelX 获取像素X坐标
  74. func (p *Node) PixelX() float64 { return p.pixelX }
  75. // PixelY 获取像素Y坐标
  76. func (p *Node) PixelY() float64 { return p.pixelY }
  77. // Cube 获取节点的cube坐标数组
  78. func (p *Node) Cube() [3]int32 {
  79. return p.cube
  80. }
  81. // String 实现fmt.Stringer接口,方便调试输出
  82. func (p *Node) String() string {
  83. return fmt.Sprintf("{%d, %d}", p.x, p.y)
  84. }