Skip to content

Commit

Permalink
Gizmo Arrows (bevyengine#10550)
Browse files Browse the repository at this point in the history
## Objective

- Add an arrow gizmo as suggested by bevyengine#9400 

## Solution

(excuse my Protomen music)


https://github.com/bevyengine/bevy/assets/14184826/192adf24-079f-4a4b-a17b-091e892974ec

Wasn't horribly hard when i remembered i can change coordinate systems
whenever I want. Gave them four tips (as suggested by @alice-i-cecile in
discord) instead of trying to decide what direction the tips should
point.

Made the tip length default to 1/10 of the arrow's length, which looked
good enough to me. Hard-coded the angle from the body to the tips to 45
degrees.

## Still TODO

- [x] actual doc comments
- [x] doctests
- [x] `ArrowBuilder.with_tip_length()`

---

## Changelog

- Added `gizmos.arrow()` and `gizmos.arrow_2d()`
- Added arrows to `2d_gizmos` and `3d_gizmos` examples

## Migration Guide

N/A

---------

Co-authored-by: Nicola Papale <nicopap@users.noreply.github.com>
  • Loading branch information
2 people authored and Ray Redondo committed Jan 9, 2024
1 parent 0689f4c commit 183e39c
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 0 deletions.
106 changes: 106 additions & 0 deletions crates/bevy_gizmos/src/arrows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//! Additional Gizmo Functions -- Arrows

use crate::prelude::Gizmos;
use bevy_math::{Quat, Vec2, Vec3};
use bevy_render::color::Color;

pub struct ArrowBuilder<'a, 's> {
gizmos: &'a mut Gizmos<'s>,
start: Vec3,
end: Vec3,
color: Color,
tip_length: f32,
}

/// A builder returned by [`Gizmos::arrow`] and [`Gizmos::arrow_2d`]
impl ArrowBuilder<'_, '_> {
/// Change the length of the tips to be `length`.
/// The default tip length is [length of the arrow]/10.
///
/// # Example
/// ```
/// # use bevy_gizmos::prelude::*;
/// # use bevy_render::prelude::*;
/// # use bevy_math::prelude::*;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.arrow(Vec3::ZERO, Vec3::ONE, Color::GREEN)
/// .with_tip_length(3.);
/// }
/// # bevy_ecs::system::assert_is_system(system);
/// ```
#[doc(alias = "arrow_head_length")]
pub fn with_tip_length(&mut self, length: f32) {
self.tip_length = length;
}
}

impl Drop for ArrowBuilder<'_, '_> {
/// Draws the arrow, by drawing lines with the stored [`Gizmos`]
fn drop(&mut self) {
// first, draw the body of the arrow
self.gizmos.line(self.start, self.end, self.color);
// now the hard part is to draw the head in a sensible way
// put us in a coordinate system where the arrow is pointing towards +x and ends at the origin
let pointing = (self.end - self.start).normalize();
let rotation = Quat::from_rotation_arc(Vec3::X, pointing);
let tips = [
Vec3::new(-1., 1., 0.),
Vec3::new(-1., 0., 1.),
Vec3::new(-1., -1., 0.),
Vec3::new(-1., 0., -1.),
];
// - extend the vectors so their length is `tip_length`
// - rotate the world so +x is facing in the same direction as the arrow
// - translate over to the tip of the arrow
let tips = tips.map(|v| rotation * (v.normalize() * self.tip_length) + self.end);
for v in tips {
// then actually draw the tips
self.gizmos.line(self.end, v, self.color);
}
}
}

impl<'s> Gizmos<'s> {
/// Draw an arrow in 3D, from `start` to `end`. Has four tips for convienent viewing from any direction.
///
/// This should be called for each frame the arrow needs to be rendered.
///
/// # Example
/// ```
/// # use bevy_gizmos::prelude::*;
/// # use bevy_render::prelude::*;
/// # use bevy_math::prelude::*;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.arrow(Vec3::ZERO, Vec3::ONE, Color::GREEN);
/// }
/// # bevy_ecs::system::assert_is_system(system);
/// ```
pub fn arrow(&mut self, start: Vec3, end: Vec3, color: Color) -> ArrowBuilder<'_, 's> {
let length = (end - start).length();
ArrowBuilder {
gizmos: self,
start,
end,
color,
tip_length: length / 10.,
}
}

/// Draw an arrow in 2D (on the xy plane), from `start` to `end`.
///
/// This should be called for each frame the arrow needs to be rendered.
///
/// # Example
/// ```
/// # use bevy_gizmos::prelude::*;
/// # use bevy_render::prelude::*;
/// # use bevy_math::prelude::*;
/// fn system(mut gizmos: Gizmos) {
/// gizmos.arrow_2d(Vec2::ZERO, Vec2::X, Color::GREEN);
/// }
/// # bevy_ecs::system::assert_is_system(system);
/// ```
pub fn arrow_2d(&mut self, start: Vec2, end: Vec2, color: Color) -> ArrowBuilder<'_, 's> {
self.arrow(start.extend(0.), end.extend(0.), color)
}
}
1 change: 1 addition & 0 deletions crates/bevy_gizmos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
//!
//! See the documentation on [`Gizmos`] for more examples.

mod arrows;
pub mod gizmos;

#[cfg(feature = "bevy_sprite")]
Expand Down
6 changes: 6 additions & 0 deletions examples/2d/2d_gizmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ fn system(mut gizmos: Gizmos, time: Res<Time>) {
// Arcs default amount of segments is linearly interpolated between
// 1 and 32, using the arc length as scalar.
gizmos.arc_2d(Vec2::ZERO, sin / 10., PI / 2., 350., Color::ORANGE_RED);

gizmos.arrow_2d(
Vec2::ZERO,
Vec2::from_angle(sin / -10. + PI / 2.) * 50.,
Color::YELLOW,
);
}

fn update_config(mut config: ResMut<GizmoConfig>, keyboard: Res<Input<KeyCode>>, time: Res<Time>) {
Expand Down
2 changes: 2 additions & 0 deletions examples/3d/3d_gizmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ fn system(mut gizmos: Gizmos, time: Res<Time>) {
gizmos
.sphere(Vec3::ZERO, Quat::IDENTITY, 3.2, Color::BLACK)
.circle_segments(64);

gizmos.arrow(Vec3::ZERO, Vec3::ONE * 1.5, Color::YELLOW);
}

fn rotate_camera(mut query: Query<&mut Transform, With<Camera>>, time: Res<Time>) {
Expand Down

0 comments on commit 183e39c

Please sign in to comment.