github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/game/ecs-mini/world.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "math/rand" 6 "reflect" 7 "time" 8 ) 9 10 type Clock struct { 11 Time float64 12 DeltaTime float64 13 WallTime float64 14 } 15 16 type Entity uint64 17 type Component interface{} 18 type System interface { 19 Tick(*World) 20 } 21 22 type World struct { 23 next Entity 24 25 Clock Clock 26 Entities []Entity 27 Storages map[reflect.Type]Storage 28 Systems []System 29 } 30 31 func NewWorld(systems ...System) *World { 32 world := &World{} 33 world.Systems = systems 34 world.Storages = make(map[reflect.Type]Storage) 35 return world 36 } 37 38 type Storage interface { 39 For(func(e Entity, c Component)) 40 Add(e Entity, c Component) 41 Get(e Entity) (Component, bool) 42 Del(e Entity) 43 } 44 45 type storage map[Entity]Component 46 47 func (s storage) For(fn func(e Entity, c Component)) { 48 for e, c := range s { 49 fn(e, c) 50 } 51 } 52 func (s storage) Add(e Entity, c Component) { s[e] = c } 53 func (s storage) Get(e Entity) (Component, bool) { c, ok := s[e]; return c, ok } 54 func (s storage) Del(e Entity) { delete(s, e) } 55 56 func (w *World) GetStorage(c Component) Storage { 57 s, ok := w.Storages[reflect.TypeOf(c)] 58 if !ok { 59 s = make(storage) 60 w.SetStorage(c, s) 61 } 62 return s 63 } 64 func (w *World) SetStorage(c Component, s Storage) { w.Storages[reflect.TypeOf(c)] = s } 65 66 func (w *World) Spawn(cs ...Component) Entity { 67 e := w.next 68 w.next++ 69 w.Entities = append(w.Entities, e) 70 71 for _, c := range cs { 72 w.GetStorage(c).Add(e, c) 73 } 74 75 return e 76 } 77 78 func (w *World) Tick() { 79 for _, sys := range w.Systems { 80 sys.Tick(w) 81 } 82 } 83 84 // usage 85 86 type Vector struct{ X, Y float32 } 87 88 type Body struct { 89 Position Vector 90 Velocity Vector 91 } 92 93 type BodyStorage struct { 94 ID map[Entity]int 95 Body []Body 96 } 97 98 func NewBodyStorage() *BodyStorage { 99 return &BodyStorage{ 100 ID: make(map[Entity]int), 101 Body: []Body{}, 102 } 103 } 104 func (s *BodyStorage) For(fn func(e Entity, c Component)) { 105 for e, i := range s.ID { 106 fn(e, s.Body[i]) 107 } 108 } 109 110 func (s *BodyStorage) Add(e Entity, c Component) { 111 s.ID[e] = len(s.Body) 112 body := c.(Body) 113 s.Body = append(s.Body, body) 114 } 115 func (s *BodyStorage) Get(e Entity) (Component, bool) { 116 i, ok := s.ID[e] 117 if ok { 118 return s.Body[i], true 119 } 120 return nil, false 121 } 122 func (s *BodyStorage) Del(e Entity) { panic("TODO") } 123 124 type Texture struct{ Name int } 125 126 type Physics struct{} 127 128 func (*Physics) Tick(world *World) { 129 bodies := world.GetStorage(Body{}).(*BodyStorage) 130 for i := range bodies.Body { 131 body := &bodies.Body[i] 132 body.Position.X += body.Velocity.X * float32(world.Clock.DeltaTime) 133 body.Position.Y += body.Velocity.Y * float32(world.Clock.DeltaTime) 134 } 135 } 136 137 type Renderer struct{} 138 139 func (*Renderer) Tick(world *World) { 140 fmt.Println("=== FRAME ===") 141 142 bodies := world.GetStorage(Body{}) 143 world.GetStorage(Texture{}).For(func(e Entity, c Component) { 144 tex := c.(Texture) 145 phy, ok := bodies.Get(e) 146 if !ok { 147 return 148 } 149 fmt.Println(tex.Name, phy.(Body).Position) 150 }) 151 } 152 153 func main() { 154 world := NewWorld( 155 &Physics{}, 156 &Renderer{}, 157 ) 158 159 world.SetStorage(Body{}, NewBodyStorage()) 160 161 var balls []Entity 162 for i := 0; i < 10; i++ { 163 body := Body{} 164 body.Position = Vector{rand.Float32() - 0.5, rand.Float32() - 0.5} 165 body.Velocity = Vector{rand.Float32() - 0.5, rand.Float32() - 0.5} 166 balls = append(balls, world.Spawn(body, Texture{i})) 167 } 168 169 for { 170 world.Tick() 171 time.Sleep(3 * time.Second) 172 } 173 }