amethyst / specs

Specs - Parallel ECS
https://amethyst.github.io/specs/
Apache License 2.0
2.51k stars 221 forks source link

How to integrate specs with Piston? #639

Closed Liby99 closed 5 years ago

Liby99 commented 5 years ago

I'm trying to use specs with Piston and I'm barely started. I followed instructions on reddit and specs book but none of them really provide working examples.

My current code looks like this:

fn main() {
  let mut window : PistonWindow = WindowSettings::new("w", [960, 720]).build().unwrap();
  let mut world = World::new();
  let mut dispatcher = DispatcherBuilder::new()
    .with_thread_local(RenderSystem { window })
    .build();

  // ----- problem starts ----- //
  while let Some((event, _)) = window.poll_event() { // <-- Error in this line
    dispatcher.dispatch(&mut world);
  }
  // ----- problem ends ----- //
}

By the way the RenderSystem is this (just drawing a rectangle):

pub struct RenderSystem {
  pub window: PistonWindow,
}

impl<'a> System<'a> for RenderSystem {
  type SystemData = ();

  fn run(&mut self, (): Self::SystemData) {
    if let Some(event) = self.window.next() {
      self.window.draw_2d(&event, |context, graphics, _device| {
        clear([1.0; 4], graphics);
        rectangle([1.0, 0.0, 0.0, 1.0], [0.0, 0.0, 100.0, 100.0], context.transform, graphics);
      });
    }
  }
}

The problem is that window is already moved to RenderSystem. If we don't access window.poll_event() we might want to do something like this (replacing the "problem starts ... ends" part in the above code):

loop {
  dispatcher.dispatch(&mut world);
}

But in this case we have no way accessing the event loop. The result is that we could not even close the window.

How do we solve this issue? Thanks in advance for reading my problem...

jmeggitt commented 5 years ago

Since you are just starting, you might want to consider trying Amethyst. Amethyst is a game engine written in rust and made with specs. It comes with its own book, a bunch of examples, and an active discord to answer any questions you might have.

Liby99 commented 5 years ago

I have tried Amethyst and found the 2d drawing API is a bit lacking. That's why I want to switch to Piston.

Liby99 commented 5 years ago

I actually have come up with a solution. The idea is to let specs manage the window state (closed or not). The code is attached below:

// finish_state.rs
pub struct FinishState(pub bool);
impl Default for FinishState { ... }
impl FinishState {
  pub fn not_finished(&self) -> bool { !self.0 }
}
// render_system.rs
pub struct RenderSystem {
  pub window: PistonWindow,
}

impl<'a> System<'a> for RenderSystem {
  type SystemData = Write<'a, FinishState>;

  fn run(&mut self, mut finished: Self::SystemData) {
    if let Some(event) = self.window.next() {
      self.window.draw_2d(&event, |context, graphics, _device| {
        clear([1.0; 4], graphics); // and other draws
      });
    } else {
      finished.0 = true; // <---- If `window.next()` is `None` then we set `finished` to `true`
    }
  }
}
fn main() {
  let mut world = World::new();
  world.insert(FinishState(false)); // <---- the initial FinishState is set to `false`

  let window : PistonWindow = WindowSettings::new("", [960, 720]).build().unwrap();
  let mut dispatcher = DispatcherBuilder::new().with_thread_local(RenderSystem { window }).build();

  while world.fetch::<FinishState>().not_finished() { // <---- Notice this line
    dispatcher.dispatch(&mut world);
  }
}

Since main owns the world we can just store the state in the world. The while loop will read the state in the world to determine if the main loop should be stopped.

Liby99 commented 5 years ago

I would suggest updating the doc here since it's quite confusing. I can work on it if you guys think that we should give it a shot. Also I'm still looking for other novel solution that is closer to what is already on the doc though. There must be some reason behind it.