# AOI 动态视野系统完整指南 本指南全面介绍 AOI(Area of Interest)系统的架构设计、实现原理和使用方法。 --- ## 目录 1. [整体架构](#整体架构) 2. [延迟离开机制 (Delayed Leave)](#延迟离开机制-delayed-leave) 3. [完整集成示例](#完整集成示例) 4. [核心 API](#核心-api) --- ## 整体架构 ### 系统职责 AOI 系统负责管理游戏世界中的对象位置和视野关系。它将地图划分为网格(灯塔),并追踪观察者(Watcher)和被观察者(Marker)的移动,实时触发视野变化回调。 ### 核心概念 - **Watcher(观察者)**:玩家,有视野范围,需要接收视野内被观察者的变化通知。 - **Marker(被观察者)**:NPC、怪物、部队或其它玩家,被观察者只属于一个灯塔。 - **Tower(灯塔)**:地图划分的网格单元,管理其范围内的观察者和被观察者。 --- ## 延迟离开机制 (Delayed Leave) 为了优化观察者频繁在灯塔边界移动导致的数据抖动(频繁的 Enter/Leave),本系统引入了 **延迟离开** 机制。 ### 工作原理 1. **离开时不立即清理**:当观察者(Watcher)移动并离开某个灯塔(Tower)的视野范围时,系统**不会**立即将其从该灯塔的观察者列表中移除。 2. **加入延迟列表**:相反,该灯塔会被加入观察者的 `delayLeaveTowers` 列表中,并设置一个过期时间(例如 3 秒后)。 3. **在延迟期间**:观察者依然能收到该灯塔内 Marker 的所有更新(Enter/Move/Update),就好像还在视野内一样。 4. **重入取消**:如果观察者在过期前重新进入该灯塔的视野范围,延迟离开任务会被立即取消,无需重新发送 Enter 数据(避免了 Delete -> Add 的开销)。 5. **过期清理**:只有当延迟时间过期后,通过 `AOI.Tick(now)` 方法的定期检查,观察者才会被真正从灯塔中移除。 ### 优势 - **减少重复推送**:避免了玩家在边界反复横跳时产生的大量 Create/Delete 协议。 - **提升体验**:视野边缘的对象切换更加平滑。 --- ## 完整集成示例 ### 基础集成 ```go package main import ( "time" "f1-game/internal/algorithm/aoi/tower" "f1-game/internal/enum" ) func main() { // 1. 创建 AOI 系统 aoiSys := tower.New(tower.Config{ Width: 1000, Height: 1000, TowerWidth: 50, TowerHeight: 50, TowerPointOffset: 0, TowerViewRange: 1, }) // 2. 设置回调函数 // 观察者进入视野 aoiSys.SetOnWatcherEnter(func(watcher *tower.Object, towers tower.Towers) { // 通知 watcher:看见了这些 towers 里的所有 markers // send(watcher.ID, "add", towers.Markers()) }) // 观察者移动(视野变化) aoiSys.SetOnWatcherMove(func(watcher *tower.Object, addTowers tower.Towers, delTowers tower.Towers) { // 通知 watcher:看见 addTowers 里的 markers // 通知 watcher:移除 delTowers 里的 markers (延迟离开机制会确保这里只在真正需要移除时触发吗? // 不,delTowers 是视野逻辑上的离开。物理上的数据移除由 AOI 内部管理,但业务层可以根据 addTowers/delTowers // 决定是否立即通知客户端删除,或者相信服务器持续推送更新直到彻底断开) // // 通常建议: // addTowers -> 推送 CREATE 协议 // delTowers -> 推送 DELETE 协议 (客户端立即从屏幕移除) // * 注意:虽然客户端移除了,但在服务器端,Watcher 可能还会短暂接收到来自 delTowers 的 Update用于保持数据一致性,直到 Tick 清理。 }) // Marker 动作回调 aoiSys.SetOnMarkerEnter(func(marker *tower.Object, tower *tower.Tower) { // 通知该 tower.watchers:有 marker 进入 }) aoiSys.SetOnMarkerMove(func(marker *tower.Object, newTower *tower.Tower, oldTower *tower.Tower) { // 通知变动 }) aoiSys.SetOnMarkerLeave(func(marker *tower.Object, tower *tower.Tower) { // 通知 tower.watchers:marker 离开 }) // 3. 启动 Tick 循环 (处理延迟离开清理) ticker := time.NewTicker(1 * time.Second) go func() { for t := range ticker.C { aoiSys.Tick(t.Unix()) } }() // 4. 业务循环 watcher := tower.NewWatcher(1001, enum.ObjectType_Player, 500, 500, 1) aoiSys.Enter(watcher) aoiSys.Move(1001, 600, 600) aoiSys.Leave(1001) } ``` ### 直通式回调:OnMarkerUpdateID 系统提供 UpdateID 回调,允许业务层传递自定义更新 ID 和上下文。 ```go aoiSys.SetOnMarkerUpdateID(func(marker *Object, tower *Tower, updateID uint32, funcName string) { // 业务处理 }) aoiSys.UpdateID(marker, 100, "LevelUp") ``` --- ## 核心 API ### AOI 系统 ```go // 创建 func New(config Config) *AOI // 生命周期 func (p *AOI) Enter(object Object) bool func (p *AOI) Move(objectID int64, x, y int32) bool func (p *AOI) Leave(objectID int64) func (p *AOI) Update(object Object) bool // 驱动 func (p *AOI) Tick(now int64) // 必须定期调用,清理过期的延迟离开连接 // 查询 func (p *AOI) GetObject(id int64) *Object func (p *AOI) GetTower(x, y int32) *Tower ``` ### 回调设置 通过 `SetOn...` 方法设置回调。 ```go func (p *AOI) SetOnWatcherEnter(callback OnWatcherEnterCallback) func (p *AOI) SetOnWatcherMove(callback OnWatcherMoveCallback) // 注意:OnWatcherLeave 已移除,Watcher 离开 AOI 系统时直接清理,不触发特定逻辑回调(通常配合 Leave 接口自行处理) func (p *AOI) SetOnMarkerEnter(callback OnMarkerEnterCallback) func (p *AOI) SetOnMarkerMove(callback OnMarkerMoveCallback) func (p *AOI) SetOnMarkerLeave(callback OnMarkerLeaveCallback) func (p *AOI) SetOnMarkerUpdate(callback OnMarkerUpdateCallback) func (p *AOI) SetOnMarkerUpdateID(callback OnMarkerUpdateIDCallback) ```