Add a Movie Quit On Finish property to AnimationPlayer
This quits the project when an animation is done playing in the given AnimationPlayer, but only in Movie Maker mode. When this happens, a message is printed with the absolute path of the AnimationPlayer node that caused the engine to quit. This can be used to create videos that stop at a specified time without having to write any script. A report is now also printed to the console when the video is done recording (as long as the engine was exited properly). This report is unfortunately not always visible in the editor's Output panel, as it's printed too late. A method was also added to get the path to the output file from the scripting API.
This commit is contained in:
parent
03987738aa
commit
aaeb60eafc
|
@ -246,6 +246,14 @@ void Engine::get_singletons(List<Singleton> *p_singletons) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String Engine::get_write_movie_path() const {
|
||||||
|
return write_movie_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::set_write_movie_path(const String &p_path) {
|
||||||
|
write_movie_path = p_path;
|
||||||
|
}
|
||||||
|
|
||||||
void Engine::set_shader_cache_path(const String &p_path) {
|
void Engine::set_shader_cache_path(const String &p_path) {
|
||||||
shader_cache_path = p_path;
|
shader_cache_path = p_path;
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,7 @@ private:
|
||||||
|
|
||||||
static Engine *singleton;
|
static Engine *singleton;
|
||||||
|
|
||||||
|
String write_movie_path;
|
||||||
String shader_cache_path;
|
String shader_cache_path;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -138,6 +139,9 @@ public:
|
||||||
Dictionary get_license_info() const;
|
Dictionary get_license_info() const;
|
||||||
String get_license_text() const;
|
String get_license_text() const;
|
||||||
|
|
||||||
|
void set_write_movie_path(const String &p_path);
|
||||||
|
String get_write_movie_path() const;
|
||||||
|
|
||||||
void set_shader_cache_path(const String &p_path);
|
void set_shader_cache_path(const String &p_path);
|
||||||
String get_shader_cache_path() const;
|
String get_shader_cache_path() const;
|
||||||
|
|
||||||
|
|
|
@ -2290,6 +2290,10 @@ bool Engine::is_editor_hint() const {
|
||||||
return ::Engine::get_singleton()->is_editor_hint();
|
return ::Engine::get_singleton()->is_editor_hint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String Engine::get_write_movie_path() const {
|
||||||
|
return ::Engine::get_singleton()->get_write_movie_path();
|
||||||
|
}
|
||||||
|
|
||||||
void Engine::set_print_error_messages(bool p_enabled) {
|
void Engine::set_print_error_messages(bool p_enabled) {
|
||||||
::Engine::get_singleton()->set_print_error_messages(p_enabled);
|
::Engine::get_singleton()->set_print_error_messages(p_enabled);
|
||||||
}
|
}
|
||||||
|
@ -2339,6 +2343,8 @@ void Engine::_bind_methods() {
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("is_editor_hint"), &Engine::is_editor_hint);
|
ClassDB::bind_method(D_METHOD("is_editor_hint"), &Engine::is_editor_hint);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("get_write_movie_path"), &Engine::get_write_movie_path);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("set_print_error_messages", "enabled"), &Engine::set_print_error_messages);
|
ClassDB::bind_method(D_METHOD("set_print_error_messages", "enabled"), &Engine::set_print_error_messages);
|
||||||
ClassDB::bind_method(D_METHOD("is_printing_error_messages"), &Engine::is_printing_error_messages);
|
ClassDB::bind_method(D_METHOD("is_printing_error_messages"), &Engine::is_printing_error_messages);
|
||||||
|
|
||||||
|
|
|
@ -676,6 +676,9 @@ public:
|
||||||
void set_editor_hint(bool p_enabled);
|
void set_editor_hint(bool p_enabled);
|
||||||
bool is_editor_hint() const;
|
bool is_editor_hint() const;
|
||||||
|
|
||||||
|
// `set_write_movie_path()` is not exposed to the scripting API as changing it at run-time has no effect.
|
||||||
|
String get_write_movie_path() const;
|
||||||
|
|
||||||
void set_print_error_messages(bool p_enabled);
|
void set_print_error_messages(bool p_enabled);
|
||||||
bool is_printing_error_messages() const;
|
bool is_printing_error_messages() const;
|
||||||
|
|
||||||
|
|
|
@ -220,6 +220,10 @@
|
||||||
<member name="method_call_mode" type="int" setter="set_method_call_mode" getter="get_method_call_mode" enum="AnimationPlayer.AnimationMethodCallMode" default="0">
|
<member name="method_call_mode" type="int" setter="set_method_call_mode" getter="get_method_call_mode" enum="AnimationPlayer.AnimationMethodCallMode" default="0">
|
||||||
The call mode to use for Call Method tracks.
|
The call mode to use for Call Method tracks.
|
||||||
</member>
|
</member>
|
||||||
|
<member name="movie_quit_on_finish" type="bool" setter="set_movie_quit_on_finish_enabled" getter="is_movie_quit_on_finish_enabled" default="false">
|
||||||
|
If [code]true[/code] and the engine is running in Movie Maker mode (see [MovieWriter]), exits the engine with [method SceneTree.quit] as soon as an animation is done playing in this [AnimationPlayer]. A message is printed when the engine quits for this reason.
|
||||||
|
[b]Note:[/b] This obeys the same logic as the [signal animation_finished] signal, so it will not quit the engine if the animation is set to be looping.
|
||||||
|
</member>
|
||||||
<member name="playback_active" type="bool" setter="set_active" getter="is_active">
|
<member name="playback_active" type="bool" setter="set_active" getter="is_active">
|
||||||
If [code]true[/code], updates animations in response to process-related notifications.
|
If [code]true[/code], updates animations in response to process-related notifications.
|
||||||
</member>
|
</member>
|
||||||
|
@ -253,6 +257,7 @@
|
||||||
<argument index="0" name="anim_name" type="StringName" />
|
<argument index="0" name="anim_name" type="StringName" />
|
||||||
<description>
|
<description>
|
||||||
Notifies when an animation finished playing.
|
Notifies when an animation finished playing.
|
||||||
|
[b]Note:[/b] This signal is not emitted if an animation is looping.
|
||||||
</description>
|
</description>
|
||||||
</signal>
|
</signal>
|
||||||
<signal name="animation_started">
|
<signal name="animation_started">
|
||||||
|
|
|
@ -151,6 +151,12 @@
|
||||||
[/codeblocks]
|
[/codeblocks]
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="get_write_movie_path" qualifiers="const">
|
||||||
|
<return type="String" />
|
||||||
|
<description>
|
||||||
|
Returns the path to the [MovieWriter]'s output file, or an empty string if the engine wasn't started in Movie Maker mode. This path can be absolute or relative depending on how the user specified it.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="has_singleton" qualifiers="const">
|
<method name="has_singleton" qualifiers="const">
|
||||||
<return type="bool" />
|
<return type="bool" />
|
||||||
<argument index="0" name="name" type="StringName" />
|
<argument index="0" name="name" type="StringName" />
|
||||||
|
|
|
@ -181,7 +181,6 @@ static bool debug_navigation = false;
|
||||||
static int frame_delay = 0;
|
static int frame_delay = 0;
|
||||||
static bool disable_render_loop = false;
|
static bool disable_render_loop = false;
|
||||||
static int fixed_fps = -1;
|
static int fixed_fps = -1;
|
||||||
static String write_movie_path;
|
|
||||||
static MovieWriter *movie_writer = nullptr;
|
static MovieWriter *movie_writer = nullptr;
|
||||||
static bool disable_vsync = false;
|
static bool disable_vsync = false;
|
||||||
static bool print_fps = false;
|
static bool print_fps = false;
|
||||||
|
@ -1162,7 +1161,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
||||||
}
|
}
|
||||||
} else if (I->get() == "--write-movie") {
|
} else if (I->get() == "--write-movie") {
|
||||||
if (I->next()) {
|
if (I->next()) {
|
||||||
write_movie_path = I->next()->get();
|
Engine::get_singleton()->set_write_movie_path(I->next()->get());
|
||||||
N = I->next()->next();
|
N = I->next()->next();
|
||||||
if (fixed_fps == -1) {
|
if (fixed_fps == -1) {
|
||||||
fixed_fps = 60;
|
fixed_fps = 60;
|
||||||
|
@ -1512,7 +1511,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
||||||
audio_driver_idx = 0;
|
audio_driver_idx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (write_movie_path != String()) {
|
if (Engine::get_singleton()->get_write_movie_path() != String()) {
|
||||||
// Always use dummy driver for audio driver (which is last), also in no threaded mode.
|
// Always use dummy driver for audio driver (which is last), also in no threaded mode.
|
||||||
audio_driver_idx = AudioDriverManager::get_driver_count() - 1;
|
audio_driver_idx = AudioDriverManager::get_driver_count() - 1;
|
||||||
AudioDriverDummy::get_dummy_singleton()->set_use_threads(false);
|
AudioDriverDummy::get_dummy_singleton()->set_use_threads(false);
|
||||||
|
@ -1609,7 +1608,7 @@ error:
|
||||||
display_driver = "";
|
display_driver = "";
|
||||||
audio_driver = "";
|
audio_driver = "";
|
||||||
tablet_driver = "";
|
tablet_driver = "";
|
||||||
write_movie_path = "";
|
Engine::get_singleton()->set_write_movie_path(String());
|
||||||
project_path = "";
|
project_path = "";
|
||||||
|
|
||||||
args.clear();
|
args.clear();
|
||||||
|
@ -1784,11 +1783,11 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
|
||||||
rendering_server->set_print_gpu_profile(true);
|
rendering_server->set_print_gpu_profile(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (write_movie_path != String()) {
|
if (Engine::get_singleton()->get_write_movie_path() != String()) {
|
||||||
movie_writer = MovieWriter::find_writer_for_file(write_movie_path);
|
movie_writer = MovieWriter::find_writer_for_file(Engine::get_singleton()->get_write_movie_path());
|
||||||
if (movie_writer == nullptr) {
|
if (movie_writer == nullptr) {
|
||||||
ERR_PRINT("Can't find movie writer for file type, aborting: " + write_movie_path);
|
ERR_PRINT("Can't find movie writer for file type, aborting: " + Engine::get_singleton()->get_write_movie_path());
|
||||||
write_movie_path = String();
|
Engine::get_singleton()->set_write_movie_path(String());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2724,7 +2723,7 @@ bool Main::start() {
|
||||||
OS::get_singleton()->set_main_loop(main_loop);
|
OS::get_singleton()->set_main_loop(main_loop);
|
||||||
|
|
||||||
if (movie_writer) {
|
if (movie_writer) {
|
||||||
movie_writer->begin(DisplayServer::get_singleton()->window_get_size(), fixed_fps, write_movie_path);
|
movie_writer->begin(DisplayServer::get_singleton()->window_get_size(), fixed_fps, Engine::get_singleton()->get_write_movie_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (minimum_time_msec) {
|
if (minimum_time_msec) {
|
||||||
|
|
|
@ -1201,11 +1201,15 @@ void AnimationPlayer::_animation_process(double p_delta) {
|
||||||
emit_signal(SceneStringNames::get_singleton()->animation_changed, old, new_name);
|
emit_signal(SceneStringNames::get_singleton()->animation_changed, old, new_name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//stop();
|
|
||||||
playing = false;
|
playing = false;
|
||||||
_set_process(false);
|
_set_process(false);
|
||||||
if (end_notify) {
|
if (end_notify) {
|
||||||
emit_signal(SceneStringNames::get_singleton()->animation_finished, playback.assigned);
|
emit_signal(SceneStringNames::get_singleton()->animation_finished, playback.assigned);
|
||||||
|
|
||||||
|
if (movie_quit_on_finish && OS::get_singleton()->has_feature("movie")) {
|
||||||
|
print_line(vformat("Movie Maker mode is enabled. Quitting on animation finish as requested by: %s", get_path()));
|
||||||
|
get_tree()->quit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end_reached = false;
|
end_reached = false;
|
||||||
|
@ -1892,6 +1896,14 @@ AnimationPlayer::AnimationMethodCallMode AnimationPlayer::get_method_call_mode()
|
||||||
return method_call_mode;
|
return method_call_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AnimationPlayer::set_movie_quit_on_finish_enabled(bool p_enabled) {
|
||||||
|
movie_quit_on_finish = p_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AnimationPlayer::is_movie_quit_on_finish_enabled() const {
|
||||||
|
return movie_quit_on_finish;
|
||||||
|
}
|
||||||
|
|
||||||
void AnimationPlayer::_set_process(bool p_process, bool p_force) {
|
void AnimationPlayer::_set_process(bool p_process, bool p_force) {
|
||||||
if (processing == p_process && !p_force) {
|
if (processing == p_process && !p_force) {
|
||||||
return;
|
return;
|
||||||
|
@ -2112,6 +2124,9 @@ void AnimationPlayer::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("set_method_call_mode", "mode"), &AnimationPlayer::set_method_call_mode);
|
ClassDB::bind_method(D_METHOD("set_method_call_mode", "mode"), &AnimationPlayer::set_method_call_mode);
|
||||||
ClassDB::bind_method(D_METHOD("get_method_call_mode"), &AnimationPlayer::get_method_call_mode);
|
ClassDB::bind_method(D_METHOD("get_method_call_mode"), &AnimationPlayer::get_method_call_mode);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("set_movie_quit_on_finish_enabled"), &AnimationPlayer::set_movie_quit_on_finish_enabled);
|
||||||
|
ClassDB::bind_method(D_METHOD("is_movie_quit_on_finish_enabled"), &AnimationPlayer::is_movie_quit_on_finish_enabled);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("get_current_animation_position"), &AnimationPlayer::get_current_animation_position);
|
ClassDB::bind_method(D_METHOD("get_current_animation_position"), &AnimationPlayer::get_current_animation_position);
|
||||||
ClassDB::bind_method(D_METHOD("get_current_animation_length"), &AnimationPlayer::get_current_animation_length);
|
ClassDB::bind_method(D_METHOD("get_current_animation_length"), &AnimationPlayer::get_current_animation_length);
|
||||||
|
|
||||||
|
@ -2133,6 +2148,8 @@ void AnimationPlayer::_bind_methods() {
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_speed", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale");
|
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "playback_speed", PROPERTY_HINT_RANGE, "-64,64,0.01"), "set_speed_scale", "get_speed_scale");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "method_call_mode", PROPERTY_HINT_ENUM, "Deferred,Immediate"), "set_method_call_mode", "get_method_call_mode");
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "method_call_mode", PROPERTY_HINT_ENUM, "Deferred,Immediate"), "set_method_call_mode", "get_method_call_mode");
|
||||||
|
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "movie_quit_on_finish"), "set_movie_quit_on_finish_enabled", "is_movie_quit_on_finish_enabled");
|
||||||
|
|
||||||
ADD_SIGNAL(MethodInfo("animation_finished", PropertyInfo(Variant::STRING_NAME, "anim_name")));
|
ADD_SIGNAL(MethodInfo("animation_finished", PropertyInfo(Variant::STRING_NAME, "anim_name")));
|
||||||
ADD_SIGNAL(MethodInfo("animation_changed", PropertyInfo(Variant::STRING_NAME, "old_name"), PropertyInfo(Variant::STRING_NAME, "new_name")));
|
ADD_SIGNAL(MethodInfo("animation_changed", PropertyInfo(Variant::STRING_NAME, "old_name"), PropertyInfo(Variant::STRING_NAME, "new_name")));
|
||||||
ADD_SIGNAL(MethodInfo("animation_started", PropertyInfo(Variant::STRING_NAME, "anim_name")));
|
ADD_SIGNAL(MethodInfo("animation_started", PropertyInfo(Variant::STRING_NAME, "anim_name")));
|
||||||
|
|
|
@ -262,6 +262,7 @@ private:
|
||||||
bool reset_on_save = true;
|
bool reset_on_save = true;
|
||||||
AnimationProcessCallback process_callback = ANIMATION_PROCESS_IDLE;
|
AnimationProcessCallback process_callback = ANIMATION_PROCESS_IDLE;
|
||||||
AnimationMethodCallMode method_call_mode = ANIMATION_METHOD_CALL_DEFERRED;
|
AnimationMethodCallMode method_call_mode = ANIMATION_METHOD_CALL_DEFERRED;
|
||||||
|
bool movie_quit_on_finish = false;
|
||||||
bool processing = false;
|
bool processing = false;
|
||||||
bool active = true;
|
bool active = true;
|
||||||
|
|
||||||
|
@ -373,6 +374,9 @@ public:
|
||||||
void set_method_call_mode(AnimationMethodCallMode p_mode);
|
void set_method_call_mode(AnimationMethodCallMode p_mode);
|
||||||
AnimationMethodCallMode get_method_call_mode() const;
|
AnimationMethodCallMode get_method_call_mode() const;
|
||||||
|
|
||||||
|
void set_movie_quit_on_finish_enabled(bool p_enabled);
|
||||||
|
bool is_movie_quit_on_finish_enabled() const;
|
||||||
|
|
||||||
void seek(double p_time, bool p_update = false);
|
void seek(double p_time, bool p_update = false);
|
||||||
void seek_delta(double p_time, float p_delta);
|
void seek_delta(double p_time, float p_delta);
|
||||||
float get_current_animation_position() const;
|
float get_current_animation_position() const;
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "movie_writer.h"
|
#include "movie_writer.h"
|
||||||
#include "core/config/project_settings.h"
|
#include "core/config/project_settings.h"
|
||||||
#include "core/io/dir_access.h"
|
#include "core/io/dir_access.h"
|
||||||
|
#include "core/os/time.h"
|
||||||
#include "servers/display_server.h"
|
#include "servers/display_server.h"
|
||||||
|
|
||||||
MovieWriter *MovieWriter::writers[MovieWriter::MAX_WRITERS];
|
MovieWriter *MovieWriter::writers[MovieWriter::MAX_WRITERS];
|
||||||
|
@ -183,4 +184,29 @@ void MovieWriter::add_frame(const Ref<Image> &p_image) {
|
||||||
|
|
||||||
void MovieWriter::end() {
|
void MovieWriter::end() {
|
||||||
write_end();
|
write_end();
|
||||||
|
|
||||||
|
// Print a report with various statistics.
|
||||||
|
print_line("----------------");
|
||||||
|
String movie_path = Engine::get_singleton()->get_write_movie_path();
|
||||||
|
if (movie_path.is_relative_path()) {
|
||||||
|
// Print absolute path to make finding the file easier,
|
||||||
|
// and to make it clickable in terminal emulators that support this.
|
||||||
|
movie_path = ProjectSettings::get_singleton()->globalize_path("res://").plus_file(movie_path);
|
||||||
|
}
|
||||||
|
print_line(vformat("Done recording movie at path: %s", movie_path));
|
||||||
|
|
||||||
|
const int movie_time_seconds = Engine::get_singleton()->get_frames_drawn() / fps;
|
||||||
|
const String movie_time = vformat("%s:%s:%s",
|
||||||
|
String::num(movie_time_seconds / 3600).pad_zeros(2),
|
||||||
|
String::num((movie_time_seconds % 3600) / 60).pad_zeros(2),
|
||||||
|
String::num(movie_time_seconds % 60).pad_zeros(2));
|
||||||
|
|
||||||
|
const int real_time_seconds = Time::get_singleton()->get_ticks_msec() / 1000;
|
||||||
|
const String real_time = vformat("%s:%s:%s",
|
||||||
|
String::num(real_time_seconds / 3600).pad_zeros(2),
|
||||||
|
String::num((real_time_seconds % 3600) / 60).pad_zeros(2),
|
||||||
|
String::num(real_time_seconds % 60).pad_zeros(2));
|
||||||
|
|
||||||
|
print_line(vformat("%d frames at %d FPS (movie length: %s), recorded in %s (%d%% of real-time speed).", Engine::get_singleton()->get_frames_drawn(), fps, movie_time, real_time, (float(movie_time_seconds) / real_time_seconds) * 100));
|
||||||
|
print_line("----------------");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue