From 0a22f6d5033e2b39a7ced2a3e8ab9f26eaefb19e Mon Sep 17 00:00:00 2001 From: kercylan98 Date: Fri, 22 Dec 2023 10:59:28 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=20moving=20?= =?UTF-8?q?=E5=8C=85=E5=AE=9E=E7=8E=B0=EF=BC=8C=E7=A7=BB=E9=99=A4=E5=AF=B9?= =?UTF-8?q?=20game.Actor=E3=80=81game.Position2D=20=E7=AD=89=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E7=9A=84=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/moving/2d.go | 63 +++++++++++++++++----------------- game/moving/2d_entity.go | 17 +++++---- game/moving/2d_events.go | 8 +++-- game/moving/2d_example_test.go | 14 ++++---- game/moving/2d_options.go | 15 ++++---- game/moving/2d_test.go | 42 +++++++++++------------ 6 files changed, 83 insertions(+), 76 deletions(-) diff --git a/game/moving/2d.go b/game/moving/2d.go index 83e7b8d..596be09 100644 --- a/game/moving/2d.go +++ b/game/moving/2d.go @@ -1,15 +1,16 @@ package moving import ( + "github.com/kercylan98/minotaur/utils/generic" "github.com/kercylan98/minotaur/utils/geometry" "sync" "time" ) // NewTwoDimensional 创建一个用于2D对象移动的实例(TwoDimensional) -func NewTwoDimensional(options ...TwoDimensionalOption) *TwoDimensional { - moving2D := &TwoDimensional{ - entities: map[int64]*moving2DTarget{}, +func NewTwoDimensional[EID generic.Basic, PosType generic.SignedNumber](options ...TwoDimensionalOption[EID, PosType]) *TwoDimensional[EID, PosType] { + moving2D := &TwoDimensional[EID, PosType]{ + entities: map[EID]*moving2DTarget[EID, PosType]{}, timeUnit: float64(time.Millisecond), idle: time.Millisecond * 100, interval: time.Millisecond * 100, @@ -25,22 +26,22 @@ func NewTwoDimensional(options ...TwoDimensionalOption) *TwoDimensional { // - 通过对象调用 MoveTo 方法后将开始执行该对象的移动 // - 移动将在根据设置的每次移动间隔时间(WithTwoDimensionalInterval)进行移动,当无对象移动需要移动时将会进入短暂的休眠 // - 当对象移动速度永久为0时,将会导致永久无法完成的移动 -type TwoDimensional struct { +type TwoDimensional[EID generic.Basic, PosType generic.SignedNumber] struct { rw sync.RWMutex - entities map[int64]*moving2DTarget + entities map[EID]*moving2DTarget[EID, PosType] timeUnit float64 idle time.Duration interval time.Duration close bool - position2DChangeEventHandles []Position2DChangeEventHandle - position2DDestinationEventHandles []Position2DDestinationEventHandle - position2DStopMoveEventHandles []Position2DStopMoveEventHandle + position2DChangeEventHandles []Position2DChangeEventHandle[EID, PosType] + position2DDestinationEventHandles []Position2DDestinationEventHandle[EID, PosType] + position2DStopMoveEventHandles []Position2DStopMoveEventHandle[EID, PosType] } // MoveTo 设置对象移动到特定位置 -func (slf *TwoDimensional) MoveTo(entity TwoDimensionalEntity, x float64, y float64) { - guid := entity.GetGuid() +func (slf *TwoDimensional[EID, PosType]) MoveTo(entity TwoDimensionalEntity[EID, PosType], x, y PosType) { + guid := entity.GetTwoDimensionalEntityID() current := time.Now().UnixMilli() slf.rw.Lock() defer slf.rw.Unlock() @@ -49,7 +50,7 @@ func (slf *TwoDimensional) MoveTo(entity TwoDimensionalEntity, x float64, y floa } entityTarget, exist := slf.entities[guid] if !exist { - entityTarget = &moving2DTarget{ + entityTarget = &moving2DTarget[EID, PosType]{ TwoDimensionalEntity: entity, x: x, y: y, @@ -64,63 +65,63 @@ func (slf *TwoDimensional) MoveTo(entity TwoDimensionalEntity, x float64, y floa } // StopMove 停止特定对象的移动 -func (slf *TwoDimensional) StopMove(guid int64) { +func (slf *TwoDimensional[EID, PosType]) StopMove(id EID) { slf.rw.Lock() defer slf.rw.Unlock() - entity, exist := slf.entities[guid] + entity, exist := slf.entities[id] if exist { slf.OnPosition2DStopMoveEvent(entity) - delete(slf.entities, guid) + delete(slf.entities, id) } } // RegPosition2DChangeEvent 在对象位置改变时将执行注册的事件处理函数 -func (slf *TwoDimensional) RegPosition2DChangeEvent(handle Position2DChangeEventHandle) { +func (slf *TwoDimensional[EID, PosType]) RegPosition2DChangeEvent(handle Position2DChangeEventHandle[EID, PosType]) { 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 { handle(slf, entity, oldX, oldY) } } // RegPosition2DDestinationEvent 在对象到达终点时将执行被注册的事件处理函数 -func (slf *TwoDimensional) RegPosition2DDestinationEvent(handle Position2DDestinationEventHandle) { +func (slf *TwoDimensional[EID, PosType]) RegPosition2DDestinationEvent(handle Position2DDestinationEventHandle[EID, PosType]) { 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 { handle(slf, entity) } } // RegPosition2DStopMoveEvent 在对象停止移动时将执行被注册的事件处理函数 -func (slf *TwoDimensional) RegPosition2DStopMoveEvent(handle Position2DStopMoveEventHandle) { +func (slf *TwoDimensional[EID, PosType]) RegPosition2DStopMoveEvent(handle Position2DStopMoveEventHandle[EID, PosType]) { 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 { handle(slf, entity) } } -type moving2DTarget struct { - TwoDimensionalEntity - x, y float64 +type moving2DTarget[EID generic.Basic, PosType generic.SignedNumber] struct { + TwoDimensionalEntity[EID, PosType] + x, y PosType lastMoveTime int64 } // Release 释放对象移动对象所占用的资源 -func (slf *TwoDimensional) Release() { +func (slf *TwoDimensional[EID, PosType]) Release() { slf.rw.Lock() defer slf.rw.Unlock() slf.close = true } -func (slf *TwoDimensional) handle() { +func (slf *TwoDimensional[EID, PosType]) handle() { for { slf.rw.Lock() if slf.close { @@ -129,8 +130,8 @@ func (slf *TwoDimensional) handle() { } for guid, entity := range slf.entities { entity := entity - x, y := entity.GetPosition() - angle := geometry.CalcAngle(x, y, entity.x, entity.y) + x, y := entity.GetPosition().GetXY() + angle := geometry.CalcAngle(float64(x), float64(y), float64(entity.x), float64(entity.y)) moveTime := time.Now().UnixMilli() interval := float64(moveTime - entity.lastMoveTime) if interval == 0 { @@ -138,14 +139,14 @@ func (slf *TwoDimensional) handle() { } distance := geometry.CalcDistanceWithCoordinate(x, y, entity.x, entity.y) moveDistance := interval * (entity.GetSpeed() / (slf.timeUnit / 1000 / 1000)) - if moveDistance >= distance || (x == entity.x && y == entity.y) { - entity.SetPosition(entity.x, entity.y) + if moveDistance >= float64(distance) || (x == entity.x && y == entity.y) { + entity.SetPosition(geometry.NewPoint(entity.x, entity.y)) delete(slf.entities, guid) slf.OnPosition2DDestinationEvent(entity) continue } else { - nx, ny := geometry.CalcNewCoordinate(x, y, angle, moveDistance) - entity.SetPosition(nx, ny) + nx, ny := geometry.CalcNewCoordinate(float64(x), float64(y), angle, moveDistance) + entity.SetPosition(geometry.NewPoint(PosType(nx), PosType(ny))) entity.lastMoveTime = moveTime slf.OnPosition2DChangeEvent(entity, x, y) } diff --git a/game/moving/2d_entity.go b/game/moving/2d_entity.go index aa7670c..bb53f9b 100644 --- a/game/moving/2d_entity.go +++ b/game/moving/2d_entity.go @@ -1,13 +1,18 @@ package moving -import "github.com/kercylan98/minotaur/game" +import ( + "github.com/kercylan98/minotaur/utils/generic" + "github.com/kercylan98/minotaur/utils/geometry" +) // TwoDimensionalEntity 2D移动对象接口定义 -type TwoDimensionalEntity interface { - game.Actor - game.Position2D - game.Position2DSet - +type TwoDimensionalEntity[EID generic.Basic, PosType generic.SignedNumber] interface { + // GetTwoDimensionalEntityID 获取 Moving 对象 ID + GetTwoDimensionalEntityID() EID // GetSpeed 获取移动速度 GetSpeed() float64 + // GetPosition 获取位置 + GetPosition() geometry.Point[PosType] + // SetPosition 设置位置 + SetPosition(geometry.Point[PosType]) } diff --git a/game/moving/2d_events.go b/game/moving/2d_events.go index 63cb19c..073705d 100644 --- a/game/moving/2d_events.go +++ b/game/moving/2d_events.go @@ -1,7 +1,9 @@ package moving +import "github.com/kercylan98/minotaur/utils/generic" + type ( - Position2DChangeEventHandle func(moving *TwoDimensional, entity TwoDimensionalEntity, oldX, oldY float64) - Position2DDestinationEventHandle func(moving *TwoDimensional, entity TwoDimensionalEntity) - Position2DStopMoveEventHandle func(moving *TwoDimensional, entity TwoDimensionalEntity) + Position2DChangeEventHandle[EID generic.Basic, PosType generic.SignedNumber] func(moving *TwoDimensional[EID, PosType], entity TwoDimensionalEntity[EID, PosType], oldX, oldY PosType) + Position2DDestinationEventHandle[EID generic.Basic, PosType generic.SignedNumber] func(moving *TwoDimensional[EID, PosType], entity TwoDimensionalEntity[EID, PosType]) + Position2DStopMoveEventHandle[EID generic.Basic, PosType generic.SignedNumber] func(moving *TwoDimensional[EID, PosType], entity TwoDimensionalEntity[EID, PosType]) ) diff --git a/game/moving/2d_example_test.go b/game/moving/2d_example_test.go index df3840c..a7e632e 100644 --- a/game/moving/2d_example_test.go +++ b/game/moving/2d_example_test.go @@ -8,7 +8,7 @@ import ( ) func ExampleNewTwoDimensional() { - m := moving.NewTwoDimensional() + m := moving.NewTwoDimensional[int64, float64]() defer func() { m.Release() }() @@ -19,13 +19,13 @@ func ExampleNewTwoDimensional() { } func ExampleTwoDimensional_MoveTo() { - m := moving.NewTwoDimensional(moving.WithTwoDimensionalTimeUnit(time.Second)) + m := moving.NewTwoDimensional(moving.WithTwoDimensionalTimeUnit[int64, float64](time.Second)) defer func() { m.Release() }() 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") wait.Done() }) @@ -41,20 +41,20 @@ func ExampleTwoDimensional_MoveTo() { } func ExampleTwoDimensional_StopMove() { - m := moving.NewTwoDimensional(moving.WithTwoDimensionalTimeUnit(time.Second)) + m := moving.NewTwoDimensional(moving.WithTwoDimensionalTimeUnit[int64, float64](time.Second)) defer func() { m.Release() }() 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") }) - 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") 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") wait.Done() }) diff --git a/game/moving/2d_options.go b/game/moving/2d_options.go index 9300d73..1f6e68f 100644 --- a/game/moving/2d_options.go +++ b/game/moving/2d_options.go @@ -2,15 +2,16 @@ package moving import ( "errors" + "github.com/kercylan98/minotaur/utils/generic" "time" ) -type TwoDimensionalOption func(moving *TwoDimensional) +type TwoDimensionalOption[EID generic.Basic, PosType generic.SignedNumber] func(moving *TwoDimensional[EID, PosType]) // WithTwoDimensionalTimeUnit 通过特定时间单位创建 // - 默认单位为1毫秒,最小单位也为1毫秒 -func WithTwoDimensionalTimeUnit(duration time.Duration) TwoDimensionalOption { - return func(moving *TwoDimensional) { +func WithTwoDimensionalTimeUnit[EID generic.Basic, PosType generic.SignedNumber](duration time.Duration) TwoDimensionalOption[EID, PosType] { + return func(moving *TwoDimensional[EID, PosType]) { if duration < time.Millisecond { panic(errors.New("time unit milliseconds minimum")) } @@ -20,15 +21,15 @@ func WithTwoDimensionalTimeUnit(duration time.Duration) TwoDimensionalOption { // WithTwoDimensionalIdleWaitTime 通过特定的空闲等待时间创建 // - 默认情况下在没有新的移动计划时将限制 100毫秒 + 移动间隔事件(默认100毫秒) -func WithTwoDimensionalIdleWaitTime(duration time.Duration) TwoDimensionalOption { - return func(moving *TwoDimensional) { +func WithTwoDimensionalIdleWaitTime[EID generic.Basic, PosType generic.SignedNumber](duration time.Duration) TwoDimensionalOption[EID, PosType] { + return func(moving *TwoDimensional[EID, PosType]) { moving.idle = duration } } // WithTwoDimensionalInterval 通过特定的移动间隔时间创建 -func WithTwoDimensionalInterval(duration time.Duration) TwoDimensionalOption { - return func(moving *TwoDimensional) { +func WithTwoDimensionalInterval[EID generic.Basic, PosType generic.SignedNumber](duration time.Duration) TwoDimensionalOption[EID, PosType] { + return func(moving *TwoDimensional[EID, PosType]) { moving.interval = duration } } diff --git a/game/moving/2d_test.go b/game/moving/2d_test.go index 9ad79c3..0efa981 100644 --- a/game/moving/2d_test.go +++ b/game/moving/2d_test.go @@ -3,6 +3,7 @@ package moving_test import ( "fmt" "github.com/kercylan98/minotaur/game/moving" + "github.com/kercylan98/minotaur/utils/geometry" "sync" "testing" "time" @@ -10,29 +11,26 @@ import ( type MoveEntity struct { guid int64 - x, y float64 + pos geometry.Point[float64] speed float64 } -func (slf *MoveEntity) SetGuid(guid int64) { -} - -func (slf *MoveEntity) GetGuid() int64 { +func (slf *MoveEntity) GetTwoDimensionalEntityID() int64 { 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 { 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 { return &MoveEntity{ guid: guid, @@ -41,7 +39,7 @@ func NewEntity(guid int64, speed float64) *MoveEntity { } func TestNewTwoDimensional(t *testing.T) { - m := moving.NewTwoDimensional() + m := moving.NewTwoDimensional[int64, float64]() defer func() { m.Release() }() @@ -50,21 +48,21 @@ func TestNewTwoDimensional(t *testing.T) { func TestTwoDimensional_StopMove(t *testing.T) { var wait sync.WaitGroup - m := moving.NewTwoDimensional(moving.WithTwoDimensionalTimeUnit(time.Second)) + m := moving.NewTwoDimensional(moving.WithTwoDimensionalTimeUnit[int64, float64](time.Second)) defer func() { m.Release() }() - m.RegPosition2DChangeEvent(func(moving *moving.TwoDimensional, entity moving.TwoDimensionalEntity, oldX, oldY float64) { - x, y := entity.GetPosition() - fmt.Println(fmt.Sprintf("%d : %d | %f, %f > %f, %f", entity.GetGuid(), time.Now().UnixMilli(), oldX, oldY, x, y)) + m.RegPosition2DChangeEvent(func(moving *moving.TwoDimensional[int64, float64], entity moving.TwoDimensionalEntity[int64, float64], oldX, oldY float64) { + x, y := entity.GetPosition().GetXY() + 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) { - fmt.Println(fmt.Sprintf("%d : %d | destination", entity.GetGuid(), time.Now().UnixMilli())) + m.RegPosition2DDestinationEvent(func(moving *moving.TwoDimensional[int64, float64], entity moving.TwoDimensionalEntity[int64, float64]) { + fmt.Println(fmt.Sprintf("%d : %d | destination", entity.GetTwoDimensionalEntityID(), time.Now().UnixMilli())) wait.Done() }) - m.RegPosition2DStopMoveEvent(func(moving *moving.TwoDimensional, entity moving.TwoDimensionalEntity) { - fmt.Println(fmt.Sprintf("%d : %d | stop", entity.GetGuid(), time.Now().UnixMilli())) + m.RegPosition2DStopMoveEvent(func(moving *moving.TwoDimensional[int64, float64], entity moving.TwoDimensionalEntity[int64, float64]) { + fmt.Println(fmt.Sprintf("%d : %d | stop", entity.GetTwoDimensionalEntityID(), time.Now().UnixMilli())) wait.Done() })