Skip to content

Commit b04dc8e

Browse files
committed
can trigger recording and camera movement from configuration
1 parent 5ff21e6 commit b04dc8e

File tree

3 files changed

+82
-19
lines changed

3 files changed

+82
-19
lines changed

crates/bevy_dev_tools/src/ci_testing/config.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use bevy_ecs::prelude::*;
2+
use bevy_math::{Quat, Vec3};
23
use serde::Deserialize;
34

45
/// A configuration struct for automated CI testing.
56
///
67
/// It gets used when the `bevy_ci_testing` feature is enabled to automatically
78
/// exit a Bevy app when run through the CI. This is needed because otherwise
89
/// Bevy apps would be stuck in the game loop and wouldn't allow the CI to progress.
9-
#[derive(Deserialize, Resource, PartialEq, Debug, Default)]
10+
#[derive(Deserialize, Resource, PartialEq, Debug, Default, Clone)]
1011
pub struct CiTestingConfig {
1112
/// The setup for this test.
1213
#[serde(default)]
@@ -17,7 +18,7 @@ pub struct CiTestingConfig {
1718
}
1819

1920
/// Setup for a test.
20-
#[derive(Deserialize, Default, PartialEq, Debug)]
21+
#[derive(Deserialize, Default, PartialEq, Debug, Clone)]
2122
pub struct CiTestingSetup {
2223
/// The amount of time in seconds between frame updates.
2324
///
@@ -28,11 +29,11 @@ pub struct CiTestingSetup {
2829
}
2930

3031
/// An event to send at a given frame, used for CI testing.
31-
#[derive(Deserialize, PartialEq, Debug)]
32+
#[derive(Deserialize, PartialEq, Debug, Clone)]
3233
pub struct CiTestingEventOnFrame(pub u32, pub CiTestingEvent);
3334

3435
/// An event to send, used for CI testing.
35-
#[derive(Deserialize, PartialEq, Debug)]
36+
#[derive(Deserialize, PartialEq, Debug, Clone)]
3637
pub enum CiTestingEvent {
3738
/// Takes a screenshot of the entire screen, and saves the results to
3839
/// `screenshot-{current_frame}.png`.
@@ -47,6 +48,17 @@ pub enum CiTestingEvent {
4748
///
4849
/// [`AppExit::Success`]: bevy_app::AppExit::Success
4950
AppExit,
51+
/// Starts recording the screen.
52+
StartScreenRecording,
53+
/// Stops recording the screen.
54+
StopScreenRecording,
55+
/// Smoothly moves the camera to the given position.
56+
MoveCamera {
57+
/// Position to move the camera to.
58+
translation: Vec3,
59+
/// Rotation to move the camera to.
60+
rotation: Quat,
61+
},
5062
/// Sends a [`CiTestingCustomEvent`] using the given [`String`].
5163
Custom(String),
5264
}

crates/bevy_dev_tools/src/ci_testing/mod.rs

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
mod config;
44
mod systems;
55

6+
use crate::{EasyCameraMovementPlugin, EasyScreenRecordPlugin};
7+
68
pub use self::config::*;
79

810
use bevy_app::prelude::*;
@@ -26,24 +28,48 @@ pub struct CiTestingPlugin;
2628

2729
impl Plugin for CiTestingPlugin {
2830
fn build(&self, app: &mut App) {
29-
#[cfg(not(target_arch = "wasm32"))]
30-
let config: CiTestingConfig = {
31-
let filename = std::env::var("CI_TESTING_CONFIG")
32-
.unwrap_or_else(|_| "ci_testing_config.ron".to_string());
33-
std::fs::read_to_string(filename)
34-
.map(|content| {
35-
ron::from_str(&content)
36-
.expect("error deserializing CI testing configuration file")
37-
})
38-
.unwrap_or_default()
39-
};
31+
let config = if !app.world().is_resource_added::<CiTestingConfig>() {
32+
// Load configuration from file if not already setup
33+
#[cfg(not(target_arch = "wasm32"))]
34+
let config: CiTestingConfig = {
35+
let filename = std::env::var("CI_TESTING_CONFIG")
36+
.unwrap_or_else(|_| "ci_testing_config.ron".to_string());
37+
std::fs::read_to_string(filename)
38+
.map(|content| {
39+
ron::from_str(&content)
40+
.expect("error deserializing CI testing configuration file")
41+
})
42+
.unwrap_or_default()
43+
};
44+
45+
#[cfg(target_arch = "wasm32")]
46+
let config: CiTestingConfig = {
47+
let config = include_str!("../../../../ci_testing_config.ron");
48+
ron::from_str(config).expect("error deserializing CI testing configuration file")
49+
};
4050

41-
#[cfg(target_arch = "wasm32")]
42-
let config: CiTestingConfig = {
43-
let config = include_str!("../../../../ci_testing_config.ron");
44-
ron::from_str(config).expect("error deserializing CI testing configuration file")
51+
config
52+
} else {
53+
app.world().resource::<CiTestingConfig>().clone()
4554
};
4655

56+
// Add the `EasyCameraMovementPlugin` to the app if it's not already added.
57+
// To configure the movement speed, add the plugin first.
58+
if !app.is_plugin_added::<EasyCameraMovementPlugin>() {
59+
app.add_plugins(EasyCameraMovementPlugin::default());
60+
}
61+
// Add the `EasyScreenRecordPlugin` to the app if it's not already added and one of the event is starting screenrecording.
62+
// To configure the recording quality, add the plugin first.
63+
#[cfg(feature = "screenrecording")]
64+
if !app.is_plugin_added::<EasyScreenRecordPlugin>()
65+
&& config
66+
.events
67+
.iter()
68+
.any(|e| matches!(e.1, CiTestingEvent::StartScreenRecording))
69+
{
70+
app.add_plugins(EasyScreenRecordPlugin::default());
71+
}
72+
4773
// Configure a fixed frame time if specified.
4874
if let Some(fixed_frame_time) = config.setup.fixed_frame_time {
4975
app.insert_resource(TimeUpdateStrategy::ManualDuration(Duration::from_secs_f32(

crates/bevy_dev_tools/src/ci_testing/systems.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
use crate::CameraMovement;
2+
13
use super::config::*;
24
use bevy_app::AppExit;
5+
use bevy_camera::Camera;
36
use bevy_ecs::prelude::*;
47
use bevy_render::view::screenshot::{save_to_disk, Screenshot};
58
use tracing::{debug, info};
@@ -51,6 +54,28 @@ pub(crate) fn send_events(world: &mut World, mut current_frame: Local<u32>) {
5154
*current_frame, name
5255
);
5356
}
57+
CiTestingEvent::StartScreenRecording => {
58+
info!("Started recording screen at frame {}.", *current_frame);
59+
#[cfg(feature = "screenrecording")]
60+
world.write_message(crate::RecordScreen::Start);
61+
}
62+
CiTestingEvent::StopScreenRecording => {
63+
info!("Stopped recording screen at frame {}.", *current_frame);
64+
#[cfg(feature = "screenrecording")]
65+
world.write_message(crate::RecordScreen::Stop);
66+
}
67+
CiTestingEvent::MoveCamera {
68+
translation,
69+
rotation,
70+
} => {
71+
info!("Moved camera at frame {}.", *current_frame);
72+
if let Ok(camera) = world.query_filtered::<Entity, With<Camera>>().single(world) {
73+
world.entity_mut(camera).insert(CameraMovement {
74+
translation,
75+
rotation,
76+
});
77+
}
78+
}
5479
// Custom events are forwarded to the world.
5580
CiTestingEvent::Custom(event_string) => {
5681
world.write_message(CiTestingCustomEvent(event_string));

0 commit comments

Comments
 (0)