refactor: 重构 aoi 包实现,移除对 game.Actor、game.Position2D 等接口的依赖

This commit is contained in:
kercylan98 2023-12-22 10:45:18 +08:00
parent af0165af71
commit d56ebde2f9
4 changed files with 90 additions and 85 deletions

View File

@ -1,25 +1,26 @@
package aoi package aoi
import ( import (
"github.com/kercylan98/minotaur/utils/generic"
"github.com/kercylan98/minotaur/utils/geometry" "github.com/kercylan98/minotaur/utils/geometry"
"github.com/kercylan98/minotaur/utils/hash" "github.com/kercylan98/minotaur/utils/hash"
"math" "math"
"sync" "sync"
) )
func NewTwoDimensional[E TwoDimensionalEntity](width, height, areaWidth, areaHeight int) *TwoDimensional[E] { func NewTwoDimensional[EID generic.Basic, PosType generic.SignedNumber, E TwoDimensionalEntity[EID, PosType]](width, height, areaWidth, areaHeight int) *TwoDimensional[EID, PosType, E] {
aoi := &TwoDimensional[E]{ aoi := &TwoDimensional[EID, PosType, E]{
event: new(event[E]), event: new(event[EID, PosType, E]),
width: float64(width), width: float64(width),
height: float64(height), height: float64(height),
focus: map[int64]map[int64]E{}, focus: map[EID]map[EID]E{},
} }
aoi.SetAreaSize(areaWidth, areaHeight) aoi.SetAreaSize(areaWidth, areaHeight)
return aoi return aoi
} }
type TwoDimensional[E TwoDimensionalEntity] struct { type TwoDimensional[EID generic.Basic, PosType generic.SignedNumber, E TwoDimensionalEntity[EID, PosType]] struct {
*event[E] *event[EID, PosType, E]
rw sync.RWMutex rw sync.RWMutex
width float64 width float64
height float64 height float64
@ -27,36 +28,36 @@ type TwoDimensional[E TwoDimensionalEntity] struct {
areaHeight float64 areaHeight float64
areaWidthLimit int areaWidthLimit int
areaHeightLimit int areaHeightLimit int
areas [][]map[int64]E areas [][]map[EID]E
focus map[int64]map[int64]E focus map[EID]map[EID]E
repartitionQueue []func() repartitionQueue []func()
} }
func (slf *TwoDimensional[E]) AddEntity(entity E) { func (slf *TwoDimensional[EID, PosType, E]) AddEntity(entity E) {
slf.rw.Lock() slf.rw.Lock()
slf.addEntity(entity) slf.addEntity(entity)
slf.rw.Unlock() slf.rw.Unlock()
} }
func (slf *TwoDimensional[E]) DeleteEntity(entity E) { func (slf *TwoDimensional[EID, PosType, E]) DeleteEntity(entity E) {
slf.rw.Lock() slf.rw.Lock()
slf.deleteEntity(entity) slf.deleteEntity(entity)
slf.rw.Unlock() slf.rw.Unlock()
} }
func (slf *TwoDimensional[E]) Refresh(entity E) { func (slf *TwoDimensional[EID, PosType, E]) Refresh(entity E) {
slf.rw.Lock() slf.rw.Lock()
defer slf.rw.Unlock() defer slf.rw.Unlock()
slf.refresh(entity) slf.refresh(entity)
} }
func (slf *TwoDimensional[E]) GetFocus(guid int64) map[int64]E { func (slf *TwoDimensional[EID, PosType, E]) GetFocus(id EID) map[EID]E {
slf.rw.RLock() slf.rw.RLock()
defer slf.rw.RUnlock() defer slf.rw.RUnlock()
return hash.Copy(slf.focus[guid]) return hash.Copy(slf.focus[id])
} }
func (slf *TwoDimensional[E]) SetSize(width, height int) { func (slf *TwoDimensional[EID, PosType, E]) SetSize(width, height int) {
fw, fh := float64(width), float64(height) fw, fh := float64(width), float64(height)
if fw == slf.width && fh == slf.height { if fw == slf.width && fh == slf.height {
return return
@ -68,7 +69,7 @@ func (slf *TwoDimensional[E]) SetSize(width, height int) {
slf.setAreaSize(int(slf.areaWidth), int(slf.areaHeight)) slf.setAreaSize(int(slf.areaWidth), int(slf.areaHeight))
} }
func (slf *TwoDimensional[E]) SetAreaSize(width, height int) { func (slf *TwoDimensional[EID, PosType, E]) SetAreaSize(width, height int) {
fw, fh := float64(width), float64(height) fw, fh := float64(width), float64(height)
if fw == slf.areaWidth && fh == slf.areaHeight { if fw == slf.areaWidth && fh == slf.areaHeight {
return return
@ -78,15 +79,15 @@ func (slf *TwoDimensional[E]) SetAreaSize(width, height int) {
slf.setAreaSize(width, height) slf.setAreaSize(width, height)
} }
func (slf *TwoDimensional[E]) setAreaSize(width, height int) { func (slf *TwoDimensional[EID, PosType, E]) setAreaSize(width, height int) {
// 旧分区备份 // 旧分区备份
var oldAreas = make([][]map[int64]E, len(slf.areas)) var oldAreas = make([][]map[EID]E, len(slf.areas))
for w := 0; w < len(slf.areas); w++ { for w := 0; w < len(slf.areas); w++ {
hs := slf.areas[w] hs := slf.areas[w]
ohs := make([]map[int64]E, len(hs)) ohs := make([]map[EID]E, len(hs))
for h := 0; h < len(hs); h++ { for h := 0; h < len(hs); h++ {
es := map[int64]E{} es := map[EID]E{}
for g, e := range hs[h] { for g, e := range hs[h] {
es[g] = e es[g] = e
} }
@ -111,11 +112,11 @@ func (slf *TwoDimensional[E]) setAreaSize(width, height int) {
slf.areaHeight = float64(height) slf.areaHeight = float64(height)
slf.areaWidthLimit = int(math.Ceil(slf.width / slf.areaWidth)) slf.areaWidthLimit = int(math.Ceil(slf.width / slf.areaWidth))
slf.areaHeightLimit = int(math.Ceil(slf.height / slf.areaHeight)) slf.areaHeightLimit = int(math.Ceil(slf.height / slf.areaHeight))
areas := make([][]map[int64]E, slf.areaWidthLimit+1) areas := make([][]map[EID]E, slf.areaWidthLimit+1)
for i := 0; i < len(areas); i++ { for i := 0; i < len(areas); i++ {
entities := make([]map[int64]E, slf.areaHeightLimit+1) entities := make([]map[EID]E, slf.areaHeightLimit+1)
for e := 0; e < len(entities); e++ { for e := 0; e < len(entities); e++ {
entities[e] = map[int64]E{} entities[e] = map[EID]E{}
} }
areas[i] = entities areas[i] = entities
} }
@ -133,50 +134,50 @@ func (slf *TwoDimensional[E]) setAreaSize(width, height int) {
} }
} }
func (slf *TwoDimensional[E]) addEntity(entity E) { func (slf *TwoDimensional[EID, PosType, E]) addEntity(entity E) {
x, y := entity.GetPosition() x, y := entity.GetPosition().GetXY()
widthArea := int(x / slf.areaWidth) widthArea := int(float64(x) / slf.areaWidth)
heightArea := int(y / slf.areaHeight) heightArea := int(float64(y) / slf.areaHeight)
guid := entity.GetGuid() id := entity.GetTwoDimensionalEntityID()
slf.areas[widthArea][heightArea][guid] = entity slf.areas[widthArea][heightArea][id] = entity
focus := map[int64]E{} focus := map[EID]E{}
slf.focus[guid] = focus slf.focus[id] = focus
slf.rangeVisionAreaEntities(entity, func(eg int64, e E) { slf.rangeVisionAreaEntities(entity, func(eg EID, e E) {
focus[eg] = e focus[eg] = e
slf.OnEntityJoinVisionEvent(entity, e) slf.OnEntityJoinVisionEvent(entity, e)
slf.refresh(e) slf.refresh(e)
}) })
} }
func (slf *TwoDimensional[E]) refresh(entity E) { func (slf *TwoDimensional[EID, PosType, E]) refresh(entity E) {
x, y := entity.GetPosition() x, y := entity.GetPosition().GetXY()
vision := entity.GetVision() vision := entity.GetVision()
guid := entity.GetGuid() id := entity.GetTwoDimensionalEntityID()
focus := slf.focus[guid] focus := slf.focus[id]
for eg, e := range focus { for eg, e := range focus {
ex, ey := e.GetPosition() ex, ey := e.GetPosition().GetXY()
if geometry.CalcDistanceWithCoordinate(x, y, ex, ey) > vision { if geometry.CalcDistanceWithCoordinate(float64(x), float64(y), float64(ex), float64(ey)) > vision {
delete(focus, eg) delete(focus, eg)
delete(slf.focus[eg], guid) delete(slf.focus[eg], id)
} }
} }
slf.rangeVisionAreaEntities(entity, func(guid int64, e E) { slf.rangeVisionAreaEntities(entity, func(id EID, e E) {
if _, exist := focus[guid]; !exist { if _, exist := focus[id]; !exist {
focus[guid] = e focus[id] = e
slf.OnEntityJoinVisionEvent(entity, e) slf.OnEntityJoinVisionEvent(entity, e)
} }
}) })
} }
func (slf *TwoDimensional[E]) rangeVisionAreaEntities(entity E, handle func(guid int64, entity E)) { func (slf *TwoDimensional[EID, PosType, E]) rangeVisionAreaEntities(entity E, handle func(id EID, entity E)) {
x, y := entity.GetPosition() x, y := entity.GetPosition().GetXY()
widthArea := int(x / slf.areaWidth) widthArea := int(float64(x) / slf.areaWidth)
heightArea := int(y / slf.areaHeight) heightArea := int(float64(y) / slf.areaHeight)
vision := entity.GetVision() vision := entity.GetVision()
widthSpan := int(math.Ceil(vision / slf.areaWidth)) widthSpan := int(math.Ceil(vision / slf.areaWidth))
heightSpan := int(math.Ceil(vision / slf.areaHeight)) heightSpan := int(math.Ceil(vision / slf.areaHeight))
guid := entity.GetGuid() id := entity.GetTwoDimensionalEntityID()
sw := widthArea - widthSpan sw := widthArea - widthSpan
if sw < 0 { if sw < 0 {
@ -211,7 +212,7 @@ func (slf *TwoDimensional[E]) rangeVisionAreaEntities(entity E, handle func(guid
} else if w > widthArea { } else if w > widthArea {
areaX = float64(w * int(slf.areaWidth)) areaX = float64(w * int(slf.areaWidth))
} else { } else {
areaX = x areaX = float64(x)
} }
if h < heightArea { if h < heightArea {
tempH := h + 1 tempH := h + 1
@ -219,15 +220,15 @@ func (slf *TwoDimensional[E]) rangeVisionAreaEntities(entity E, handle func(guid
} else if h > heightArea { } else if h > heightArea {
areaY = float64(h * int(slf.areaHeight)) areaY = float64(h * int(slf.areaHeight))
} else { } else {
areaY = y areaY = float64(y)
} }
areaDistance := geometry.CalcDistanceWithCoordinate(x, y, areaX, areaY) areaDistance := geometry.CalcDistanceWithCoordinate(float64(x), float64(y), areaX, areaY)
if areaDistance <= vision { if areaDistance <= vision {
for eg, e := range slf.areas[w][h] { for eg, e := range slf.areas[w][h] {
if eg == guid { if eg == id {
continue continue
} }
if ex, ey := e.GetPosition(); geometry.CalcDistanceWithCoordinate(x, y, ex, ey) > vision { if ex, ey := e.GetPosition().GetXY(); geometry.CalcDistanceWithCoordinate(float64(x), float64(y), float64(ex), float64(ey)) > vision {
continue continue
} }
handle(eg, e) handle(eg, e)
@ -237,17 +238,17 @@ func (slf *TwoDimensional[E]) rangeVisionAreaEntities(entity E, handle func(guid
} }
} }
func (slf *TwoDimensional[E]) deleteEntity(entity E) { func (slf *TwoDimensional[EID, PosType, E]) deleteEntity(entity E) {
x, y := entity.GetPosition() x, y := entity.GetPosition().GetXY()
widthArea := int(x / slf.areaWidth) widthArea := int(float64(x) / slf.areaWidth)
heightArea := int(y / slf.areaHeight) heightArea := int(float64(y) / slf.areaHeight)
guid := entity.GetGuid() id := entity.GetTwoDimensionalEntityID()
focus := slf.focus[guid] focus := slf.focus[id]
for g, e := range focus { for g, e := range focus {
slf.OnEntityLeaveVisionEvent(entity, e) slf.OnEntityLeaveVisionEvent(entity, e)
slf.OnEntityLeaveVisionEvent(e, entity) slf.OnEntityLeaveVisionEvent(e, entity)
delete(slf.focus[g], guid) delete(slf.focus[g], id)
} }
delete(slf.focus, guid) delete(slf.focus, id)
delete(slf.areas[widthArea][heightArea], guid) delete(slf.areas[widthArea][heightArea], id)
} }

View File

@ -1,12 +1,17 @@
package aoi package aoi
import "github.com/kercylan98/minotaur/game" import (
"github.com/kercylan98/minotaur/utils/generic"
"github.com/kercylan98/minotaur/utils/geometry"
)
// TwoDimensionalEntity 基于2D定义的AOI对象功能接口 // TwoDimensionalEntity 基于2D定义的AOI对象功能接口
// - AOI 对象提供了 AOI 系统中常用的属性,诸如位置坐标和视野范围等 // - AOI 对象提供了 AOI 系统中常用的属性,诸如位置坐标和视野范围等
type TwoDimensionalEntity interface { type TwoDimensionalEntity[EID generic.Basic, PosType generic.SignedNumber] interface {
game.Actor // GetTwoDimensionalEntityID 获取 AOI 对象 ID
game.Position2D GetTwoDimensionalEntityID() EID
// GetVision 获取视距 // GetVision 获取视距
GetVision() float64 GetVision() float64
// GetPosition 获取位置
GetPosition() geometry.Point[PosType]
} }

View File

@ -1,34 +1,36 @@
package aoi package aoi
import "github.com/kercylan98/minotaur/utils/generic"
type ( type (
EntityJoinVisionEventHandle[E TwoDimensionalEntity] func(entity, target E) EntityJoinVisionEventHandle[EID generic.Basic, PosType generic.SignedNumber, E TwoDimensionalEntity[EID, PosType]] func(entity, target E)
EntityLeaveVisionEventHandle[E TwoDimensionalEntity] func(entity, target E) EntityLeaveVisionEventHandle[EID generic.Basic, PosType generic.SignedNumber, E TwoDimensionalEntity[EID, PosType]] func(entity, target E)
) )
type event[E TwoDimensionalEntity] struct { type event[EID generic.Basic, PosType generic.SignedNumber, E TwoDimensionalEntity[EID, PosType]] struct {
entityJoinVisionEventHandles []EntityJoinVisionEventHandle[E] entityJoinVisionEventHandles []EntityJoinVisionEventHandle[EID, PosType, E]
entityLeaveVisionEventHandles []EntityLeaveVisionEventHandle[E] entityLeaveVisionEventHandles []EntityLeaveVisionEventHandle[EID, PosType, E]
} }
// RegEntityJoinVisionEvent 在新对象进入视野时将会立刻执行被注册的事件处理函数 // RegEntityJoinVisionEvent 在新对象进入视野时将会立刻执行被注册的事件处理函数
func (slf *event[E]) RegEntityJoinVisionEvent(handle EntityJoinVisionEventHandle[E]) { func (slf *event[EID, PosType, E]) RegEntityJoinVisionEvent(handle EntityJoinVisionEventHandle[EID, PosType, E]) {
slf.entityJoinVisionEventHandles = append(slf.entityJoinVisionEventHandles, handle) slf.entityJoinVisionEventHandles = append(slf.entityJoinVisionEventHandles, handle)
} }
// OnEntityJoinVisionEvent 在新对象进入视野时将会立刻执行被注册的事件处理函数 // OnEntityJoinVisionEvent 在新对象进入视野时将会立刻执行被注册的事件处理函数
func (slf *event[E]) OnEntityJoinVisionEvent(entity, target E) { func (slf *event[EID, PosType, E]) OnEntityJoinVisionEvent(entity, target E) {
for _, handle := range slf.entityJoinVisionEventHandles { for _, handle := range slf.entityJoinVisionEventHandles {
handle(entity, target) handle(entity, target)
} }
} }
// RegEntityLeaveVisionEvent 在新对象离开视野时将会立刻执行被注册的事件处理函数 // RegEntityLeaveVisionEvent 在新对象离开视野时将会立刻执行被注册的事件处理函数
func (slf *event[E]) RegEntityLeaveVisionEvent(handle EntityLeaveVisionEventHandle[E]) { func (slf *event[EID, PosType, E]) RegEntityLeaveVisionEvent(handle EntityLeaveVisionEventHandle[EID, PosType, E]) {
slf.entityLeaveVisionEventHandles = append(slf.entityLeaveVisionEventHandles, handle) slf.entityLeaveVisionEventHandles = append(slf.entityLeaveVisionEventHandles, handle)
} }
// OnEntityLeaveVisionEvent 在新对象离开视野时将会立刻执行被注册的事件处理函数 // OnEntityLeaveVisionEvent 在新对象离开视野时将会立刻执行被注册的事件处理函数
func (slf *event[E]) OnEntityLeaveVisionEvent(entity, target E) { func (slf *event[EID, PosType, E]) OnEntityLeaveVisionEvent(entity, target E) {
for _, handle := range slf.entityLeaveVisionEventHandles { for _, handle := range slf.entityLeaveVisionEventHandles {
handle(entity, target) handle(entity, target)
} }

View File

@ -3,26 +3,24 @@ package aoi_test
import ( import (
"fmt" "fmt"
"github.com/kercylan98/minotaur/game/aoi" "github.com/kercylan98/minotaur/game/aoi"
"github.com/kercylan98/minotaur/utils/geometry"
"github.com/kercylan98/minotaur/utils/random" "github.com/kercylan98/minotaur/utils/random"
"testing" "testing"
"time" "time"
) )
type Ent struct { type Ent struct {
guid int64 guid int64
x, y, vision float64 pos geometry.Point[float64]
vision float64
} }
func (slf *Ent) SetGuid(guid int64) { func (slf *Ent) GetTwoDimensionalEntityID() int64 {
slf.guid = guid
}
func (slf *Ent) GetGuid() int64 {
return slf.guid return slf.guid
} }
func (slf *Ent) GetPosition() (x, y float64) { func (slf *Ent) GetPosition() geometry.Point[float64] {
return slf.x, slf.y return slf.pos
} }
func (slf *Ent) GetVision() float64 { func (slf *Ent) GetVision() float64 {
@ -30,14 +28,13 @@ func (slf *Ent) GetVision() float64 {
} }
func TestNewTwoDimensional(t *testing.T) { func TestNewTwoDimensional(t *testing.T) {
aoiTW := aoi.NewTwoDimensional[*Ent](10000, 10000, 100, 100) aoiTW := aoi.NewTwoDimensional[int64, float64, *Ent](10000, 10000, 100, 100)
start := time.Now() start := time.Now()
for i := 0; i < 50000; i++ { for i := 0; i < 50000; i++ {
aoiTW.AddEntity(&Ent{ aoiTW.AddEntity(&Ent{
guid: int64(i), guid: int64(i),
x: float64(random.Int(0, 10000)), pos: geometry.NewPoint[float64](float64(random.Int64(0, 10000)), float64(random.Int64(0, 10000))),
y: float64(random.Int(0, 10000)),
vision: 200, vision: 200,
}) })
} }