jl-ecs/world.go
2023-08-31 16:19:41 +08:00

254 lines
5.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package ecs
import (
"bytes"
"fmt"
"log"
"runtime"
"strconv"
"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)
Close()
Tick() int
Running() bool
GoroutineId() uint64
}
type world struct {
gId uint64
world donburi.World
systems []ISystem
state WorldState
tick int
speed float64
quit chan struct{}
chanManageEvent chan ManageEventFunc
}
func NewComponentType[T any](opts ...interface{}) *ComponentType[T] {
ct := donburi.NewComponentType[T](opts...)
return &ComponentType[T]{ct}
}
func NewWorld(tick int) World {
return &world{
world: donburi.NewWorld(),
systems: make([]ISystem, 0),
state: Init,
tick: tick,
speed: 1,
quit: make(chan struct{}),
chanManageEvent: make(chan ManageEventFunc, 1024),
}
}
func (w *world) GoroutineId() uint64 {
return w.gId
}
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) Close() {
w.quit <- struct{}{}
}
// 获取world与事件系统间的管道的只读引用
func (w *world) eventChanReader() <-chan ManageEventFunc {
return w.chanManageEvent
}
// 事件管理相关处理
func (w *world) processManageEventFuncs() {
manageEventChan := w.eventChanReader()
for {
select {
case callBack := <-manageEventChan:
{
callBack()
}
default:
return
}
}
}
func (w *world) run() {
w.gId = currentGoId()
for {
select {
case <-w.quit: // 退出信号
log.Println("仿真退出,id:", w.world.Id())
w.state = Closed
default:
}
if w.state == Error {
log.Println("世界错误,关闭世界,id:", w.world.Id())
return
}
if w.state == Closed {
log.Println("世界正常关闭,id:", w.world.Id())
return
}
if w.state == Pause { // 暂停不更新
log.Println("仿真暂停中,id:", w.world.Id())
sleep := int64(float64(w.tick) / (w.speed))
time.Sleep(time.Duration(sleep) * time.Millisecond)
continue
}
start := time.Now()
// fmt.Println("仿真更新,id:", info.id)
for _, sys := range w.systems {
sys.Update(w)
}
// 处理事件管理相关
w.processManageEventFuncs()
// 处理所有事件
processAllEvents(w)
// 执行逻辑花费时间单位ms
ot := time.Duration(time.Now().Nanosecond() - start.Nanosecond()).Milliseconds()
// 根据间隔和速度计算休眠时间
sleep := int64(float64(w.tick)/(w.speed)) - ot
if sleep > 0 {
time.Sleep(time.Duration(sleep) * time.Millisecond)
} else {
log.Println("仿真无休眠,id:", w.world.Id())
}
}
}
// 获取当前协程id
func currentGoId() (gid uint64) {
b := make([]byte, 16)
b = b[:runtime.Stack(b, false)]
b = bytes.TrimPrefix(b, []byte("goroutine "))
b = b[:bytes.IndexByte(b, ' ')]
n, err := strconv.ParseUint(string(b), 10, 64)
if err != nil {
panic(err)
}
return n
}