refactor: 从 builtin 包中单独抽离到 aoi 包,更名为 TwoDimensional
This commit is contained in:
parent
c8f181f63e
commit
bca8a98463
|
@ -1,24 +1,25 @@
|
||||||
package builtin
|
package aoi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kercylan98/minotaur/game"
|
|
||||||
"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 NewAOI2D(width, height, areaWidth, areaHeight int) *AOI2D {
|
func NewTwoDimensional[E TwoDimensionalEntity](width, height, areaWidth, areaHeight int) *TwoDimensional[E] {
|
||||||
aoi := &AOI2D{
|
aoi := &TwoDimensional[E]{
|
||||||
|
event: new(event[E]),
|
||||||
width: float64(width),
|
width: float64(width),
|
||||||
height: float64(height),
|
height: float64(height),
|
||||||
focus: map[int64]map[int64]game.AOIEntity2D{},
|
focus: map[int64]map[int64]E{},
|
||||||
}
|
}
|
||||||
aoi.SetAreaSize(areaWidth, areaHeight)
|
aoi.SetAreaSize(areaWidth, areaHeight)
|
||||||
return aoi
|
return aoi
|
||||||
}
|
}
|
||||||
|
|
||||||
type AOI2D struct {
|
type TwoDimensional[E TwoDimensionalEntity] struct {
|
||||||
|
*event[E]
|
||||||
rw sync.RWMutex
|
rw sync.RWMutex
|
||||||
width float64
|
width float64
|
||||||
height float64
|
height float64
|
||||||
|
@ -26,39 +27,36 @@ type AOI2D struct {
|
||||||
areaHeight float64
|
areaHeight float64
|
||||||
areaWidthLimit int
|
areaWidthLimit int
|
||||||
areaHeightLimit int
|
areaHeightLimit int
|
||||||
areas [][]map[int64]game.AOIEntity2D
|
areas [][]map[int64]E
|
||||||
focus map[int64]map[int64]game.AOIEntity2D
|
focus map[int64]map[int64]E
|
||||||
repartitionQueue []func()
|
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.rw.Lock()
|
||||||
slf.addEntity(entity)
|
slf.addEntity(entity)
|
||||||
slf.rw.Unlock()
|
slf.rw.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *AOI2D) DeleteEntity(entity game.AOIEntity2D) {
|
func (slf *TwoDimensional[E]) DeleteEntity(entity E) {
|
||||||
slf.rw.Lock()
|
slf.rw.Lock()
|
||||||
slf.deleteEntity(entity)
|
slf.deleteEntity(entity)
|
||||||
slf.rw.Unlock()
|
slf.rw.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *AOI2D) Refresh(entity game.AOIEntity2D) {
|
func (slf *TwoDimensional[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 *AOI2D) GetFocus(guid int64) map[int64]game.AOIEntity2D {
|
func (slf *TwoDimensional[E]) GetFocus(guid int64) map[int64]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[guid])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *AOI2D) SetSize(width, height int) {
|
func (slf *TwoDimensional[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
|
||||||
|
@ -70,7 +68,7 @@ func (slf *AOI2D) SetSize(width, height int) {
|
||||||
slf.setAreaSize(int(slf.areaWidth), int(slf.areaHeight))
|
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)
|
fw, fh := float64(width), float64(height)
|
||||||
if fw == slf.areaWidth && fh == slf.areaHeight {
|
if fw == slf.areaWidth && fh == slf.areaHeight {
|
||||||
return
|
return
|
||||||
|
@ -80,35 +78,15 @@ func (slf *AOI2D) SetAreaSize(width, height int) {
|
||||||
slf.setAreaSize(width, height)
|
slf.setAreaSize(width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *AOI2D) RegEntityJoinVisionEvent(handle game.EntityJoinVisionEventHandle) {
|
func (slf *TwoDimensional[E]) setAreaSize(width, height int) {
|
||||||
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) {
|
|
||||||
|
|
||||||
// 旧分区备份
|
// 旧分区备份
|
||||||
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++ {
|
for w := 0; w < len(slf.areas); w++ {
|
||||||
hs := 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++ {
|
for h := 0; h < len(hs); h++ {
|
||||||
es := map[int64]game.AOIEntity2D{}
|
es := map[int64]E{}
|
||||||
for g, e := range hs[h] {
|
for g, e := range hs[h] {
|
||||||
es[g] = e
|
es[g] = e
|
||||||
}
|
}
|
||||||
|
@ -133,11 +111,11 @@ func (slf *AOI2D) 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]game.AOIEntity2D, slf.areaWidthLimit+1)
|
areas := make([][]map[int64]E, slf.areaWidthLimit+1)
|
||||||
for i := 0; i < len(areas); i++ {
|
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++ {
|
for e := 0; e < len(entities); e++ {
|
||||||
entities[e] = map[int64]game.AOIEntity2D{}
|
entities[e] = map[int64]E{}
|
||||||
}
|
}
|
||||||
areas[i] = entities
|
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()
|
x, y := entity.GetPosition()
|
||||||
widthArea := int(x / slf.areaWidth)
|
widthArea := int(x / slf.areaWidth)
|
||||||
heightArea := int(y / slf.areaHeight)
|
heightArea := int(y / slf.areaHeight)
|
||||||
guid := entity.GetGuid()
|
guid := entity.GetGuid()
|
||||||
slf.areas[widthArea][heightArea][guid] = entity
|
slf.areas[widthArea][heightArea][guid] = entity
|
||||||
focus := map[int64]game.AOIEntity2D{}
|
focus := map[int64]E{}
|
||||||
slf.focus[guid] = focus
|
slf.focus[guid] = focus
|
||||||
slf.rangeVisionAreaEntities(entity, func(eg int64, e game.AOIEntity2D) {
|
slf.rangeVisionAreaEntities(entity, func(eg int64, e E) {
|
||||||
focus[eg] = e
|
focus[eg] = e
|
||||||
slf.OnEntityJoinVisionEvent(entity, e)
|
slf.OnEntityJoinVisionEvent(entity, e)
|
||||||
slf.refresh(e)
|
slf.refresh(e)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *AOI2D) refresh(entity game.AOIEntity2D) {
|
func (slf *TwoDimensional[E]) refresh(entity E) {
|
||||||
x, y := entity.GetPosition()
|
x, y := entity.GetPosition()
|
||||||
vision := entity.GetVision()
|
vision := entity.GetVision()
|
||||||
guid := entity.GetGuid()
|
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 {
|
if _, exist := focus[guid]; !exist {
|
||||||
focus[guid] = e
|
focus[guid] = e
|
||||||
slf.OnEntityJoinVisionEvent(entity, 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()
|
x, y := entity.GetPosition()
|
||||||
widthArea := int(x / slf.areaWidth)
|
widthArea := int(x / slf.areaWidth)
|
||||||
heightArea := int(y / slf.areaHeight)
|
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()
|
x, y := entity.GetPosition()
|
||||||
widthArea := int(x / slf.areaWidth)
|
widthArea := int(x / slf.areaWidth)
|
||||||
heightArea := int(y / slf.areaHeight)
|
heightArea := int(y / slf.areaHeight)
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
package builtin_test
|
package aoi_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/kercylan98/minotaur/game/builtin"
|
"github.com/kercylan98/minotaur/game/aoi"
|
||||||
"github.com/kercylan98/minotaur/utils/random"
|
"github.com/kercylan98/minotaur/utils/random"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -29,12 +29,12 @@ func (slf *Ent) GetVision() float64 {
|
||||||
return slf.vision
|
return slf.vision
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewAOI2D(t *testing.T) {
|
func TestNewTwoDimensional(t *testing.T) {
|
||||||
aoi := builtin.NewAOI2D(10000, 10000, 100, 100)
|
aoiTW := aoi.NewTwoDimensional[*Ent](10000, 10000, 100, 100)
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
for i := 0; i < 50000; i++ {
|
for i := 0; i < 50000; i++ {
|
||||||
aoi.AddEntity(&Ent{
|
aoiTW.AddEntity(&Ent{
|
||||||
guid: int64(i),
|
guid: int64(i),
|
||||||
x: float64(random.Int(0, 10000)),
|
x: float64(random.Int(0, 10000)),
|
||||||
y: 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))
|
fmt.Println("添加耗时:", time.Since(start))
|
||||||
|
|
||||||
//start = time.Now()
|
//start = time.Now()
|
||||||
//aoi.SetAreaSize(1000, 1000)
|
//aoiTW.SetAreaSize(1000, 1000)
|
||||||
//fmt.Println("重设区域大小耗时:", time.Since(start))
|
//fmt.Println("重设区域大小耗时:", time.Since(start))
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
aoi.SetSize(10100, 10100)
|
aoiTW.SetSize(10100, 10100)
|
||||||
fmt.Println("重设大小耗时:", time.Since(start))
|
fmt.Println("重设大小耗时:", time.Since(start))
|
||||||
}
|
}
|
|
@ -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)
|
|
||||||
)
|
|
|
@ -1,10 +0,0 @@
|
||||||
package game
|
|
||||||
|
|
||||||
// AOIEntity2D 基于2D定义的AOI对象功能接口
|
|
||||||
// - AOI 对象提供了 AOI 系统中常用的属性,诸如位置坐标和视野范围等
|
|
||||||
type AOIEntity2D interface {
|
|
||||||
Actor
|
|
||||||
Position2D
|
|
||||||
// GetVision 获取视距
|
|
||||||
GetVision() float64
|
|
||||||
}
|
|
Loading…
Reference in New Issue