-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor of camera control/movement and tower shooting #2
Comments
I figured it out in the end by using #[derive(Debug, Clone)]
struct CameraMovement {
forward: Vec3,
left: Vec3,
speed: f32,
rotate_speed: f32
}
impl CameraMovement {
fn new(camera: Transform) -> CameraMovement {
CameraMovement {
forward: CameraMovement::create_forward(camera),
left: CameraMovement::create_left(camera),
speed: 4.0,
rotate_speed: 0.4
}
}
fn create_left(camera: Transform) -> Vec3 {
let mut left = camera.left();
left.y = 0.0;
left = left.normalize();
left
}
fn create_forward(camera: Transform) -> Vec3 {
let mut forward = camera.forward();
forward.y = 0.0;
forward = forward.normalize();
forward
}
}
fn camera_controls(
keyboard: Res<Input<KeyCode>>,
mut camera_query: Query<&mut Transform, With<Camera3d>>,
time: Res<Time>,
) {
let mut camera = camera_query.single_mut();
let movement = CameraMovement::new(camera.clone());
camera_keyboard_control(&mut camera, movement, keyboard, time);
}
#[derive(Debug, Clone)]
struct Movement {
movement: CameraMovement,
time: Time
}
impl Movement {
fn new(movement: CameraMovement, time: Time) -> Self {
Movement {
movement,
time
}
}
fn delta(&self) -> f32 {
self.time.delta_seconds()
}
fn delta_speed(&self) -> f32 {
self.delta() * self.movement.speed
}
fn forward(&self) -> Vec3 {
self.movement.forward * self.delta_speed()
}
fn left(&self) -> Vec3 {
self.movement.left * self.delta_speed()
}
fn angle_rotate(&self) -> f32 {
self.movement.rotate_speed * self.delta()
}
}
fn camera_keyboard_control(camera: &mut Mut<Transform>, movement: CameraMovement, keyboard: Res<Input<KeyCode>>, time: Res<Time>) {
let mov: Movement = Movement::new(movement.clone(), time.clone());
if keyboard.pressed(KeyCode::W) {
camera.translation += mov.forward();
}
if keyboard.pressed(KeyCode::S) {
camera.translation -= mov.forward();
}
if keyboard.pressed(KeyCode::A) {
camera.translation += mov.left();
}
if keyboard.pressed(KeyCode::D) {
camera.translation -= mov.left();
}
if keyboard.pressed(KeyCode::Q) {
camera.rotate_axis(Vec3::Y, mov.angle_rotate())
}
if keyboard.pressed(KeyCode::E) {
camera.rotate_axis(Vec3::Y, -mov.angle_rotate())
}
} |
I figured it out in the end... #[derive(Debug, Clone)]
struct Movement<'a, 'b> {
movement: &'a CameraMovement,
time: &'b Time
}
impl<'a, 'b> Movement<'a, 'b> {
fn new(movement: &'a CameraMovement, time: &'b Time) -> Self {
Movement {
movement,
time
}
}
fn delta(&self) -> f32 {
self.time.delta_seconds()
}
fn delta_speed(&self) -> f32 {
self.delta() * self.movement.speed
}
fn forward(&self) -> Vec3 {
self.movement.forward * self.delta_speed()
}
fn left(&self) -> Vec3 {
self.movement.left * self.delta_speed()
}
fn angle_rotate(&self) -> f32 {
self.movement.rotate_speed * self.delta()
}
}
fn camera_keyboard_control(camera: &mut Mut<Transform>, movement: CameraMovement, keyboard: Res<Input<KeyCode>>, time: Res<Time>) {
// let m2 = M2::new(&movement, &time);
let mov: Movement = Movement::new(&movement, &time);
if keyboard.pressed(KeyCode::W) {
camera.translation += mov.forward();
}
if keyboard.pressed(KeyCode::S) {
camera.translation -= mov.forward();
}
if keyboard.pressed(KeyCode::A) {
camera.translation += mov.left();
}
if keyboard.pressed(KeyCode::D) {
camera.translation -= mov.left();
}
if keyboard.pressed(KeyCode::Q) {
camera.rotate_axis(Vec3::Y, mov.angle_rotate())
}
if keyboard.pressed(KeyCode::E) {
camera.rotate_axis(Vec3::Y, -mov.angle_rotate())
}
} Helped to watch a few tutorials on it like this one: https://www.youtube.com/watch?v=rAl-9HwD858 Still not sure about how to properly use |
I almost solved it and reduced the complexity: impl Plugin for TowerPlugin {
fn build<'a>(&self, app: &mut App) {
app.register_type::<Tower>()
.add_system(tower_shooting)
.add_system(tower_button_clicked)
.add_system(create_ui_on_selection);
}
fn name(&self) -> &str {
std::any::type_name::<Self>()
}
}
#[derive(Copy, Clone)]
struct TowerCtx<'a> {
tower_ent: Entity,
tower: &'a Tower,
tower_type: &'a TowerType,
transform: &'a GlobalTransform
}
impl<'a> TowerCtx<'a> {
fn new(tower_ent: Entity,
tower: &'a Tower,
tower_type: &'a TowerType,
transform: &'a GlobalTransform) -> Self {
TowerCtx {
tower_ent,
tower,
tower_type,
transform
}
}
}
struct TowerShooter<'a> {
commands: &'a mut Commands<'a, 'a>,
time: &'a Time,
ctx: Option<TowerCtx<'a>>
}
impl<'a> TowerShooter<'a> {
fn new(commands: &'a mut Commands<'a, 'a>, time: &'a Time, ) -> Self {
TowerShooter {
time,
commands,
ctx: None
}
}
fn setCtx(&mut self, ctx: &'a TowerCtx<'a>) {
self.ctx = Some(*ctx)
}
fn shoot_from(&self, tower: &Tower, commands: &mut Commands, direction: Option<Vec3>, bullet_assets: &GameAssets) {
if let Some(direction) = direction {
let ctx = self.ctx.unwrap();
let tower_type = ctx.tower_type;
let tower_ent = ctx.tower_ent;
let (model, bullet) = tower_type.get_bullet(direction, &bullet_assets);
commands.entity(tower_ent).with_children(|commands| {
commands
.spawn_bundle(SceneBundle {
scene: model,
transform: Transform::from_translation(tower.bullet_offset),
..Default::default()
})
.insert(Lifetime {
timer: Timer::from_seconds(10.0, false),
})
.insert(bullet)
.insert(Name::new("Bullet"));
});
}
else { return };
}
}
fn get_bullet_spawn(transform: &GlobalTransform, tower: &Tower) -> Vec3 {
transform.translation() + tower.bullet_offset
}
fn get_direction(targets: &Query<&GlobalTransform, With<Target>>, bullet_spawn: Vec3) -> Option<Vec3> {
targets
.iter()
.min_by_key(|target_transform| {
FloatOrd(Vec3::distance(target_transform.translation(), bullet_spawn))
})
.map(|closest_target| closest_target.translation() - bullet_spawn)
}
fn tower_shooting<'a>(
mut commands: Commands<'a, 'a>,
mut towers: Query<(Entity, &mut Tower, &TowerType, &GlobalTransform)>,
targets: Query<&GlobalTransform, With<Target>>,
bullet_assets: Res<GameAssets>,
time: Res<Time>,
) {
let tower_shooter = TowerShooter::new(&mut commands, &time);
for (tower_ent, tower, tower_type, transform) in &mut towers {
let ctx = TowerCtx::new(tower_ent, &tower, tower_type, transform);
let bullet_spawn = get_bullet_spawn(transform, &tower);
let direction = get_direction(&targets, bullet_spawn);
tower_shooter.setCtx(&ctx);
tower.as_ref().shooting_timer.tick(time.delta());
if tower.shooting_timer.just_finished() {
tower_shooter.shoot_from(&tower, &mut commands, direction, &bullet_assets)
}
}
} Is this the right approach or is there a simpler way? It only produces this single error which I don't understand:
|
I can remove the lifetime params for Commands<...> to simplify it to this, but then it complains that it expects struct TowerShooter<'a> {
commands: &'a mut Commands,
time: &'a Time,
ctx: Option<TowerCtx<'a>>
}
impl<'a> TowerShooter<'a> {
fn new(commands: &'a mut Commands, time: &'a Time, ) -> Self { fn tower_shooting(
mut commands: Commands,
mut towers: Query<(Entity, &mut Tower, &TowerType, &GlobalTransform)>,
targets: Query<&GlobalTransform, With<Target>>,
bullet_assets: Res<GameAssets>,
time: Res<Time>,
) { Error
pub struct Commands<'w, 's> {
queue: &'s mut CommandQueue,
entities: &'w Entities,
}
impl<'w, 's> Commands<'w, 's> { I've been through various lifetime tutorials but still doesn't quite make sense to me |
I finally managed it :) struct TowerShooter<'a> {
entity: Entity,
tower_type: &'a TowerType,
transform: &'a GlobalTransform
}
impl<'a> TowerShooter<'a> {
fn new(entity: Entity, tower_type: &'a TowerType, transform: &'a GlobalTransform) -> Self {
TowerShooter {
entity,
tower_type,
transform
}
}
fn get_bullet_spawn(&self, tower: &Tower) -> Vec3 {
self.transform.translation() + tower.bullet_offset
}
fn get_direction(&self, tower: &Tower, targets: &Query<&GlobalTransform, With<Target>>) -> Option<Vec3> {
let bullet_spawn: Vec3 = self.get_bullet_spawn(tower);
targets
.iter()
.min_by_key(|target_transform| {
FloatOrd(Vec3::distance(target_transform.translation(), bullet_spawn))
})
.map(|closest_target| closest_target.translation() - bullet_spawn)
}
fn shoot_from(&self, commands: &mut Commands, tower: &Tower, targets: &Query<&GlobalTransform, With<Target>>, bullet_assets: &GameAssets) {
if let Some(direction) = self.get_direction(tower, targets) {
self.shoot_direction(commands, tower, direction, bullet_assets)
}
else { return };
}
fn shoot_direction(&self, commands: &mut Commands, tower: &Tower, direction: Vec3, bullet_assets: &GameAssets) {
let (model, bullet) = self.tower_type.get_bullet(direction, &bullet_assets);
self.spawn_bullet(commands, tower, model, bullet)
}
fn spawn_bullet(&self, commands: &mut Commands, tower: &Tower, model: Handle<Scene>, bullet: Bullet) {
commands.entity(self.entity).with_children(|commands| {
commands
.spawn_bundle(SceneBundle {
scene: model,
transform: Transform::from_translation(tower.bullet_offset),
..Default::default()
})
.insert(Lifetime {
timer: Timer::from_seconds(10.0, false),
})
.insert(bullet)
.insert(Name::new("Bullet"));
});
}
}
fn tower_shooting(
mut commands: Commands,
mut towers: Query<(Entity, &mut Tower, &TowerType, &GlobalTransform)>,
targets: Query<&GlobalTransform, With<Target>>,
bullet_assets: Res<GameAssets>,
time: Res<Time>,
) {
for (entity, mut tower, tower_type, transform) in &mut towers {
let tower_shooter = TowerShooter::new( entity, &tower_type, &transform);
tower.shooting_timer.tick(time.delta());
if tower.shooting_timer.just_finished() {
tower_shooter.shoot_from( &mut commands, &tower, &targets, &bullet_assets)
}
}
} |
My only frustration is that I was not able to set mutable Commands or Tower on the TowerShooter instance, so I have to pass these around to each method instead. Would love to know a better approach. I thought I could still find a way to have multiple references to commands, but discovered it was I believe the correct approach you hinted at would be to create a new |
Resolved via discord discussions |
Hi,
Thank you so much for your youtube tutorial series and code. I've been trying to work on the survival game and refactor it to be encapsulated in a
CameraMovement
struct and acamera_keyboard_control
function for the keyboard control.I initially got some lifetime errors, but now it compiles just fine. However the functionality is broken, pressing keyboard controls doesn't move the camera. I can see it printing each time I press
w
andcamera.translation
andtranslation
change each time...I'm pretty sure it is due to not using
Mut<Transform>
inlet mut camera: Transform = *movement.camera;
but I couldn't make it compile otherwise.I also tried to use type aliases for
Keyboard
andCamera
types but ran into similar lifetime issues. Struggling a bit with how to use the BevyMut
type correctly.The text was updated successfully, but these errors were encountered: