Created
March 22, 2017 00:40
-
-
Save minecrawler/b9bd7f6fae1477b56f15784541f0a758 to your computer and use it in GitHub Desktop.
Simple Snake implemented on top of Amethyst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate rand; | |
extern crate amethyst; | |
use amethyst::{Application, Event, State, Trans, VirtualKeyCode, WindowEvent}; | |
use amethyst::asset_manager::AssetManager; | |
use amethyst::config::Element; | |
use amethyst::ecs::{World, Join, VecStorage, Component, RunArg, System, Entity}; | |
use amethyst::ecs::components::{Mesh, LocalTransform, Texture, Transform, Renderable}; | |
use amethyst::gfx_device::DisplayConfig; | |
use amethyst::renderer::{VertexPosNormal, Pipeline}; | |
#[derive(PartialEq)] | |
enum Direction { | |
North, | |
East, | |
South, | |
West, | |
} | |
struct Game; | |
struct Food { | |
pub position: [u8; 2], | |
} | |
struct SnakePart { | |
pub value: u8, | |
pub position: [u8; 2], | |
pub entity: Entity, | |
} | |
struct SnakeHead { | |
pub position: [u8; 2], | |
pub direction: Direction, | |
} | |
struct GameState { | |
pub score: u8, | |
pub delta_time: f32, | |
} | |
struct GameSystem; | |
impl SnakeHead { | |
pub fn new() -> Self { | |
Self { | |
position: [1, 1], | |
direction: Direction::North, | |
} | |
} | |
} | |
impl Component for Food { | |
type Storage = VecStorage<Food>; | |
} | |
impl Component for SnakeHead { | |
type Storage = VecStorage<SnakeHead>; | |
} | |
impl Component for SnakePart { | |
type Storage = VecStorage<SnakePart>; | |
} | |
impl Component for GameState { | |
type Storage = VecStorage<GameState>; | |
} | |
unsafe impl Sync for GameSystem {} | |
impl System<()> for GameSystem { | |
fn run(&mut self, arg: RunArg, _: ()) { | |
use amethyst::ecs::resources::{InputHandler, Time}; | |
fn pos_to_trans(pos: u8) -> f32 { | |
pos as f32 * 0.05 - 0.975 | |
} | |
let (mut snake_heads, mut foods, mut snake_parts, mut locals, mut transforms, mut renderables, time, input, w_square, mut state) = arg.fetch(|w| {( | |
w.write::<SnakeHead>(), | |
w.write::<Food>(), | |
w.write::<SnakePart>(), | |
w.write::<LocalTransform>(), | |
w.write::<Transform>(), | |
w.write::<Renderable>(), | |
w.read_resource::<Time>(), | |
w.read_resource::<InputHandler>(), | |
w.read_resource::<Renderable>(), | |
w.write_resource::<GameState>(), | |
)}); | |
let mut food_pos = [0, 0]; | |
for (food, local) in (&mut foods, &mut locals).iter() { | |
local.scale = [0.05, 0.05, 1.]; | |
food_pos = food.position; | |
local.translation[0] = pos_to_trans(food_pos[0]); | |
local.translation[1] = pos_to_trans(food_pos[1]); | |
} | |
for (snake_head, local) in (&mut snake_heads, &mut locals).iter() { | |
if input.key_down(VirtualKeyCode::Down) && snake_head.direction != Direction::North { | |
snake_head.direction = Direction::South; | |
} | |
if input.key_down(VirtualKeyCode::Left) && snake_head.direction != Direction::East { | |
snake_head.direction = Direction::West; | |
} | |
if input.key_down(VirtualKeyCode::Up) && snake_head.direction != Direction::South { | |
snake_head.direction = Direction::North; | |
} | |
if input.key_down(VirtualKeyCode::Right) && snake_head.direction != Direction::West { | |
snake_head.direction = Direction::East; | |
} | |
local.scale = [0.05, 0.05, 1.]; | |
} | |
state.delta_time += time.delta_time.subsec_nanos() as f32 / 1.0e9; | |
if state.delta_time < 0.1 { return; } | |
state.delta_time = 0.; | |
let mut game_over = false; | |
let mut snake_head_pos = [0,0]; | |
for (snake_head, local) in (&mut snake_heads, &mut locals).iter() { | |
let pos = snake_head.position[0]; | |
match snake_head.direction { | |
Direction::East => { | |
let new_pos = pos.checked_add(1).unwrap_or_else(|| -> u8 { game_over = true; pos }); | |
snake_head.position[0] = if new_pos < 40 { new_pos } else { game_over = true; pos }; | |
}, | |
Direction::West => { | |
snake_head.position[0] = pos.checked_sub(1).unwrap_or_else(|| -> u8 { game_over = true; pos }); | |
}, | |
_ => {}, | |
}; | |
let pos = snake_head.position[1]; | |
match snake_head.direction { | |
Direction::North => { | |
let new_pos = pos.checked_add(1).unwrap_or_else(|| -> u8 { game_over = true; pos }); | |
snake_head.position[1] = if new_pos < 40 { new_pos } else { game_over = true; pos }; | |
}, | |
Direction::South => { | |
snake_head.position[1] = pos.checked_sub(1).unwrap_or_else(|| -> u8 { game_over = true; pos }); | |
}, | |
_ => {}, | |
}; | |
local.translation[0] = pos_to_trans(snake_head.position[0]); | |
local.translation[1] = pos_to_trans(snake_head.position[1]); | |
snake_head_pos = snake_head.position; | |
} | |
for (snake_part, local) in (&mut snake_parts, &mut locals).iter() { | |
if snake_head_pos[0] == snake_part.position[0] && snake_head_pos[1] == snake_part.position[1] { | |
game_over = true; | |
} | |
local.scale = [0.05, 0.05, 1.]; | |
local.translation[0] = pos_to_trans(snake_part.position[0]); | |
local.translation[1] = pos_to_trans(snake_part.position[1]); | |
snake_part.value = snake_part.value.checked_sub(1).unwrap_or_else(|| -> u8 { | |
arg.delete(snake_part.entity); | |
0 | |
}); | |
} | |
if snake_head_pos[0] == food_pos[0] && snake_head_pos[1] == food_pos[1] { | |
state.score += 1; | |
for (food, local) in (&mut foods, &mut locals).iter() { | |
local.scale = [0.05, 0.05, 1.]; | |
food.position = [rand::random::<u8>() % 40, rand::random::<u8>() % 40]; | |
food_pos = food.position; | |
local.translation[0] = pos_to_trans(food_pos[0]); | |
local.translation[1] = pos_to_trans(food_pos[1]); | |
} | |
} | |
let e = arg.create(); | |
snake_parts.insert(e, SnakePart{ | |
position: [ | |
snake_head_pos[0], | |
snake_head_pos[1], | |
], | |
value: state.score, | |
entity: e.clone(), | |
}); | |
let mut local = LocalTransform::default(); | |
(*local).scale = [0.05, 0.05, 1.]; | |
(*local).translation[0] = pos_to_trans(snake_head_pos[0]); | |
(*local).translation[1] = pos_to_trans(snake_head_pos[1]); | |
transforms.insert(e, Transform::default()); | |
locals.insert(e, local); | |
renderables.insert(e, w_square.clone()); | |
if game_over { | |
println!("Game Over! Score: {}", state.score); | |
for (snake_head, _) in (&mut snake_heads, &mut locals).iter() { | |
snake_head.position = [1,1]; | |
snake_head.direction = Direction::North; | |
} | |
state.score = 0; | |
state.delta_time = 0.; | |
for (snake_part, _) in (&mut snake_parts, &mut locals).iter() { | |
arg.delete(snake_part.entity); | |
} | |
} | |
} | |
} | |
impl State for Game { | |
fn on_start(&mut self, world: &mut World, assets: &mut AssetManager, pipe: &mut Pipeline) { | |
use amethyst::ecs::resources::{Camera, InputHandler, Projection, ScreenDimensions}; | |
use amethyst::renderer::Layer; | |
use amethyst::renderer::pass::{Clear, DrawFlat}; | |
let layer = Layer::new("main", | |
vec![ | |
Clear::new([0.0, 0.0, 0.0, 1.0]), | |
DrawFlat::new("main", "main"), | |
]); | |
pipe.layers.push(layer); | |
{ | |
let dim = world.read_resource::<ScreenDimensions>(); | |
let mut camera = world.write_resource::<Camera>(); | |
let aspect_ratio = dim.aspect_ratio; | |
let eye = [0., 0., 0.1]; | |
let target = [0., 0., 0.]; | |
let up = [0., 1., 0.]; | |
// Get an Orthographic projection | |
let proj = Projection::Orthographic { | |
left: -1.0 * aspect_ratio, | |
right: 1.0 * aspect_ratio, | |
bottom: -1.0, | |
top: 1.0, | |
near: 0.0, | |
far: 1.0, | |
}; | |
camera.proj = proj; | |
camera.eye = eye; | |
camera.target = target; | |
camera.up = up; | |
} | |
world.add_resource::<InputHandler>(InputHandler::new()); | |
world.add_resource::<GameState>(GameState { score: 0, delta_time: 0., }); | |
assets.register_asset::<Mesh>(); | |
assets.register_asset::<Texture>(); | |
assets.load_asset_from_data::<Texture, [f32; 4]>("white", [1.0, 1.0, 1.0, 1.0]); | |
assets.load_asset_from_data::<Texture, [f32; 4]>("red", [1.0, 0.0, 0.0, 1.0]); | |
let square_verts = gen_rectangle(1.0, 1.0); | |
assets.load_asset_from_data::<Mesh, Vec<VertexPosNormal>>("square", square_verts); | |
let w_square = assets.create_renderable("square", "white", "white", "white", 1.0).unwrap(); | |
let r_square = assets.create_renderable("square", "red", "red", "red", 1.0).unwrap(); | |
world.add_resource::<Renderable>(w_square.clone()); | |
let food = Food { position: [rand::random::<u8>() % 40, rand::random::<u8>() % 40], }; | |
world.create_now() | |
.with(r_square) | |
.with(food) | |
.with(LocalTransform::default()) | |
.with(Transform::default()) | |
.build(); | |
let snake_head = SnakeHead::new(); | |
world.create_now() | |
.with(w_square) | |
.with(snake_head) | |
.with(LocalTransform::default()) | |
.with(Transform::default()) | |
.build(); | |
} | |
fn handle_events(&mut self, | |
events: &[WindowEvent], | |
world: &mut World, | |
_: &mut AssetManager, | |
_: &mut Pipeline) | |
-> Trans { | |
use amethyst::ecs::resources::InputHandler; | |
let mut input = world.write_resource::<InputHandler>(); | |
input.update(events); | |
for e in events { | |
match **e { | |
Event::KeyboardInput(_, _, Some(VirtualKeyCode::Escape)) => return Trans::Quit, | |
Event::Closed => return Trans::Quit, | |
_ => (), | |
} | |
} | |
Trans::None | |
} | |
} | |
fn main() { | |
let path = format!("./resources/config.yml"); | |
let cfg = DisplayConfig::from_file(path).unwrap(); | |
let mut game = Application::build(Game, cfg) | |
.register::<SnakeHead>() | |
.register::<SnakePart>() | |
.register::<Food>() | |
.register::<GameState>() | |
.with::<GameSystem>(GameSystem, "crystal_snake_system", 1) | |
.done(); | |
game.run(); | |
} | |
fn gen_rectangle(w: f32, h: f32) -> Vec<VertexPosNormal> { | |
let data: Vec<VertexPosNormal> = vec![ | |
VertexPosNormal { | |
pos: [-w / 2., -h / 2., 0.], | |
normal: [0., 0., 1.], | |
tex_coord: [0., 0.], | |
}, | |
VertexPosNormal { | |
pos: [w / 2., -h / 2., 0.], | |
normal: [0., 0., 1.], | |
tex_coord: [1., 0.], | |
}, | |
VertexPosNormal { | |
pos: [w / 2., h / 2., 0.], | |
normal: [0., 0., 1.], | |
tex_coord: [1., 1.], | |
}, | |
VertexPosNormal { | |
pos: [w / 2., h / 2., 0.], | |
normal: [0., 0., 1.], | |
tex_coord: [1., 1.], | |
}, | |
VertexPosNormal { | |
pos: [-w / 2., h / 2., 0.], | |
normal: [0., 0., 1.], | |
tex_coord: [1., 1.], | |
}, | |
VertexPosNormal { | |
pos: [-w / 2., -h / 2., 0.], | |
normal: [0., 0., 1.], | |
tex_coord: [1., 1.], | |
} | |
]; | |
data | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
why are you
format!
ting the path? Why not useto_string()
?