maybe rewrite with ecs, idk

This commit is contained in:
Miroslav Vasilev 2024-05-18 20:38:46 +03:00
parent 3c83d97a34
commit 53483ac870
15 changed files with 416 additions and 181 deletions

View file

@ -0,0 +1,73 @@
package ecs
/* ================== ComponentType =================== */
type ComponentType uint64
const (
ComponentType_0 ComponentType = 0b0000000000000000000000000000000000000000000000000000000000000000
ComponentType_1 ComponentType = 0b0000000000000000000000000000000000000000000000000000000000000001
ComponentType_2 ComponentType = 0b0000000000000000000000000000000000000000000000000000000000000010
ComponentType_3 ComponentType = 0b0000000000000000000000000000000000000000000000000000000000000100
ComponentType_4 ComponentType = 0b0000000000000000000000000000000000000000000000000000000000001000
ComponentType_5 ComponentType = 0b0000000000000000000000000000000000000000000000000000000000010000
ComponentType_6 ComponentType = 0b0000000000000000000000000000000000000000000000000000000000100000
ComponentType_7 ComponentType = 0b0000000000000000000000000000000000000000000000000000000001000000
ComponentType_8 ComponentType = 0b0000000000000000000000000000000000000000000000000000000010000000
ComponentType_9 ComponentType = 0b0000000000000000000000000000000000000000000000000000000100000000
ComponentType_10 ComponentType = 0b0000000000000000000000000000000000000000000000000000001000000000
ComponentType_11 ComponentType = 0b0000000000000000000000000000000000000000000000000000010000000000
ComponentType_12 ComponentType = 0b0000000000000000000000000000000000000000000000000000100000000000
ComponentType_13 ComponentType = 0b0000000000000000000000000000000000000000000000000001000000000000
ComponentType_14 ComponentType = 0b0000000000000000000000000000000000000000000000000010000000000000
ComponentType_15 ComponentType = 0b0000000000000000000000000000000000000000000000000100000000000000
ComponentType_16 ComponentType = 0b0000000000000000000000000000000000000000000000001000000000000000
ComponentType_17 ComponentType = 0b0000000000000000000000000000000000000000000000010000000000000000
ComponentType_18 ComponentType = 0b0000000000000000000000000000000000000000000000100000000000000000
ComponentType_19 ComponentType = 0b0000000000000000000000000000000000000000000001000000000000000000
ComponentType_20 ComponentType = 0b0000000000000000000000000000000000000000000010000000000000000000
ComponentType_21 ComponentType = 0b0000000000000000000000000000000000000000000100000000000000000000
ComponentType_22 ComponentType = 0b0000000000000000000000000000000000000000001000000000000000000000
ComponentType_23 ComponentType = 0b0000000000000000000000000000000000000000010000000000000000000000
ComponentType_24 ComponentType = 0b0000000000000000000000000000000000000000100000000000000000000000
ComponentType_25 ComponentType = 0b0000000000000000000000000000000000000001000000000000000000000000
ComponentType_26 ComponentType = 0b0000000000000000000000000000000000000010000000000000000000000000
ComponentType_27 ComponentType = 0b0000000000000000000000000000000000000100000000000000000000000000
ComponentType_28 ComponentType = 0b0000000000000000000000000000000000001000000000000000000000000000
ComponentType_29 ComponentType = 0b0000000000000000000000000000000000010000000000000000000000000000
ComponentType_30 ComponentType = 0b0000000000000000000000000000000000100000000000000000000000000000
ComponentType_31 ComponentType = 0b0000000000000000000000000000000001000000000000000000000000000000
ComponentType_32 ComponentType = 0b0000000000000000000000000000000010000000000000000000000000000000
ComponentType_33 ComponentType = 0b0000000000000000000000000000000100000000000000000000000000000000
ComponentType_34 ComponentType = 0b0000000000000000000000000000001000000000000000000000000000000000
ComponentType_35 ComponentType = 0b0000000000000000000000000000010000000000000000000000000000000000
ComponentType_36 ComponentType = 0b0000000000000000000000000000100000000000000000000000000000000000
ComponentType_37 ComponentType = 0b0000000000000000000000000001000000000000000000000000000000000000
ComponentType_38 ComponentType = 0b0000000000000000000000000010000000000000000000000000000000000000
ComponentType_39 ComponentType = 0b0000000000000000000000000100000000000000000000000000000000000000
ComponentType_40 ComponentType = 0b0000000000000000000000001000000000000000000000000000000000000000
ComponentType_41 ComponentType = 0b0000000000000000000000010000000000000000000000000000000000000000
ComponentType_42 ComponentType = 0b0000000000000000000000100000000000000000000000000000000000000000
ComponentType_43 ComponentType = 0b0000000000000000000001000000000000000000000000000000000000000000
ComponentType_44 ComponentType = 0b0000000000000000000010000000000000000000000000000000000000000000
ComponentType_45 ComponentType = 0b0000000000000000000100000000000000000000000000000000000000000000
ComponentType_46 ComponentType = 0b0000000000000000001000000000000000000000000000000000000000000000
ComponentType_47 ComponentType = 0b0000000000000000010000000000000000000000000000000000000000000000
ComponentType_48 ComponentType = 0b0000000000000000100000000000000000000000000000000000000000000000
ComponentType_49 ComponentType = 0b0000000000000001000000000000000000000000000000000000000000000000
ComponentType_50 ComponentType = 0b0000000000000010000000000000000000000000000000000000000000000000
ComponentType_51 ComponentType = 0b0000000000000100000000000000000000000000000000000000000000000000
ComponentType_52 ComponentType = 0b0000000000001000000000000000000000000000000000000000000000000000
ComponentType_53 ComponentType = 0b0000000000010000000000000000000000000000000000000000000000000000
ComponentType_54 ComponentType = 0b0000000000100000000000000000000000000000000000000000000000000000
ComponentType_55 ComponentType = 0b0000000001000000000000000000000000000000000000000000000000000000
ComponentType_56 ComponentType = 0b0000000010000000000000000000000000000000000000000000000000000000
ComponentType_57 ComponentType = 0b0000000100000000000000000000000000000000000000000000000000000000
ComponentType_58 ComponentType = 0b0000001000000000000000000000000000000000000000000000000000000000
ComponentType_59 ComponentType = 0b0000010000000000000000000000000000000000000000000000000000000000
ComponentType_60 ComponentType = 0b0000100000000000000000000000000000000000000000000000000000000000
ComponentType_61 ComponentType = 0b0001000000000000000000000000000000000000000000000000000000000000
ComponentType_62 ComponentType = 0b0010000000000000000000000000000000000000000000000000000000000000
ComponentType_63 ComponentType = 0b0100000000000000000000000000000000000000000000000000000000000000
ComponentType_64 ComponentType = 0b1000000000000000000000000000000000000000000000000000000000000000
)

View file

@ -1,10 +1,9 @@
package ecs package ecs
import ( import (
"fmt"
"slices" "slices"
"time" "time"
"github.com/gdamore/tcell/v2"
) )
/* ===================== ECSError ===================== */ /* ===================== ECSError ===================== */
@ -25,30 +24,8 @@ func (e *ECSError) Error() string {
/* ==================================================== */ /* ==================================================== */
/* ================== ComponentType =================== */
type ComponentType uint64
func TypeFrom(powerOf2 uint64) (ComponentType, *ECSError) {
if powerOf2 > 63 {
return 0, ecsError("Failure: Provided number is too high ( component types must be represented by a power of 2, up to 63 )")
}
t := uint64(0)
for i := range powerOf2 {
t *= i
}
return ComponentType(t), nil
}
/* ==================================================== */
/* ====================== Types ======================= */ /* ====================== Types ======================= */
type SystemOrder uint8
type EntityId uint64 type EntityId uint64
type ComponentMask uint64 type ComponentMask uint64
@ -90,7 +67,6 @@ func (c ComponentMask) ContainsMultiple(ids ComponentMask) bool {
type System interface { type System interface {
Name() string Name() string
Order() int Order() int
Input(world *World, e tcell.EventKey)
Tick(world *World, deltaTime int64) Tick(world *World, deltaTime int64)
} }
@ -116,17 +92,11 @@ func CreateRandomEntityId() EntityId {
return EntityId(time.Now().UnixNano()) return EntityId(time.Now().UnixNano())
} }
func createEntity(components ...Component) *BasicEntity { func createEntity() *BasicEntity {
ent := &BasicEntity{ return &BasicEntity{
id: CreateRandomEntityId(), id: CreateRandomEntityId(),
components: make(map[ComponentType]Component, 0), components: make(map[ComponentType]Component, 0),
} }
for _, c := range components {
ent.components[c.Type()] = c
}
return ent
} }
func (ent *BasicEntity) Id() EntityId { func (ent *BasicEntity) Id() EntityId {
@ -192,6 +162,7 @@ type World struct {
entities map[EntityId]*BasicEntity entities map[EntityId]*BasicEntity
components map[ComponentType][]Component components map[ComponentType][]Component
singletons map[ComponentType]Component
systems []System systems []System
} }
@ -200,6 +171,8 @@ func CreateWorld() *World {
entities: make(map[EntityId]*BasicEntity, 0), entities: make(map[EntityId]*BasicEntity, 0),
systems: make([]System, 0), systems: make([]System, 0),
components: make(map[ComponentType][]Component, 0), components: make(map[ComponentType][]Component, 0),
registeredComponentNames: make(map[ComponentType]string, 0),
singletons: make(map[ComponentType]Component, 0),
} }
} }
@ -225,6 +198,22 @@ func (w *World) QueryComponents(componentIds ...ComponentType) (components map[C
return comps, nil return comps, nil
} }
func (w *World) FetchSingletonComponent(componentId ComponentType) (component Component, err *ECSError) {
comp := w.singletons[componentId]
if comp == nil {
componentName := w.registeredComponentNames[componentId]
return nil, ecsError(fmt.Sprintf("Failure: No singleton component of type %v could be found", componentName))
}
return comp, nil
}
func (w *World) AddSingletonComponent(component Component) {
w.singletons[component.Type()] = component
}
func (w *World) RegisterComponentType(t ComponentType, name string) (err *ECSError) { func (w *World) RegisterComponentType(t ComponentType, name string) (err *ECSError) {
if w.registeredComponentTypes.Contains(t) { if w.registeredComponentTypes.Contains(t) {
return ecsError("Failure: ComponentType conflict, another component already exists with that type number") return ecsError("Failure: ComponentType conflict, another component already exists with that type number")
@ -268,19 +257,21 @@ func (w *World) AddComponentToEntity(ent *BasicEntity, comp Component) (modified
return ent, nil return ent, nil
} }
func (w *World) AddSystem(s System) (err *ECSError) { func (w *World) AddSystem(s System) {
w.systems = append(w.systems, s) w.systems = append(w.systems, s)
slices.SortFunc(w.systems, func(a System, b System) int { return a.Order() - b.Order() }) slices.SortFunc(w.systems, func(a System, b System) int { return a.Order() - b.Order() })
return nil
} }
func (w *World) CreateEntity(comps ...Component) *BasicEntity { func (w *World) CreateEntity(comps ...Component) *BasicEntity {
ent := createEntity(comps...) ent := createEntity()
w.entities[ent.Id()] = ent w.entities[ent.Id()] = ent
for _, c := range comps {
w.AddComponentToEntity(ent, c)
}
return ent return ent
} }
@ -304,10 +295,4 @@ func (w *World) Tick(dt int64) {
} }
} }
func (w *World) Input(e tcell.EventKey) {
for _, s := range w.systems {
s.Input(w, e)
}
}
/* ==================================================== */ /* ==================================================== */

View file

@ -3,8 +3,6 @@ package ecs
import ( import (
"reflect" "reflect"
"testing" "testing"
"github.com/gdamore/tcell/v2"
) )
func Test_ecsError(t *testing.T) { func Test_ecsError(t *testing.T) {
@ -171,9 +169,8 @@ func TestCreateRandomEntityId(t *testing.T) {
} }
func Test_createEntity(t *testing.T) { func Test_createEntity(t *testing.T) {
type args struct { type args struct{}
components []Component
}
tests := []struct { tests := []struct {
name string name string
args args args args
@ -183,7 +180,7 @@ func Test_createEntity(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if got := createEntity(tt.args.components...); !reflect.DeepEqual(got, tt.want) { if got := createEntity(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("createEntity() = %v, want %v", got, tt.want) t.Errorf("createEntity() = %v, want %v", got, tt.want)
} }
}) })
@ -535,41 +532,6 @@ func TestWorld_AddComponentToEntity(t *testing.T) {
} }
} }
func TestWorld_AddSystem(t *testing.T) {
type fields struct {
registeredComponentTypes ComponentMask
registeredComponentNames map[ComponentType]string
entities map[EntityId]*BasicEntity
components map[ComponentType][]Component
systems []System
}
type args struct {
s System
}
tests := []struct {
name string
fields fields
args args
wantErr *ECSError
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &World{
registeredComponentTypes: tt.fields.registeredComponentTypes,
registeredComponentNames: tt.fields.registeredComponentNames,
entities: tt.fields.entities,
components: tt.fields.components,
systems: tt.fields.systems,
}
if gotErr := w.AddSystem(tt.args.s); !reflect.DeepEqual(gotErr, tt.wantErr) {
t.Errorf("World.AddSystem() = %v, want %v", gotErr, tt.wantErr)
}
})
}
}
func TestWorld_CreateEntity(t *testing.T) { func TestWorld_CreateEntity(t *testing.T) {
type fields struct { type fields struct {
registeredComponentTypes ComponentMask registeredComponentTypes ComponentMask
@ -709,38 +671,6 @@ func TestWorld_Tick(t *testing.T) {
} }
} }
func TestWorld_Input(t *testing.T) {
type fields struct {
registeredComponentTypes ComponentMask
registeredComponentNames map[ComponentType]string
entities map[EntityId]*BasicEntity
components map[ComponentType][]Component
systems []System
}
type args struct {
e tcell.EventKey
}
tests := []struct {
name string
fields fields
args args
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &World{
registeredComponentTypes: tt.fields.registeredComponentTypes,
registeredComponentNames: tt.fields.registeredComponentNames,
entities: tt.fields.entities,
components: tt.fields.components,
systems: tt.fields.systems,
}
w.Input(tt.args.e)
})
}
}
func TestWorld_FindEntitiesWithComponents(t *testing.T) { func TestWorld_FindEntitiesWithComponents(t *testing.T) {
type fields struct { type fields struct {
registeredComponentTypes ComponentMask registeredComponentTypes ComponentMask
@ -775,3 +705,35 @@ func TestWorld_FindEntitiesWithComponents(t *testing.T) {
}) })
} }
} }
func TestWorld_AddSystem(t *testing.T) {
type fields struct {
registeredComponentTypes ComponentMask
registeredComponentNames map[ComponentType]string
entities map[EntityId]*BasicEntity
components map[ComponentType][]Component
systems []System
}
type args struct {
s System
}
tests := []struct {
name string
fields fields
args args
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
w := &World{
registeredComponentTypes: tt.fields.registeredComponentTypes,
registeredComponentNames: tt.fields.registeredComponentNames,
entities: tt.fields.entities,
components: tt.fields.components,
systems: tt.fields.systems,
}
w.AddSystem(tt.args.s)
})
}
}

22
engine/event.go Normal file
View file

@ -0,0 +1,22 @@
package engine
import "github.com/gdamore/tcell/v2"
type Event interface {
}
type InputEvent struct {
tcellEvent *tcell.EventKey
}
func (e *InputEvent) TcellEvent() *tcell.EventKey {
return e.tcellEvent
}
type ResizeEvent struct {
tcellEvent *tcell.EventResize
}
func (e *ResizeEvent) TcellEvent() *tcell.EventResize {
return e.tcellEvent
}

View file

@ -2,7 +2,6 @@ package engine
import ( import (
"errors" "errors"
"fmt"
"log" "log"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
@ -40,16 +39,15 @@ func Multidraw(drawables ...Drawable) []Drawable {
return arr return arr
} }
type RenderContext struct { type EngineContext struct {
screen tcell.Screen screen tcell.Screen
view *views.ViewPort view *views.ViewPort
events chan tcell.Event events chan tcell.Event
quit chan struct{} quit chan struct{}
drawables chan Drawable
} }
func CreateRenderContext() (*RenderContext, error) { func InitEngine() (*EngineContext, error) {
screen, sErr := tcell.NewScreen() screen, sErr := tcell.NewScreen()
if sErr != nil { if sErr != nil {
@ -87,22 +85,21 @@ func CreateRenderContext() (*RenderContext, error) {
go screen.ChannelEvents(events, quit) go screen.ChannelEvents(events, quit)
context := new(RenderContext) context := new(EngineContext)
context.screen = screen context.screen = screen
context.events = events context.events = events
context.quit = quit context.quit = quit
context.view = view context.view = view
context.drawables = make(chan Drawable)
return context, nil return context, nil
} }
func (c *RenderContext) Stop() { func (c *EngineContext) Stop() {
c.screen.Fini() c.screen.Fini()
} }
func (c *RenderContext) CollectInputEvents() []*tcell.EventKey { func (c *EngineContext) CollectInputEvents() []*tcell.EventKey {
events := make([]tcell.Event, len(c.events)) events := make([]tcell.Event, len(c.events))
select { select {
@ -118,20 +115,14 @@ func (c *RenderContext) CollectInputEvents() []*tcell.EventKey {
case *tcell.EventKey: case *tcell.EventKey:
inputEvents = append(inputEvents, ev) inputEvents = append(inputEvents, ev)
case *tcell.EventResize: case *tcell.EventResize:
c.onResize(ev) c.Resize(ev.Size())
} }
} }
return inputEvents return inputEvents
} }
func (c *RenderContext) DrawableQueue() chan Drawable { func (c *EngineContext) Resize(width, height int) {
return c.drawables
}
func (c *RenderContext) onResize(ev *tcell.EventResize) {
width, height := ev.Size()
c.screen.Clear() c.screen.Clear()
c.view.Resize( c.view.Resize(
@ -144,20 +135,16 @@ func (c *RenderContext) onResize(ev *tcell.EventResize) {
c.screen.Sync() c.screen.Sync()
} }
func (c *RenderContext) Draw(deltaTime int64, drawables []Drawable) { func (c *EngineContext) Clear() {
fps := 1_000_000 / deltaTime
c.view.Clear() c.view.Clear()
}
msPerFrame := float32(fps) / 1000.0 func (c *EngineContext) Show() {
c.screen.Show()
fpsText := CreateText(0, 0, 16, 1, fmt.Sprintf("%vms", msPerFrame), tcell.StyleDefault) }
func (c *EngineContext) Draw(drawables []Drawable) {
for _, d := range drawables { for _, d := range drawables {
d.Draw(c.view) d.Draw(c.view)
} }
fpsText.Draw(c.view)
c.screen.Show()
} }

View file

@ -0,0 +1,17 @@
package component
import (
"mvvasilev/last_light/engine"
"mvvasilev/last_light/engine/ecs"
)
const ComponentType_RenderableComponent = ecs.ComponentType_0
type DrawablesComponent struct {
Priority int
Drawables []engine.Drawable
}
func (rc *DrawablesComponent) Type() ecs.ComponentType {
return ComponentType_RenderableComponent
}

View file

@ -0,0 +1,16 @@
package component
import (
"mvvasilev/last_light/engine/ecs"
"mvvasilev/last_light/game/state"
)
const ComponentType_GameStateComponent = ecs.ComponentType_2
type GameStateComponent struct {
GameState *state.GameState
}
func (gsc *GameStateComponent) Type() ecs.ComponentType {
return ComponentType_GameStateComponent
}

17
game/component/input.go Normal file
View file

@ -0,0 +1,17 @@
package component
import (
"mvvasilev/last_light/engine/ecs"
"github.com/gdamore/tcell/v2"
)
const ComponentType_InputComponent = ecs.ComponentType_1
type InputComponent struct {
KeyEvents []*tcell.EventKey
}
func (ic *InputComponent) Type() ecs.ComponentType {
return ComponentType_InputComponent
}

View file

@ -1,60 +1,30 @@
package game package game
import ( import (
"log"
"mvvasilev/last_light/engine"
"os"
"time" "time"
) )
const TICK_RATE int64 = 50 // tick every 50ms ( 20 ticks per second ) const TICK_RATE int64 = 50 // tick every 50ms ( 20 ticks per second )
type GameContext struct { type GameContext struct {
renderContext *engine.RenderContext world *GameWorld
game *Game
} }
func CreateGameContext() *GameContext { func CreateGameContext() *GameContext {
gc := new(GameContext) gc := new(GameContext)
rc, err := engine.CreateRenderContext() gc.world = CreateGameWorld()
if err != nil {
log.Fatalf("%~v", err)
}
gc.renderContext = rc
gc.game = CreateGame()
return gc return gc
} }
func (gc *GameContext) Run() { func (gc *GameContext) Run() {
lastLoop := time.Now() lastLoop := time.Now()
lastTick := time.Now()
for { for {
deltaTime := 1 + time.Since(lastLoop).Microseconds() deltaTime := 1 + time.Since(lastLoop).Milliseconds()
lastLoop = time.Now() lastLoop = time.Now()
for _, e := range gc.renderContext.CollectInputEvents() { gc.world.Tick(deltaTime)
gc.game.Input(e)
}
if time.Since(lastTick).Milliseconds() >= TICK_RATE {
stop := !gc.game.Tick(deltaTime)
if stop {
gc.renderContext.Stop()
os.Exit(0)
break
}
lastTick = time.Now()
}
drawables := gc.game.CollectDrawables()
gc.renderContext.Draw(deltaTime, drawables)
} }
} }

52
game/game_world.go Normal file
View file

@ -0,0 +1,52 @@
package game
import (
"log"
"mvvasilev/last_light/engine"
"mvvasilev/last_light/engine/ecs"
"mvvasilev/last_light/game/component"
"mvvasilev/last_light/game/system"
)
type GameWorld struct {
ecs *ecs.World
}
func CreateGameWorld() *GameWorld {
world := &GameWorld{
ecs: ecs.CreateWorld(),
}
engineContext, err := engine.InitEngine()
if err != nil {
// TODO: error logs
log.Fatalf("%~v", err)
return nil
}
world.ecs.RegisterComponentType(component.ComponentType_RenderableComponent, "RenderableComponent")
world.ecs.RegisterComponentType(component.ComponentType_InputComponent, "InputComponent")
world.ecs.RegisterComponentType(component.ComponentType_GameStateComponent, "GameStateComponent")
world.ecs.AddSystem(system.CreateRenderingSystem(engineContext))
world.ecs.AddSystem(system.CreateInputSystem(engineContext))
world.ecs.AddSystem(system.CreateGameStateSystem())
world.ecs.AddSingletonComponent(&component.InputComponent{})
world.ecs.CreateEntity(&component.DrawablesComponent{
Priority: 0,
Drawables: []engine.Drawable{},
})
return world
}
func (gw *GameWorld) World() *ecs.World {
return gw.ecs
}
func (gw *GameWorld) Tick(dt int64) {
gw.ecs.Tick(dt)
}

View file

@ -0,0 +1,27 @@
package system
import (
"math"
"mvvasilev/last_light/engine/ecs"
"mvvasilev/last_light/game/component"
)
type GameStateSystem struct {
}
func CreateGameStateSystem() *GameStateSystem {
return &GameStateSystem{}
}
func (gss *GameStateSystem) Name() string {
return "GameStateSystem"
}
func (gss *GameStateSystem) Order() int {
return math.MinInt + 100
}
func (gss *GameStateSystem) Tick(world *ecs.World, deltaTime int64) {
comp, err := world.FetchSingletonComponent(component.ComponentType_GameStateComponent)
}

View file

@ -0,0 +1,32 @@
package system
import (
"math"
"mvvasilev/last_light/engine"
"mvvasilev/last_light/engine/ecs"
"mvvasilev/last_light/game/component"
)
type InputSystem struct {
engineContext *engine.EngineContext
}
func CreateInputSystem(ec *engine.EngineContext) *InputSystem {
return &InputSystem{
engineContext: ec,
}
}
func (is *InputSystem) Name() string {
return "InputSystem"
}
func (is *InputSystem) Order() int {
return math.MinInt
}
func (is *InputSystem) Tick(world *ecs.World, deltaTime int64) {
world.AddSingletonComponent(&component.InputComponent{
KeyEvents: is.engineContext.CollectInputEvents(),
})
}

View file

@ -0,0 +1,65 @@
package system
import (
"fmt"
"math"
"mvvasilev/last_light/engine"
"mvvasilev/last_light/engine/ecs"
"mvvasilev/last_light/game/component"
"slices"
"github.com/gdamore/tcell/v2"
)
type RenderingSystem struct {
engineContext *engine.EngineContext
}
func CreateRenderingSystem(renderContext *engine.EngineContext) *RenderingSystem {
return &RenderingSystem{
engineContext: renderContext,
}
}
func (rs *RenderingSystem) Name() string {
return "RenderingSystem"
}
func (rs *RenderingSystem) Order() int {
return math.MaxInt
}
func (rs *RenderingSystem) Tick(world *ecs.World, deltaTime int64) {
comps, err := world.QueryComponents(component.ComponentType_RenderableComponent)
if err != nil {
// Skip this frame since an error occured // TODO: error logging
return
}
components := comps[component.ComponentType_RenderableComponent]
slices.SortFunc(components, func(a ecs.Component, b ecs.Component) int {
aDrawable := a.(*component.DrawablesComponent)
bDrawable := b.(*component.DrawablesComponent)
return aDrawable.Priority - bDrawable.Priority
})
fps := 1_000 / deltaTime
msPerFrame := float32(fps) / 1000.0
fpsText := engine.CreateText(0, 0, 16, 1, fmt.Sprintf("%vms", msPerFrame), tcell.StyleDefault)
rs.engineContext.Clear()
for _, c := range components {
drawables := c.(*component.DrawablesComponent).Drawables
rs.engineContext.Draw(drawables)
}
rs.engineContext.Draw([]engine.Drawable{fpsText})
rs.engineContext.Show()
}

5
go.sum
View file

@ -1,11 +1,15 @@
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v1.4.0 h1:vUnHwJRvcPQa3tzi+0QI4U9JINXYJlOz9yiaiPQ2wMU=
github.com/gdamore/tcell v1.4.0/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0=
github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU= github.com/gdamore/tcell/v2 v2.7.4 h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU=
github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg= github.com/gdamore/tcell/v2 v2.7.4/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@ -24,6 +28,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View file

@ -6,3 +6,8 @@ func main() {
gc := game.CreateGameContext() gc := game.CreateGameContext()
gc.Run() gc.Run()
} }
func runGame() {
gc := game.CreateGameContext()
gc.Run()
}