游戏基本元素优化、完善游戏世界

This commit is contained in:
kercylan98 2023-04-25 14:13:15 +08:00
parent 6ff54e7a1a
commit c685fa163b
10 changed files with 467 additions and 48 deletions

View File

@ -2,6 +2,9 @@ package game
// Actor 表示游戏中的对象,具有唯一标识符
type Actor interface {
// SetGuid 设置对象的唯一标识符
// - 需要注意的是该函数不应该主动执行,否则可能产生意想不到的情况
SetGuid(guid int64)
// GetGuid 获取对象的唯一标识符
GetGuid() int64
}

View File

@ -11,61 +11,49 @@ type ActorMove struct {
}
func (slf *ActorMove) MoveTo2D(x, y float64) {
//TODO implement me
panic("implement me")
slf.SetXY(x, y)
}
func (slf *ActorMove) MoveBy2D(dx, dy float64) {
//TODO implement me
panic("implement me")
slf.SetXY(slf.GetX()+dx, slf.GetY()+dy)
}
func (slf *ActorMove) MoveTo3D(x, y, z float64) {
//TODO implement me
panic("implement me")
slf.SetXYZ(x, y, z)
}
func (slf *ActorMove) MoveBy3D(dx, dy, dz float64) {
//TODO implement me
panic("implement me")
slf.SetXYZ(slf.GetX()+dx, slf.GetY()+dy, slf.GetZ()+dz)
}
func (slf *ActorMove) MoveToX(x float64) {
//TODO implement me
panic("implement me")
slf.SetX(x)
}
func (slf *ActorMove) MoveByX(dx float64) {
//TODO implement me
panic("implement me")
slf.SetX(slf.GetX() + dx)
}
func (slf *ActorMove) MoveToY(y float64) {
//TODO implement me
panic("implement me")
slf.SetY(y)
}
func (slf *ActorMove) MoveByY(dy float64) {
//TODO implement me
panic("implement me")
slf.SetY(slf.GetY() + dy)
}
func (slf *ActorMove) MoveToZ(z float64) {
//TODO implement me
panic("implement me")
slf.SetZ(z)
}
func (slf *ActorMove) MoveByZ(dz float64) {
//TODO implement me
panic("implement me")
slf.SetZ(slf.GetZ() + dz)
}
func (slf *ActorMove) GetSpeed() float64 {
//TODO implement me
panic("implement me")
return slf.speed
}
func (slf *ActorMove) SetSpeed(speed float64) {
//TODO implement me
panic("implement me")
slf.speed = speed
}

View File

@ -1,5 +1,7 @@
package builtin
import "minotaur/game"
// NewPosition 创建一个新的Position对象。
func NewPosition(x, y, z float64) *Position {
return &Position{
@ -11,58 +13,93 @@ func NewPosition(x, y, z float64) *Position {
// Position 是一个具有位置信息的对象。
type Position struct {
x, y, z float64
x, y, z float64
positionChangeEventHandles []game.PositionChangeEventHandle
}
// GetX 返回Position对象的X坐标。
func (p *Position) GetX() float64 {
return p.x
func (slf *Position) GetX() float64 {
return slf.x
}
// GetY 返回Position对象的Y坐标。
func (p *Position) GetY() float64 {
return p.y
func (slf *Position) GetY() float64 {
return slf.y
}
// GetZ 返回Position对象的Z坐标。
func (p *Position) GetZ() float64 {
return p.z
func (slf *Position) GetZ() float64 {
return slf.z
}
// GetXY 返回Position对象的X和Y坐标。
func (p *Position) GetXY() (float64, float64) {
return p.x, p.y
func (slf *Position) GetXY() (float64, float64) {
return slf.x, slf.y
}
// GetXYZ 返回Position对象的X、Y和Z坐标。
func (p *Position) GetXYZ() (float64, float64, float64) {
return p.x, p.y, p.z
func (slf *Position) GetXYZ() (float64, float64, float64) {
return slf.x, slf.y, slf.z
}
// SetX 设置Position对象的X坐标。
func (p *Position) SetX(x float64) {
p.x = x
func (slf *Position) SetX(x float64) {
old := slf.Clone()
defer slf.OnPositionChangeEvent(old, slf)
slf.x = x
}
// SetY 设置Position对象的Y坐标。
func (p *Position) SetY(y float64) {
p.y = y
func (slf *Position) SetY(y float64) {
old := slf.Clone()
defer slf.OnPositionChangeEvent(old, slf)
slf.y = y
}
// SetZ 设置Position对象的Z坐标。
func (p *Position) SetZ(z float64) {
p.z = z
func (slf *Position) SetZ(z float64) {
old := slf.Clone()
defer slf.OnPositionChangeEvent(old, slf)
slf.z = z
}
// SetXY 设置Position对象的X和Y坐标。
func (p *Position) SetXY(x, y float64) {
p.x = x
p.y = y
func (slf *Position) SetXY(x, y float64) {
old := slf.Clone()
defer slf.OnPositionChangeEvent(old, slf)
slf.x = x
slf.y = y
}
// SetXYZ 设置Position对象的X、Y和Z坐标。
func (p *Position) SetXYZ(x, y, z float64) {
p.x = x
p.y = y
p.z = z
func (slf *Position) SetXYZ(x, y, z float64) {
old := slf.Clone()
defer slf.OnPositionChangeEvent(old, slf)
slf.x = x
slf.y = y
slf.z = z
}
func (slf *Position) Clone() game.Position {
return &Position{
x: slf.x,
y: slf.y,
z: slf.z,
}
}
func (slf *Position) Compare(position game.Position) bool {
return slf.x == position.GetX() && slf.y == position.GetY() && slf.z == position.GetZ()
}
func (slf *Position) RegPositionChangeEvent(handle game.PositionChangeEventHandle) {
slf.positionChangeEventHandles = append(slf.positionChangeEventHandles, handle)
}
func (slf *Position) OnPositionChangeEvent(old, new game.Position) {
if !old.Compare(new) {
for _, handle := range slf.positionChangeEventHandles {
handle(old, new)
}
}
}

237
game/builtin/world.go Normal file
View File

@ -0,0 +1,237 @@
package builtin
import (
"minotaur/game"
"minotaur/utils/synchronization"
"sync/atomic"
)
func NewWorld[PlayerID comparable](guid int64, options ...WorldOption[PlayerID]) *World[PlayerID] {
world := &World[PlayerID]{
guid: guid,
players: synchronization.NewMap[PlayerID, game.Player[PlayerID]](),
playerActors: synchronization.NewMap[PlayerID, *synchronization.Map[int64, game.Actor]](),
owners: synchronization.NewMap[int64, PlayerID](),
actors: synchronization.NewMap[int64, game.Actor](),
}
for _, option := range options {
option(world)
}
return world
}
type World[PlayerID comparable] struct {
guid int64
actorGuid atomic.Int64
playerLimit int
players *synchronization.Map[PlayerID, game.Player[PlayerID]]
playerActors *synchronization.Map[PlayerID, *synchronization.Map[int64, game.Actor]]
owners *synchronization.Map[int64, PlayerID]
actors *synchronization.Map[int64, game.Actor]
playerJoinWorldEventHandles []game.PlayerJoinWorldEventHandle[PlayerID]
playerLeaveWorldEventHandles []game.PlayerLeaveWorldEventHandle[PlayerID]
actorGeneratedEventHandles []game.ActorGeneratedEventHandle
actorAnnihilationEventHandles []game.ActorAnnihilationEventHandle
actorOwnerChangeEventHandles []game.ActorOwnerChangeEventHandle[PlayerID]
released atomic.Bool
}
func (slf *World[PlayerID]) GetGuid() int64 {
return slf.guid
}
func (slf *World[PlayerID]) GetPlayerLimit() int {
return slf.playerLimit
}
func (slf *World[PlayerID]) GetPlayer(id PlayerID) game.Player[PlayerID] {
return slf.players.Get(id)
}
func (slf *World[PlayerID]) GetPlayers() map[PlayerID]game.Player[PlayerID] {
return slf.players.Map()
}
func (slf *World[PlayerID]) GetActor(guid int64) game.Actor {
return slf.actors.Get(guid)
}
func (slf *World[PlayerID]) GetActors() map[int64]game.Actor {
return slf.actors.Map()
}
func (slf *World[PlayerID]) GetPlayerActor(id PlayerID, guid int64) game.Actor {
if actors := slf.playerActors.Get(id); actors != nil {
return actors.Get(guid)
}
return nil
}
func (slf *World[PlayerID]) GetPlayerActors(id PlayerID) map[int64]game.Actor {
return slf.playerActors.Get(id).Map()
}
func (slf *World[PlayerID]) IsExistPlayer(id PlayerID) bool {
return slf.players.Exist(id)
}
func (slf *World[PlayerID]) IsExistActor(guid int64) bool {
return slf.actors.Exist(guid)
}
func (slf *World[PlayerID]) IsOwner(id PlayerID, guid int64) bool {
actors := slf.playerActors.Get(id)
if actors != nil {
return actors.Exist(guid)
}
return false
}
func (slf *World[PlayerID]) Join(player game.Player[PlayerID]) error {
if slf.released.Load() {
return ErrWorldReleased
}
if slf.players.Size() >= slf.playerLimit && slf.playerLimit > 0 {
return ErrWorldPlayerLimit
}
slf.players.Set(player.GetID(), player)
if actors := slf.playerActors.Get(player.GetID()); actors == nil {
actors = synchronization.NewMap[int64, game.Actor]()
slf.playerActors.Set(player.GetID(), actors)
}
slf.OnPlayerJoinWorldEvent(player)
return nil
}
func (slf *World[PlayerID]) Leave(player game.Player[PlayerID]) {
if !slf.players.Exist(player.GetID()) {
return
}
slf.OnPlayerLeaveWorldEvent(player)
slf.playerActors.Get(player.GetID()).Range(func(guid int64, actor game.Actor) {
slf.OnActorAnnihilationEvent(actor)
slf.owners.Delete(guid)
})
slf.playerActors.Delete(player.GetID())
slf.players.Delete(player.GetID())
}
func (slf *World[PlayerID]) AddActor(actor game.Actor) {
guid := slf.actorGuid.Add(1)
actor.SetGuid(guid)
slf.actors.Set(actor.GetGuid(), actor)
slf.OnActorGeneratedEvent(actor)
}
func (slf *World[PlayerID]) RemoveActor(guid int64) {
if actor, exist := slf.actors.GetExist(guid); exist {
slf.OnActorAnnihilationEvent(actor)
if id, exist := slf.owners.DeleteGetExist(guid); exist {
slf.playerActors.Get(id).Delete(guid)
}
slf.actors.Delete(guid)
}
}
func (slf *World[PlayerID]) SetActorOwner(guid int64, id PlayerID) {
oldId, exist := slf.owners.GetExist(guid)
if exist && oldId == id {
return
}
actor := slf.GetActor(guid)
if actor == nil {
return
}
slf.owners.Set(guid, id)
slf.playerActors.Get(id).Set(guid, actor)
slf.OnActorOwnerChangeEvent(actor, oldId, id, false)
}
func (slf *World[PlayerID]) RemoveActorOwner(guid int64) {
id, exist := slf.owners.GetExist(guid)
if !exist {
return
}
slf.owners.Delete(guid)
slf.playerActors.Get(id).Delete(guid)
slf.OnActorOwnerChangeEvent(slf.GetActor(guid), id, id, true)
}
func (slf *World[PlayerID]) Reset() {
slf.players.Clear()
slf.playerActors.Range(func(id PlayerID, actors *synchronization.Map[int64, game.Actor]) {
actors.Clear()
})
slf.playerActors.Clear()
slf.owners.Clear()
slf.actors.Clear()
slf.actorGuid.Store(0)
}
func (slf *World[PlayerID]) Release() {
if !slf.released.Swap(true) {
slf.Reset()
slf.players = nil
slf.playerActors = nil
slf.owners = nil
slf.actors = nil
slf.playerJoinWorldEventHandles = nil
slf.playerLeaveWorldEventHandles = nil
slf.actorGeneratedEventHandles = nil
slf.actorAnnihilationEventHandles = nil
slf.actorOwnerChangeEventHandles = nil
}
}
func (slf *World[PlayerID]) RegPlayerJoinWorldEvent(handle game.PlayerJoinWorldEventHandle[PlayerID]) {
slf.playerJoinWorldEventHandles = append(slf.playerJoinWorldEventHandles, handle)
}
func (slf *World[PlayerID]) OnPlayerJoinWorldEvent(player game.Player[PlayerID]) {
for _, handle := range slf.playerJoinWorldEventHandles {
handle(player)
}
}
func (slf *World[PlayerID]) RegPlayerLeaveWorldEvent(handle game.PlayerLeaveWorldEventHandle[PlayerID]) {
slf.playerLeaveWorldEventHandles = append(slf.playerLeaveWorldEventHandles, handle)
}
func (slf *World[PlayerID]) OnPlayerLeaveWorldEvent(player game.Player[PlayerID]) {
for _, handle := range slf.playerLeaveWorldEventHandles {
handle(player)
}
}
func (slf *World[PlayerID]) RegActorGeneratedEvent(handle game.ActorGeneratedEventHandle) {
slf.actorGeneratedEventHandles = append(slf.actorGeneratedEventHandles, handle)
}
func (slf *World[PlayerID]) OnActorGeneratedEvent(actor game.Actor) {
for _, handle := range slf.actorGeneratedEventHandles {
handle(actor)
}
}
func (slf *World[PlayerID]) RegActorAnnihilationEvent(handle game.ActorAnnihilationEventHandle) {
slf.actorAnnihilationEventHandles = append(slf.actorAnnihilationEventHandles, handle)
}
func (slf *World[PlayerID]) OnActorAnnihilationEvent(actor game.Actor) {
for _, handle := range slf.actorAnnihilationEventHandles {
handle(actor)
}
}
func (slf *World[PlayerID]) RegActorOwnerChangeEvent(handle game.ActorOwnerChangeEventHandle[PlayerID]) {
slf.actorOwnerChangeEventHandles = append(slf.actorOwnerChangeEventHandles, handle)
}
func (slf *World[PlayerID]) OnActorOwnerChangeEvent(actor game.Actor, old, new PlayerID, isolated bool) {
for _, handle := range slf.actorOwnerChangeEventHandles {
handle(actor, old, new, isolated)
}
}

View File

@ -0,0 +1,8 @@
package builtin
import "errors"
var (
ErrWorldPlayerLimit = errors.New("the number of players in the world has reached the upper limit")
ErrWorldReleased = errors.New("the world has been released")
)

View File

@ -0,0 +1,9 @@
package builtin
type WorldOption[PlayerID comparable] func(world *World[PlayerID])
func WithWorldPlayerLimit[PlayerID comparable](playerLimit int) WorldOption[PlayerID] {
return func(world *World[PlayerID]) {
world.playerLimit = playerLimit
}
}

View File

@ -24,4 +24,15 @@ type Position interface {
SetXY(x, y float64)
// SetXYZ 设置X、Y和Z轴坐标
SetXYZ(x, y, z float64)
// Clone 克隆当前位置到新结构体
Clone() Position
// Compare 比较两个坐标是否相同
Compare(position Position) bool
// RegPositionChangeEvent 当位置发生改变时,将立即执行注册的事件处理函数
RegPositionChangeEvent(handle PositionChangeEventHandle)
OnPositionChangeEvent(old, new Position)
}
type (
PositionChangeEventHandle func(old, new Position)
)

70
game/world.go Normal file
View File

@ -0,0 +1,70 @@
package game
// World 游戏世界接口定义
type World[PlayerID comparable] interface {
// GetGuid 获取世界的唯一标识符
GetGuid() int64
// GetPlayerLimit 获取玩家人数上限
GetPlayerLimit() int
// GetPlayer 根据玩家id获取玩家
GetPlayer(id PlayerID) Player[PlayerID]
// GetPlayers 获取世界中的所有玩家
GetPlayers() map[PlayerID]Player[PlayerID]
// GetActor 根据唯一标识符获取世界中的游戏对象
GetActor(guid int64) Actor
// GetActors 获取世界中的所有游戏对象
GetActors() map[int64]Actor
// GetPlayerActor 获取游戏世界中归属特定玩家的特定游戏对象
GetPlayerActor(id PlayerID, guid int64) Actor
// GetPlayerActors 获取游戏世界中归属特定玩家的所有游戏对象
GetPlayerActors(id PlayerID) map[int64]Actor
// IsExistPlayer 检查游戏世界中是否存在特定玩家
IsExistPlayer(id PlayerID) bool
// IsExistActor 检查游戏世界中是否存在特定游戏对象
IsExistActor(guid int64) bool
// IsOwner 检查游戏世界中的特定游戏对象是否归属特定玩家
IsOwner(id PlayerID, guid int64) bool
// Join 使特定玩家加入游戏世界
Join(player Player[PlayerID]) error
// Leave 使特定玩家离开游戏世界
Leave(player Player[PlayerID])
// AddActor 添加游戏对象
AddActor(actor Actor)
// RemoveActor 移除游戏对象
RemoveActor(guid int64)
// SetActorOwner 设置游戏对象归属玩家
SetActorOwner(guid int64, id PlayerID)
// RemoveActorOwner 移除游戏对象归属,置为无主的
RemoveActorOwner(guid int64)
// Reset 重置世界资源
Reset()
// Release 释放世界资源,释放后世界将不可用
Release()
// RegPlayerJoinWorldEvent 玩家进入世界时将立即执行被注册的事件处理函数
RegPlayerJoinWorldEvent(handle PlayerJoinWorldEventHandle[PlayerID])
OnPlayerJoinWorldEvent(player Player[PlayerID])
// RegPlayerLeaveWorldEvent 玩家离开世界时将立即执行被注册的事件处理函数
RegPlayerLeaveWorldEvent(handle PlayerLeaveWorldEventHandle[PlayerID])
OnPlayerLeaveWorldEvent(player Player[PlayerID])
// RegActorGeneratedEvent 游戏世界中的游戏对象生成完成时将立即执行被注册的事件处理函数
RegActorGeneratedEvent(handle ActorGeneratedEventHandle)
OnActorGeneratedEvent(actor Actor)
// RegActorAnnihilationEvent 游戏世界中的游戏对象被移除前执行被注册的事件处理函数
RegActorAnnihilationEvent(handle ActorAnnihilationEventHandle)
OnActorAnnihilationEvent(actor Actor)
// RegActorOwnerChangeEvent 游戏对象的归属被改变时立刻执行被注册的事件处理函数
RegActorOwnerChangeEvent(handle ActorOwnerChangeEventHandle[PlayerID])
OnActorOwnerChangeEvent(actor Actor, old, new PlayerID, isolated bool)
}
type (
PlayerJoinWorldEventHandle[ID comparable] func(player Player[ID])
PlayerLeaveWorldEventHandle[ID comparable] func(player Player[ID])
ActorGeneratedEventHandle func(actor Actor)
ActorAnnihilationEventHandle func(actor Actor)
ActorOwnerChangeEventHandle[ID comparable] func(actor Actor, old, new ID, isolated bool)
)

View File

@ -178,3 +178,9 @@ func (slf *Map[Key, Value]) Map() map[Key]Value {
slf.lock.RUnlock()
return m
}
func (slf *Map[Key, Value]) Size() int {
slf.lock.RLock()
defer slf.lock.RUnlock()
return len(slf.data)
}

View File

@ -0,0 +1,50 @@
package synchronization
import "sync"
func NewPool[T any](bufferSize int, generator func() T, releaser func(data T)) *Pool[T] {
pool := &Pool[T]{
bufferSize: bufferSize,
generator: generator,
releaser: releaser,
}
for i := 0; i < bufferSize; i++ {
pool.put(generator())
}
return pool
}
type Pool[T any] struct {
mutex sync.Mutex
buffers []T
bufferSize int
generator func() T
releaser func(data T)
}
func (slf *Pool[T]) Get() T {
slf.mutex.Lock()
if len(slf.buffers) > 0 {
data := slf.buffers[0]
slf.buffers = slf.buffers[1:]
slf.mutex.Unlock()
return data
}
slf.mutex.Unlock()
return slf.generator()
}
func (slf *Pool[T]) Release(data T) {
slf.releaser(data)
slf.put(data)
}
func (slf *Pool[T]) put(data T) {
slf.mutex.Lock()
if len(slf.buffers) > slf.bufferSize {
slf.mutex.Unlock()
return
}
slf.buffers = append(slf.buffers, data)
slf.mutex.Unlock()
}