Skip to content

Commit

Permalink
add generic and index-based ways to overwrite complete components
Browse files Browse the repository at this point in the history
  • Loading branch information
mlange-42 committed Feb 10, 2023
1 parent aa34e74 commit 53b3f82
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 4 deletions.
17 changes: 16 additions & 1 deletion ecs/generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,37 @@ type Getter[T any] struct {
//
// Getter provides a type-safe way to access components by entity ID.
//
// See also [World.Get] and [World.Has].
// See also [World.Get], [World.Has] and [World.Set].
func NewGetter[T any](w *World) Getter[T] {
return Getter[T]{
id: ComponentID[T](w),
world: w,
}
}

// Get gets the component for the given entity.
//
// See also [World.Get].
func (g *Getter[T]) Get(entity Entity) *T {
return (*T)(g.world.Get(entity, g.id))
}

// Has returns whether the entity has the component.
//
// See also [World.Has].
func (g *Getter[T]) Has(entity Entity) bool {
return g.world.Has(entity, g.id)
}

// Set overwrites the component for the given entity.
//
// Panics if the entity does not have a component of that type.
//
// See also [World.Set].
func (g *Getter[T]) Set(entity Entity, comp *T) *T {
return (*T)(g.world.Set(entity, g.id, comp))
}

// Add adds a component type to an entity.
//
// See also [World.Add].
Expand Down
22 changes: 22 additions & 0 deletions ecs/generic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,30 @@ package ecs

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestGenericGetter(t *testing.T) {
w := NewWorld()
get := NewGetter[testStruct0](&w)

e0 := w.NewEntity()

Add[testStruct0](&w, e0)
has := get.Has(e0)
_ = get.Get(e0)
assert.True(t, has)

_ = get.Set(e0, &testStruct0{100})
str := get.Get(e0)

assert.Equal(t, 100, int(str.val))

get2 := NewGetter[testStruct1](&w)
assert.Panics(t, func() { get2.Set(e0, &testStruct1{}) })
}

func TestGenericAddRemove(t *testing.T) {
w := NewWorld()
get := NewGetter[testStruct0](&w)
Expand Down
20 changes: 18 additions & 2 deletions ecs/world.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func (w *World) Alive(entity Entity) bool {
// Returns `nil` if the entity has no such component.
// Panics when called for an already removed entity.
//
// See also [Getter]/[NewGetter].
// See also [Getter.Get] for a generic variant.
func (w *World) Get(entity Entity, comp ID) unsafe.Pointer {
index := w.entities[entity.id]
arch := index.arch
Expand All @@ -120,7 +120,7 @@ func (w *World) Get(entity Entity, comp ID) unsafe.Pointer {
//
// Panics when called for an already removed entity.
//
// See also [Getter]/[NewGetter].
// See also [Getter.Has] for a generic variant.
func (w *World) Has(entity Entity, comp ID) bool {
index := w.entities[entity.id]
return index.arch.HasComponent(comp)
Expand Down Expand Up @@ -152,6 +152,22 @@ func (w *World) Assign(entity Entity, id ID, comp interface{}) unsafe.Pointer {
return w.copyTo(entity, id, comp)
}

// Set overwrites a component for an [Entity], using a given pointer for the content.
//
// The passed component must be a pointer.
// Returns a pointer to the assigned memory.
// The passed in pointer is not a valid reference to that memory!
//
// Panics when called on a locked world or for an already removed entity.
// Do not use during [Query] iteration!
//
// Panics if the entity does not have a component of that type.
//
// See also [Getter.Set] for a generic variant.
func (w *World) Set(entity Entity, id ID, comp interface{}) unsafe.Pointer {
return w.copyTo(entity, id, comp)
}

// AssignN assigns multiple components to an [Entity], using pointers for the content.
// See also [World.Assign].
//
Expand Down
6 changes: 5 additions & 1 deletion ecs/world_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ func TestWorldExchange(t *testing.T) {
assert.Panics(t, func() { w.Exchange(e1, []ID{}, []ID{posID}) })
}

func TestWorldAssign(t *testing.T) {
func TestWorldAssignSet(t *testing.T) {
w := NewWorld()

posID := ComponentID[position](&w)
Expand Down Expand Up @@ -201,6 +201,10 @@ func TestWorldAssign(t *testing.T) {

pos = (*position)(w.Get(e2, posID))
assert.Equal(t, 4, pos.X)

pos = (*position)(w.Set(e2, posID, &position{7, 8}))
pos = (*position)(w.Get(e2, posID))
assert.Equal(t, 7, pos.X)
}
func TestWorldGetComponents(t *testing.T) {
w := NewWorld()
Expand Down

0 comments on commit 53b3f82

Please sign in to comment.