Commit: 4f141c0
Parent: b5bce54

Factor out dog systems

Mårten Åsberg committed on 2026-04-14 at 09:56
src/dog.rs +60 -0
diff --git a/src/dog.rs b/src/dog.rs
new file mode 100644
index 0000000..3e0addc
@@ -0,0 +1,60 @@
use bevy::{
app::{App, Startup, Update},
asset::AssetServer,
camera::Camera,
ecs::{
component::Component,
query::With,
system::{Commands, Res, Single},
},
gltf::GltfAssetLabel,
math::{Dir3, Vec3, primitives::InfinitePlane3d},
scene::SceneRoot,
transform::components::{GlobalTransform, Transform},
window::Window,
};
pub trait DogSystems {
fn add_dog_systems(&mut self) -> &mut Self;
}
impl DogSystems for App {
fn add_dog_systems(&mut self) -> &mut Self {
self.add_systems(Startup, setup)
.add_systems(Update, control)
}
}
#[derive(Component, Default)]
struct Dog;
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn((
SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/animal-dog.glb"))),
Transform::from_xyz(0.0, 0.0, 0.0),
Dog::default(),
));
}
fn control(
window: Single<&Window>,
camera: Single<(&Camera, &GlobalTransform)>,
mut dog: Single<&mut Transform, With<Dog>>,
) {
let (camera, camera_transform) = *camera;
let Some(cursor_position) = window
.cursor_position()
.and_then(|cursor| camera.viewport_to_world(camera_transform, cursor).ok())
else {
return;
};
let Some(cursor_position) =
cursor_position.plane_intersection_point(Vec3::ZERO, InfinitePlane3d { normal: Dir3::Y })
else {
return;
};
dog.look_at(-cursor_position, Vec3::Y);
}
src/main.rs +11 -47
diff --git a/src/main.rs b/src/main.rs
index f252166..c49187a 100644
@@ -1,30 +1,26 @@
mod dog;
use bevy::{
DefaultPlugins,
app::{App, Startup, Update},
asset::AssetServer,
camera::{Camera, Camera3d, OrthographicProjection, Projection, ScalingMode},
ecs::{
component::Component,
query::With,
system::{Commands, Res, Single},
},
gltf::GltfAssetLabel,
app::{App, Startup},
camera::{Camera3d, OrthographicProjection, Projection, ScalingMode},
ecs::system::Commands,
light::DirectionalLight,
math::{Dir3, Vec3, primitives::InfinitePlane3d},
scene::SceneRoot,
transform::components::{GlobalTransform, Transform},
window::Window,
math::Vec3,
transform::components::Transform,
};
use crate::dog::DogSystems;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, control_dog)
.add_dog_systems()
.run();
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
fn setup(mut commands: Commands) {
commands.spawn((
Camera3d::default(),
Projection::from(OrthographicProjection {
@@ -37,12 +33,6 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
));
commands.spawn((
SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/animal-dog.glb"))),
Transform::from_xyz(0.0, 0.0, 0.0),
Dog::default(),
));
commands.spawn((
DirectionalLight {
illuminance: 15000.0,
..Default::default()
@@ -50,29 +40,3 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
Transform::from_xyz(0.0, 0.0, 0.0).looking_to(Vec3::NEG_Y, Vec3::Z),
));
}
#[derive(Component, Default)]
struct Dog;
fn control_dog(
window: Single<&Window>,
camera: Single<(&Camera, &GlobalTransform)>,
mut dog: Single<&mut Transform, With<Dog>>,
) {
let (camera, camera_transform) = *camera;
let Some(cursor_position) = window
.cursor_position()
.and_then(|cursor| camera.viewport_to_world(camera_transform, cursor).ok())
else {
return;
};
let Some(cursor_position) =
cursor_position.plane_intersection_point(Vec3::ZERO, InfinitePlane3d { normal: Dir3::Y })
else {
return;
};
dog.look_at(-cursor_position, Vec3::Y);
}