zllangct / ecs

A Go-implementation of the ECS (Entity-Component-System), focus on the development of game server.
BSD 3-Clause "New" or "Revised" License
132 stars 10 forks source link

Moving System into separate file stops example from working #4

Closed cchandel closed 2 years ago

cchandel commented 2 years ago

Hi, If I move the component and systems into different files, the ecs.GetInterestedComponents[components.Position](m) function returns nil.

package components

import (
    "github.com/zllangct/ecs"
)

type Movement struct {
    ecs.Component[Movement]
    V   int
    Dir []int
}
package systems

import (
    "github.com/zllangct/ecs"
    "astt-ecs/components"
    "reflect"
)

type MoveSystemData struct {
    P *components.Position
    M *components.Movement
}

type MoveSystem struct {
    ecs.System[MoveSystem]
    logger     ecs.IInternalLogger
}

func (m *MoveSystem) Init() {
    //m.logger = m.GetWorld().logger
    m.SetRequirements(&components.Position{}, &components.Movement{})
}

func (m *MoveSystem) Filter(ls map[reflect.Type][]ecs.ComponentOptResult) {
    if len(ls) > 0 {
        ecs.Log.Info("new component:", len(ls))
    }
}

func (m *MoveSystem) Update(event ecs.Event) {
    delta := event.Delta

    nc := m.GetInterestedNew()
    m.Filter(nc)

    //csPosition := m.GetInterested(ecs.GetType[components.Position]()).(*ecs.Collection[components.Position])
    csPosition := ecs.GetInterestedComponents[components.Position](m)
    if csPosition == nil {
        println("here")
        return
    }

    //csMovement := m.GetInterested(ecs.GetType[components.Movement]()).(*ecs.Collection[components.Movement])
    csMovement := ecs.GetInterestedComponents[components.Movement](m)
    if csMovement == nil {
        println("here1")
        return
    }

    d := map[int64]*MoveSystemData{}

    for iter := ecs.NewIterator(csPosition); !iter.End(); iter.Next() {
        position := iter.Val()
        owner := position.Owner()
        movement := ecs.CheckComponent[components.Movement](m, owner)
        if movement == nil {
            continue
        }

        d[position.Owner().ID()] = &MoveSystemData{P: position, M: movement}
        println("here")
    }

    for e, data := range d {
        if data.M == nil || data.P == nil {
            //When the aggregated data components are not uniform, skip the processing
            continue
        }
        data.P.X = data.P.X + int(float64(data.M.Dir[0]*data.M.V)*delta.Seconds())
        data.P.Y = data.P.Y + int(float64(data.M.Dir[1]*data.M.V)*delta.Seconds())
        data.P.Z = data.P.Z + int(float64(data.M.Dir[2]*data.M.V)*delta.Seconds())

        ecs.Log.Info("target id:", e, "delta:", delta, " current position:", data.P.X, data.P.Y, data.P.Z)
    }
    println("here")
}
package main

import (
    "astt-ecs/components"
    "astt-ecs/systems"
    "time"

    "github.com/zllangct/ecs"
)

//main function
func Runtime0() {
    // Create runtime, runtime unique
    rt := ecs.Runtime
    rt.Run()

    // Create a world, there can be multiple worlds
    world := rt.NewWorld(ecs.NewDefaultWorldConfig())
    world.Run()

    // Registration system
    world.Register(&systems.MoveSystem{})

    // Create entities and add components
    ee1 := world.NewEntity()
    ee2 := world.NewEntity()
    ee3 := world.NewEntity()

    ecs.Log.Info(ee1.ID(), ee2.ID(), ee3.ID())

    p1 := &components.Position{
        X: 100,
        Y: 100,
        Z: 100,
    }
    m1 := &components.Movement{
        V:   2000,
        Dir: []int{1, 0, 0},
    }
    world.NewEntity().Add(p1, m1)

    p2 := &components.Position{
        X: 110,
        Y: 110,
        Z: 110,
    }
    m2 := &components.Movement{
        V:   2000,
        Dir: []int{0, 1, 0},
    }
    world.NewEntity().Add(p2, m2)

    //The example only runs for 10 second
    time.Sleep(time.Second * 10)
}

func main() {
    Runtime0()
}

What am I not doing correctly ?

zllangct commented 2 years ago

This component will take effect in next frame, in line with expectations.

cchandel commented 2 years ago

Hi, I get the output

gotip run main.go 
main.go:58: 9222228420207929885 9222233599938490092 9222235498314037447
world.go:89: start world success
here
collection.go:33: collection Add:{"V":2000,"Dir":[1,0,0]}
collection.go:33: collection Add:{"X":100,"Y":100,"Z":100}
collection.go:33: collection Add:{"V":2000,"Dir":[0,1,0]}
collection.go:33: collection Add:{"X":110,"Y":110,"Z":110}
moveSystem.go:26: new component:2

So, it seems like after adding the new entities and sleeping, the update function is not being called. How does the next frame get called ?

Thanks.

zllangct commented 2 years ago

The "here" you noticed is printed end of function "Update"

func (m *MoveSystem) Update(event ecs.Event) {
    delta := event.Delta

    nc := m.GetInterestedNew()
    m.Filter(nc)

    //csPosition := m.GetInterested(ecs.GetType[components.Position]()).(*ecs.Collection[components.Position])
    csPosition := ecs.GetInterestedComponents[components.Position](m)
    if csPosition == nil {
        println("here")
        return
    }

    //csMovement := m.GetInterested(ecs.GetType[components.Movement]()).(*ecs.Collection[components.Movement])
    csMovement := ecs.GetInterestedComponents[components.Movement](m)
    if csMovement == nil {
        println("here1")
        return
    }

    d := map[int64]*MoveSystemData{}

    for iter := ecs.NewIterator(csPosition); !iter.End(); iter.Next() {
        position := iter.Val()
        owner := position.Owner()
        movement := ecs.CheckComponent[components.Movement](m, owner)
        if movement == nil {
            continue
        }

        d[position.Owner().ID()] = &MoveSystemData{P: position, M: movement}
        println("here") // **!!!!!!!!!! HERE**
    }

    for e, data := range d {
        if data.M == nil || data.P == nil {
            //When the aggregated data components are not uniform, skip the processing
            continue
        }
        data.P.X = data.P.X + int(float64(data.M.Dir[0]*data.M.V)*delta.Seconds())
        data.P.Y = data.P.Y + int(float64(data.M.Dir[1]*data.M.V)*delta.Seconds())
        data.P.Z = data.P.Z + int(float64(data.M.Dir[2]*data.M.V)*delta.Seconds())

        ecs.Log.Info("target id:", e, "delta:", delta, " current position:", data.P.X, data.P.Y, data.P.Z)
    }
    println("here")  // **!!!!!!!!!! HERE**
}
zllangct commented 2 years ago

The return will be nil in the first frame, but be not nil start with secend frame

cchandel commented 2 years ago

Hi, I've changed the print statements to be clearer.

func (m *MoveSystem) Update(event ecs.Event) {
    delta := event.Delta

    //All newly created and deleted components of the current frame
    nc := m.GetInterestedNew()
    m.Filter(nc)
    //csPosition := m.GetInterested(ecs.GetType[components.Position]()).(*ecs.Collection[components.Position])
    csPosition := ecs.GetInterestedComponents[components.Position](m)
    if csPosition == nil {
        println("here")
        return
    }

    //csMovement := m.GetInterested(ecs.GetType[components.Movement]()).(*ecs.Collection[components.Movement])
    csMovement := ecs.GetInterestedComponents[components.Movement](m)
    if csMovement == nil {
        println("here1")
        return
    }

    d := map[int64]*MoveSystemData{}

    //Aggregation 2: Aggregate related components directly from Entity
    for iter := ecs.NewIterator(csPosition); !iter.End(); iter.Next() {
        position := iter.Val()
        owner := position.Owner()

        movement := ecs.CheckComponent[components.Movement](m, owner)
        if movement == nil {
            continue
        }

        d[position.Owner().ID()] = &MoveSystemData{P: position, M: movement}
        println("here2")
    }

    for e, data := range d {
        if data.M == nil || data.P == nil {
            //When the aggregated data components are not uniform, skip the processing
            continue
        }
        data.P.X = data.P.X + int(float64(data.M.Dir[0]*data.M.V)*delta.Seconds())
        data.P.Y = data.P.Y + int(float64(data.M.Dir[1]*data.M.V)*delta.Seconds())
        data.P.Z = data.P.Z + int(float64(data.M.Dir[2]*data.M.V)*delta.Seconds())

        ecs.Log.Info("target id:", e, "delta:", delta, " current position:", data.P.X, data.P.Y, data.P.Z)
    }
    println("here3")
}

This is what I get now.

main.go:58: -6071344217479635695 -6071339703469003691 -6071337908172654456
world.go:89: start world success
here
collection.go:33: collection Add:{"X":110,"Y":110,"Z":110}
collection.go:33: collection Add:{"X":100,"Y":100,"Z":100}
collection.go:33: collection Add:{"V":2000,"Dir":[0,1,0]}
collection.go:33: collection Add:{"V":2000,"Dir":[1,0,0]}
moveSystem.go:26: new component:2

Also, if the update function was being called in the next frame (as it was with the code in 1 file) - shouldn't the

ecs.Log.Info("target id:", e, "delta:", delta, " current position:", data.P.X, data.P.Y, data.P.Z)

print some output?

Apologies if I'm still missing something obvious.

zllangct commented 2 years ago

I run your code, everthing was expected, update fucntion will be called every frame, but conponent will take effect in next frame, output:

world.go:89: start world success
main.go:29: 7873313594945934826 7873313594946025041 7873313594946054595
systems.go:39: here
collection.go:33: collection Add:{"V":2000,"Dir":[0,1,0]}
collection.go:33: collection Add:{"X":110,"Y":110,"Z":110}
collection.go:33: collection Add:{"V":2000,"Dir":[1,0,0]}
collection.go:33: collection Add:{"X":100,"Y":100,"Z":100}
systems.go:26: new component:2
systems.go:72: target id:7873313594946173032delta:33ms current position:110 176 110
systems.go:72: target id:7873313594946129962delta:33ms current position:166 100 100
systems.go:72: target id:7873313594946173032delta:33ms current position:110 242 110
systems.go:72: target id:7873313594946129962delta:33ms current position:232 100 100
systems.go:72: target id:7873313594946173032delta:33ms current position:110 308 110
systems.go:72: target id:7873313594946129962delta:33ms current position:298 100 100
systems.go:72: target id:7873313594946173032delta:33ms current position:110 374 110
systems.go:72: target id:7873313594946129962delta:33ms current position:364 100 100
systems.go:72: target id:7873313594946173032delta:33ms current position:110 440 110
systems.go:72: target id:7873313594946129962delta:33ms current position:430 100 100
systems.go:72: target id:7873313594946173032delta:33ms current position:110 506 110
systems.go:72: target id:7873313594946129962delta:33ms current position:496 100 100
systems.go:72: target id:7873313594946173032delta:33ms current position:110 572 110
systems.go:72: target id:7873313594946129962delta:33ms current position:562 100 100
systems.go:72: target id:7873313594946173032delta:33ms current position:110 638 110
cchandel commented 2 years ago

Odd, In my case the output is

gotip run main.go 
main.go:58: -4833115771791913929 -4833110351543205529 -4833108491822325544
world.go:89: start world success
here
collection.go:33: collection Add:{"V":2000,"Dir":[1,0,0]}
collection.go:33: collection Add:{"V":2000,"Dir":[0,1,0]}
collection.go:33: collection Add:{"X":100,"Y":100,"Z":100}
collection.go:33: collection Add:{"X":110,"Y":110,"Z":110}
moveSystem.go:26: new component:2

The code doesn't seem to go to

systems.go:72: target id:7873313594946173032delta:33ms current position:110 176 110 ...

at all.

Am I missing something in main() ?

zllangct commented 2 years ago

I tryied it with gotip, everything was ok image

D:\workspace\ecs\example\issue4>gotip run main.go
world.go:89: start world success
systems.go:39: here
main.go:29: -8489123686563854554 -8489123686563800566 -8489123686563739877
systems.go:39: here
collection.go:33: collection Add:{"X":110,"Y":110,"Z":110}
collection.go:33: collection Add:{"X":100,"Y":100,"Z":100}
collection.go:33: collection Add:{"V":2000,"Dir":[0,1,0]}
collection.go:33: collection Add:{"V":2000,"Dir":[1,0,0]}
systems.go:26: new component:2
systems.go:72: target id:-8486884290615695162delta:33ms current position:110 176 110
systems.go:72: target id:-8486884290615758674delta:33ms current position:166 100 100
systems.go:72: target id:-8486884290615695162delta:33ms current position:110 242 110
systems.go:72: target id:-8486884290615758674delta:33ms current position:232 100 100
systems.go:72: target id:-8486884290615695162delta:33ms current position:110 308 110
systems.go:72: target id:-8486884290615758674delta:33ms current position:298 100 100
systems.go:72: target id:-8486884290615758674delta:33ms current position:364 100 100
systems.go:72: target id:-8486884290615695162delta:33ms current position:110 374 110
systems.go:72: target id:-8486884290615695162delta:33ms current position:110 440 110
systems.go:72: target id:-8486884290615758674delta:33ms current position:430 100 100
systems.go:72: target id:-8486884290615695162delta:33ms current position:110 506 110
systems.go:72: target id:-8486884290615758674delta:33ms current position:496 100 100
systems.go:72: target id:-8486884290615695162delta:33ms current position:110 572 110
cchandel commented 2 years ago

Is func (m *MoveSystem) Update(event ecs.Event) in the systems.go file?

cchandel commented 2 years ago

Hi, Update gotip and it's working now.

Thank you for taking the time to explain.

Regards.