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  }