mirror of
https://github.com/mvvasilev/last_light.git
synced 2025-04-18 20:29:52 +03:00
maybe rewrite with ecs, idk
This commit is contained in:
parent
3c83d97a34
commit
53483ac870
15 changed files with 416 additions and 181 deletions
73
engine/ecs/component_type.go
Normal file
73
engine/ecs/component_type.go
Normal 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
|
||||
)
|
|
@ -1,10 +1,9 @@
|
|||
package ecs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
)
|
||||
|
||||
/* ===================== 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 ======================= */
|
||||
|
||||
type SystemOrder uint8
|
||||
|
||||
type EntityId uint64
|
||||
|
||||
type ComponentMask uint64
|
||||
|
@ -90,7 +67,6 @@ func (c ComponentMask) ContainsMultiple(ids ComponentMask) bool {
|
|||
type System interface {
|
||||
Name() string
|
||||
Order() int
|
||||
Input(world *World, e tcell.EventKey)
|
||||
Tick(world *World, deltaTime int64)
|
||||
}
|
||||
|
||||
|
@ -116,17 +92,11 @@ func CreateRandomEntityId() EntityId {
|
|||
return EntityId(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
func createEntity(components ...Component) *BasicEntity {
|
||||
ent := &BasicEntity{
|
||||
func createEntity() *BasicEntity {
|
||||
return &BasicEntity{
|
||||
id: CreateRandomEntityId(),
|
||||
components: make(map[ComponentType]Component, 0),
|
||||
}
|
||||
|
||||
for _, c := range components {
|
||||
ent.components[c.Type()] = c
|
||||
}
|
||||
|
||||
return ent
|
||||
}
|
||||
|
||||
func (ent *BasicEntity) Id() EntityId {
|
||||
|
@ -192,14 +162,17 @@ type World struct {
|
|||
|
||||
entities map[EntityId]*BasicEntity
|
||||
components map[ComponentType][]Component
|
||||
singletons map[ComponentType]Component
|
||||
systems []System
|
||||
}
|
||||
|
||||
func CreateWorld() *World {
|
||||
return &World{
|
||||
entities: make(map[EntityId]*BasicEntity, 0),
|
||||
systems: make([]System, 0),
|
||||
components: make(map[ComponentType][]Component, 0),
|
||||
entities: make(map[EntityId]*BasicEntity, 0),
|
||||
systems: make([]System, 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
|
||||
}
|
||||
|
||||
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) {
|
||||
if w.registeredComponentTypes.Contains(t) {
|
||||
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
|
||||
}
|
||||
|
||||
func (w *World) AddSystem(s System) (err *ECSError) {
|
||||
func (w *World) AddSystem(s System) {
|
||||
w.systems = append(w.systems, s)
|
||||
|
||||
slices.SortFunc(w.systems, func(a System, b System) int { return a.Order() - b.Order() })
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *World) CreateEntity(comps ...Component) *BasicEntity {
|
||||
ent := createEntity(comps...)
|
||||
ent := createEntity()
|
||||
|
||||
w.entities[ent.Id()] = ent
|
||||
|
||||
for _, c := range comps {
|
||||
w.AddComponentToEntity(ent, c)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/* ==================================================== */
|
||||
|
|
|
@ -3,8 +3,6 @@ package ecs
|
|||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
)
|
||||
|
||||
func Test_ecsError(t *testing.T) {
|
||||
|
@ -171,9 +169,8 @@ func TestCreateRandomEntityId(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_createEntity(t *testing.T) {
|
||||
type args struct {
|
||||
components []Component
|
||||
}
|
||||
type args struct{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
|
@ -183,7 +180,7 @@ func Test_createEntity(t *testing.T) {
|
|||
}
|
||||
for _, tt := range tests {
|
||||
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)
|
||||
}
|
||||
})
|
||||
|
@ -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) {
|
||||
type fields struct {
|
||||
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) {
|
||||
type fields struct {
|
||||
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
22
engine/event.go
Normal 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
|
||||
}
|
|
@ -2,7 +2,6 @@ package engine
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
|
@ -40,16 +39,15 @@ func Multidraw(drawables ...Drawable) []Drawable {
|
|||
return arr
|
||||
}
|
||||
|
||||
type RenderContext struct {
|
||||
type EngineContext struct {
|
||||
screen tcell.Screen
|
||||
view *views.ViewPort
|
||||
|
||||
events chan tcell.Event
|
||||
quit chan struct{}
|
||||
drawables chan Drawable
|
||||
events chan tcell.Event
|
||||
quit chan struct{}
|
||||
}
|
||||
|
||||
func CreateRenderContext() (*RenderContext, error) {
|
||||
func InitEngine() (*EngineContext, error) {
|
||||
screen, sErr := tcell.NewScreen()
|
||||
|
||||
if sErr != nil {
|
||||
|
@ -87,22 +85,21 @@ func CreateRenderContext() (*RenderContext, error) {
|
|||
|
||||
go screen.ChannelEvents(events, quit)
|
||||
|
||||
context := new(RenderContext)
|
||||
context := new(EngineContext)
|
||||
|
||||
context.screen = screen
|
||||
context.events = events
|
||||
context.quit = quit
|
||||
context.view = view
|
||||
context.drawables = make(chan Drawable)
|
||||
|
||||
return context, nil
|
||||
}
|
||||
|
||||
func (c *RenderContext) Stop() {
|
||||
func (c *EngineContext) Stop() {
|
||||
c.screen.Fini()
|
||||
}
|
||||
|
||||
func (c *RenderContext) CollectInputEvents() []*tcell.EventKey {
|
||||
func (c *EngineContext) CollectInputEvents() []*tcell.EventKey {
|
||||
events := make([]tcell.Event, len(c.events))
|
||||
|
||||
select {
|
||||
|
@ -118,20 +115,14 @@ func (c *RenderContext) CollectInputEvents() []*tcell.EventKey {
|
|||
case *tcell.EventKey:
|
||||
inputEvents = append(inputEvents, ev)
|
||||
case *tcell.EventResize:
|
||||
c.onResize(ev)
|
||||
c.Resize(ev.Size())
|
||||
}
|
||||
}
|
||||
|
||||
return inputEvents
|
||||
}
|
||||
|
||||
func (c *RenderContext) DrawableQueue() chan Drawable {
|
||||
return c.drawables
|
||||
}
|
||||
|
||||
func (c *RenderContext) onResize(ev *tcell.EventResize) {
|
||||
width, height := ev.Size()
|
||||
|
||||
func (c *EngineContext) Resize(width, height int) {
|
||||
c.screen.Clear()
|
||||
|
||||
c.view.Resize(
|
||||
|
@ -144,20 +135,16 @@ func (c *RenderContext) onResize(ev *tcell.EventResize) {
|
|||
c.screen.Sync()
|
||||
}
|
||||
|
||||
func (c *RenderContext) Draw(deltaTime int64, drawables []Drawable) {
|
||||
fps := 1_000_000 / deltaTime
|
||||
|
||||
func (c *EngineContext) Clear() {
|
||||
c.view.Clear()
|
||||
}
|
||||
|
||||
msPerFrame := float32(fps) / 1000.0
|
||||
|
||||
fpsText := CreateText(0, 0, 16, 1, fmt.Sprintf("%vms", msPerFrame), tcell.StyleDefault)
|
||||
func (c *EngineContext) Show() {
|
||||
c.screen.Show()
|
||||
}
|
||||
|
||||
func (c *EngineContext) Draw(drawables []Drawable) {
|
||||
for _, d := range drawables {
|
||||
d.Draw(c.view)
|
||||
}
|
||||
|
||||
fpsText.Draw(c.view)
|
||||
|
||||
c.screen.Show()
|
||||
}
|
||||
|
|
17
game/component/drawables.go
Normal file
17
game/component/drawables.go
Normal 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
|
||||
}
|
16
game/component/game_state.go
Normal file
16
game/component/game_state.go
Normal 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
17
game/component/input.go
Normal 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
|
||||
}
|
|
@ -1,60 +1,30 @@
|
|||
package game
|
||||
|
||||
import (
|
||||
"log"
|
||||
"mvvasilev/last_light/engine"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
const TICK_RATE int64 = 50 // tick every 50ms ( 20 ticks per second )
|
||||
|
||||
type GameContext struct {
|
||||
renderContext *engine.RenderContext
|
||||
|
||||
game *Game
|
||||
world *GameWorld
|
||||
}
|
||||
|
||||
func CreateGameContext() *GameContext {
|
||||
gc := new(GameContext)
|
||||
|
||||
rc, err := engine.CreateRenderContext()
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("%~v", err)
|
||||
}
|
||||
|
||||
gc.renderContext = rc
|
||||
gc.game = CreateGame()
|
||||
gc.world = CreateGameWorld()
|
||||
|
||||
return gc
|
||||
}
|
||||
|
||||
func (gc *GameContext) Run() {
|
||||
lastLoop := time.Now()
|
||||
lastTick := time.Now()
|
||||
|
||||
for {
|
||||
deltaTime := 1 + time.Since(lastLoop).Microseconds()
|
||||
deltaTime := 1 + time.Since(lastLoop).Milliseconds()
|
||||
lastLoop = time.Now()
|
||||
|
||||
for _, e := range gc.renderContext.CollectInputEvents() {
|
||||
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)
|
||||
gc.world.Tick(deltaTime)
|
||||
}
|
||||
}
|
||||
|
|
52
game/game_world.go
Normal file
52
game/game_world.go
Normal 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)
|
||||
}
|
27
game/system/game_state_system.go
Normal file
27
game/system/game_state_system.go
Normal 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)
|
||||
|
||||
}
|
32
game/system/input_system.go
Normal file
32
game/system/input_system.go
Normal 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(),
|
||||
})
|
||||
}
|
65
game/system/rendering_system.go
Normal file
65
game/system/rendering_system.go
Normal 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
5
go.sum
|
@ -1,11 +1,15 @@
|
|||
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/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/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg=
|
||||
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/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/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/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
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.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-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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
5
main.go
5
main.go
|
@ -6,3 +6,8 @@ func main() {
|
|||
gc := game.CreateGameContext()
|
||||
gc.Run()
|
||||
}
|
||||
|
||||
func runGame() {
|
||||
gc := game.CreateGameContext()
|
||||
gc.Run()
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue