Skip to content

Instantly share code, notes, and snippets.

@redblobgames
Created November 5, 2020 17:22
Show Gist options
  • Save redblobgames/01966bd070c920d05c6b0ae178d9f222 to your computer and use it in GitHub Desktop.
Save redblobgames/01966bd070c920d05c6b0ae178d9f222 to your computer and use it in GitHub Desktop.
Experiment: Legion ECS vs using Rust enum + pattern matching
use legion::*;
use rltk::{GameState, Rltk, VirtualKeyCode, RGB};
use std::cmp::{max, min};
mod map;
pub use map::*;
struct Player {}
#[derive(PartialEq, Copy, Clone)]
struct Position {
x: i32,
y: i32,
}
struct Renderable {
glyph: rltk::FontCharType,
fg: RGB,
bg: RGB,
}
struct State {
world: World,
resources: Resources,
schedule: Schedule,
}
impl GameState for State {
fn tick(&mut self, rltk: &mut Rltk) {
// Simulation
player_input(self, rltk);
self.schedule.execute(&mut self.world, &mut self.resources);
// Rendering
rltk.cls();
draw_map(&self.resources.get::<GameMap>().unwrap(), rltk);
let mut query = <(&Position, &Renderable)>::query();
for (pos, render) in query.iter(&self.world) {
rltk.set(pos.x, pos.y, render.fg, render.bg, render.glyph);
}
rltk.print(1, 1, "Hello Rust World");
}
}
fn draw_map(map: &GameMap, rltk: &mut Rltk) {
for y in 0..map.h {
for x in 0..map.w {
let (fg, bg, ch) = match map.get(x, y) {
TileType::Floor => (RGB::from_f32(0.5, 0.5, 0.5), RGB::from_f32(0., 0., 0.), '.'),
TileType::Wall => (RGB::from_f32(0.7, 0.7, 0.5), RGB::from_f32(0., 0., 0.), '#'),
};
rltk.set(x, y, fg, bg, rltk::to_cp437(ch));
}
}
}
fn try_move_player(delta_x: i32, delta_y: i32, world: &mut World, game_map: &GameMap) {
let mut query = <(&Player, &mut Position)>::query();
for (_player, pos) in query.iter_mut(world) {
let new_x = min(game_map.w - 1, max(0, pos.x + delta_x));
let new_y = min(game_map.h - 1, max(0, pos.y + delta_y));
if game_map.get(new_x, new_y) != TileType::Wall {
pos.x = new_x;
pos.y = new_y;
}
}
}
fn player_input(gs: &mut State, rltk: &mut Rltk) {
let game_map = &gs.resources.get::<GameMap>().unwrap();
if let Some(key) = rltk.key {
match key {
VirtualKeyCode::H |
VirtualKeyCode::Left => try_move_player(-1, 0, &mut gs.world, game_map),
VirtualKeyCode::L |
VirtualKeyCode::Right => try_move_player(1, 0, &mut gs.world, game_map),
VirtualKeyCode::K |
VirtualKeyCode::Up => try_move_player(0, -1, &mut gs.world, game_map),
VirtualKeyCode::J |
VirtualKeyCode::Down => try_move_player(0, 1, &mut gs.world, game_map),
_ => {}
}
}
}
fn main() -> rltk::BError {
use rltk::RltkBuilder;
let context = RltkBuilder::simple80x50()
.with_title("Roguelike Tutorial")
.build()?;
let mut gs = State {
world: World::default(),
resources: Resources::default(),
schedule: Schedule::builder().build(),
};
gs.resources.insert(GameMap::new());
let player = gs.world.push((
Player {},
Position {
x: 80 / 2,
y: 50 / 2,
},
Renderable {
glyph: rltk::to_cp437('@'),
fg: RGB::named(rltk::YELLOW),
bg: RGB::named(rltk::BLACK),
},
));
for i in 0..10 {
gs.world.push((
Position { x: i * 7, y: 20 },
Renderable {
glyph: rltk::to_cp437('☺'),
fg: RGB::named(rltk::RED),
bg: RGB::named(rltk::BLACK),
},
));
}
rltk::main_loop(context, gs)
}
use rltk::{GameState, Rltk, VirtualKeyCode, RGB};
use std::cmp::{max, min};
mod map;
pub use map::*;
#[derive(PartialEq, Copy, Clone)]
struct Position {
x: i32,
y: i32,
}
struct Renderable {
glyph: rltk::FontCharType,
fg: RGB,
bg: RGB,
}
struct GameObject {
is_player: bool,
pos: Option<Position>,
render: Option<Renderable>,
}
struct State {
world: Vec<GameObject>,
game_map: GameMap,
}
impl GameState for State {
fn tick(&mut self, rltk: &mut Rltk) {
// Simulation
player_input(self, rltk);
// Rendering
rltk.cls();
draw_map(&self.game_map, rltk);
for game_object in self.world.iter() {
if let GameObject{pos: Some(pos), render: Some(render), ..} = game_object {
rltk.set(pos.x, pos.y, render.fg, render.bg, render.glyph);
}
}
rltk.print(1, 1, "Hello Rust World");
}
}
fn draw_map(map: &GameMap, rltk: &mut Rltk) {
for y in 0..map.h {
for x in 0..map.w {
let (fg, bg, ch) = match map.get(x, y) {
TileType::Floor => (RGB::from_f32(0.5, 0.5, 0.5), RGB::from_f32(0., 0., 0.), '.'),
TileType::Wall => (RGB::from_f32(0.7, 0.7, 0.5), RGB::from_f32(0., 0., 0.), '#'),
};
rltk.set(x, y, fg, bg, rltk::to_cp437(ch));
}
}
}
fn try_move_player(delta_x: i32, delta_y: i32, world: &mut Vec<GameObject>, game_map: &GameMap) {
for game_object in world.iter_mut() {
if game_object.is_player {
let pos = game_object.pos.unwrap();
let new_x = min(game_map.w - 1, max(0, pos.x + delta_x));
let new_y = min(game_map.h - 1, max(0, pos.y + delta_y));
if game_map.get(new_x, new_y) != TileType::Wall {
game_object.pos = Some(Position{x: new_x, y: new_y});
}
}
}
}
fn player_input(gs: &mut State, rltk: &mut Rltk) {
if let Some(key) = rltk.key {
match key {
VirtualKeyCode::H |
VirtualKeyCode::Left => try_move_player(-1, 0, &mut gs.world, &gs.game_map),
VirtualKeyCode::L |
VirtualKeyCode::Right => try_move_player(1, 0, &mut gs.world, &gs.game_map),
VirtualKeyCode::K |
VirtualKeyCode::Up => try_move_player(0, -1, &mut gs.world, &gs.game_map),
VirtualKeyCode::J |
VirtualKeyCode::Down => try_move_player(0, 1, &mut gs.world, &gs.game_map),
_ => {}
}
}
}
fn main() -> rltk::BError {
use rltk::RltkBuilder;
let context = RltkBuilder::simple80x50()
.with_title("Roguelike Tutorial")
.build()?;
let mut gs = State {
world: vec![],
game_map: GameMap::new(),
};
let player = gs.world.push(GameObject{
is_player: true,
pos: Some(Position {
x: 80 / 2,
y: 50 / 2,
}),
render: Some(Renderable {
glyph: rltk::to_cp437('@'),
fg: RGB::named(rltk::YELLOW),
bg: RGB::named(rltk::BLACK),
}),
});
for i in 0..10 {
gs.world.push(GameObject{
is_player: false,
pos: Some(Position { x: i * 7, y: 20 }),
render: Some(Renderable {
glyph: rltk::to_cp437('☺'),
fg: RGB::named(rltk::RED),
bg: RGB::named(rltk::BLACK),
}),
});
}
rltk::main_loop(context, gs)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment