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

This commit is contained in:
kercylan98 2023-12-22 10:59:28 +08:00
parent d56ebde2f9
commit 0a22f6d503
6 changed files with 83 additions and 76 deletions

View File

@ -1,15 +1,16 @@
package moving package moving
import ( import (
"github.com/kercylan98/minotaur/utils/generic"
"github.com/kercylan98/minotaur/utils/geometry" "github.com/kercylan98/minotaur/utils/geometry"
"sync" "sync"
"time" "time"
) )
// NewTwoDimensional 创建一个用于2D对象移动的实例(TwoDimensional) // NewTwoDimensional 创建一个用于2D对象移动的实例(TwoDimensional)
func NewTwoDimensional(options ...TwoDimensionalOption) *TwoDimensional { func NewTwoDimensional[EID generic.Basic, PosType generic.SignedNumber](options ...TwoDimensionalOption[EID, PosType]) *TwoDimensional[EID, PosType] {
moving2D := &TwoDimensional{ moving2D := &TwoDimensional[EID, PosType]{
entities: map[int64]*moving2DTarget{}, entities: map[EID]*moving2DTarget[EID, PosType]{},
timeUnit: float64(time.Millisecond), timeUnit: float64(time.Millisecond),
idle: time.Millisecond * 100, idle: time.Millisecond * 100,
interval: time.Millisecond * 100, interval: time.Millisecond * 100,
@ -25,22 +26,22 @@ func NewTwoDimensional(options ...TwoDimensionalOption) *TwoDimensional {
// - 通过对象调用 MoveTo 方法后将开始执行该对象的移动 // - 通过对象调用 MoveTo 方法后将开始执行该对象的移动
// - 移动将在根据设置的每次移动间隔时间(WithTwoDimensionalInterval)进行移动,当无对象移动需要移动时将会进入短暂的休眠 // - 移动将在根据设置的每次移动间隔时间(WithTwoDimensionalInterval)进行移动,当无对象移动需要移动时将会进入短暂的休眠
// - 当对象移动速度永久为0时将会导致永久无法完成的移动 // - 当对象移动速度永久为0时将会导致永久无法完成的移动
type TwoDimensional struct { type TwoDimensional[EID generic.Basic, PosType generic.SignedNumber] struct {
rw sync.RWMutex rw sync.RWMutex
entities map[int64]*moving2DTarget entities map[EID]*moving2DTarget[EID, PosType]
timeUnit float64 timeUnit float64
idle time.Duration idle time.Duration
interval time.Duration interval time.Duration
close bool close bool
position2DChangeEventHandles []Position2DChangeEventHandle position2DChangeEventHandles []Position2DChangeEventHandle[EID, PosType]
position2DDestinationEventHandles []Position2DDestinationEventHandle position2DDestinationEventHandles []Position2DDestinationEventHandle[EID, PosType]
position2DStopMoveEventHandles []Position2DStopMoveEventHandle position2DStopMoveEventHandles []Position2DStopMoveEventHandle[EID, PosType]
} }
// MoveTo 设置对象移动到特定位置 // MoveTo 设置对象移动到特定位置
func (slf *TwoDimensional) MoveTo(entity TwoDimensionalEntity, x float64, y float64) { func (slf *TwoDimensional[EID, PosType]) MoveTo(entity TwoDimensionalEntity[EID, PosType], x, y PosType) {
guid := entity.GetGuid() guid := entity.GetTwoDimensionalEntityID()
current := time.Now().UnixMilli() current := time.Now().UnixMilli()
slf.rw.Lock() slf.rw.Lock()
defer slf.rw.Unlock() defer slf.rw.Unlock()
@ -49,7 +50,7 @@ func (slf *TwoDimensional) MoveTo(entity TwoDimensionalEntity, x float64, y floa
} }
entityTarget, exist := slf.entities[guid] entityTarget, exist := slf.entities[guid]
if !exist { if !exist {
entityTarget = &moving2DTarget{ entityTarget = &moving2DTarget[EID, PosType]{
TwoDimensionalEntity: entity, TwoDimensionalEntity: entity,
x: x, x: x,
y: y, y: y,
@ -64,63 +65,63 @@ func (slf *TwoDimensional) MoveTo(entity TwoDimensionalEntity, x float64, y floa
} }
// StopMove 停止特定对象的移动 // StopMove 停止特定对象的移动
func (slf *TwoDimensional) StopMove(guid int64) { func (slf *TwoDimensional[EID, PosType]) StopMove(id EID) {
slf.rw.Lock() slf.rw.Lock()
defer slf.rw.Unlock() defer slf.rw.Unlock()
entity, exist := slf.entities[guid] entity, exist := slf.entities[id]
if exist { if exist {
slf.OnPosition2DStopMoveEvent(entity) slf.OnPosition2DStopMoveEvent(entity)
delete(slf.entities, guid) delete(slf.entities, id)
} }
} }
// RegPosition2DChangeEvent 在对象位置改变时将执行注册的事件处理函数 // RegPosition2DChangeEvent 在对象位置改变时将执行注册的事件处理函数
func (slf *TwoDimensional) RegPosition2DChangeEvent(handle Position2DChangeEventHandle) { func (slf *TwoDimensional[EID, PosType]) RegPosition2DChangeEvent(handle Position2DChangeEventHandle[EID, PosType]) {
slf.position2DChangeEventHandles = append(slf.position2DChangeEventHandles, handle) slf.position2DChangeEventHandles = append(slf.position2DChangeEventHandles, handle)
} }
func (slf *TwoDimensional) OnPosition2DChangeEvent(entity TwoDimensionalEntity, oldX, oldY float64) { func (slf *TwoDimensional[EID, PosType]) OnPosition2DChangeEvent(entity TwoDimensionalEntity[EID, PosType], oldX, oldY PosType) {
for _, handle := range slf.position2DChangeEventHandles { for _, handle := range slf.position2DChangeEventHandles {
handle(slf, entity, oldX, oldY) handle(slf, entity, oldX, oldY)
} }
} }
// RegPosition2DDestinationEvent 在对象到达终点时将执行被注册的事件处理函数 // RegPosition2DDestinationEvent 在对象到达终点时将执行被注册的事件处理函数
func (slf *TwoDimensional) RegPosition2DDestinationEvent(handle Position2DDestinationEventHandle) { func (slf *TwoDimensional[EID, PosType]) RegPosition2DDestinationEvent(handle Position2DDestinationEventHandle[EID, PosType]) {
slf.position2DDestinationEventHandles = append(slf.position2DDestinationEventHandles, handle) slf.position2DDestinationEventHandles = append(slf.position2DDestinationEventHandles, handle)
} }
func (slf *TwoDimensional) OnPosition2DDestinationEvent(entity TwoDimensionalEntity) { func (slf *TwoDimensional[EID, PosType]) OnPosition2DDestinationEvent(entity TwoDimensionalEntity[EID, PosType]) {
for _, handle := range slf.position2DDestinationEventHandles { for _, handle := range slf.position2DDestinationEventHandles {
handle(slf, entity) handle(slf, entity)
} }
} }
// RegPosition2DStopMoveEvent 在对象停止移动时将执行被注册的事件处理函数 // RegPosition2DStopMoveEvent 在对象停止移动时将执行被注册的事件处理函数
func (slf *TwoDimensional) RegPosition2DStopMoveEvent(handle Position2DStopMoveEventHandle) { func (slf *TwoDimensional[EID, PosType]) RegPosition2DStopMoveEvent(handle Position2DStopMoveEventHandle[EID, PosType]) {
slf.position2DStopMoveEventHandles = append(slf.position2DStopMoveEventHandles, handle) slf.position2DStopMoveEventHandles = append(slf.position2DStopMoveEventHandles, handle)
} }
func (slf *TwoDimensional) OnPosition2DStopMoveEvent(entity TwoDimensionalEntity) { func (slf *TwoDimensional[EID, PosType]) OnPosition2DStopMoveEvent(entity TwoDimensionalEntity[EID, PosType]) {
for _, handle := range slf.position2DStopMoveEventHandles { for _, handle := range slf.position2DStopMoveEventHandles {
handle(slf, entity) handle(slf, entity)
} }
} }
type moving2DTarget struct { type moving2DTarget[EID generic.Basic, PosType generic.SignedNumber] struct {
TwoDimensionalEntity TwoDimensionalEntity[EID, PosType]
x, y float64 x, y PosType
lastMoveTime int64 lastMoveTime int64
} }
// Release 释放对象移动对象所占用的资源 // Release 释放对象移动对象所占用的资源
func (slf *TwoDimensional) Release() { func (slf *TwoDimensional[EID, PosType]) Release() {
slf.rw.Lock() slf.rw.Lock()
defer slf.rw.Unlock() defer slf.rw.Unlock()
slf.close = true slf.close = true
} }
func (slf *TwoDimensional) handle() { func (slf *TwoDimensional[EID, PosType]) handle() {
for { for {
slf.rw.Lock() slf.rw.Lock()
if slf.close { if slf.close {
@ -129,8 +130,8 @@ func (slf *TwoDimensional) handle() {
} }
for guid, entity := range slf.entities { for guid, entity := range slf.entities {
entity := entity entity := entity
x, y := entity.GetPosition() x, y := entity.GetPosition().GetXY()
angle := geometry.CalcAngle(x, y, entity.x, entity.y) angle := geometry.CalcAngle(float64(x), float64(y), float64(entity.x), float64(entity.y))
moveTime := time.Now().UnixMilli() moveTime := time.Now().UnixMilli()
interval := float64(moveTime - entity.lastMoveTime) interval := float64(moveTime - entity.lastMoveTime)
if interval == 0 { if interval == 0 {
@ -138,14 +139,14 @@ func (slf *TwoDimensional) handle() {
} }
distance := geometry.CalcDistanceWithCoordinate(x, y, entity.x, entity.y) distance := geometry.CalcDistanceWithCoordinate(x, y, entity.x, entity.y)
moveDistance := interval * (entity.GetSpeed() / (slf.timeUnit / 1000 / 1000)) moveDistance := interval * (entity.GetSpeed() / (slf.timeUnit / 1000 / 1000))
if moveDistance >= distance || (x == entity.x && y == entity.y) { if moveDistance >= float64(distance) || (x == entity.x && y == entity.y) {
entity.SetPosition(entity.x, entity.y) entity.SetPosition(geometry.NewPoint(entity.x, entity.y))
delete(slf.entities, guid) delete(slf.entities, guid)
slf.OnPosition2DDestinationEvent(entity) slf.OnPosition2DDestinationEvent(entity)
continue continue
} else { } else {
nx, ny := geometry.CalcNewCoordinate(x, y, angle, moveDistance) nx, ny := geometry.CalcNewCoordinate(float64(x), float64(y), angle, moveDistance)
entity.SetPosition(nx, ny) entity.SetPosition(geometry.NewPoint(PosType(nx), PosType(ny)))
entity.lastMoveTime = moveTime entity.lastMoveTime = moveTime
slf.OnPosition2DChangeEvent(entity, x, y) slf.OnPosition2DChangeEvent(entity, x, y)
} }

View File

@ -1,13 +1,18 @@
package moving package moving
import "github.com/kercylan98/minotaur/game" import (
"github.com/kercylan98/minotaur/utils/generic"
"github.com/kercylan98/minotaur/utils/geometry"
)
// TwoDimensionalEntity 2D移动对象接口定义 // TwoDimensionalEntity 2D移动对象接口定义
type TwoDimensionalEntity interface { type TwoDimensionalEntity[EID generic.Basic, PosType generic.SignedNumber] interface {
game.Actor // GetTwoDimensionalEntityID 获取 Moving 对象 ID
game.Position2D GetTwoDimensionalEntityID() EID
game.Position2DSet
// GetSpeed 获取移动速度 // GetSpeed 获取移动速度
GetSpeed() float64 GetSpeed() float64
// GetPosition 获取位置
GetPosition() geometry.Point[PosType]
// SetPosition 设置位置
SetPosition(geometry.Point[PosType])
} }

View File

@ -1,7 +1,9 @@
package moving package moving
import "github.com/kercylan98/minotaur/utils/generic"
type ( type (
Position2DChangeEventHandle func(moving *TwoDimensional, entity TwoDimensionalEntity, oldX, oldY float64) Position2DChangeEventHandle[EID generic.Basic, PosType generic.SignedNumber] func(moving *TwoDimensional[EID, PosType], entity TwoDimensionalEntity[EID, PosType], oldX, oldY PosType)
Position2DDestinationEventHandle func(moving *TwoDimensional, entity TwoDimensionalEntity) Position2DDestinationEventHandle[EID generic.Basic, PosType generic.SignedNumber] func(moving *TwoDimensional[EID, PosType], entity TwoDimensionalEntity[EID, PosType])
Position2DStopMoveEventHandle func(moving *TwoDimensional, entity TwoDimensionalEntity) Position2DStopMoveEventHandle[EID generic.Basic, PosType generic.SignedNumber] func(moving *TwoDimensional[EID, PosType], entity TwoDimensionalEntity[EID, PosType])
) )

View File

@ -8,7 +8,7 @@ import (
) )
func ExampleNewTwoDimensional() { func ExampleNewTwoDimensional() {
m := moving.NewTwoDimensional() m := moving.NewTwoDimensional[int64, float64]()
defer func() { defer func() {
m.Release() m.Release()
}() }()
@ -19,13 +19,13 @@ func ExampleNewTwoDimensional() {
} }
func ExampleTwoDimensional_MoveTo() { func ExampleTwoDimensional_MoveTo() {
m := moving.NewTwoDimensional(moving.WithTwoDimensionalTimeUnit(time.Second)) m := moving.NewTwoDimensional(moving.WithTwoDimensionalTimeUnit[int64, float64](time.Second))
defer func() { defer func() {
m.Release() m.Release()
}() }()
var wait sync.WaitGroup var wait sync.WaitGroup
m.RegPosition2DDestinationEvent(func(moving *moving.TwoDimensional, entity moving.TwoDimensionalEntity) { m.RegPosition2DDestinationEvent(func(moving *moving.TwoDimensional[int64, float64], entity moving.TwoDimensionalEntity[int64, float64]) {
fmt.Println("done") fmt.Println("done")
wait.Done() wait.Done()
}) })
@ -41,20 +41,20 @@ func ExampleTwoDimensional_MoveTo() {
} }
func ExampleTwoDimensional_StopMove() { func ExampleTwoDimensional_StopMove() {
m := moving.NewTwoDimensional(moving.WithTwoDimensionalTimeUnit(time.Second)) m := moving.NewTwoDimensional(moving.WithTwoDimensionalTimeUnit[int64, float64](time.Second))
defer func() { defer func() {
m.Release() m.Release()
}() }()
var wait sync.WaitGroup var wait sync.WaitGroup
m.RegPosition2DChangeEvent(func(moving *moving.TwoDimensional, entity moving.TwoDimensionalEntity, oldX, oldY float64) { m.RegPosition2DChangeEvent(func(moving *moving.TwoDimensional[int64, float64], entity moving.TwoDimensionalEntity[int64, float64], oldX, oldY float64) {
fmt.Println("move") fmt.Println("move")
}) })
m.RegPosition2DStopMoveEvent(func(moving *moving.TwoDimensional, entity moving.TwoDimensionalEntity) { m.RegPosition2DStopMoveEvent(func(moving *moving.TwoDimensional[int64, float64], entity moving.TwoDimensionalEntity[int64, float64]) {
fmt.Println("stop") fmt.Println("stop")
wait.Done() wait.Done()
}) })
m.RegPosition2DDestinationEvent(func(moving *moving.TwoDimensional, entity moving.TwoDimensionalEntity) { m.RegPosition2DDestinationEvent(func(moving *moving.TwoDimensional[int64, float64], entity moving.TwoDimensionalEntity[int64, float64]) {
fmt.Println("done") fmt.Println("done")
wait.Done() wait.Done()
}) })

View File

@ -2,15 +2,16 @@ package moving
import ( import (
"errors" "errors"
"github.com/kercylan98/minotaur/utils/generic"
"time" "time"
) )
type TwoDimensionalOption func(moving *TwoDimensional) type TwoDimensionalOption[EID generic.Basic, PosType generic.SignedNumber] func(moving *TwoDimensional[EID, PosType])
// WithTwoDimensionalTimeUnit 通过特定时间单位创建 // WithTwoDimensionalTimeUnit 通过特定时间单位创建
// - 默认单位为1毫秒最小单位也为1毫秒 // - 默认单位为1毫秒最小单位也为1毫秒
func WithTwoDimensionalTimeUnit(duration time.Duration) TwoDimensionalOption { func WithTwoDimensionalTimeUnit[EID generic.Basic, PosType generic.SignedNumber](duration time.Duration) TwoDimensionalOption[EID, PosType] {
return func(moving *TwoDimensional) { return func(moving *TwoDimensional[EID, PosType]) {
if duration < time.Millisecond { if duration < time.Millisecond {
panic(errors.New("time unit milliseconds minimum")) panic(errors.New("time unit milliseconds minimum"))
} }
@ -20,15 +21,15 @@ func WithTwoDimensionalTimeUnit(duration time.Duration) TwoDimensionalOption {
// WithTwoDimensionalIdleWaitTime 通过特定的空闲等待时间创建 // WithTwoDimensionalIdleWaitTime 通过特定的空闲等待时间创建
// - 默认情况下在没有新的移动计划时将限制 100毫秒 + 移动间隔事件(默认100毫秒) // - 默认情况下在没有新的移动计划时将限制 100毫秒 + 移动间隔事件(默认100毫秒)
func WithTwoDimensionalIdleWaitTime(duration time.Duration) TwoDimensionalOption { func WithTwoDimensionalIdleWaitTime[EID generic.Basic, PosType generic.SignedNumber](duration time.Duration) TwoDimensionalOption[EID, PosType] {
return func(moving *TwoDimensional) { return func(moving *TwoDimensional[EID, PosType]) {
moving.idle = duration moving.idle = duration
} }
} }
// WithTwoDimensionalInterval 通过特定的移动间隔时间创建 // WithTwoDimensionalInterval 通过特定的移动间隔时间创建
func WithTwoDimensionalInterval(duration time.Duration) TwoDimensionalOption { func WithTwoDimensionalInterval[EID generic.Basic, PosType generic.SignedNumber](duration time.Duration) TwoDimensionalOption[EID, PosType] {
return func(moving *TwoDimensional) { return func(moving *TwoDimensional[EID, PosType]) {
moving.interval = duration moving.interval = duration
} }
} }

View File

@ -3,6 +3,7 @@ package moving_test
import ( import (
"fmt" "fmt"
"github.com/kercylan98/minotaur/game/moving" "github.com/kercylan98/minotaur/game/moving"
"github.com/kercylan98/minotaur/utils/geometry"
"sync" "sync"
"testing" "testing"
"time" "time"
@ -10,29 +11,26 @@ import (
type MoveEntity struct { type MoveEntity struct {
guid int64 guid int64
x, y float64 pos geometry.Point[float64]
speed float64 speed float64
} }
func (slf *MoveEntity) SetGuid(guid int64) { func (slf *MoveEntity) GetTwoDimensionalEntityID() int64 {
}
func (slf *MoveEntity) GetGuid() int64 {
return slf.guid return slf.guid
} }
func (slf *MoveEntity) GetPosition() (x, y float64) {
return slf.x, slf.y
}
func (slf *MoveEntity) SetPosition(x, y float64) {
slf.x, slf.y = x, y
}
func (slf *MoveEntity) GetSpeed() float64 { func (slf *MoveEntity) GetSpeed() float64 {
return slf.speed return slf.speed
} }
func (slf *MoveEntity) GetPosition() geometry.Point[float64] {
return slf.pos
}
func (slf *MoveEntity) SetPosition(pos geometry.Point[float64]) {
slf.pos = pos
}
func NewEntity(guid int64, speed float64) *MoveEntity { func NewEntity(guid int64, speed float64) *MoveEntity {
return &MoveEntity{ return &MoveEntity{
guid: guid, guid: guid,
@ -41,7 +39,7 @@ func NewEntity(guid int64, speed float64) *MoveEntity {
} }
func TestNewTwoDimensional(t *testing.T) { func TestNewTwoDimensional(t *testing.T) {
m := moving.NewTwoDimensional() m := moving.NewTwoDimensional[int64, float64]()
defer func() { defer func() {
m.Release() m.Release()
}() }()
@ -50,21 +48,21 @@ func TestNewTwoDimensional(t *testing.T) {
func TestTwoDimensional_StopMove(t *testing.T) { func TestTwoDimensional_StopMove(t *testing.T) {
var wait sync.WaitGroup var wait sync.WaitGroup
m := moving.NewTwoDimensional(moving.WithTwoDimensionalTimeUnit(time.Second)) m := moving.NewTwoDimensional(moving.WithTwoDimensionalTimeUnit[int64, float64](time.Second))
defer func() { defer func() {
m.Release() m.Release()
}() }()
m.RegPosition2DChangeEvent(func(moving *moving.TwoDimensional, entity moving.TwoDimensionalEntity, oldX, oldY float64) { m.RegPosition2DChangeEvent(func(moving *moving.TwoDimensional[int64, float64], entity moving.TwoDimensionalEntity[int64, float64], oldX, oldY float64) {
x, y := entity.GetPosition() x, y := entity.GetPosition().GetXY()
fmt.Println(fmt.Sprintf("%d : %d | %f, %f > %f, %f", entity.GetGuid(), time.Now().UnixMilli(), oldX, oldY, x, y)) fmt.Println(fmt.Sprintf("%d : %d | %f, %f > %f, %f", entity.GetTwoDimensionalEntityID(), time.Now().UnixMilli(), oldX, oldY, x, y))
}) })
m.RegPosition2DDestinationEvent(func(moving *moving.TwoDimensional, entity moving.TwoDimensionalEntity) { m.RegPosition2DDestinationEvent(func(moving *moving.TwoDimensional[int64, float64], entity moving.TwoDimensionalEntity[int64, float64]) {
fmt.Println(fmt.Sprintf("%d : %d | destination", entity.GetGuid(), time.Now().UnixMilli())) fmt.Println(fmt.Sprintf("%d : %d | destination", entity.GetTwoDimensionalEntityID(), time.Now().UnixMilli()))
wait.Done() wait.Done()
}) })
m.RegPosition2DStopMoveEvent(func(moving *moving.TwoDimensional, entity moving.TwoDimensionalEntity) { m.RegPosition2DStopMoveEvent(func(moving *moving.TwoDimensional[int64, float64], entity moving.TwoDimensionalEntity[int64, float64]) {
fmt.Println(fmt.Sprintf("%d : %d | stop", entity.GetGuid(), time.Now().UnixMilli())) fmt.Println(fmt.Sprintf("%d : %d | stop", entity.GetTwoDimensionalEntityID(), time.Now().UnixMilli()))
wait.Done() wait.Done()
}) })