Adding Event channels in Amethyst ECS architecture

275 Views Asked by At

In the Amethyst Pong tutorial, The Entity/Component/System (ECS) architecture is explained in a way that makes me think that systems are something added to the game in the main function, and cannot be added or removed at runtime.

In the same book, I was following this part to add a Producer system and a Receiver system, both associated to a customized EventChannel.

Since Receiver and Producer are systems, following the tutorial I only learnt how to add them in the main function and in no other place, while in the code example the Receiver’s new method is called upon an instance of the World struct. I do not have any instance of World when in the main function, as it seems too early in the creation process to have one of those.

How can it be done in a proper ECS-compliant way? Is there a way to retrieve a system from a state during the game loop, and subscribe it there? Would it be correct?

1

There are 1 best solutions below

0
On

Little late to answer, but in case anyone else runs into this issue.

Some of the examples given in the amethyst book (in this case the event channel examples) rely on having bundled your systems instead of feeding them directly into the GameDataBuilder.

Bundles have access to the world object when they are built.

See this example in the official amethyst examples which shows how to utilize event channels and accessing the world object in your systems construction.

https://github.com/amethyst/amethyst/tree/master/examples/events

For example:

# Custom Bundle.

#[derive(Debug)]
struct MyBundle;

impl<'a, 'b> SystemBundle<'a, 'b> for MyBundle {
    fn build(
        self,
        world: &mut World,
        builder: &mut DispatcherBuilder<'a, 'b>,
    ) -> Result<(), Error> {
        builder.add(SpammingSystem, "spamming_system", &[]);
        builder.add(
            ReceivingSystemDesc::default().build(world),
            "receiving_system",
            &[],
        );
        Ok(())
    }
}

# System.

#[derive(SystemDesc)]
#[system_desc(name(ReceivingSystemDesc))]
struct ReceivingSystem {
    #[system_desc(event_channel_reader)]
    reader: ReaderId<MyEvent>,
}

impl ReceivingSystem {
    pub fn new(reader: ReaderId<MyEvent>) -> Self {
        ReceivingSystem { reader }
    }
}

impl<'a> System<'a> for ReceivingSystem {
    type SystemData = Read<'a, EventChannel<MyEvent>>;

    fn run(&mut self, my_event_channel: Self::SystemData) {
        for event in my_event_channel.read(&mut self.reader) {
            println!("Received an event: {:?}", event);
        }
    }
}

# Adding your bundle.

fn main() -> amethyst::Result<()> {
    amethyst::start_logger(Default::default());

    let assets_dir = application_root_dir()?.join("examples/events/assets");

    let game_data = GameDataBuilder::default().with_bundle(MyBundle)?;

    let mut game = Application::build(assets_dir, GameplayState)?
        .with_frame_limit(FrameRateLimitStrategy::Sleep, 1)
        .build(game_data)?;

    game.run();
    Ok(())
}