Updated the 2D examples to make them uniform (#17237)
# Objective Make the examples look more uniform and more polished. following the issue #17167 ## Solution - [x] Added a minimal UI explaining how to interact with the examples only when needed. - [x] Used the same notation for interactions ex : "Up Arrow: Move Forward \nLeft / Right Arrow: Turn" - [x] Set the color to [GRAY](https://github.com/bevyengine/bevy/pull/17237#discussion_r1907560092) when it's not visible enough - [x] Changed some colors to be easy on the eyes - [x] removed the //camera comment - [x] Unified the use of capital letters in the examples. - [x] Simplified the mesh2d_arc offset calculations. ... --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Rob Parrett <robparrett@gmail.com>
This commit is contained in:
parent
9bc0ae33c3
commit
94e0e1f031
@ -31,4 +31,15 @@ fn draw_cursor(
|
||||
|
||||
fn setup(mut commands: Commands) {
|
||||
commands.spawn(Camera2d);
|
||||
|
||||
// Create a minimal UI explaining how to interact with the example
|
||||
commands.spawn((
|
||||
Text::new("Move the mouse to see the circle follow your cursor."),
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(12.0),
|
||||
left: Val::Px(12.0),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ fn setup(
|
||||
Text::default(),
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
bottom: Val::Px(12.0),
|
||||
top: Val::Px(12.0),
|
||||
left: Val::Px(12.0),
|
||||
..default()
|
||||
},
|
||||
|
@ -201,6 +201,7 @@ const OFFSET_Y: f32 = 75.;
|
||||
|
||||
fn setup(mut commands: Commands) {
|
||||
commands.spawn(Camera2d);
|
||||
|
||||
commands.spawn((
|
||||
Transform::from_xyz(-OFFSET_X, OFFSET_Y, 0.),
|
||||
Shape::Circle(Circle::new(45.)),
|
||||
@ -259,7 +260,7 @@ fn setup(mut commands: Commands) {
|
||||
Text::default(),
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
bottom: Val::Px(12.0),
|
||||
top: Val::Px(12.0),
|
||||
left: Val::Px(12.0),
|
||||
..default()
|
||||
},
|
||||
|
@ -38,10 +38,9 @@ struct MyProcGenImage(Handle<Image>);
|
||||
struct SeededRng(ChaCha8Rng);
|
||||
|
||||
fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
|
||||
// spawn a camera
|
||||
commands.spawn(Camera2d);
|
||||
|
||||
// create an image that we are going to draw into
|
||||
// Create an image that we are going to draw into
|
||||
let mut image = Image::new_fill(
|
||||
// 2D image of size 256x256
|
||||
Extent3d {
|
||||
@ -57,8 +56,8 @@ fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
|
||||
RenderAssetUsages::MAIN_WORLD | RenderAssetUsages::RENDER_WORLD,
|
||||
);
|
||||
|
||||
// to make it extra fancy, we can set the Alpha of each pixel
|
||||
// so that it fades out in a circular fashion
|
||||
// To make it extra fancy, we can set the Alpha of each pixel,
|
||||
// so that it fades out in a circular fashion.
|
||||
for y in 0..IMAGE_HEIGHT {
|
||||
for x in 0..IMAGE_WIDTH {
|
||||
let center = Vec2::new(IMAGE_WIDTH as f32 / 2.0, IMAGE_HEIGHT as f32 / 2.0);
|
||||
@ -66,22 +65,22 @@ fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
|
||||
let r = Vec2::new(x as f32, y as f32).distance(center);
|
||||
let a = 1.0 - (r / max_radius).clamp(0.0, 1.0);
|
||||
|
||||
// here we will set the A value by accessing the raw data bytes
|
||||
// Here we will set the A value by accessing the raw data bytes.
|
||||
// (it is the 4th byte of each pixel, as per our `TextureFormat`)
|
||||
|
||||
// find our pixel by its coordinates
|
||||
// Find our pixel by its coordinates
|
||||
let pixel_bytes = image.pixel_bytes_mut(UVec3::new(x, y, 0)).unwrap();
|
||||
// convert our f32 to u8
|
||||
// Convert our f32 to u8
|
||||
pixel_bytes[3] = (a * u8::MAX as f32) as u8;
|
||||
}
|
||||
}
|
||||
|
||||
// add it to Bevy's assets, so it can be used for rendering
|
||||
// Add it to Bevy's assets, so it can be used for rendering
|
||||
// this will give us a handle we can use
|
||||
// (to display it in a sprite, or as part of UI, etc.)
|
||||
let handle = images.add(image);
|
||||
|
||||
// create a sprite entity using our image
|
||||
// Create a sprite entity using our image
|
||||
commands.spawn(Sprite::from_image(handle.clone()));
|
||||
commands.insert_resource(MyProcGenImage(handle));
|
||||
|
||||
@ -95,7 +94,7 @@ fn setup(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
|
||||
fn draw(
|
||||
my_handle: Res<MyProcGenImage>,
|
||||
mut images: ResMut<Assets<Image>>,
|
||||
// used to keep track of where we are
|
||||
// Used to keep track of where we are
|
||||
mut i: Local<u32>,
|
||||
mut draw_color: Local<Color>,
|
||||
mut seeded_rng: ResMut<SeededRng>,
|
||||
|
@ -63,7 +63,6 @@ fn setup(
|
||||
Transform::from_scale(150.0 * Vec3::ONE),
|
||||
));
|
||||
|
||||
// Add a camera
|
||||
commands.spawn(Camera2d);
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ fn setup(
|
||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||
) {
|
||||
commands.spawn(Camera2d);
|
||||
|
||||
commands.spawn((
|
||||
Mesh2d(meshes.add(Rectangle::default())),
|
||||
MeshMaterial2d(materials.add(Color::from(PURPLE))),
|
||||
|
@ -5,7 +5,7 @@
|
||||
use std::f32::consts::FRAC_PI_2;
|
||||
|
||||
use bevy::{
|
||||
color::palettes::css::{BLUE, DARK_SLATE_GREY, RED},
|
||||
color::palettes::css::{BLUE, GRAY, RED},
|
||||
math::{
|
||||
bounding::{Bounded2d, BoundingVolume},
|
||||
Isometry2d,
|
||||
@ -42,16 +42,14 @@ fn setup(
|
||||
commands.spawn((
|
||||
Camera2d,
|
||||
Camera {
|
||||
clear_color: ClearColorConfig::Custom(DARK_SLATE_GREY.into()),
|
||||
clear_color: ClearColorConfig::Custom(GRAY.into()),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
const UPPER_Y: f32 = 50.0;
|
||||
const LOWER_Y: f32 = -50.0;
|
||||
const FIRST_X: f32 = -450.0;
|
||||
const OFFSET: f32 = 100.0;
|
||||
const NUM_SLICES: i32 = 8;
|
||||
const SPACING_X: f32 = 100.0;
|
||||
const OFFSET_X: f32 = SPACING_X * (NUM_SLICES - 1) as f32 / 2.0;
|
||||
|
||||
// This draws NUM_SLICES copies of the Bevy logo as circular sectors and segments,
|
||||
// with successively larger angles up to a complete circle.
|
||||
@ -70,7 +68,7 @@ fn setup(
|
||||
Mesh2d(meshes.add(sector_mesh)),
|
||||
MeshMaterial2d(material.clone()),
|
||||
Transform {
|
||||
translation: Vec3::new(FIRST_X + OFFSET * i as f32, 2.0 * UPPER_Y, 0.0),
|
||||
translation: Vec3::new(SPACING_X * i as f32 - OFFSET_X, 50.0, 0.0),
|
||||
rotation: Quat::from_rotation_z(sector_angle),
|
||||
..default()
|
||||
},
|
||||
@ -94,7 +92,7 @@ fn setup(
|
||||
Mesh2d(meshes.add(segment_mesh)),
|
||||
MeshMaterial2d(material.clone()),
|
||||
Transform {
|
||||
translation: Vec3::new(FIRST_X + OFFSET * i as f32, LOWER_Y, 0.0),
|
||||
translation: Vec3::new(SPACING_X * i as f32 - OFFSET_X, -50.0, 0.0),
|
||||
rotation: Quat::from_rotation_z(segment_angle),
|
||||
..default()
|
||||
},
|
||||
|
@ -115,7 +115,6 @@ fn star(
|
||||
Mesh2d(meshes.add(star)),
|
||||
));
|
||||
|
||||
// Spawn the camera
|
||||
commands.spawn(Camera2d);
|
||||
}
|
||||
|
||||
@ -126,7 +125,7 @@ pub struct ColoredMesh2d;
|
||||
/// Custom pipeline for 2d meshes with vertex colors
|
||||
#[derive(Resource)]
|
||||
pub struct ColoredMesh2dPipeline {
|
||||
/// this pipeline wraps the standard [`Mesh2dPipeline`]
|
||||
/// This pipeline wraps the standard [`Mesh2dPipeline`]
|
||||
mesh2d_pipeline: Mesh2dPipeline,
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,6 @@ fn setup(
|
||||
|
||||
let mesh_handle = meshes.add(mesh);
|
||||
|
||||
// Spawn camera
|
||||
commands.spawn(Camera2d);
|
||||
|
||||
// Spawn the quad with vertex colors
|
||||
|
@ -12,16 +12,17 @@ fn main() {
|
||||
|
||||
#[derive(Component)]
|
||||
enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
commands.spawn(Camera2d);
|
||||
|
||||
commands.spawn((
|
||||
Sprite::from_image(asset_server.load("branding/icon.png")),
|
||||
Transform::from_xyz(100., 0., 0.),
|
||||
Direction::Up,
|
||||
Transform::from_xyz(0., 0., 0.),
|
||||
Direction::Right,
|
||||
));
|
||||
}
|
||||
|
||||
@ -30,14 +31,14 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
fn sprite_movement(time: Res<Time>, mut sprite_position: Query<(&mut Direction, &mut Transform)>) {
|
||||
for (mut logo, mut transform) in &mut sprite_position {
|
||||
match *logo {
|
||||
Direction::Up => transform.translation.y += 150. * time.delta_secs(),
|
||||
Direction::Down => transform.translation.y -= 150. * time.delta_secs(),
|
||||
Direction::Right => transform.translation.x += 150. * time.delta_secs(),
|
||||
Direction::Left => transform.translation.x -= 150. * time.delta_secs(),
|
||||
}
|
||||
|
||||
if transform.translation.y > 200. {
|
||||
*logo = Direction::Down;
|
||||
} else if transform.translation.y < -200. {
|
||||
*logo = Direction::Up;
|
||||
if transform.translation.x > 200. {
|
||||
*logo = Direction::Left;
|
||||
} else if transform.translation.x < -200. {
|
||||
*logo = Direction::Right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
//! Shows how to create graphics that snap to the pixel grid by rendering to a texture in 2D
|
||||
|
||||
use bevy::{
|
||||
color::palettes::css::GRAY,
|
||||
prelude::*,
|
||||
render::{
|
||||
camera::RenderTarget,
|
||||
@ -50,18 +51,18 @@ struct OuterCamera;
|
||||
struct Rotate;
|
||||
|
||||
fn setup_sprite(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
// the sample sprite that will be rendered to the pixel-perfect canvas
|
||||
// The sample sprite that will be rendered to the pixel-perfect canvas
|
||||
commands.spawn((
|
||||
Sprite::from_image(asset_server.load("pixel/bevy_pixel_dark.png")),
|
||||
Transform::from_xyz(-40., 20., 2.),
|
||||
Transform::from_xyz(-45., 20., 2.),
|
||||
Rotate,
|
||||
PIXEL_PERFECT_LAYERS,
|
||||
));
|
||||
|
||||
// the sample sprite that will be rendered to the high-res "outer world"
|
||||
// The sample sprite that will be rendered to the high-res "outer world"
|
||||
commands.spawn((
|
||||
Sprite::from_image(asset_server.load("pixel/bevy_pixel_light.png")),
|
||||
Transform::from_xyz(-40., -20., 2.),
|
||||
Transform::from_xyz(-45., -20., 2.),
|
||||
Rotate,
|
||||
HIGH_RES_LAYERS,
|
||||
));
|
||||
@ -76,7 +77,7 @@ fn setup_mesh(
|
||||
commands.spawn((
|
||||
Mesh2d(meshes.add(Capsule2d::default())),
|
||||
MeshMaterial2d(materials.add(Color::BLACK)),
|
||||
Transform::from_xyz(40., 0., 2.).with_scale(Vec3::splat(32.)),
|
||||
Transform::from_xyz(25., 0., 2.).with_scale(Vec3::splat(32.)),
|
||||
Rotate,
|
||||
PIXEL_PERFECT_LAYERS,
|
||||
));
|
||||
@ -89,7 +90,7 @@ fn setup_camera(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
|
||||
..default()
|
||||
};
|
||||
|
||||
// this Image serves as a canvas representing the low-resolution game screen
|
||||
// This Image serves as a canvas representing the low-resolution game screen
|
||||
let mut canvas = Image {
|
||||
texture_descriptor: TextureDescriptor {
|
||||
label: None,
|
||||
@ -106,18 +107,19 @@ fn setup_camera(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
|
||||
..default()
|
||||
};
|
||||
|
||||
// fill image.data with zeroes
|
||||
// Fill image.data with zeroes
|
||||
canvas.resize(canvas_size);
|
||||
|
||||
let image_handle = images.add(canvas);
|
||||
|
||||
// this camera renders whatever is on `PIXEL_PERFECT_LAYERS` to the canvas
|
||||
// This camera renders whatever is on `PIXEL_PERFECT_LAYERS` to the canvas
|
||||
commands.spawn((
|
||||
Camera2d,
|
||||
Camera {
|
||||
// render before the "main pass" camera
|
||||
// Render before the "main pass" camera
|
||||
order: -1,
|
||||
target: RenderTarget::Image(image_handle.clone().into()),
|
||||
clear_color: ClearColorConfig::Custom(GRAY.into()),
|
||||
..default()
|
||||
},
|
||||
Msaa::Off,
|
||||
@ -125,10 +127,10 @@ fn setup_camera(mut commands: Commands, mut images: ResMut<Assets<Image>>) {
|
||||
PIXEL_PERFECT_LAYERS,
|
||||
));
|
||||
|
||||
// spawn the canvas
|
||||
// Spawn the canvas
|
||||
commands.spawn((Sprite::from_image(image_handle), Canvas, HIGH_RES_LAYERS));
|
||||
|
||||
// the "outer" camera renders whatever is on `HIGH_RES_LAYERS` to the screen.
|
||||
// The "outer" camera renders whatever is on `HIGH_RES_LAYERS` to the screen.
|
||||
// here, the canvas and one of the sample sprites will be rendered by this camera
|
||||
commands.spawn((Camera2d, Msaa::Off, OuterCamera, HIGH_RES_LAYERS));
|
||||
}
|
||||
|
@ -20,23 +20,23 @@ fn main() {
|
||||
.run();
|
||||
}
|
||||
|
||||
/// player component
|
||||
/// Player component
|
||||
#[derive(Component)]
|
||||
struct Player {
|
||||
/// linear speed in meters per second
|
||||
/// Linear speed in meters per second
|
||||
movement_speed: f32,
|
||||
/// rotation speed in radians per second
|
||||
/// Rotation speed in radians per second
|
||||
rotation_speed: f32,
|
||||
}
|
||||
|
||||
/// snap to player ship behavior
|
||||
/// Snap to player ship behavior
|
||||
#[derive(Component)]
|
||||
struct SnapToPlayer;
|
||||
|
||||
/// rotate to face player ship behavior
|
||||
/// Rotate to face player ship behavior
|
||||
#[derive(Component)]
|
||||
struct RotateToPlayer {
|
||||
/// rotation speed in radians per second
|
||||
/// Rotation speed in radians per second
|
||||
rotation_speed: f32,
|
||||
}
|
||||
|
||||
@ -54,22 +54,32 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
let enemy_a_handle = asset_server.load("textures/simplespace/enemy_A.png");
|
||||
let enemy_b_handle = asset_server.load("textures/simplespace/enemy_B.png");
|
||||
|
||||
// 2D orthographic camera
|
||||
commands.spawn(Camera2d);
|
||||
|
||||
// Create a minimal UI explaining how to interact with the example
|
||||
commands.spawn((
|
||||
Text::new("Up Arrow: Move Forward\nLeft / Right Arrow: Turn"),
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(12.0),
|
||||
left: Val::Px(12.0),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
let horizontal_margin = BOUNDS.x / 4.0;
|
||||
let vertical_margin = BOUNDS.y / 4.0;
|
||||
|
||||
// player controlled ship
|
||||
// Player controlled ship
|
||||
commands.spawn((
|
||||
Sprite::from_image(ship_handle),
|
||||
Player {
|
||||
movement_speed: 500.0, // meters per second
|
||||
rotation_speed: f32::to_radians(360.0), // degrees per second
|
||||
movement_speed: 500.0, // Meters per second
|
||||
rotation_speed: f32::to_radians(360.0), // Degrees per second
|
||||
},
|
||||
));
|
||||
|
||||
// enemy that snaps to face the player spawns on the bottom and left
|
||||
// Enemy that snaps to face the player spawns on the bottom and left
|
||||
commands.spawn((
|
||||
Sprite::from_image(enemy_a_handle.clone()),
|
||||
Transform::from_xyz(0.0 - horizontal_margin, 0.0, 0.0),
|
||||
@ -81,19 +91,19 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
SnapToPlayer,
|
||||
));
|
||||
|
||||
// enemy that rotates to face the player enemy spawns on the top and right
|
||||
// Enemy that rotates to face the player enemy spawns on the top and right
|
||||
commands.spawn((
|
||||
Sprite::from_image(enemy_b_handle.clone()),
|
||||
Transform::from_xyz(0.0 + horizontal_margin, 0.0, 0.0),
|
||||
RotateToPlayer {
|
||||
rotation_speed: f32::to_radians(45.0), // degrees per second
|
||||
rotation_speed: f32::to_radians(45.0), // Degrees per second
|
||||
},
|
||||
));
|
||||
commands.spawn((
|
||||
Sprite::from_image(enemy_b_handle),
|
||||
Transform::from_xyz(0.0, 0.0 + vertical_margin, 0.0),
|
||||
RotateToPlayer {
|
||||
rotation_speed: f32::to_radians(90.0), // degrees per second
|
||||
rotation_speed: f32::to_radians(90.0), // Degrees per second
|
||||
},
|
||||
));
|
||||
}
|
||||
@ -121,21 +131,21 @@ fn player_movement_system(
|
||||
movement_factor += 1.0;
|
||||
}
|
||||
|
||||
// update the ship rotation around the Z axis (perpendicular to the 2D plane of the screen)
|
||||
// Update the ship rotation around the Z axis (perpendicular to the 2D plane of the screen)
|
||||
transform.rotate_z(rotation_factor * ship.rotation_speed * time.delta_secs());
|
||||
|
||||
// get the ship's forward vector by applying the current rotation to the ships initial facing
|
||||
// Get the ship's forward vector by applying the current rotation to the ships initial facing
|
||||
// vector
|
||||
let movement_direction = transform.rotation * Vec3::Y;
|
||||
// get the distance the ship will move based on direction, the ship's movement speed and delta
|
||||
// Get the distance the ship will move based on direction, the ship's movement speed and delta
|
||||
// time
|
||||
let movement_distance = movement_factor * ship.movement_speed * time.delta_secs();
|
||||
// create the change in translation using the new movement direction and distance
|
||||
// Create the change in translation using the new movement direction and distance
|
||||
let translation_delta = movement_direction * movement_distance;
|
||||
// update the ship translation with our new translation delta
|
||||
// Update the ship translation with our new translation delta
|
||||
transform.translation += translation_delta;
|
||||
|
||||
// bound the ship within the invisible level bounds
|
||||
// Bound the ship within the invisible level bounds
|
||||
let extents = Vec3::from((BOUNDS / 2.0, 0.0));
|
||||
transform.translation = transform.translation.min(extents).max(-extents);
|
||||
}
|
||||
@ -145,18 +155,18 @@ fn snap_to_player_system(
|
||||
mut query: Query<&mut Transform, (With<SnapToPlayer>, Without<Player>)>,
|
||||
player_transform: Single<&Transform, With<Player>>,
|
||||
) {
|
||||
// get the player translation in 2D
|
||||
// Get the player translation in 2D
|
||||
let player_translation = player_transform.translation.xy();
|
||||
|
||||
for mut enemy_transform in &mut query {
|
||||
// get the vector from the enemy ship to the player ship in 2D and normalize it.
|
||||
// Get the vector from the enemy ship to the player ship in 2D and normalize it.
|
||||
let to_player = (player_translation - enemy_transform.translation.xy()).normalize();
|
||||
|
||||
// get the quaternion to rotate from the initial enemy facing direction to the direction
|
||||
// Get the quaternion to rotate from the initial enemy facing direction to the direction
|
||||
// facing the player
|
||||
let rotate_to_player = Quat::from_rotation_arc(Vec3::Y, to_player.extend(0.));
|
||||
|
||||
// rotate the enemy to face the player
|
||||
// Rotate the enemy to face the player
|
||||
enemy_transform.rotation = rotate_to_player;
|
||||
}
|
||||
}
|
||||
@ -187,50 +197,50 @@ fn rotate_to_player_system(
|
||||
mut query: Query<(&RotateToPlayer, &mut Transform), Without<Player>>,
|
||||
player_transform: Single<&Transform, With<Player>>,
|
||||
) {
|
||||
// get the player translation in 2D
|
||||
// Get the player translation in 2D
|
||||
let player_translation = player_transform.translation.xy();
|
||||
|
||||
for (config, mut enemy_transform) in &mut query {
|
||||
// get the enemy ship forward vector in 2D (already unit length)
|
||||
// Get the enemy ship forward vector in 2D (already unit length)
|
||||
let enemy_forward = (enemy_transform.rotation * Vec3::Y).xy();
|
||||
|
||||
// get the vector from the enemy ship to the player ship in 2D and normalize it.
|
||||
// Get the vector from the enemy ship to the player ship in 2D and normalize it.
|
||||
let to_player = (player_translation - enemy_transform.translation.xy()).normalize();
|
||||
|
||||
// get the dot product between the enemy forward vector and the direction to the player.
|
||||
// Get the dot product between the enemy forward vector and the direction to the player.
|
||||
let forward_dot_player = enemy_forward.dot(to_player);
|
||||
|
||||
// if the dot product is approximately 1.0 then the enemy is already facing the player and
|
||||
// If the dot product is approximately 1.0 then the enemy is already facing the player and
|
||||
// we can early out.
|
||||
if (forward_dot_player - 1.0).abs() < f32::EPSILON {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get the right vector of the enemy ship in 2D (already unit length)
|
||||
// Get the right vector of the enemy ship in 2D (already unit length)
|
||||
let enemy_right = (enemy_transform.rotation * Vec3::X).xy();
|
||||
|
||||
// get the dot product of the enemy right vector and the direction to the player ship.
|
||||
// if the dot product is negative them we need to rotate counter clockwise, if it is
|
||||
// Get the dot product of the enemy right vector and the direction to the player ship.
|
||||
// If the dot product is negative them we need to rotate counter clockwise, if it is
|
||||
// positive we need to rotate clockwise. Note that `copysign` will still return 1.0 if the
|
||||
// dot product is 0.0 (because the player is directly behind the enemy, so perpendicular
|
||||
// with the right vector).
|
||||
let right_dot_player = enemy_right.dot(to_player);
|
||||
|
||||
// determine the sign of rotation from the right dot player. We need to negate the sign
|
||||
// Determine the sign of rotation from the right dot player. We need to negate the sign
|
||||
// here as the 2D bevy co-ordinate system rotates around +Z, which is pointing out of the
|
||||
// screen. Due to the right hand rule, positive rotation around +Z is counter clockwise and
|
||||
// negative is clockwise.
|
||||
let rotation_sign = -f32::copysign(1.0, right_dot_player);
|
||||
|
||||
// limit rotation so we don't overshoot the target. We need to convert our dot product to
|
||||
// Limit rotation so we don't overshoot the target. We need to convert our dot product to
|
||||
// an angle here so we can get an angle of rotation to clamp against.
|
||||
let max_angle = ops::acos(forward_dot_player.clamp(-1.0, 1.0)); // clamp acos for safety
|
||||
let max_angle = ops::acos(forward_dot_player.clamp(-1.0, 1.0)); // Clamp acos for safety
|
||||
|
||||
// calculate angle of rotation with limit
|
||||
// Calculate angle of rotation with limit
|
||||
let rotation_angle =
|
||||
rotation_sign * (config.rotation_speed * time.delta_secs()).min(max_angle);
|
||||
|
||||
// rotate the enemy to face the player
|
||||
// Rotate the enemy to face the player
|
||||
enemy_transform.rotate_z(rotation_angle);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ fn main() {
|
||||
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
commands.spawn(Camera2d);
|
||||
|
||||
commands.spawn(Sprite::from_image(
|
||||
asset_server.load("branding/bevy_bird_dark.png"),
|
||||
));
|
||||
|
@ -14,9 +14,9 @@ fn main() {
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
// press the right arrow key to animate the right sprite
|
||||
// Press the right arrow key to animate the right sprite
|
||||
trigger_animation::<RightSprite>.run_if(input_just_pressed(KeyCode::ArrowRight)),
|
||||
// press the left arrow key to animate the left sprite
|
||||
// Press the left arrow key to animate the left sprite
|
||||
trigger_animation::<LeftSprite>.run_if(input_just_pressed(KeyCode::ArrowLeft)),
|
||||
),
|
||||
)
|
||||
@ -25,7 +25,7 @@ fn main() {
|
||||
|
||||
// This system runs when the user clicks the left arrow key or right arrow key
|
||||
fn trigger_animation<S: Component>(mut animation: Single<&mut AnimationConfig, With<S>>) {
|
||||
// we create a new timer when the animation is triggered
|
||||
// We create a new timer when the animation is triggered
|
||||
animation.frame_timer = AnimationConfig::timer_from_fps(animation.fps);
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ impl AnimationConfig {
|
||||
// `last_sprite_index` (both defined in `AnimationConfig`).
|
||||
fn execute_animations(time: Res<Time>, mut query: Query<(&mut AnimationConfig, &mut Sprite)>) {
|
||||
for (mut config, mut sprite) in &mut query {
|
||||
// we track how long the current sprite has been displayed for
|
||||
// We track how long the current sprite has been displayed for
|
||||
config.frame_timer.tick(time.delta());
|
||||
|
||||
// If it has been displayed for the user-defined amount of time (fps)...
|
||||
@ -89,17 +89,28 @@ fn setup(
|
||||
) {
|
||||
commands.spawn(Camera2d);
|
||||
|
||||
// load the sprite sheet using the `AssetServer`
|
||||
// Create a minimal UI explaining how to interact with the example
|
||||
commands.spawn((
|
||||
Text::new("Left Arrow: Animate Left Sprite\nRight Arrow: Animate Right Sprite"),
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(12.0),
|
||||
left: Val::Px(12.0),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
// Load the sprite sheet using the `AssetServer`
|
||||
let texture = asset_server.load("textures/rpg/chars/gabe/gabe-idle-run.png");
|
||||
|
||||
// the sprite sheet has 7 sprites arranged in a row, and they are all 24px x 24px
|
||||
// The sprite sheet has 7 sprites arranged in a row, and they are all 24px x 24px
|
||||
let layout = TextureAtlasLayout::from_grid(UVec2::splat(24), 7, 1, None, None);
|
||||
let texture_atlas_layout = texture_atlas_layouts.add(layout);
|
||||
|
||||
// the first (left-hand) sprite runs at 10 FPS
|
||||
// The first (left-hand) sprite runs at 10 FPS
|
||||
let animation_config_1 = AnimationConfig::new(1, 6, 10);
|
||||
|
||||
// create the first (left-hand) sprite
|
||||
// Create the first (left-hand) sprite
|
||||
commands.spawn((
|
||||
Sprite {
|
||||
image: texture.clone(),
|
||||
@ -109,15 +120,15 @@ fn setup(
|
||||
}),
|
||||
..default()
|
||||
},
|
||||
Transform::from_scale(Vec3::splat(6.0)).with_translation(Vec3::new(-50.0, 0.0, 0.0)),
|
||||
Transform::from_scale(Vec3::splat(6.0)).with_translation(Vec3::new(-70.0, 0.0, 0.0)),
|
||||
LeftSprite,
|
||||
animation_config_1,
|
||||
));
|
||||
|
||||
// the second (right-hand) sprite runs at 20 FPS
|
||||
// The second (right-hand) sprite runs at 20 FPS
|
||||
let animation_config_2 = AnimationConfig::new(1, 6, 20);
|
||||
|
||||
// create the second (right-hand) sprite
|
||||
// Create the second (right-hand) sprite
|
||||
commands.spawn((
|
||||
Sprite {
|
||||
image: texture.clone(),
|
||||
@ -127,19 +138,8 @@ fn setup(
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
Transform::from_scale(Vec3::splat(6.0)).with_translation(Vec3::new(50.0, 0.0, 0.0)),
|
||||
Transform::from_scale(Vec3::splat(6.0)).with_translation(Vec3::new(70.0, 0.0, 0.0)),
|
||||
RightSprite,
|
||||
animation_config_2,
|
||||
));
|
||||
|
||||
// create a minimal UI explaining how to interact with the example
|
||||
commands.spawn((
|
||||
Text::new("Left Arrow Key: Animate Left Sprite\nRight Arrow Key: Animate Right Sprite"),
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
top: Val::Px(12.0),
|
||||
left: Val::Px(12.0),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ fn main() {
|
||||
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
commands.spawn(Camera2d);
|
||||
|
||||
commands.spawn(Sprite {
|
||||
image: asset_server.load("branding/bevy_bird_dark.png"),
|
||||
// Flip the logo to the left
|
||||
|
@ -49,7 +49,9 @@ fn setup(
|
||||
let texture_atlas_layout = texture_atlas_layouts.add(layout);
|
||||
// Use only the subset of sprites in the sheet that make up the run animation
|
||||
let animation_indices = AnimationIndices { first: 1, last: 6 };
|
||||
|
||||
commands.spawn(Camera2d);
|
||||
|
||||
commands.spawn((
|
||||
Sprite::from_atlas_image(
|
||||
texture,
|
||||
|
@ -107,6 +107,7 @@ fn spawn_sprites(
|
||||
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
commands.spawn(Camera2d);
|
||||
|
||||
let font = asset_server.load("fonts/FiraSans-Bold.ttf");
|
||||
let style = TextFont {
|
||||
font: font.clone(),
|
||||
@ -120,7 +121,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
spawn_sprites(
|
||||
&mut commands,
|
||||
handle_1,
|
||||
Vec3::new(-600.0, 200.0, 0.0),
|
||||
Vec3::new(-600.0, 150.0, 0.0),
|
||||
200.0,
|
||||
style.clone(),
|
||||
40.,
|
||||
@ -129,7 +130,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
spawn_sprites(
|
||||
&mut commands,
|
||||
handle_2,
|
||||
Vec3::new(-600.0, -200.0, 0.0),
|
||||
Vec3::new(-600.0, -150.0, 0.0),
|
||||
80.0,
|
||||
style,
|
||||
40.,
|
||||
|
@ -20,6 +20,7 @@ struct AnimationState {
|
||||
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
commands.spawn(Camera2d);
|
||||
|
||||
commands.insert_resource(AnimationState {
|
||||
min: 128.0,
|
||||
max: 512.0,
|
||||
|
@ -41,7 +41,6 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
..default()
|
||||
};
|
||||
let text_justification = JustifyText::Center;
|
||||
// 2d camera
|
||||
commands.spawn(Camera2d);
|
||||
// Demonstrate changing translation
|
||||
commands.spawn((
|
||||
@ -75,7 +74,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
let box_position = Vec2::new(0.0, -250.0);
|
||||
commands
|
||||
.spawn((
|
||||
Sprite::from_color(Color::srgb(0.25, 0.25, 0.75), box_size),
|
||||
Sprite::from_color(Color::srgb(0.25, 0.25, 0.55), box_size),
|
||||
Transform::from_translation(box_position.extend(0.0)),
|
||||
))
|
||||
.with_children(|builder| {
|
||||
@ -85,7 +84,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
TextLayout::new(JustifyText::Left, LineBreak::WordBoundary),
|
||||
// Wrap text in the rectangle
|
||||
TextBounds::from(box_size),
|
||||
// ensure the text is drawn on top of the box
|
||||
// Ensure the text is drawn on top of the box
|
||||
Transform::from_translation(Vec3::Z),
|
||||
));
|
||||
});
|
||||
@ -94,7 +93,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
let other_box_position = Vec2::new(320.0, -250.0);
|
||||
commands
|
||||
.spawn((
|
||||
Sprite::from_color(Color::srgb(0.20, 0.3, 0.70), other_box_size),
|
||||
Sprite::from_color(Color::srgb(0.25, 0.25, 0.55), other_box_size),
|
||||
Transform::from_translation(other_box_position.extend(0.0)),
|
||||
))
|
||||
.with_children(|builder| {
|
||||
@ -104,7 +103,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
TextLayout::new(JustifyText::Left, LineBreak::AnyCharacter),
|
||||
// Wrap text in the rectangle
|
||||
TextBounds::from(other_box_size),
|
||||
// ensure the text is drawn on top of the box
|
||||
// Ensure the text is drawn on top of the box
|
||||
Transform::from_translation(Vec3::Z),
|
||||
));
|
||||
});
|
||||
@ -130,10 +129,10 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
))
|
||||
.with_children(|commands| {
|
||||
for (text_anchor, color) in [
|
||||
(Anchor::TopLeft, Color::Srgba(RED)),
|
||||
(Anchor::TopRight, Color::Srgba(LIME)),
|
||||
(Anchor::BottomRight, Color::Srgba(BLUE)),
|
||||
(Anchor::BottomLeft, Color::Srgba(YELLOW)),
|
||||
(Anchor::TopLeft, Color::Srgba(LIGHT_SALMON)),
|
||||
(Anchor::TopRight, Color::Srgba(LIGHT_GREEN)),
|
||||
(Anchor::BottomRight, Color::Srgba(LIGHT_BLUE)),
|
||||
(Anchor::BottomLeft, Color::Srgba(LIGHT_YELLOW)),
|
||||
] {
|
||||
commands
|
||||
.spawn((
|
||||
|
@ -30,7 +30,7 @@ enum AppState {
|
||||
struct RpgSpriteFolder(Handle<LoadedFolder>);
|
||||
|
||||
fn load_textures(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
// load multiple, individual sprites from a folder
|
||||
// Load multiple, individual sprites from a folder
|
||||
commands.insert_resource(RpgSpriteFolder(asset_server.load_folder("textures/rpg")));
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ fn setup(
|
||||
) {
|
||||
let loaded_folder = loaded_folders.get(&rpg_sprite_handles.0).unwrap();
|
||||
|
||||
// create texture atlases with different padding and sampling
|
||||
// Create texture atlases with different padding and sampling
|
||||
|
||||
let (texture_atlas_linear, linear_sources, linear_texture) = create_texture_atlas(
|
||||
loaded_folder,
|
||||
@ -93,59 +93,58 @@ fn setup(
|
||||
);
|
||||
let atlas_nearest_padded_handle = texture_atlases.add(texture_atlas_nearest_padded);
|
||||
|
||||
// setup 2d scene
|
||||
commands.spawn(Camera2d);
|
||||
|
||||
// padded textures are to the right, unpadded to the left
|
||||
// Padded textures are to the right, unpadded to the left
|
||||
|
||||
// draw unpadded texture atlas
|
||||
// Draw unpadded texture atlas
|
||||
commands.spawn((
|
||||
Sprite::from_image(linear_texture.clone()),
|
||||
Transform {
|
||||
translation: Vec3::new(-250.0, -130.0, 0.0),
|
||||
scale: Vec3::splat(0.8),
|
||||
translation: Vec3::new(-250.0, -160.0, 0.0),
|
||||
scale: Vec3::splat(0.5),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
// draw padded texture atlas
|
||||
// Draw padded texture atlas
|
||||
commands.spawn((
|
||||
Sprite::from_image(linear_padded_texture.clone()),
|
||||
Transform {
|
||||
translation: Vec3::new(250.0, -130.0, 0.0),
|
||||
scale: Vec3::splat(0.8),
|
||||
translation: Vec3::new(250.0, -160.0, 0.0),
|
||||
scale: Vec3::splat(0.5),
|
||||
..default()
|
||||
},
|
||||
));
|
||||
|
||||
let font = asset_server.load("fonts/FiraSans-Bold.ttf");
|
||||
|
||||
// padding label text style
|
||||
// Padding label text style
|
||||
let text_style: TextFont = TextFont {
|
||||
font: font.clone(),
|
||||
font_size: 42.0,
|
||||
..default()
|
||||
};
|
||||
|
||||
// labels to indicate padding
|
||||
// Labels to indicate padding
|
||||
|
||||
// No padding
|
||||
create_label(
|
||||
&mut commands,
|
||||
(-250.0, 330.0, 0.0),
|
||||
(-250.0, 250.0, 0.0),
|
||||
"No padding",
|
||||
text_style.clone(),
|
||||
);
|
||||
|
||||
// Padding
|
||||
create_label(&mut commands, (250.0, 330.0, 0.0), "Padding", text_style);
|
||||
create_label(&mut commands, (250.0, 250.0, 0.0), "Padding", text_style);
|
||||
|
||||
// get handle to a sprite to render
|
||||
// Get handle to a sprite to render
|
||||
let vendor_handle: Handle<Image> = asset_server
|
||||
.get_handle("textures/rpg/chars/vendor/generic-rpg-vendor.png")
|
||||
.unwrap();
|
||||
|
||||
// configuration array to render sprites through iteration
|
||||
// Configuration array to render sprites through iteration
|
||||
let configurations: [(
|
||||
&str,
|
||||
Handle<TextureAtlasLayout>,
|
||||
@ -183,17 +182,17 @@ fn setup(
|
||||
),
|
||||
];
|
||||
|
||||
// label text style
|
||||
// Label text style
|
||||
let sampling_label_style = TextFont {
|
||||
font,
|
||||
font_size: 25.0,
|
||||
..default()
|
||||
};
|
||||
|
||||
let base_y = 170.0; // y position of the sprites
|
||||
let base_y = 80.0; // y position of the sprites
|
||||
|
||||
for (sampling, atlas_handle, atlas_sources, atlas_texture, x) in configurations {
|
||||
// render a sprite from the texture_atlas
|
||||
// Render a sprite from the texture_atlas
|
||||
create_sprite_from_atlas(
|
||||
&mut commands,
|
||||
(x, base_y, 0.0),
|
||||
@ -203,10 +202,10 @@ fn setup(
|
||||
&vendor_handle,
|
||||
);
|
||||
|
||||
// render a label to indicate the sampling setting
|
||||
// Render a label to indicate the sampling setting
|
||||
create_label(
|
||||
&mut commands,
|
||||
(x, base_y + 110.0, 0.0), // offset to y position of the sprite
|
||||
(x, base_y + 110.0, 0.0), // Offset to y position of the sprite
|
||||
sampling,
|
||||
sampling_label_style.clone(),
|
||||
);
|
||||
|
@ -15,7 +15,10 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
|
||||
let sprite_handle = asset_server.load("branding/icon.png");
|
||||
|
||||
commands.spawn(Sprite::from_image(sprite_handle.clone()));
|
||||
commands.spawn((
|
||||
Sprite::from_image(sprite_handle.clone()),
|
||||
Transform::from_xyz(-100.0, 0.0, 0.0),
|
||||
));
|
||||
commands.spawn((
|
||||
Sprite {
|
||||
image: sprite_handle.clone(),
|
||||
@ -23,7 +26,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
color: Color::srgba(0.0, 0.0, 1.0, 0.7),
|
||||
..default()
|
||||
},
|
||||
Transform::from_xyz(100.0, 0.0, 0.1),
|
||||
Transform::from_xyz(0.0, 0.0, 0.1),
|
||||
));
|
||||
commands.spawn((
|
||||
Sprite {
|
||||
@ -31,6 +34,6 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
color: Color::srgba(0.0, 1.0, 0.0, 0.3),
|
||||
..default()
|
||||
},
|
||||
Transform::from_xyz(200.0, 0.0, 0.2),
|
||||
Transform::from_xyz(100.0, 0.0, 0.2),
|
||||
));
|
||||
}
|
||||
|
@ -84,7 +84,6 @@ fn setup(
|
||||
},
|
||||
));
|
||||
|
||||
// Camera
|
||||
commands.spawn(Camera2d);
|
||||
|
||||
// Text used to show controls
|
||||
|
Loading…
Reference in New Issue
Block a user