diff --git a/game/builtin/aoi2d.go b/game/aoi/2d.go similarity index 66% rename from game/builtin/aoi2d.go rename to game/aoi/2d.go index 0a4c6d9..4aa6019 100644 --- a/game/builtin/aoi2d.go +++ b/game/aoi/2d.go @@ -1,24 +1,25 @@ -package builtin +package aoi import ( - "github.com/kercylan98/minotaur/game" "github.com/kercylan98/minotaur/utils/geometry" "github.com/kercylan98/minotaur/utils/hash" "math" "sync" ) -func NewAOI2D(width, height, areaWidth, areaHeight int) *AOI2D { - aoi := &AOI2D{ +func NewTwoDimensional[E TwoDimensionalEntity](width, height, areaWidth, areaHeight int) *TwoDimensional[E] { + aoi := &TwoDimensional[E]{ + event: new(event[E]), width: float64(width), height: float64(height), - focus: map[int64]map[int64]game.AOIEntity2D{}, + focus: map[int64]map[int64]E{}, } aoi.SetAreaSize(areaWidth, areaHeight) return aoi } -type AOI2D struct { +type TwoDimensional[E TwoDimensionalEntity] struct { + *event[E] rw sync.RWMutex width float64 height float64 @@ -26,39 +27,36 @@ type AOI2D struct { areaHeight float64 areaWidthLimit int areaHeightLimit int - areas [][]map[int64]game.AOIEntity2D - focus map[int64]map[int64]game.AOIEntity2D + areas [][]map[int64]E + focus map[int64]map[int64]E repartitionQueue []func() - - entityJoinVisionEventHandles []game.EntityJoinVisionEventHandle - entityLeaveVisionEventHandles []game.EntityLeaveVisionEventHandle } -func (slf *AOI2D) AddEntity(entity game.AOIEntity2D) { +func (slf *TwoDimensional[E]) AddEntity(entity E) { slf.rw.Lock() slf.addEntity(entity) slf.rw.Unlock() } -func (slf *AOI2D) DeleteEntity(entity game.AOIEntity2D) { +func (slf *TwoDimensional[E]) DeleteEntity(entity E) { slf.rw.Lock() slf.deleteEntity(entity) slf.rw.Unlock() } -func (slf *AOI2D) Refresh(entity game.AOIEntity2D) { +func (slf *TwoDimensional[E]) Refresh(entity E) { slf.rw.Lock() defer slf.rw.Unlock() slf.refresh(entity) } -func (slf *AOI2D) GetFocus(guid int64) map[int64]game.AOIEntity2D { +func (slf *TwoDimensional[E]) GetFocus(guid int64) map[int64]E { slf.rw.RLock() defer slf.rw.RUnlock() return hash.Copy(slf.focus[guid]) } -func (slf *AOI2D) SetSize(width, height int) { +func (slf *TwoDimensional[E]) SetSize(width, height int) { fw, fh := float64(width), float64(height) if fw == slf.width && fh == slf.height { return @@ -70,7 +68,7 @@ func (slf *AOI2D) SetSize(width, height int) { slf.setAreaSize(int(slf.areaWidth), int(slf.areaHeight)) } -func (slf *AOI2D) SetAreaSize(width, height int) { +func (slf *TwoDimensional[E]) SetAreaSize(width, height int) { fw, fh := float64(width), float64(height) if fw == slf.areaWidth && fh == slf.areaHeight { return @@ -80,35 +78,15 @@ func (slf *AOI2D) SetAreaSize(width, height int) { slf.setAreaSize(width, height) } -func (slf *AOI2D) RegEntityJoinVisionEvent(handle game.EntityJoinVisionEventHandle) { - slf.entityJoinVisionEventHandles = append(slf.entityJoinVisionEventHandles, handle) -} - -func (slf *AOI2D) OnEntityJoinVisionEvent(entity, target game.AOIEntity2D) { - for _, handle := range slf.entityJoinVisionEventHandles { - handle(entity, target) - } -} - -func (slf *AOI2D) RegEntityLeaveVisionEvent(handle game.EntityLeaveVisionEventHandle) { - slf.entityLeaveVisionEventHandles = append(slf.entityLeaveVisionEventHandles, handle) -} - -func (slf *AOI2D) OnEntityLeaveVisionEvent(entity, target game.AOIEntity2D) { - for _, handle := range slf.entityLeaveVisionEventHandles { - handle(entity, target) - } -} - -func (slf *AOI2D) setAreaSize(width, height int) { +func (slf *TwoDimensional[E]) setAreaSize(width, height int) { // 旧分区备份 - var oldAreas = make([][]map[int64]game.AOIEntity2D, len(slf.areas)) + var oldAreas = make([][]map[int64]E, len(slf.areas)) for w := 0; w < len(slf.areas); w++ { hs := slf.areas[w] - ohs := make([]map[int64]game.AOIEntity2D, len(hs)) + ohs := make([]map[int64]E, len(hs)) for h := 0; h < len(hs); h++ { - es := map[int64]game.AOIEntity2D{} + es := map[int64]E{} for g, e := range hs[h] { es[g] = e } @@ -133,11 +111,11 @@ func (slf *AOI2D) setAreaSize(width, height int) { slf.areaHeight = float64(height) slf.areaWidthLimit = int(math.Ceil(slf.width / slf.areaWidth)) slf.areaHeightLimit = int(math.Ceil(slf.height / slf.areaHeight)) - areas := make([][]map[int64]game.AOIEntity2D, slf.areaWidthLimit+1) + areas := make([][]map[int64]E, slf.areaWidthLimit+1) for i := 0; i < len(areas); i++ { - entities := make([]map[int64]game.AOIEntity2D, slf.areaHeightLimit+1) + entities := make([]map[int64]E, slf.areaHeightLimit+1) for e := 0; e < len(entities); e++ { - entities[e] = map[int64]game.AOIEntity2D{} + entities[e] = map[int64]E{} } areas[i] = entities } @@ -155,22 +133,22 @@ func (slf *AOI2D) setAreaSize(width, height int) { } } -func (slf *AOI2D) addEntity(entity game.AOIEntity2D) { +func (slf *TwoDimensional[E]) addEntity(entity E) { x, y := entity.GetPosition() widthArea := int(x / slf.areaWidth) heightArea := int(y / slf.areaHeight) guid := entity.GetGuid() slf.areas[widthArea][heightArea][guid] = entity - focus := map[int64]game.AOIEntity2D{} + focus := map[int64]E{} slf.focus[guid] = focus - slf.rangeVisionAreaEntities(entity, func(eg int64, e game.AOIEntity2D) { + slf.rangeVisionAreaEntities(entity, func(eg int64, e E) { focus[eg] = e slf.OnEntityJoinVisionEvent(entity, e) slf.refresh(e) }) } -func (slf *AOI2D) refresh(entity game.AOIEntity2D) { +func (slf *TwoDimensional[E]) refresh(entity E) { x, y := entity.GetPosition() vision := entity.GetVision() guid := entity.GetGuid() @@ -183,7 +161,7 @@ func (slf *AOI2D) refresh(entity game.AOIEntity2D) { } } - slf.rangeVisionAreaEntities(entity, func(guid int64, e game.AOIEntity2D) { + slf.rangeVisionAreaEntities(entity, func(guid int64, e E) { if _, exist := focus[guid]; !exist { focus[guid] = e slf.OnEntityJoinVisionEvent(entity, e) @@ -191,7 +169,7 @@ func (slf *AOI2D) refresh(entity game.AOIEntity2D) { }) } -func (slf *AOI2D) rangeVisionAreaEntities(entity game.AOIEntity2D, handle func(guid int64, entity game.AOIEntity2D)) { +func (slf *TwoDimensional[E]) rangeVisionAreaEntities(entity E, handle func(guid int64, entity E)) { x, y := entity.GetPosition() widthArea := int(x / slf.areaWidth) heightArea := int(y / slf.areaHeight) @@ -259,7 +237,7 @@ func (slf *AOI2D) rangeVisionAreaEntities(entity game.AOIEntity2D, handle func(g } } -func (slf *AOI2D) deleteEntity(entity game.AOIEntity2D) { +func (slf *TwoDimensional[E]) deleteEntity(entity E) { x, y := entity.GetPosition() widthArea := int(x / slf.areaWidth) heightArea := int(y / slf.areaHeight) diff --git a/game/aoi/2d_entity.go b/game/aoi/2d_entity.go new file mode 100644 index 0000000..84aae03 --- /dev/null +++ b/game/aoi/2d_entity.go @@ -0,0 +1,12 @@ +package aoi + +import "github.com/kercylan98/minotaur/game" + +// TwoDimensionalEntity 基于2D定义的AOI对象功能接口 +// - AOI 对象提供了 AOI 系统中常用的属性,诸如位置坐标和视野范围等 +type TwoDimensionalEntity interface { + game.Actor + game.Position2D + // GetVision 获取视距 + GetVision() float64 +} diff --git a/game/aoi/2d_events.go b/game/aoi/2d_events.go new file mode 100644 index 0000000..62bcb68 --- /dev/null +++ b/game/aoi/2d_events.go @@ -0,0 +1,35 @@ +package aoi + +type ( + EntityJoinVisionEventHandle[E TwoDimensionalEntity] func(entity, target E) + EntityLeaveVisionEventHandle[E TwoDimensionalEntity] func(entity, target E) +) + +type event[E TwoDimensionalEntity] struct { + entityJoinVisionEventHandles []EntityJoinVisionEventHandle[E] + entityLeaveVisionEventHandles []EntityLeaveVisionEventHandle[E] +} + +// RegEntityJoinVisionEvent 在新对象进入视野时将会立刻执行被注册的事件处理函数 +func (slf *event[E]) RegEntityJoinVisionEvent(handle EntityJoinVisionEventHandle[E]) { + slf.entityJoinVisionEventHandles = append(slf.entityJoinVisionEventHandles, handle) +} + +// OnEntityJoinVisionEvent 在新对象进入视野时将会立刻执行被注册的事件处理函数 +func (slf *event[E]) OnEntityJoinVisionEvent(entity, target E) { + for _, handle := range slf.entityJoinVisionEventHandles { + handle(entity, target) + } +} + +// RegEntityLeaveVisionEvent 在新对象离开视野时将会立刻执行被注册的事件处理函数 +func (slf *event[E]) RegEntityLeaveVisionEvent(handle EntityLeaveVisionEventHandle[E]) { + slf.entityLeaveVisionEventHandles = append(slf.entityLeaveVisionEventHandles, handle) +} + +// OnEntityLeaveVisionEvent 在新对象离开视野时将会立刻执行被注册的事件处理函数 +func (slf *event[E]) OnEntityLeaveVisionEvent(entity, target E) { + for _, handle := range slf.entityLeaveVisionEventHandles { + handle(entity, target) + } +} diff --git a/game/builtin/aoi2d_test.go b/game/aoi/2d_test.go similarity index 75% rename from game/builtin/aoi2d_test.go rename to game/aoi/2d_test.go index 30bed7a..4f1a503 100644 --- a/game/builtin/aoi2d_test.go +++ b/game/aoi/2d_test.go @@ -1,8 +1,8 @@ -package builtin_test +package aoi_test import ( "fmt" - "github.com/kercylan98/minotaur/game/builtin" + "github.com/kercylan98/minotaur/game/aoi" "github.com/kercylan98/minotaur/utils/random" "testing" "time" @@ -29,12 +29,12 @@ func (slf *Ent) GetVision() float64 { return slf.vision } -func TestNewAOI2D(t *testing.T) { - aoi := builtin.NewAOI2D(10000, 10000, 100, 100) +func TestNewTwoDimensional(t *testing.T) { + aoiTW := aoi.NewTwoDimensional[*Ent](10000, 10000, 100, 100) start := time.Now() for i := 0; i < 50000; i++ { - aoi.AddEntity(&Ent{ + aoiTW.AddEntity(&Ent{ guid: int64(i), x: float64(random.Int(0, 10000)), y: float64(random.Int(0, 10000)), @@ -44,9 +44,9 @@ func TestNewAOI2D(t *testing.T) { fmt.Println("添加耗时:", time.Since(start)) //start = time.Now() - //aoi.SetAreaSize(1000, 1000) + //aoiTW.SetAreaSize(1000, 1000) //fmt.Println("重设区域大小耗时:", time.Since(start)) start = time.Now() - aoi.SetSize(10100, 10100) + aoiTW.SetSize(10100, 10100) fmt.Println("重设大小耗时:", time.Since(start)) } diff --git a/game/aoi2d.go b/game/aoi2d.go deleted file mode 100644 index d6421a5..0000000 --- a/game/aoi2d.go +++ /dev/null @@ -1,35 +0,0 @@ -package game - -// AOI2D 基于2D定义的AOI功能接口 -// - AOI(Area Of Interest)翻译过来叫感兴趣的区域,是大型多人在线的游戏服务器中一个非常重要的基础模块,用于游戏对象在场景中的视野管理 -// - 透过 AOI 系统可以在其他玩家或怪物进入视野时得到感知,从而进行相应处理 -// - 内置实现:builtin.AOI2D -// - 构建函数:builtin.NewAOI2D -type AOI2D interface { - // AddEntity 添加对象 - AddEntity(entity AOIEntity2D) - // DeleteEntity 移除对象 - DeleteEntity(entity AOIEntity2D) - // Refresh 刷新对象焦点 - Refresh(entity AOIEntity2D) - // GetFocus 获取对象焦点列表 - GetFocus(guid int64) map[int64]AOIEntity2D - // SetSize 设置总区域大小 - // - 将会导致区域的重新划分 - SetSize(width, height int) - // SetAreaSize 设置区域大小 - // - 将会导致区域的重新划分 - SetAreaSize(width, height int) - - // RegEntityJoinVisionEvent 在新对象进入视野时将会立刻执行被注册的事件处理函数 - RegEntityJoinVisionEvent(handle EntityJoinVisionEventHandle) - OnEntityJoinVisionEvent(entity, target AOIEntity2D) - // RegEntityLeaveVisionEvent 在对象离开视野时将会立刻执行被注册的事件处理函数 - RegEntityLeaveVisionEvent(handle EntityLeaveVisionEventHandle) - OnEntityLeaveVisionEvent(entity, target AOIEntity2D) -} - -type ( - EntityJoinVisionEventHandle func(entity, target AOIEntity2D) - EntityLeaveVisionEventHandle func(entity, target AOIEntity2D) -) diff --git a/game/aoi2d_entity.go b/game/aoi2d_entity.go deleted file mode 100644 index 1e6aede..0000000 --- a/game/aoi2d_entity.go +++ /dev/null @@ -1,10 +0,0 @@ -package game - -// AOIEntity2D 基于2D定义的AOI对象功能接口 -// - AOI 对象提供了 AOI 系统中常用的属性,诸如位置坐标和视野范围等 -type AOIEntity2D interface { - Actor - Position2D - // GetVision 获取视距 - GetVision() float64 -}