游戏的AOI算法应该算作游戏的基础核心了,许多逻辑都是因为AOI进出事件驱动的,许多网络同步数据也是因为AOI进出事件产生的。因此,良好的AOI算法和基于AOI算法的优化,是提高游戏性能的关键。
我在实践中所熟知的游戏AOI算法大致有两种,在此做一些总结,顺便梳理一下,打算设计出一套统一的接口封装不同的算法实现(网络上还有些其他算法,因为不熟悉不作记录了)。我所记录的这两种算法也算经典了,一个叫做网格法,一个叫做双链表法。具体细节可参考如下:https://zhuanlan.zhihu.com/p/201588990
根据 文章描述,自己动手初步实现了十字链表篇算法,该版本支持不同半径AOI。
grid_link.go代码如下:
package grid
import "fmt"
// 十字链表法, 实现AOI
// 节点增加左右哨兵功能, 同时增加地标功能
type nodeType int
var NormalNode nodeType = 0 // 正常玩家节点
var LeftSentinel nodeType = 1 // 左哨兵节点
var RightSentinel nodeType = 2 // 右哨兵节点
var LandMark nodeType = 3 // 地标节点
type Direction int
var XDirect Direction = 0
var YDirect Direction = 1
var MaxArea int = 10 // 最大视距
type Pos [2]int
// 双链表结构
type Node struct {
EntityId int
PreNode *Node
NextNode *Node
Pos Pos
NodeType nodeType
}
type SceneAOIManagerByLink struct {
SceneId int
Length int
Width int
XNodeList *Node
YNodeList *Node
XLandMarkList []*Node //地标节点,用于二分查找
YLandMarkList []*Node
}
func initNode(EntityId, X, Y int, NodeType nodeType) *Node {
node := &Node{
EntityId: EntityId,
Pos: Pos{X, Y},
NodeType: NodeType,
}
return node
}
func minInt(x, y int) int {
if x <= y {
return x
}
return y
}
func maxInt(x, y int) int {
if x >= y {
return x
}
return y
}
func disInt(node1, node2 *Node) int {
return (node1.Pos[0]-node2.Pos[0])*(node1.Pos[0]-node2.Pos[0]) + (node1.Pos[1]-node2.Pos[1])*(node1.Pos[1]-node2.Pos[1])
}
func (scene *SceneAOIManagerByLink) initLandMarkList(direct Direction) {
var FirstNode *Node
var LandMarkList []*Node = make([]*Node, 0)
if direct == XDirect {
FirstNode = scene.XNodeList
} else {
FirstNode = scene.YNodeList
}
LandMarkList = append(LandMarkList, FirstNode)
x := FirstNode.Pos[0]
y := FirstNode.Pos[1]
for {
x = x + MaxArea*int(direct^YDirect)
y = y + MaxArea*int(direct^XDirect)
if x > scene.Length+MaxArea || y > scene.Width+MaxArea {
break
}
node := initNode(0, x, y, LandMark)
FirstNode.NextNode = node
node.PreNode = FirstNode
FirstNode = node
LandMarkList = append(LandMarkList, node)
}
if direct == XDirect {
scene.XLandMarkList = LandMarkList
} else {
scene.YLandMarkList = LandMarkList
}
}
func (scene *SceneAOIManagerByLink) Travel() {
fmt.Printf("Pos info:\nX: ")
node := scene.XNodeList
for node != nil {
fmt.Printf("(%d, %d) ", node.Pos[0], node.Pos[1])
node = node.NextNode
}
fmt.Printf("\nY: ")
node = scene.YNodeList
for node != nil {
fmt.Printf("(%d, %d) ", node.Pos[0], node.Pos[1])
node = node.NextNode
}
fmt.Println()
}
func (scene *SceneAOIManagerByLi