256 lines
5.3 KiB
Go
256 lines
5.3 KiB
Go
package ecs
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"log"
|
||
"math"
|
||
"time"
|
||
|
||
"github.com/yohamta/donburi"
|
||
"github.com/yohamta/donburi/features/events"
|
||
)
|
||
|
||
type WorldState int
|
||
|
||
type WorldId int
|
||
|
||
const (
|
||
Init WorldState = iota
|
||
Running
|
||
Pause
|
||
Error
|
||
Closed
|
||
)
|
||
|
||
type (
|
||
World interface {
|
||
|
||
// Id returns the unique identifier for the world.
|
||
Id() WorldId
|
||
// Create creates a new entity with the specified components.
|
||
Create(components ...donburi.IComponentType) *Entry
|
||
// CreateMany creates a new entity with the specified components.
|
||
CreateMany(n int, components ...donburi.IComponentType) []*Entry
|
||
// Entry returns an entry for the specified entity.
|
||
Entry(entity Entity) *Entry
|
||
// Remove removes the specified entity.
|
||
Remove(entity Entity)
|
||
// Valid returns true if the specified entity is valid.
|
||
Valid(e Entity) bool
|
||
// Len returns the number of entities in the world.
|
||
Len() int
|
||
|
||
StartUp()
|
||
Pause()
|
||
Resume()
|
||
SetSpeed(speed float64) error
|
||
AddSystem(sys ...ISystem)
|
||
// 在世界中执行逻辑(在世界运行线程中)
|
||
Execute(fn ExecuteFunc)
|
||
Close()
|
||
// 世界时间间隔
|
||
Tick() int
|
||
Running() bool
|
||
}
|
||
// 世界执行函数
|
||
ExecuteFunc func()
|
||
RequestFunc func() any
|
||
|
||
WorldRequest struct {
|
||
req RequestFunc
|
||
context context.Context
|
||
}
|
||
)
|
||
|
||
type world struct {
|
||
world donburi.World
|
||
systems []ISystem
|
||
state WorldState
|
||
tick int
|
||
ticker *time.Ticker
|
||
speed float64
|
||
// 下一帧需要执行的次数
|
||
times float64
|
||
|
||
// 退出信号
|
||
quit chan struct{}
|
||
// 待执行函数
|
||
toBeExecuteds chan ExecuteFunc
|
||
}
|
||
|
||
// 新建一个组件类型
|
||
func NewComponentType[T any](opts ...interface{}) *ComponentType[T] {
|
||
ct := donburi.NewComponentType[T](opts...)
|
||
return &ComponentType[T]{ct}
|
||
}
|
||
|
||
// 新建一个标签(注意:新建的标签如果作为全局变量,使用Each,First等方法时必须在World线程中执行,即调用World.Execute执行)
|
||
func NewTag() *ComponentType[struct{}] {
|
||
return NewComponentType[struct{}]()
|
||
}
|
||
|
||
// 初始化一个新World
|
||
// tick 单位为ms,且必须大于0,(小于15ms的值在Windows系统中会达不到,Windows系统中系统中断好像默认是15.6ms,也就是一秒最多64次)
|
||
func NewWorld(tick int) World {
|
||
return &world{
|
||
world: donburi.NewWorld(),
|
||
systems: make([]ISystem, 0),
|
||
state: Init,
|
||
tick: tick,
|
||
ticker: time.NewTicker(time.Duration(tick) * time.Millisecond),
|
||
speed: 1,
|
||
times: 1,
|
||
quit: make(chan struct{}),
|
||
toBeExecuteds: make(chan ExecuteFunc, 1024),
|
||
}
|
||
}
|
||
func (w *world) Running() bool {
|
||
return w.state == Running
|
||
}
|
||
func (w *world) Tick() int {
|
||
return w.tick
|
||
}
|
||
func (w *world) Id() WorldId {
|
||
return WorldId(w.world.Id())
|
||
}
|
||
|
||
func (w *world) Create(components ...donburi.IComponentType) *Entry {
|
||
entity := w.world.Create(components...)
|
||
return &Entry{w.world.Entry(entity)}
|
||
}
|
||
|
||
func (w *world) CreateMany(n int, components ...donburi.IComponentType) []*Entry {
|
||
entitys := w.world.CreateMany(n, components...)
|
||
ets := make([]*Entry, len(entitys))
|
||
for i, e := range entitys {
|
||
ets[i] = &Entry{w.world.Entry(e)}
|
||
}
|
||
return ets
|
||
}
|
||
|
||
func (w *world) Entry(entity Entity) *Entry {
|
||
return &Entry{w.world.Entry(entity.Entity)}
|
||
}
|
||
|
||
func (w *world) Remove(entity Entity) {
|
||
w.world.Remove(entity.Entity)
|
||
}
|
||
|
||
func (w *world) Valid(e Entity) bool {
|
||
return w.world.Valid(e.Entity)
|
||
}
|
||
|
||
func (w *world) Len() int {
|
||
return w.world.Len()
|
||
}
|
||
|
||
// 添加系统
|
||
func (w *world) AddSystem(sys ...ISystem) {
|
||
w.systems = append(w.systems, sys...)
|
||
}
|
||
|
||
// 执行所有事件处理
|
||
func (w *world) ProcessAllEvents() {
|
||
events.ProcessAllEvents(w.world)
|
||
}
|
||
|
||
// 暂停世界
|
||
func (w *world) Pause() {
|
||
if w.state == Running {
|
||
w.state = Pause
|
||
}
|
||
}
|
||
|
||
// 恢复世界运行
|
||
func (w *world) Resume() {
|
||
if w.state == Pause {
|
||
w.state = Running
|
||
}
|
||
}
|
||
|
||
const (
|
||
SpeedMin = 0.1
|
||
SpeedMax = 10
|
||
)
|
||
|
||
// 设置世界运行速度
|
||
func (w *world) SetSpeed(speed float64) error {
|
||
if speed < SpeedMin || speed > SpeedMax {
|
||
return fmt.Errorf("速度必须在[%f, %d]之间", SpeedMin, SpeedMax)
|
||
}
|
||
w.speed = speed
|
||
return nil
|
||
}
|
||
|
||
// 启动世界,世界逻辑开始执行且世界为运行状态
|
||
func (w *world) StartUp() {
|
||
if w.state == Init { // 避免重复运行
|
||
w.state = Running
|
||
go w.run()
|
||
}
|
||
}
|
||
|
||
// 在世界线程执行逻辑
|
||
func (w *world) Execute(fn ExecuteFunc) {
|
||
w.toBeExecuteds <- fn
|
||
}
|
||
|
||
// 关闭世界
|
||
func (w *world) Close() {
|
||
w.quit <- struct{}{}
|
||
}
|
||
|
||
// 执行待处理方法
|
||
func (w *world) executeTodos() {
|
||
manageEventChan := w.toBeExecuteds
|
||
for {
|
||
select {
|
||
case callBack := <-manageEventChan:
|
||
{
|
||
callBack()
|
||
}
|
||
default:
|
||
return
|
||
}
|
||
}
|
||
}
|
||
func (w *world) run() {
|
||
for {
|
||
select {
|
||
case <-w.quit: // 退出信号
|
||
// 仿真退出,更新状态
|
||
log.Println("仿真退出,id:", w.world.Id())
|
||
w.state = Closed
|
||
default:
|
||
}
|
||
if w.state == Error {
|
||
// 世界错误,关闭世界
|
||
return
|
||
}
|
||
if w.state == Closed {
|
||
// 世界正常关闭
|
||
return
|
||
}
|
||
<-w.ticker.C
|
||
if w.state == Pause { // 暂停不更新
|
||
continue
|
||
}
|
||
if w.times > 1 {
|
||
times := int(math.Floor(w.times))
|
||
for i := 0; i < times; i++ {
|
||
for _, sys := range w.systems {
|
||
sys.Update(w)
|
||
}
|
||
// 处理事件管理相关
|
||
w.executeTodos()
|
||
// 处理所有事件
|
||
processAllEvents(w)
|
||
}
|
||
w.times = w.times - float64(times) + w.speed
|
||
} else {
|
||
w.times += w.speed
|
||
}
|
||
}
|
||
}
|