diff --git a/Cargo.lock b/Cargo.lock index da3ec54d..aa197424 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -808,6 +808,17 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "bevy_ecs_tilemap" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d880047f5deaf5166ffc08238125a4ccbd2837f781ca6525fa200fcf5785ba3b" +dependencies = [ + "bevy", + "log", + "regex", +] + [[package]] name = "bevy_encase_derive" version = "0.14.2" @@ -3818,6 +3829,8 @@ name = "my_bevy_game" version = "0.1.0" dependencies = [ "bevy", + "bevy_ecs_tilemap", + "rand 0.8.5", "trunk", "wasm-bindgen", "web-sys", diff --git a/Cargo.toml b/Cargo.toml index afe6fd02..3b412e4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,8 @@ edition = "2021" [dependencies] bevy = "0.14.2" +bevy_ecs_tilemap = "0.14.0" +rand = "0.8.5" wasm-bindgen = "0.2.78" [dependencies.web-sys] diff --git a/assets/Pixel Art Top Down Basic/TX Plant.png b/assets/Pixel Art Top Down Basic/TX Plant.png new file mode 100644 index 00000000..1f183454 Binary files /dev/null and b/assets/Pixel Art Top Down Basic/TX Plant.png differ diff --git a/assets/Pixel Art Top Down Basic/TX Player.png b/assets/Pixel Art Top Down Basic/TX Player.png new file mode 100644 index 00000000..71d6b16f Binary files /dev/null and b/assets/Pixel Art Top Down Basic/TX Player.png differ diff --git a/assets/Pixel Art Top Down Basic/TX Props.png b/assets/Pixel Art Top Down Basic/TX Props.png new file mode 100644 index 00000000..2fb5bf16 Binary files /dev/null and b/assets/Pixel Art Top Down Basic/TX Props.png differ diff --git a/assets/Pixel Art Top Down Basic/TX Shadow Plant.png b/assets/Pixel Art Top Down Basic/TX Shadow Plant.png new file mode 100644 index 00000000..b5650547 Binary files /dev/null and b/assets/Pixel Art Top Down Basic/TX Shadow Plant.png differ diff --git a/assets/Pixel Art Top Down Basic/TX Shadow.png b/assets/Pixel Art Top Down Basic/TX Shadow.png new file mode 100644 index 00000000..67a2a646 Binary files /dev/null and b/assets/Pixel Art Top Down Basic/TX Shadow.png differ diff --git a/assets/Pixel Art Top Down Basic/TX Struct.png b/assets/Pixel Art Top Down Basic/TX Struct.png new file mode 100644 index 00000000..dd3f1b04 Binary files /dev/null and b/assets/Pixel Art Top Down Basic/TX Struct.png differ diff --git a/assets/Pixel Art Top Down Basic/TX Tileset Grass.png b/assets/Pixel Art Top Down Basic/TX Tileset Grass.png new file mode 100644 index 00000000..dd5d66c0 Binary files /dev/null and b/assets/Pixel Art Top Down Basic/TX Tileset Grass.png differ diff --git a/assets/Pixel Art Top Down Basic/TX Tileset Stone Ground.png b/assets/Pixel Art Top Down Basic/TX Tileset Stone Ground.png new file mode 100644 index 00000000..4dee6c0c Binary files /dev/null and b/assets/Pixel Art Top Down Basic/TX Tileset Stone Ground.png differ diff --git a/assets/Pixel Art Top Down Basic/TX Tileset Wall.png b/assets/Pixel Art Top Down Basic/TX Tileset Wall.png new file mode 100644 index 00000000..555a2ffb Binary files /dev/null and b/assets/Pixel Art Top Down Basic/TX Tileset Wall.png differ diff --git a/index.html b/index.html index e7ee0f73..27fb56e8 100644 --- a/index.html +++ b/index.html @@ -1,7 +1,9 @@ + + diff --git a/readme.md b/readme.md index cdfc7222..0a06be6c 100644 --- a/readme.md +++ b/readme.md @@ -14,4 +14,10 @@ $ trunk serve - https://trunkrs.dev/ - https://bevy-cheatbook.github.io/platforms/wasm.html -- https://gist.github.com/nakedible/f6a0d4bcbea1df7768e9ed425f6f33db \ No newline at end of file +- https://gist.github.com/nakedible/f6a0d4bcbea1df7768e9ed425f6f33db + + + +### Assets + +- https://cainos.itch.io/pixel-art-top-down-basic \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 935e1e27..f7114cb3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,98 +1,180 @@ -//! Shows how to render simple primitive shapes with a single color. - use bevy::{ + asset::{AssetMetaCheck, AssetPlugin}, prelude::*, - sprite::{MaterialMesh2dBundle, Mesh2dHandle}, + render::camera::CameraRenderGraph, }; +use bevy_ecs_tilemap::prelude::*; +use rand::prelude::*; use wasm_bindgen::JsValue; use web_sys::console; +#[derive(Component)] +struct Person; + +#[derive(Component)] +struct HUD; + fn main() { console::log_1(&JsValue::from("start")); let mut app = App::new(); - app.add_plugins((DefaultPlugins,)) - .add_systems(Startup, setup); - app.add_systems(Update, keyboard_input); + + app.add_plugins( + DefaultPlugins + .set(AssetPlugin { + // https://github.com/bevyengine/bevy/issues/10157 + meta_check: AssetMetaCheck::Never, + ..default() + }) + .set(ImagePlugin::default_nearest()), + ) + .add_plugins(TilemapPlugin) + .add_systems(Startup, setup) + .add_systems(Update, update); + app.run(); } -const X_EXTENT: f32 = 900.; +fn setup(mut commands: Commands, asset_server: Res) { + // デフォルトでは far: 1000, near: -1000でカメラが作成される + // この範囲を超えるとクリップされることに注意 + let mut camera_bundle = Camera2dBundle::default(); + camera_bundle.projection.scale = 0.5; -fn setup( - mut commands: Commands, - mut meshes: ResMut>, - mut materials: ResMut>, -) { - commands.spawn(Camera2dBundle::default()); - - let shapes = [ - Mesh2dHandle(meshes.add(Circle { radius: 50.0 })), - Mesh2dHandle(meshes.add(CircularSector::new(50.0, 1.0))), - Mesh2dHandle(meshes.add(CircularSegment::new(50.0, 1.25))), - Mesh2dHandle(meshes.add(Ellipse::new(25.0, 50.0))), - Mesh2dHandle(meshes.add(Annulus::new(25.0, 50.0))), - Mesh2dHandle(meshes.add(Capsule2d::new(25.0, 50.0))), - Mesh2dHandle(meshes.add(Rhombus::new(75.0, 100.0))), - Mesh2dHandle(meshes.add(Rectangle::new(50.0, 100.0))), - Mesh2dHandle(meshes.add(RegularPolygon::new(50.0, 6))), - Mesh2dHandle(meshes.add(Triangle2d::new( - Vec2::Y * 50.0, - Vec2::new(-50.0, -50.0), - Vec2::new(50.0, -50.0), - ))), - ]; - let num_shapes = shapes.len(); - - for (i, shape) in shapes.into_iter().enumerate() { - // Distribute colors evenly across the rainbow. - let color = Color::hsl(360. * i as f32 / num_shapes as f32, 0.95, 0.7); - - commands.spawn(MaterialMesh2dBundle { - mesh: shape, - material: materials.add(color), - transform: Transform::from_xyz( - // Distribute shapes from -X_EXTENT/2 to +X_EXTENT/2. - -X_EXTENT / 2. + i as f32 / (num_shapes - 1) as f32 * X_EXTENT, - 0.0, - 0.0, - ), - ..default() - }); - } + commands.spawn(camera_bundle); - commands.spawn( - TextBundle::from_section("Primitives Test", TextStyle::default()).with_style(Style { + commands.spawn(( + TextBundle::from_section("Test", TextStyle::default()).with_style(Style { position_type: PositionType::Absolute, top: Val::Px(12.0), left: Val::Px(12.0), ..default() }), - ); -} + HUD, + )); -fn keyboard_input(keys: Res>) { - if keys.just_pressed(KeyCode::Space) { - // Space was pressed - console::log_1(&JsValue::from("space was pressed")); - } - if keys.just_released(KeyCode::ControlLeft) { - // Left Ctrl was released - console::log_1(&JsValue::from("left ctrl was released")); - } - if keys.pressed(KeyCode::KeyW) { - // W is being held down - console::log_1(&JsValue::from("W is being held down")); - } - // we can check multiple at once with `.any_*` - if keys.any_pressed([KeyCode::ShiftLeft, KeyCode::ShiftRight]) { - // Either the left or right shift are being held down - console::log_1(&JsValue::from( - "Either the left or right shift are being held down", - )); + for _ in 0..10 { + let x = 400.0 * rand::random::(); + let y = 400.0 * rand::random::(); + commands.spawn(SpriteBundle { + transform: Transform::from_xyz(x, y, -y), + texture: asset_server.load("Pixel Art Top Down Basic/TX Plant.png"), + sprite: Sprite { + anchor: bevy::sprite::Anchor::BottomCenter, + rect: Some(Rect::new(0.0, 0.0, 8.0 * 19.0, 155.0)), + ..default() + }, + ..default() + }); } - if keys.any_just_pressed([KeyCode::Delete, KeyCode::Backspace]) { - // Either delete or backspace was just pressed + + commands.spawn(( + Person, + SpriteBundle { + transform: Transform::from_xyz(0.0, 0.0, 1.0), + texture: asset_server.load("Pixel Art Top Down Basic/TX Player.png"), + sprite: Sprite { + anchor: bevy::sprite::Anchor::BottomCenter, + rect: Some(Rect::new(0.0, 0.0, 32.0, 58.0)), + ..default() + }, + ..default() + }, + )); + + let texture_handle: Handle = + asset_server.load("Pixel Art Top Down Basic/TX Tileset Grass.png"); + + let map_size = TilemapSize { x: 256, y: 256 }; + + let tilemap_entity = commands.spawn_empty().id(); + + let mut tile_storage = TileStorage::empty(map_size); + + for x in 0..map_size.x { + for y in 0..map_size.y { + let tile_pos = TilePos { x, y }; + let tile_entity = commands + .spawn(TileBundle { + // 0~120くらいが原っぱのタイル、それ以降は石畳など + // ひとまず原っぱをランダムに配置 + texture_index: TileTextureIndex(rand::random::() % 120), + position: tile_pos, + tilemap_id: TilemapId(tilemap_entity), + ..Default::default() + }) + .id(); + tile_storage.set(&tile_pos, tile_entity); + } } + + let tile_size = TilemapTileSize { x: 16.0, y: 16.0 }; + let grid_size = tile_size.into(); + let map_type = TilemapType::default(); + + // fill_tilemap_rect_color( + // TileTextureIndex(0), + // TilePos { x: 0, y: 0 }, + // TilemapSize { x: 1, y: 1 }, + // Color::srgba(1.0, 0.0, 0.0, 1.0), + // TilemapId(tilemap_entity), + // &mut commands, + // &mut tile_storage, + // ); + + commands.entity(tilemap_entity).insert(TilemapBundle { + grid_size, + map_type, + size: map_size, + storage: tile_storage, + texture: TilemapTexture::Single(texture_handle), + tile_size, + // タイルマップは最も背後に配置したいので z: -1000にする + // サンプルコードでは get_tilemap_center_transform を使って画面中央に揃えているが、 + // 座標計算が面倒になるので x:0, y:0 に配置している + // transform: get_tilemap_center_transform(&map_size, &grid_size, &map_type, -1000.0), + transform: Transform::from_xyz(0.0, 0.0, -1000.0), + ..Default::default() + }); +} + +fn update( + keys: Res>, + mut player_query: Query<&mut Transform, (With, Without)>, + mut camera_query: Query<&mut Transform, (With, Without)>, + mut hud_query: Query<&mut Text, With>, +) { + let speed = 3.0; + + let velocity = Vec2::new( + to_s(&keys, KeyCode::KeyD) - to_s(&keys, KeyCode::KeyA), + to_s(&keys, KeyCode::KeyW) - to_s(&keys, KeyCode::KeyS), + ) + .normalize_or_zero() + * speed; + + let mut player = player_query.single_mut(); + + player.translation.x += velocity.x; + player.translation.y += velocity.y; + player.translation.z = -player.translation.y; + + let mut camera = camera_query.single_mut(); + + camera.translation.x += (player.translation.x - camera.translation.x) * 0.1; + camera.translation.y += (player.translation.y - camera.translation.y) * 0.1; + + let mut hud = hud_query.single_mut(); + + let text = format!( + "Player: ({:.2}, {:.2})\nCamera: ({:.2}, {:.2})", + player.translation.x, player.translation.y, camera.translation.x, camera.translation.y + ); + + hud.sections = vec![TextSection::from(text)]; +} + +fn to_s(keys: &Res>, code: bevy::input::keyboard::KeyCode) -> f32 { + return if keys.pressed(code) { 1.0 } else { 0.0 }; }