diff --git a/game/aoi/2d.go b/game/aoi/2d.go index 4aa60192..631b2cd0 100644 --- a/game/aoi/2d.go +++ b/game/aoi/2d.go @@ -1,25 +1,26 @@ package aoi import ( + "github.com/kercylan98/minotaur/utils/generic" "github.com/kercylan98/minotaur/utils/geometry" "github.com/kercylan98/minotaur/utils/hash" "math" "sync" ) -func NewTwoDimensional[E TwoDimensionalEntity](width, height, areaWidth, areaHeight int) *TwoDimensional[E] { - aoi := &TwoDimensional[E]{ - event: new(event[E]), +func NewTwoDimensional[EID generic.Basic, PosType generic.SignedNumber, E TwoDimensionalEntity[EID, PosType]](width, height, areaWidth, areaHeight int) *TwoDimensional[EID, PosType, E] { + aoi := &TwoDimensional[EID, PosType, E]{ + event: new(event[EID, PosType, E]), width: float64(width), height: float64(height), - focus: map[int64]map[int64]E{}, + focus: map[EID]map[EID]E{}, } aoi.SetAreaSize(areaWidth, areaHeight) return aoi } -type TwoDimensional[E TwoDimensionalEntity] struct { - *event[E] +type TwoDimensional[EID generic.Basic, PosType generic.SignedNumber, E TwoDimensionalEntity[EID, PosType]] struct { + *event[EID, PosType, E] rw sync.RWMutex width float64 height float64 @@ -27,36 +28,36 @@ type TwoDimensional[E TwoDimensionalEntity] struct { areaHeight float64 areaWidthLimit int areaHeightLimit int - areas [][]map[int64]E - focus map[int64]map[int64]E + areas [][]map[EID]E + focus map[EID]map[EID]E repartitionQueue []func() } -func (slf *TwoDimensional[E]) AddEntity(entity E) { +func (slf *TwoDimensional[EID, PosType, E]) AddEntity(entity E) { slf.rw.Lock() slf.addEntity(entity) slf.rw.Unlock() } -func (slf *TwoDimensional[E]) DeleteEntity(entity E) { +func (slf *TwoDimensional[EID, PosType, E]) DeleteEntity(entity E) { slf.rw.Lock() slf.deleteEntity(entity) slf.rw.Unlock() } -func (slf *TwoDimensional[E]) Refresh(entity E) { +func (slf *TwoDimensional[EID, PosType, E]) Refresh(entity E) { slf.rw.Lock() defer slf.rw.Unlock() 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() 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) if fw == slf.width && fh == slf.height { return @@ -68,7 +69,7 @@ func (slf *TwoDimensional[E]) SetSize(width, height int) { 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) if fw == slf.areaWidth && fh == slf.areaHeight { return @@ -78,15 +79,15 @@ func (slf *TwoDimensional[E]) SetAreaSize(width, height int) { 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++ { 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++ { - es := map[int64]E{} + es := map[EID]E{} for g, e := range hs[h] { es[g] = e } @@ -111,11 +112,11 @@ func (slf *TwoDimensional[E]) 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]E, slf.areaWidthLimit+1) + areas := make([][]map[EID]E, slf.areaWidthLimit+1) 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++ { - entities[e] = map[int64]E{} + entities[e] = map[EID]E{} } areas[i] = entities } @@ -133,50 +134,50 @@ func (slf *TwoDimensional[E]) setAreaSize(width, height int) { } } -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]E{} - slf.focus[guid] = focus - slf.rangeVisionAreaEntities(entity, func(eg int64, e E) { +func (slf *TwoDimensional[EID, PosType, E]) addEntity(entity E) { + x, y := entity.GetPosition().GetXY() + widthArea := int(float64(x) / slf.areaWidth) + heightArea := int(float64(y) / slf.areaHeight) + id := entity.GetTwoDimensionalEntityID() + slf.areas[widthArea][heightArea][id] = entity + focus := map[EID]E{} + slf.focus[id] = focus + slf.rangeVisionAreaEntities(entity, func(eg EID, e E) { focus[eg] = e slf.OnEntityJoinVisionEvent(entity, e) slf.refresh(e) }) } -func (slf *TwoDimensional[E]) refresh(entity E) { - x, y := entity.GetPosition() +func (slf *TwoDimensional[EID, PosType, E]) refresh(entity E) { + x, y := entity.GetPosition().GetXY() vision := entity.GetVision() - guid := entity.GetGuid() - focus := slf.focus[guid] + id := entity.GetTwoDimensionalEntityID() + focus := slf.focus[id] for eg, e := range focus { - ex, ey := e.GetPosition() - if geometry.CalcDistanceWithCoordinate(x, y, ex, ey) > vision { + ex, ey := e.GetPosition().GetXY() + if geometry.CalcDistanceWithCoordinate(float64(x), float64(y), float64(ex), float64(ey)) > vision { delete(focus, eg) - delete(slf.focus[eg], guid) + delete(slf.focus[eg], id) } } - slf.rangeVisionAreaEntities(entity, func(guid int64, e E) { - if _, exist := focus[guid]; !exist { - focus[guid] = e + slf.rangeVisionAreaEntities(entity, func(id EID, e E) { + if _, exist := focus[id]; !exist { + focus[id] = e slf.OnEntityJoinVisionEvent(entity, e) } }) } -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) +func (slf *TwoDimensional[EID, PosType, E]) rangeVisionAreaEntities(entity E, handle func(id EID, entity E)) { + x, y := entity.GetPosition().GetXY() + widthArea := int(float64(x) / slf.areaWidth) + heightArea := int(float64(y) / slf.areaHeight) vision := entity.GetVision() widthSpan := int(math.Ceil(vision / slf.areaWidth)) heightSpan := int(math.Ceil(vision / slf.areaHeight)) - guid := entity.GetGuid() + id := entity.GetTwoDimensionalEntityID() sw := widthArea - widthSpan if sw < 0 { @@ -211,7 +212,7 @@ func (slf *TwoDimensional[E]) rangeVisionAreaEntities(entity E, handle func(guid } else if w > widthArea { areaX = float64(w * int(slf.areaWidth)) } else { - areaX = x + areaX = float64(x) } if h < heightArea { tempH := h + 1 @@ -219,15 +220,15 @@ func (slf *TwoDimensional[E]) rangeVisionAreaEntities(entity E, handle func(guid } else if h > heightArea { areaY = float64(h * int(slf.areaHeight)) } 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 { for eg, e := range slf.areas[w][h] { - if eg == guid { + if eg == id { 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 } handle(eg, e) @@ -237,17 +238,17 @@ func (slf *TwoDimensional[E]) rangeVisionAreaEntities(entity E, handle func(guid } } -func (slf *TwoDimensional[E]) deleteEntity(entity E) { - x, y := entity.GetPosition() - widthArea := int(x / slf.areaWidth) - heightArea := int(y / slf.areaHeight) - guid := entity.GetGuid() - focus := slf.focus[guid] +func (slf *TwoDimensional[EID, PosType, E]) deleteEntity(entity E) { + x, y := entity.GetPosition().GetXY() + widthArea := int(float64(x) / slf.areaWidth) + heightArea := int(float64(y) / slf.areaHeight) + id := entity.GetTwoDimensionalEntityID() + focus := slf.focus[id] for g, e := range focus { slf.OnEntityLeaveVisionEvent(entity, e) slf.OnEntityLeaveVisionEvent(e, entity) - delete(slf.focus[g], guid) + delete(slf.focus[g], id) } - delete(slf.focus, guid) - delete(slf.areas[widthArea][heightArea], guid) + delete(slf.focus, id) + delete(slf.areas[widthArea][heightArea], id) } diff --git a/game/aoi/2d_entity.go b/game/aoi/2d_entity.go index 84aae037..2a5be58d 100644 --- a/game/aoi/2d_entity.go +++ b/game/aoi/2d_entity.go @@ -1,12 +1,17 @@ package aoi -import "github.com/kercylan98/minotaur/game" +import ( + "github.com/kercylan98/minotaur/utils/generic" + "github.com/kercylan98/minotaur/utils/geometry" +) // TwoDimensionalEntity 基于2D定义的AOI对象功能接口 // - AOI 对象提供了 AOI 系统中常用的属性,诸如位置坐标和视野范围等 -type TwoDimensionalEntity interface { - game.Actor - game.Position2D +type TwoDimensionalEntity[EID generic.Basic, PosType generic.SignedNumber] interface { + // GetTwoDimensionalEntityID 获取 AOI 对象 ID + GetTwoDimensionalEntityID() EID // GetVision 获取视距 GetVision() float64 + // GetPosition 获取位置 + GetPosition() geometry.Point[PosType] } diff --git a/game/aoi/2d_events.go b/game/aoi/2d_events.go index 62bcb68a..cadc8bf0 100644 --- a/game/aoi/2d_events.go +++ b/game/aoi/2d_events.go @@ -1,34 +1,36 @@ package aoi +import "github.com/kercylan98/minotaur/utils/generic" + type ( - EntityJoinVisionEventHandle[E TwoDimensionalEntity] func(entity, target E) - EntityLeaveVisionEventHandle[E TwoDimensionalEntity] func(entity, target E) + EntityJoinVisionEventHandle[EID generic.Basic, PosType generic.SignedNumber, E TwoDimensionalEntity[EID, PosType]] func(entity, target E) + EntityLeaveVisionEventHandle[EID generic.Basic, PosType generic.SignedNumber, E TwoDimensionalEntity[EID, PosType]] func(entity, target E) ) -type event[E TwoDimensionalEntity] struct { - entityJoinVisionEventHandles []EntityJoinVisionEventHandle[E] - entityLeaveVisionEventHandles []EntityLeaveVisionEventHandle[E] +type event[EID generic.Basic, PosType generic.SignedNumber, E TwoDimensionalEntity[EID, PosType]] struct { + entityJoinVisionEventHandles []EntityJoinVisionEventHandle[EID, PosType, E] + entityLeaveVisionEventHandles []EntityLeaveVisionEventHandle[EID, PosType, E] } // 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) } // OnEntityJoinVisionEvent 在新对象进入视野时将会立刻执行被注册的事件处理函数 -func (slf *event[E]) OnEntityJoinVisionEvent(entity, target E) { +func (slf *event[EID, PosType, E]) OnEntityJoinVisionEvent(entity, target E) { for _, handle := range slf.entityJoinVisionEventHandles { handle(entity, target) } } // 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) } // OnEntityLeaveVisionEvent 在新对象离开视野时将会立刻执行被注册的事件处理函数 -func (slf *event[E]) OnEntityLeaveVisionEvent(entity, target E) { +func (slf *event[EID, PosType, E]) OnEntityLeaveVisionEvent(entity, target E) { for _, handle := range slf.entityLeaveVisionEventHandles { handle(entity, target) } diff --git a/game/aoi/2d_test.go b/game/aoi/2d_test.go index 4f1a5034..2ea6557d 100644 --- a/game/aoi/2d_test.go +++ b/game/aoi/2d_test.go @@ -3,26 +3,24 @@ package aoi_test import ( "fmt" "github.com/kercylan98/minotaur/game/aoi" + "github.com/kercylan98/minotaur/utils/geometry" "github.com/kercylan98/minotaur/utils/random" "testing" "time" ) type Ent struct { - guid int64 - x, y, vision float64 + guid int64 + pos geometry.Point[float64] + vision float64 } -func (slf *Ent) SetGuid(guid int64) { - slf.guid = guid -} - -func (slf *Ent) GetGuid() int64 { +func (slf *Ent) GetTwoDimensionalEntityID() int64 { return slf.guid } -func (slf *Ent) GetPosition() (x, y float64) { - return slf.x, slf.y +func (slf *Ent) GetPosition() geometry.Point[float64] { + return slf.pos } func (slf *Ent) GetVision() float64 { @@ -30,14 +28,13 @@ func (slf *Ent) GetVision() float64 { } 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() for i := 0; i < 50000; i++ { aoiTW.AddEntity(&Ent{ guid: int64(i), - x: float64(random.Int(0, 10000)), - y: float64(random.Int(0, 10000)), + pos: geometry.NewPoint[float64](float64(random.Int64(0, 10000)), float64(random.Int64(0, 10000))), vision: 200, }) }