ReactiveECS.jl v1.0.0: Powerful reactive ECS balancing flexibility and performances

Hello guys…
Happy today to provide you the latest version of ReactiveECS.jl (look at this post if you don’t know yet the package.). Adding some powerful features to fills all the remaining gaps. Without further ado, let’s look at the new features:

  • Optimized Performances : With less types instabilities and better data representation
  • Advanced Event System: Built on Notifyers.jl, supporting merging, filtering, one-shot listeners, and prioritized events.
  • Hierarchical Relationships: Managed via NodeTree.jl with BFS/DFS traversal.
  • Profiling Tools: Built-in debugging and performance visualization.
  • Refined API: For a more straight forward utilisation

Example code

using ReactiveECS

# This will create a new component
# And set boilerplates for us
# The constructor is just the name passed here plus the suffix "Component"
@component Health begin
    hp::Int
end

@component Transform begin
    x::Float32
    y::Float32
end

@component Physic begin
    velocity::Float32
end

@system PhysicSystem begin
    delta::Float32
end
@system RenderSystem

function ReactiveECS.run!(world, sys::PhysicSystem, data)
    E = world[sys] # Our data
    indices::Vector{Int} = data.value # The indices of the entities requested by the system
    L = length(indices)

    transforms = E.Transform # A struct array of transform components
    physics = E.Physic # A struct array of physic components

    x_pos = transforms.x # A vector of all the x fields of the transform components
    velo = physics.velocity
    dt::Float32 = sys.delta

    @inbounds for i in indices
        x_pos[i] += velo[i]*dt
    end

    return transforms # This data will be passed to the system listening to this one
end

function ReactiveECS.run!(world, ::RenderSystem, pos)
    for i in eachindex(pos)
        t = pos[i]
        println("Rendering entity at position ($(t.x), $(t.y))")
    end
end

physic_sys = PhysicSystem()
render_sys = RenderSystem()

ecs = ECSManager()

subscribe!(ecs, physic_sys, (TransformComponent, PhysicComponent))
listen_to(physic_sys,render_sys)

# Creating 4 entity
# We pass as keywork argument the component of the entity
create_entity!(ecs; Health = HealthComponent(100), Transform = TransformComponent(1.0,2.0))
e1 = create_entity!(ecs; Health = HealthComponent(150), Transform = TransformComponent(1.0,2.0))

# This entity will have e1 as parent
e2 = create_entity!(ecs,e1; Health = HealthComponent(50), Transform = TransformComponent(-5.0,0.0), Physic = PhysicComponent(1.0))
e3 = create_entity!(ecs; Health = HealthComponent(50), Transform = TransformComponent(-5.0,0.0), Physic = PhysicComponent(1.0))

# We launch the system. Internally, it's creating an asynchronous task
run_system!(physic_sys)
run_system!(render_sys)

N = 3

for i in 1:N
    println("FRAME $i")

    # We dispatch data and each system will execute his `run!` function
    dispatch_data(ecs)
    blocker(ecs) # Will make the process wait for all systems to finish
    yield()
    sleep(0.016)
end

I will be glad to hear your feedback and enhancement !!
The oackage is still young and could hepl some contributions.

5 Likes