youtube:bevy-sokoban
差分
このページの2つのバージョン間の差分を表示します。
両方とも前のリビジョン前のリビジョン次のリビジョン | 前のリビジョン | ||
youtube:bevy-sokoban [2024/02/28 18:26] – 削除 - 外部編集 (不明な日付) 127.0.0.1 | youtube:bevy-sokoban [2024/02/29 11:49] (現在) – freemikan | ||
---|---|---|---|
行 1: | 行 1: | ||
+ | ====== 倉庫番 ====== | ||
+ | |||
+ | {{youtube: | ||
+ | |||
+ | ===== src/main.rs ===== | ||
+ | |||
+ | |||
+ | <file rust> | ||
+ | use bevy:: | ||
+ | use bevy:: | ||
+ | |||
+ | const ARENA_WIDTH: | ||
+ | const ARENA_HEIGHT: | ||
+ | const TILE_SIZE: Vec2 = Vec2:: | ||
+ | const SCREEN_SIZE: | ||
+ | TILE_SIZE.x * ARENA_WIDTH as f32, | ||
+ | TILE_SIZE.y * ARENA_HEIGHT as f32, | ||
+ | ); | ||
+ | const PLAYER_COLOR: | ||
+ | const CARGO_COLOR: | ||
+ | const GOAL_COLOR: Color = Color:: | ||
+ | |||
+ | fn main() { | ||
+ | App::new() | ||
+ | .add_plugins(DefaultPlugins.set(WindowPlugin { | ||
+ | window: WindowDescriptor { | ||
+ | title: " | ||
+ | width: SCREEN_SIZE.x, | ||
+ | height: SCREEN_SIZE.y, | ||
+ | ..default() | ||
+ | }, | ||
+ | ..default() | ||
+ | })) | ||
+ | .insert_resource(ClearColor(Color:: | ||
+ | .add_startup_system(setup) | ||
+ | .add_system(bevy:: | ||
+ | .add_system_set( | ||
+ | SystemSet:: | ||
+ | .with_run_criteria(FixedTimestep:: | ||
+ | .with_system(move_player_and_cargo) | ||
+ | .with_system(position_translation.after(move_player_and_cargo)) | ||
+ | .with_system(check_level_complete.after(move_player_and_cargo)) | ||
+ | .with_system(complete_level.after(check_level_complete)), | ||
+ | ) | ||
+ | .add_event::< | ||
+ | .run(); | ||
+ | } | ||
+ | |||
+ | # | ||
+ | struct Position { | ||
+ | x: i32, | ||
+ | y: i32, | ||
+ | z: i32, | ||
+ | } | ||
+ | |||
+ | # | ||
+ | struct Cargo; | ||
+ | |||
+ | # | ||
+ | struct Player; | ||
+ | |||
+ | # | ||
+ | struct Goal; | ||
+ | |||
+ | # | ||
+ | struct LevelCompleteEvent; | ||
+ | |||
+ | fn setup(mut commands: Commands) { | ||
+ | commands.spawn(Camera2dBundle:: | ||
+ | |||
+ | // spawn Player | ||
+ | commands | ||
+ | .spawn(SpriteBundle { | ||
+ | sprite: Sprite { | ||
+ | color: PLAYER_COLOR, | ||
+ | ..default() | ||
+ | }, | ||
+ | transform: Transform { | ||
+ | scale: Vec3:: | ||
+ | ..default() | ||
+ | }, | ||
+ | ..default() | ||
+ | }) | ||
+ | .insert(Player) | ||
+ | .insert(Position { | ||
+ | x: ARENA_WIDTH / 2, | ||
+ | y: 0, | ||
+ | z: 3, | ||
+ | }); | ||
+ | |||
+ | // spawn Cargo | ||
+ | for (x, y) in (0..ARENA_WIDTH) | ||
+ | .step_by(2) | ||
+ | .zip(std:: | ||
+ | .chain( | ||
+ | (1..ARENA_WIDTH) | ||
+ | .step_by(2) | ||
+ | .zip(std:: | ||
+ | ) | ||
+ | { | ||
+ | commands | ||
+ | .spawn(SpriteBundle { | ||
+ | sprite: Sprite { | ||
+ | color: CARGO_COLOR, | ||
+ | ..default() | ||
+ | }, | ||
+ | transform: Transform { | ||
+ | scale: Vec3:: | ||
+ | ..default() | ||
+ | }, | ||
+ | ..default() | ||
+ | }) | ||
+ | .insert(Cargo) | ||
+ | .insert(Position { x, y, z: 2 }); | ||
+ | } | ||
+ | |||
+ | // spawn Goal | ||
+ | for x in 0..ARENA_WIDTH { | ||
+ | commands | ||
+ | .spawn(SpriteBundle { | ||
+ | sprite: Sprite { | ||
+ | color: GOAL_COLOR, | ||
+ | ..default() | ||
+ | }, | ||
+ | transform: Transform { | ||
+ | scale: Vec3:: | ||
+ | ..default() | ||
+ | }, | ||
+ | ..default() | ||
+ | }) | ||
+ | .insert(Goal) | ||
+ | .insert(Position { | ||
+ | x, | ||
+ | y: ARENA_HEIGHT - 1, | ||
+ | z: 1, | ||
+ | }); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | fn position_translation(mut query: Query< | ||
+ | for (p, mut t) in query.iter_mut() { | ||
+ | t.translation = Vec3::new( | ||
+ | p.x as f32 * TILE_SIZE.x - (SCREEN_SIZE.x / 2.0) + (TILE_SIZE.x / 2.0), | ||
+ | p.y as f32 * TILE_SIZE.y - (SCREEN_SIZE.y / 2.0) + (TILE_SIZE.y / 2.0), | ||
+ | p.z as f32, | ||
+ | ); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | # | ||
+ | enum Direction { | ||
+ | Up, | ||
+ | Down, | ||
+ | Left, | ||
+ | Right, | ||
+ | None, | ||
+ | } | ||
+ | |||
+ | fn move_player_and_cargo( | ||
+ | keyboard_input: | ||
+ | mut player_positions: | ||
+ | mut cargo_positions: | ||
+ | ) { | ||
+ | let direction = if keyboard_input.just_pressed(KeyCode:: | ||
+ | println!(" | ||
+ | Direction:: | ||
+ | } else if keyboard_input.just_pressed(KeyCode:: | ||
+ | Direction:: | ||
+ | } else if keyboard_input.just_pressed(KeyCode:: | ||
+ | Direction:: | ||
+ | } else if keyboard_input.just_pressed(KeyCode:: | ||
+ | Direction:: | ||
+ | } else { | ||
+ | Direction:: | ||
+ | }; | ||
+ | |||
+ | let mut player_position = player_positions.single_mut(); | ||
+ | |||
+ | let next_position = |p: & | ||
+ | Direction:: | ||
+ | x: p.x, | ||
+ | y: p.y + step, | ||
+ | z: p.z, | ||
+ | }, | ||
+ | Direction:: | ||
+ | x: p.x, | ||
+ | y: p.y - step, | ||
+ | z: p.z, | ||
+ | }, | ||
+ | Direction:: | ||
+ | x: p.x - step, | ||
+ | y: p.y, | ||
+ | z: p.z, | ||
+ | }, | ||
+ | Direction:: | ||
+ | x: p.x + step, | ||
+ | y: p.y, | ||
+ | z: p.z, | ||
+ | }, | ||
+ | _ => *p, | ||
+ | }; | ||
+ | |||
+ | let inside_arene = | ||
+ | |p: & | ||
+ | |||
+ | // try to push cargo and update player and cargo position if it possible | ||
+ | let new_player_position = next_position(& | ||
+ | let new_cargo_position = next_position(& | ||
+ | let new_cargo_position_empty = cargo_positions | ||
+ | .iter() | ||
+ | .all(|cp| !(cp.x == new_cargo_position.x && cp.y == new_cargo_position.y)); | ||
+ | |||
+ | if let Some(mut cargo_forward) = cargo_positions | ||
+ | .iter_mut() | ||
+ | .find(|cp| cp.x == new_player_position.x && cp.y == new_player_position.y) | ||
+ | { | ||
+ | if new_cargo_position_empty && inside_arene(& | ||
+ | *cargo_forward = new_cargo_position; | ||
+ | *player_position = new_player_position; | ||
+ | } | ||
+ | } else { | ||
+ | if inside_arene(& | ||
+ | *player_position = new_player_position; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | |||
+ | fn check_level_complete( | ||
+ | debug_input: | ||
+ | cargo_positions: | ||
+ | goal_positions: | ||
+ | mut level_complete_writer: | ||
+ | ) { | ||
+ | if debug_input.just_pressed(KeyCode:: | ||
+ | level_complete_writer.send_default(); | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | for cargo in cargo_positions.iter() { | ||
+ | let maybe_on_goal = goal_positions | ||
+ | .iter() | ||
+ | .find(|gp| gp.x == cargo.x && gp.y == cargo.y); | ||
+ | if maybe_on_goal.is_none() { | ||
+ | return; | ||
+ | } | ||
+ | } | ||
+ | level_complete_writer.send_default(); | ||
+ | } | ||
+ | |||
+ | fn complete_level( | ||
+ | mut commands: Commands, | ||
+ | events: EventReader< | ||
+ | asset_server: | ||
+ | ) { | ||
+ | if !events.is_empty() { | ||
+ | events.clear(); | ||
+ | |||
+ | println!(" | ||
+ | |||
+ | let font_size = 50.0; | ||
+ | |||
+ | // show screen message | ||
+ | commands.spawn( | ||
+ | TextBundle:: | ||
+ | " | ||
+ | TextStyle { | ||
+ | font: asset_server.load(" | ||
+ | font_size, | ||
+ | color: Color:: | ||
+ | }, | ||
+ | ) | ||
+ | .with_text_alignment(TextAlignment:: | ||
+ | .with_style(Style { | ||
+ | position_type: | ||
+ | position: UiRect { | ||
+ | // hard-code position to display text center of screen | ||
+ | top: Val:: | ||
+ | left: Val:: | ||
+ | ..default() | ||
+ | }, | ||
+ | ..default() | ||
+ | }), | ||
+ | ); | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Cargo.toml ===== | ||
+ | |||
+ | |||
+ | < | ||
+ | [package] | ||
+ | name = " | ||
+ | version = " | ||
+ | edition = " | ||
+ | |||
+ | # See more keys and their definitions at https:// | ||
+ | |||
+ | [dependencies] | ||
+ | bevy = { version = " | ||
+ | |||
+ | [profile.dev] | ||
+ | opt-level = 1 | ||
+ | |||
+ | [profile.dev.package." | ||
+ | opt-level = 3 | ||
+ | </ | ||