Skip to content

Commit 9754672

Browse files
committed
Add game speed controls to the embedded game window
1 parent 4d1f26e commit 9754672

File tree

10 files changed

+129
-10
lines changed

10 files changed

+129
-10
lines changed

core/config/engine.cpp

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,24 +38,40 @@
3838
#include "core/version.h"
3939
#include "servers/rendering/rendering_device.h"
4040

41+
void Engine::_update_time_scale() {
42+
_time_scale = _user_time_scale * _game_time_scale;
43+
user_ips = MAX(1, ips * _user_time_scale);
44+
max_user_physics_steps_per_frame = MAX(max_physics_steps_per_frame, max_physics_steps_per_frame * _user_time_scale);
45+
}
46+
4147
void Engine::set_physics_ticks_per_second(int p_ips) {
4248
ERR_FAIL_COND_MSG(p_ips <= 0, "Engine iterations per second must be greater than 0.");
4349
ips = p_ips;
50+
_update_time_scale();
4451
}
4552

4653
int Engine::get_physics_ticks_per_second() const {
4754
return ips;
4855
}
4956

57+
int Engine::get_user_physics_ticks_per_second() const {
58+
return user_ips;
59+
}
60+
5061
void Engine::set_max_physics_steps_per_frame(int p_max_physics_steps) {
5162
ERR_FAIL_COND_MSG(p_max_physics_steps <= 0, "Maximum number of physics steps per frame must be greater than 0.");
5263
max_physics_steps_per_frame = p_max_physics_steps;
64+
_update_time_scale();
5365
}
5466

5567
int Engine::get_max_physics_steps_per_frame() const {
5668
return max_physics_steps_per_frame;
5769
}
5870

71+
int Engine::get_user_max_physics_steps_per_frame() const {
72+
return max_user_physics_steps_per_frame;
73+
}
74+
5975
void Engine::set_physics_jitter_fix(double p_threshold) {
6076
if (p_threshold < 0) {
6177
p_threshold = 0;
@@ -112,10 +128,20 @@ uint32_t Engine::get_frame_delay() const {
112128
}
113129

114130
void Engine::set_time_scale(double p_scale) {
115-
_time_scale = p_scale;
131+
_game_time_scale = p_scale;
132+
_update_time_scale();
116133
}
117134

118135
double Engine::get_time_scale() const {
136+
return freeze_time_scale ? 0 : _game_time_scale;
137+
}
138+
139+
void Engine::set_user_time_scale(double p_scale) {
140+
_user_time_scale = p_scale;
141+
_update_time_scale();
142+
}
143+
144+
double Engine::get_effective_time_scale() {
119145
return freeze_time_scale ? 0 : _time_scale;
120146
}
121147

core/config/engine.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,17 @@ class Engine {
5959
double _process_step = 0;
6060

6161
int ips = 60;
62+
int user_ips = 60;
6263
double physics_jitter_fix = 0.5;
6364
double _fps = 1;
6465
int _max_fps = 0;
6566
int _audio_output_latency = 0;
6667
double _time_scale = 1.0;
68+
double _game_time_scale = 1.0;
69+
double _user_time_scale = 1.0;
6770
uint64_t _physics_frames = 0;
6871
int max_physics_steps_per_frame = 8;
72+
int max_user_physics_steps_per_frame = 8;
6973
double _physics_interpolation_fraction = 0.0f;
7074
bool abort_on_gpu_errors = false;
7175
bool use_validation_layers = false;
@@ -101,14 +105,19 @@ class Engine {
101105

102106
bool freeze_time_scale = false;
103107

108+
protected:
109+
void _update_time_scale();
110+
104111
public:
105112
static Engine *get_singleton();
106113

107114
virtual void set_physics_ticks_per_second(int p_ips);
108115
virtual int get_physics_ticks_per_second() const;
116+
virtual int get_user_physics_ticks_per_second() const;
109117

110118
virtual void set_max_physics_steps_per_frame(int p_max_physics_steps);
111119
virtual int get_max_physics_steps_per_frame() const;
120+
virtual int get_user_max_physics_steps_per_frame() const;
112121

113122
void set_physics_jitter_fix(double p_threshold);
114123
double get_physics_jitter_fix() const;
@@ -132,6 +141,8 @@ class Engine {
132141

133142
void set_time_scale(double p_scale);
134143
double get_time_scale() const;
144+
void set_user_time_scale(double p_scale);
145+
double get_effective_time_scale();
135146
double get_unfrozen_time_scale() const;
136147

137148
void set_print_to_stdout(bool p_enabled);

editor/run/game_view_plugin.cpp

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,8 @@ void GameView::_update_debugger_buttons() {
499499

500500
suspend_button->set_disabled(empty);
501501
camera_override_button->set_disabled(empty);
502+
speed_state_button->set_disabled(empty);
503+
reset_speed_button->set_disabled(empty);
502504

503505
PopupMenu *menu = camera_override_menu->get_popup();
504506

@@ -511,6 +513,8 @@ void GameView::_update_debugger_buttons() {
511513
camera_override_button->set_pressed(false);
512514
}
513515
next_frame_button->set_disabled(!suspend_button->is_pressed());
516+
517+
_reset_time_scales();
514518
}
515519

516520
void GameView::_handle_shortcut_requested(int p_embed_action) {
@@ -591,6 +595,43 @@ void GameView::_size_mode_button_pressed(int size_mode) {
591595
_update_embed_window_size();
592596
}
593597

598+
void GameView::_reset_time_scales() {
599+
time_scale_index = DEFAULT_TIME_SCALE_INDEX;
600+
_update_time_scales();
601+
}
602+
603+
void GameView::_update_time_scales() {
604+
Array message;
605+
message.append(time_scale_range[time_scale_index]);
606+
Array sessions = debugger->get_sessions();
607+
for (int i = 0; i < sessions.size(); i++) {
608+
if (Object::cast_to<EditorDebuggerSession>(sessions[i])->is_active()) {
609+
Ref<EditorDebuggerSession> session = sessions[i];
610+
session->send_message("scene:speed_changed", message);
611+
}
612+
}
613+
Color text_color;
614+
if (time_scale_index == DEFAULT_TIME_SCALE_INDEX) {
615+
text_color = Color(1.0f, 1.0f, 1.0f);
616+
} else if (time_scale_index > DEFAULT_TIME_SCALE_INDEX) {
617+
text_color = Color(0.0f, 1.0f, 0.0f);
618+
} else if (time_scale_index < DEFAULT_TIME_SCALE_INDEX) {
619+
text_color = Color(1.0f, 1.0f, 0.0f);
620+
}
621+
bool disabled = time_scale_index == DEFAULT_TIME_SCALE_INDEX;
622+
reset_speed_button->set_disabled(disabled);
623+
speed_state_button->add_theme_color_override("font_color", text_color);
624+
speed_state_button->set_text(vformat("%0.2fx", time_scale_range[time_scale_index]));
625+
}
626+
627+
void GameView::_speed_state_menu_pressed(int p_id) {
628+
if (time_scale_index == p_id) {
629+
return;
630+
}
631+
time_scale_index = p_id;
632+
_update_time_scales();
633+
}
634+
594635
GameView::EmbedAvailability GameView::_get_embed_available() {
595636
if (!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_WINDOW_EMBEDDING)) {
596637
return EMBED_NOT_AVAILABLE_FEATURE_NOT_SUPPORTED;
@@ -780,6 +821,7 @@ void GameView::_notification(int p_what) {
780821
case NOTIFICATION_THEME_CHANGED: {
781822
suspend_button->set_button_icon(get_editor_theme_icon(SNAME("Pause")));
782823
next_frame_button->set_button_icon(get_editor_theme_icon(SNAME("NextFrame")));
824+
reset_speed_button->set_button_icon(get_editor_theme_icon(SNAME("Reload")));
783825

784826
node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_button_icon(get_editor_theme_icon(SNAME("InputEventJoypadMotion")));
785827
node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_button_icon(get_editor_theme_icon(SNAME("2DNodes")));
@@ -1074,6 +1116,27 @@ GameView::GameView(Ref<GameViewDebugger> p_debugger, EmbeddedProcessBase *p_embe
10741116
next_frame_button->set_accessibility_name(TTRC("Next Frame"));
10751117
next_frame_button->set_shortcut(ED_GET_SHORTCUT("editor/next_frame_embedded_project"));
10761118

1119+
speed_state_button = memnew(MenuButton);
1120+
main_menu_hbox->add_child(speed_state_button);
1121+
speed_state_button->set_text(TTRC("1.00x"));
1122+
speed_state_button->set_theme_type_variation("FlatButton");
1123+
speed_state_button->set_tooltip_text(TTRC("Change the game speed."));
1124+
speed_state_button->set_accessibility_name(TTRC("Speed State"));
1125+
speed_state_button->set_custom_minimum_size(Vector2(48, 0));
1126+
1127+
PopupMenu *menu = speed_state_button->get_popup();
1128+
menu->connect(SceneStringName(id_pressed), callable_mp(this, &GameView::_speed_state_menu_pressed));
1129+
for (float f : time_scale_range) {
1130+
menu->add_item(TTRC(vformat("%0.2fx", f)));
1131+
}
1132+
1133+
reset_speed_button = memnew(Button);
1134+
main_menu_hbox->add_child(reset_speed_button);
1135+
reset_speed_button->set_theme_type_variation("FlatButton");
1136+
reset_speed_button->set_tooltip_text(TTRC("Reset the game speed."));
1137+
reset_speed_button->set_accessibility_name(TTRC("Reset Speed"));
1138+
reset_speed_button->connect(SceneStringName(pressed), callable_mp(this, &GameView::_reset_time_scales));
1139+
10771140
main_menu_hbox->add_child(memnew(VSeparator));
10781141

10791142
node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE] = memnew(Button);
@@ -1155,7 +1218,7 @@ GameView::GameView(Ref<GameViewDebugger> p_debugger, EmbeddedProcessBase *p_embe
11551218
camera_override_menu->set_h_size_flags(SIZE_SHRINK_END);
11561219
camera_override_menu->set_tooltip_text(TTRC("Camera Override Options"));
11571220

1158-
PopupMenu *menu = camera_override_menu->get_popup();
1221+
menu = camera_override_menu->get_popup();
11591222
menu->connect(SceneStringName(id_pressed), callable_mp(this, &GameView::_camera_override_menu_id_pressed));
11601223
menu->add_item(TTRC("Reset 2D Camera"), CAMERA_RESET_2D);
11611224
menu->add_item(TTRC("Reset 3D Camera"), CAMERA_RESET_3D);

editor/run/game_view_plugin.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,13 @@ class GameView : public VBoxContainer {
172172
EmbeddedProcessBase *embedded_process = nullptr;
173173
Label *state_label = nullptr;
174174

175+
int const DEFAULT_TIME_SCALE_INDEX = 5;
176+
Array time_scale_range = { 0.0625f, 0.125f, 0.25f, 0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 4.0f, 8.0f, 16.0f };
177+
int time_scale_index = DEFAULT_TIME_SCALE_INDEX;
178+
179+
MenuButton *speed_state_button = nullptr;
180+
Button *reset_speed_button = nullptr;
181+
175182
void _sessions_changed();
176183

177184
void _update_debugger_buttons();
@@ -185,6 +192,10 @@ class GameView : public VBoxContainer {
185192
void _embed_options_menu_menu_id_pressed(int p_id);
186193
void _size_mode_button_pressed(int size_mode);
187194

195+
void _reset_time_scales();
196+
void _update_time_scales();
197+
void _speed_state_menu_pressed(int p_id);
198+
188199
void _play_pressed();
189200
static void _instance_starting_static(int p_idx, List<String> &r_arguments);
190201
void _instance_starting(int p_idx, List<String> &r_arguments);

main/main.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4653,10 +4653,10 @@ bool Main::iteration() {
46534653

46544654
const uint64_t ticks_elapsed = ticks - last_ticks;
46554655

4656-
const int physics_ticks_per_second = Engine::get_singleton()->get_physics_ticks_per_second();
4656+
const int physics_ticks_per_second = Engine::get_singleton()->get_user_physics_ticks_per_second();
46574657
const double physics_step = 1.0 / physics_ticks_per_second;
46584658

4659-
const double time_scale = Engine::get_singleton()->get_time_scale();
4659+
const double time_scale = Engine::get_singleton()->get_effective_time_scale();
46604660

46614661
MainFrameTime advance = main_timer_sync.advance(physics_step, physics_ticks_per_second);
46624662
double process_step = advance.process_step;
@@ -4675,7 +4675,7 @@ bool Main::iteration() {
46754675

46764676
last_ticks = ticks;
46774677

4678-
const int max_physics_steps = Engine::get_singleton()->get_max_physics_steps_per_frame();
4678+
const int max_physics_steps = Engine::get_singleton()->get_user_max_physics_steps_per_frame();
46794679
if (fixed_fps == -1 && advance.physics_steps > max_physics_steps) {
46804680
process_step -= (advance.physics_steps - max_physics_steps) * physics_step;
46814681
advance.physics_steps = max_physics_steps;

modules/jolt_physics/joints/jolt_hinge_joint_3d.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ constexpr double HINGE_DEFAULT_RELAXATION = 1.0;
4949
double estimate_physics_step() {
5050
Engine *engine = Engine::get_singleton();
5151

52-
const double step = 1.0 / engine->get_physics_ticks_per_second();
53-
const double step_scaled = step * engine->get_time_scale();
52+
const double step = 1.0 / engine->get_user_physics_ticks_per_second();
53+
const double step_scaled = step * engine->get_effective_time_scale();
5454

5555
return step_scaled;
5656
}

scene/3d/velocity_tracker_3d.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Vector3 VelocityTracker3D::get_tracked_linear_velocity() const {
7171
if (position_history_len) {
7272
if (physics_step) {
7373
uint64_t base = Engine::get_singleton()->get_physics_frames();
74-
base_time = double(base - position_history[0].frame) / Engine::get_singleton()->get_physics_ticks_per_second();
74+
base_time = double(base - position_history[0].frame) / Engine::get_singleton()->get_user_physics_ticks_per_second();
7575
} else {
7676
uint64_t base = Engine::get_singleton()->get_frame_ticks();
7777
base_time = double(base - position_history[0].frame) / 1000000.0;
@@ -84,7 +84,7 @@ Vector3 VelocityTracker3D::get_tracked_linear_velocity() const {
8484
Vector3 distance = position_history[i].position - position_history[i + 1].position;
8585

8686
if (physics_step) {
87-
delta = double(diff) / Engine::get_singleton()->get_physics_ticks_per_second();
87+
delta = double(diff) / Engine::get_singleton()->get_user_physics_ticks_per_second();
8888
} else {
8989
delta = double(diff) / 1000000.0;
9090
}

scene/debugger/scene_debugger.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,12 @@ Error SceneDebugger::_msg_next_frame(const Array &p_args) {
185185
return OK;
186186
}
187187

188+
Error SceneDebugger::_msg_speed_changed(const Array &p_args) {
189+
double time_scale_user = p_args[0];
190+
Engine::get_singleton()->set_user_time_scale(time_scale_user);
191+
return OK;
192+
}
193+
188194
Error SceneDebugger::_msg_debug_mute_audio(const Array &p_args) {
189195
ERR_FAIL_COND_V(p_args.is_empty(), ERR_INVALID_DATA);
190196
bool do_mute = p_args[0];
@@ -499,6 +505,7 @@ void SceneDebugger::_init_message_handlers() {
499505
message_handlers["clear_selection"] = _msg_clear_selection;
500506
message_handlers["suspend_changed"] = _msg_suspend_changed;
501507
message_handlers["next_frame"] = _msg_next_frame;
508+
message_handlers["speed_changed"] = _msg_speed_changed;
502509
message_handlers["debug_mute_audio"] = _msg_debug_mute_audio;
503510
message_handlers["override_cameras"] = _msg_override_cameras;
504511
message_handlers["transform_camera_2d"] = _msg_transform_camera_2d;

scene/debugger/scene_debugger.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class SceneDebugger {
8686
static Error _msg_clear_selection(const Array &p_args);
8787
static Error _msg_suspend_changed(const Array &p_args);
8888
static Error _msg_next_frame(const Array &p_args);
89+
static Error _msg_speed_changed(const Array &p_args);
8990
static Error _msg_debug_mute_audio(const Array &p_args);
9091
static Error _msg_override_cameras(const Array &p_args);
9192
static Error _msg_transform_camera_2d(const Array &p_args);

scene/gui/video_stream_player.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ void VideoStreamPlayer::_notification(int p_notification) {
157157
double delta = first_frame ? 0 : get_process_delta_time();
158158
first_frame = false;
159159

160-
resampler.set_playback_speed(Engine::get_singleton()->get_time_scale() * speed_scale);
160+
resampler.set_playback_speed(Engine::get_singleton()->get_effective_time_scale() * speed_scale);
161161

162162
playback->update(delta * speed_scale); // playback->is_playing() returns false in the last video frame
163163

0 commit comments

Comments
 (0)