11.6 C
New York
Friday, March 14, 2025

structure – Find out how to strategy implementing composition-based entities


I used to be engaged on a bit engine wrapper for ebitengine (Go) to assist with dealing with entities (not ECS, simply EC) and got here up with this closely purposeful implementation the place every entity is a closure(??) that registers replace features within the “constructor”

I wished to leverage the advantages of ECS (utilizing solely knowledge that’s obligatory in a given context and storing that every one collectively for comparable objects) however with the liberty to outline particular behaviors for every entity sort.

entity.go:

bundle sport

import (
    "picture/shade"

    "github.com/hajimehoshi/ebiten/v2"
    "github.com/hajimehoshi/ebiten/v2/vector"
    "starblock-studios.com/ebitengine-project/mathutil"
    v2 "starblock-studios.com/ebitengine-project/mathutil"
)

func (w *world) DebugEntity(
    shade shade.RGBA, dimension float64, velocity float64) (Entity *struct{ Place v2.Vector2 }) {
    Entity = &struct{ Place v2.Vector2 }{}
    Entity.Place = *v2.NewV2(0, 0)
    getUpdate := func() EventCallback[InfoUpdate] {
        cpos := mathutil.NewV2(0, 0)

        return func(occasion InfoUpdate) {
            x, y := ebiten.CursorPosition()
            cpos.Set(float64(x), float64(y))
            boon := 1 - mathutil.Clamp(cpos.DistanceSquared(&Entity.Place)/10000, 0, 1)
            Entity.Place = *Entity.Place.MoveTowards(cpos, velocity*boon)
        }
    }
    getDraw := func() EventCallback[InfoDraw] {
        radius := float32(dimension)
        circleColor := shade
        imageSize := int(dimension * 2)
        picture := ebiten.NewImage(imageSize, imageSize)
        vector.DrawFilledCircle(picture, float32(imageSize/2), float32(imageSize/2), radius, circleColor, false)
        choices := &ebiten.DrawImageOptions{}
        return func(occasion InfoDraw) {
            choices.GeoM = ebiten.GeoM{}
            choices.GeoM.Translate(Entity.Place.X-float64(imageSize)/2, Entity.Place.Y-float64(imageSize)/2)
            occasion.Display screen.DrawImage(picture, choices)
        }
    }
    w.OnUpdate.register(getUpdate())
    w.OnDrawLayers[0].register(getDraw())
    return Entity
}

world.go:

bundle sport

import (
    "time"

    "github.com/hajimehoshi/ebiten/v2"
)

sort EventCallback[T any] func(T)

sort InfoUpdate struct {
    Delta       float64
    TimeSeconds float64
}

sort InfoDraw struct {
    Display screen *ebiten.Picture
}
sort EventGroup[T any] []EventCallback[T]

sort world struct {
    OnUpdate     EventGroup[InfoUpdate]
    OnDrawLayers []EventGroup[InfoDraw]

    begin          time.Time
    previousHandle time.Time
}

func (g EventGroup[T]) Notify(p T) {
    for _, fn := vary g {
        fn(p)
    }
}
func CreateWorld() *world {
    w := &world{}
    w.begin = time.Now()
    w.previousHandle = w.begin
    w.OnDrawLayers = make([]EventGroup[InfoDraw], 1)
    return w
}

func (w *world) Replace() {
    w.OnUpdate.Notify(
        InfoUpdate{
            Delta:       time.Since(w.previousHandle).Seconds(),
            TimeSeconds: time.Since(w.begin).Seconds(),
        },
    )
    w.previousHandle = time.Now()

}

func (w *world) Draw(display *ebiten.Picture) {
    for _, v := vary w.OnDrawLayers {
        v.Notify(
            InfoDraw{
                Display screen: display,
            },
        )
    }
}

func (g *EventGroup[T]) register(handler EventCallback[T]) {
    *g = append(*g, handler)
}

I examined this out and it truly performs fairly a bit higher than an identical implementation I attempted the place every entity is an interface (replace, draw)
As I perceive it, closures get ‘compiled’ into structs that solely maintain the info that they want, just like how ECS solely {couples} elements that require one another.

I am not extraordinarily involved about efficiency however I wish to have as a lot efficiency as I can whereas nonetheless having the flexibleness to make use of these particular person occasion ‘strategies’ (just like different composition-based engines)

I wished to know if that is truly a superb strategy that I ought to pursue and, if not, what could be an easier implementation with comparable efficiency and capabilities.

To make clear, I am working manner out of my realm of experience so would recognize suggestions from anybody skilled with this type of system (:

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles