Skip to content

Instantly share code, notes, and snippets.

@minecrawler
Created March 22, 2017 00:40
Show Gist options
  • Save minecrawler/b9bd7f6fae1477b56f15784541f0a758 to your computer and use it in GitHub Desktop.
Save minecrawler/b9bd7f6fae1477b56f15784541f0a758 to your computer and use it in GitHub Desktop.
Simple Snake implemented on top of Amethyst
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
}
@torkleyy
Copy link

why are you format!ting the path? Why not use to_string()?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment