PC-HEWEI\hewei b8162da97f update vor 4 Monaten
..
README.md 6ec054be01 1 vor 4 Monaten
aoi.go b8162da97f update vor 4 Monaten
aoi_test.go 6ec054be01 1 vor 4 Monaten
object.go 6ec054be01 1 vor 4 Monaten
objects.go 6ec054be01 1 vor 4 Monaten
tower.go 6ec054be01 1 vor 4 Monaten

README.md

AOI 动态视野系统完整指南

本指南全面介绍 AOI(Area of Interest)系统的架构设计、实现原理和使用方法。


目录

  1. 整体架构
  2. 延迟离开机制 (Delayed Leave)
  3. 完整集成示例
  4. 核心 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 协议。
  • 提升体验:视野边缘的对象切换更加平滑。

完整集成示例

基础集成

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 和上下文。

aoiSys.SetOnMarkerUpdateID(func(marker *Object, tower *Tower, updateID uint32, funcName string) {
    // 业务处理
})

aoiSys.UpdateID(marker, 100, "LevelUp")

核心 API

AOI 系统

// 创建
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... 方法设置回调。

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)