Skip to content

Commit

Permalink
Merge pull request #65312 from SaracenOne/auto_advance_behaviour
Browse files Browse the repository at this point in the history
Make auto-advance flag a requirement for conditional/expression evaluation
  • Loading branch information
akien-mga committed Dec 23, 2022
2 parents cc5ba0a + 092dbe5 commit 9b4888b
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 76 deletions.
18 changes: 12 additions & 6 deletions doc/classes/AnimationNodeStateMachineTransition.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,11 @@
<member name="advance_expression" type="String" setter="set_advance_expression" getter="get_advance_expression" default="&quot;&quot;">
Use an expression as a condition for state machine transitions. It is possible to create complex animation advance conditions for switching between states and gives much greater flexibility for creating complex state machines by directly interfacing with the script code.
</member>
<member name="auto_advance" type="bool" setter="set_auto_advance" getter="has_auto_advance" default="false">
Turn on the transition automatically when this state is reached. This works best with [constant SWITCH_MODE_AT_END].
</member>
<member name="disabled" type="bool" setter="set_disabled" getter="is_disabled" default="false">
Don't use this transition during [method AnimationNodeStateMachinePlayback.travel] or [member auto_advance].
<member name="advance_mode" type="int" setter="set_advance_mode" getter="get_advance_mode" enum="AnimationNodeStateMachineTransition.AdvanceMode" default="1">
Determines whether the transition should disabled, enabled when using [method AnimationNodeStateMachinePlayback.travel], or traversed automatically if the [member advance_condition] and [member advance_expression] checks are true (if assigned).
</member>
<member name="priority" type="int" setter="set_priority" getter="get_priority" default="1">
Lower priority transitions are preferred when travelling through the tree via [method AnimationNodeStateMachinePlayback.travel] or [member auto_advance].
Lower priority transitions are preferred when travelling through the tree via [method AnimationNodeStateMachinePlayback.travel] or [member advance_mode] is set to [constant ADVANCE_MODE_AUTO].
</member>
<member name="switch_mode" type="int" setter="set_switch_mode" getter="get_switch_mode" enum="AnimationNodeStateMachineTransition.SwitchMode" default="0">
The transition type.
Expand Down Expand Up @@ -58,5 +55,14 @@
<constant name="SWITCH_MODE_AT_END" value="2" enum="SwitchMode">
Wait for the current state playback to end, then switch to the beginning of the next state animation.
</constant>
<constant name="ADVANCE_MODE_DISABLED" value="0" enum="AdvanceMode">
Don't use this transition.
</constant>
<constant name="ADVANCE_MODE_ENABLED" value="1" enum="AdvanceMode">
Only use this transition during [method AnimationNodeStateMachinePlayback.travel].
</constant>
<constant name="ADVANCE_MODE_AUTO" value="2" enum="AdvanceMode">
Automatically use this transition if the [member advance_condition] and [member advance_expression] checks are true (if assigned).
</constant>
</constants>
</class>
66 changes: 44 additions & 22 deletions editor/plugins/animation_state_machine_editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1097,7 +1097,8 @@ void AnimationNodeStateMachineEditor::_add_transition(const bool p_nested_action

Ref<AnimationNodeStateMachineTransition> tr;
tr.instantiate();
tr->set_switch_mode(AnimationNodeStateMachineTransition::SwitchMode(transition_mode->get_selected()));
tr->set_advance_mode(auto_advance->is_pressed() ? AnimationNodeStateMachineTransition::AdvanceMode::ADVANCE_MODE_AUTO : AnimationNodeStateMachineTransition::AdvanceMode::ADVANCE_MODE_ENABLED);
tr->set_switch_mode(AnimationNodeStateMachineTransition::SwitchMode(switch_mode->get_selected()));

Ref<EditorUndoRedoManager> &undo_redo = EditorNode::get_undo_redo();
if (!p_nested_action) {
Expand Down Expand Up @@ -1326,7 +1327,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
}
}

_connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(transition_mode->get_selected()), true, false, false, false, false);
_connection_draw(from, to, AnimationNodeStateMachineTransition::SwitchMode(switch_mode->get_selected()), true, false, false, false, false);
}

Ref<Texture2D> tr_reference_icon = get_theme_icon(SNAME("TransitionImmediateBig"), SNAME("EditorIcons"));
Expand All @@ -1349,8 +1350,8 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
tl.to = (state_machine->get_node_position(local_to) * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE;

Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i);
tl.disabled = tr->is_disabled();
tl.auto_advance = tr->has_auto_advance();
tl.disabled = bool(tr->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED);
tl.auto_advance = bool(tr->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_AUTO);
tl.advance_condition_name = tr->get_advance_condition_name();
tl.advance_condition_state = false;
tl.mode = tr->get_switch_mode();
Expand Down Expand Up @@ -1590,10 +1591,12 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) {
tool_create->set_icon(get_theme_icon(SNAME("ToolAddNode"), SNAME("EditorIcons")));
tool_connect->set_icon(get_theme_icon(SNAME("ToolConnect"), SNAME("EditorIcons")));

transition_mode->clear();
transition_mode->add_icon_item(get_theme_icon(SNAME("TransitionImmediate"), SNAME("EditorIcons")), TTR("Immediate"));
transition_mode->add_icon_item(get_theme_icon(SNAME("TransitionSync"), SNAME("EditorIcons")), TTR("Sync"));
transition_mode->add_icon_item(get_theme_icon(SNAME("TransitionEnd"), SNAME("EditorIcons")), TTR("At End"));
switch_mode->clear();
switch_mode->add_icon_item(get_theme_icon(SNAME("TransitionImmediate"), SNAME("EditorIcons")), TTR("Immediate"));
switch_mode->add_icon_item(get_theme_icon(SNAME("TransitionSync"), SNAME("EditorIcons")), TTR("Sync"));
switch_mode->add_icon_item(get_theme_icon(SNAME("TransitionEnd"), SNAME("EditorIcons")), TTR("At End"));

auto_advance->set_icon(get_theme_icon(SNAME("AutoPlay"), SNAME("EditorIcons")));

tool_erase->set_icon(get_theme_icon(SNAME("Remove"), SNAME("EditorIcons")));
tool_group->set_icon(get_theme_icon(SNAME("Group"), SNAME("EditorIcons")));
Expand Down Expand Up @@ -1652,12 +1655,12 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) {
break;
}

if (transition_lines[i].disabled != state_machine->get_transition(tidx)->is_disabled()) {
if (transition_lines[i].disabled != bool(state_machine->get_transition(tidx)->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED)) {
state_machine_draw->queue_redraw();
break;
}

if (transition_lines[i].auto_advance != state_machine->get_transition(tidx)->has_auto_advance()) {
if (transition_lines[i].auto_advance != bool(state_machine->get_transition(tidx)->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_AUTO)) {
state_machine_draw->queue_redraw();
break;
}
Expand Down Expand Up @@ -1904,7 +1907,7 @@ void AnimationNodeStateMachineEditor::_erase_selected(const bool p_nested_action

void AnimationNodeStateMachineEditor::_update_mode() {
if (tool_select->is_pressed()) {
tool_erase_hb->show();
selection_tools_hb->show();
bool nothing_selected = selected_nodes.is_empty() && selected_transition_from == StringName() && selected_transition_to == StringName();
bool start_end_selected = selected_nodes.size() == 1 && (*selected_nodes.begin() == state_machine->start_node || *selected_nodes.begin() == state_machine->end_node);
tool_erase->set_disabled(nothing_selected || start_end_selected || read_only);
Expand All @@ -1927,7 +1930,13 @@ void AnimationNodeStateMachineEditor::_update_mode() {
}
}
} else {
tool_erase_hb->hide();
selection_tools_hb->hide();
}

if (tool_connect->is_pressed()) {
transition_tools_hb->show();
} else {
transition_tools_hb->hide();
}
}

Expand Down Expand Up @@ -1978,35 +1987,48 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() {
tool_connect->set_tooltip_text(TTR("Connect nodes."));
tool_connect->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_update_mode), CONNECT_DEFERRED);

tool_erase_hb = memnew(HBoxContainer);
top_hb->add_child(tool_erase_hb);
tool_erase_hb->add_child(memnew(VSeparator));
// Context-sensitive selection tools:
selection_tools_hb = memnew(HBoxContainer);
top_hb->add_child(selection_tools_hb);
selection_tools_hb->add_child(memnew(VSeparator));

tool_group = memnew(Button);
tool_group->set_flat(true);
tool_group->set_tooltip_text(TTR("Group Selected Node(s)") + " (Ctrl+G)");
tool_group->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_group_selected_nodes));
tool_group->set_disabled(true);
tool_erase_hb->add_child(tool_group);
selection_tools_hb->add_child(tool_group);

tool_ungroup = memnew(Button);
tool_ungroup->set_flat(true);
tool_ungroup->set_tooltip_text(TTR("Ungroup Selected Node") + " (Ctrl+Shift+G)");
tool_ungroup->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_ungroup_selected_nodes));
tool_ungroup->set_visible(false);
tool_erase_hb->add_child(tool_ungroup);
selection_tools_hb->add_child(tool_ungroup);

tool_erase = memnew(Button);
tool_erase->set_flat(true);
tool_erase->set_tooltip_text(TTR("Remove selected node or transition."));
tool_erase->connect("pressed", callable_mp(this, &AnimationNodeStateMachineEditor::_erase_selected).bind(false));
tool_erase->set_disabled(true);
tool_erase_hb->add_child(tool_erase);
selection_tools_hb->add_child(tool_erase);

transition_tools_hb = memnew(HBoxContainer);
top_hb->add_child(transition_tools_hb);
transition_tools_hb->add_child(memnew(VSeparator));

transition_tools_hb->add_child(memnew(Label(TTR("Transition:"))));
switch_mode = memnew(OptionButton);
transition_tools_hb->add_child(switch_mode);

auto_advance = memnew(Button);
auto_advance->set_flat(true);
auto_advance->set_tooltip_text(TTR("New Transitions Should Auto Advance"));
auto_advance->set_toggle_mode(true);
auto_advance->set_pressed(true);
transition_tools_hb->add_child(auto_advance);

top_hb->add_child(memnew(VSeparator));
top_hb->add_child(memnew(Label(TTR("Transition:"))));
transition_mode = memnew(OptionButton);
top_hb->add_child(transition_mode);
//

top_hb->add_spacer();

Expand Down
11 changes: 7 additions & 4 deletions editor/plugins/animation_state_machine_editor.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,18 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin {
Button *tool_select = nullptr;
Button *tool_create = nullptr;
Button *tool_connect = nullptr;
Button *tool_group = nullptr;
Button *tool_ungroup = nullptr;
Popup *name_edit_popup = nullptr;
LineEdit *name_edit = nullptr;

HBoxContainer *tool_erase_hb = nullptr;
HBoxContainer *selection_tools_hb = nullptr;
Button *tool_group = nullptr;
Button *tool_ungroup = nullptr;
Button *tool_erase = nullptr;

OptionButton *transition_mode = nullptr;
HBoxContainer *transition_tools_hb = nullptr;
OptionButton *switch_mode = nullptr;
Button *auto_advance = nullptr;

OptionButton *play_mode = nullptr;

PanelContainer *panel = nullptr;
Expand Down
62 changes: 25 additions & 37 deletions scene/animation/animation_node_state_machine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ AnimationNodeStateMachineTransition::SwitchMode AnimationNodeStateMachineTransit
return switch_mode;
}

void AnimationNodeStateMachineTransition::set_auto_advance(bool p_enable) {
auto_advance = p_enable;
void AnimationNodeStateMachineTransition::set_advance_mode(AdvanceMode p_mode) {
advance_mode = p_mode;
}

bool AnimationNodeStateMachineTransition::has_auto_advance() const {
return auto_advance;
AnimationNodeStateMachineTransition::AdvanceMode AnimationNodeStateMachineTransition::get_advance_mode() const {
return advance_mode;
}

void AnimationNodeStateMachineTransition::set_advance_condition(const StringName &p_condition) {
Expand Down Expand Up @@ -107,15 +107,6 @@ Ref<Curve> AnimationNodeStateMachineTransition::get_xfade_curve() const {
return xfade_curve;
}

void AnimationNodeStateMachineTransition::set_disabled(bool p_disabled) {
disabled = p_disabled;
emit_changed();
}

bool AnimationNodeStateMachineTransition::is_disabled() const {
return disabled;
}

void AnimationNodeStateMachineTransition::set_priority(int p_priority) {
priority = p_priority;
emit_changed();
Expand All @@ -129,8 +120,8 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_switch_mode", "mode"), &AnimationNodeStateMachineTransition::set_switch_mode);
ClassDB::bind_method(D_METHOD("get_switch_mode"), &AnimationNodeStateMachineTransition::get_switch_mode);

ClassDB::bind_method(D_METHOD("set_auto_advance", "auto_advance"), &AnimationNodeStateMachineTransition::set_auto_advance);
ClassDB::bind_method(D_METHOD("has_auto_advance"), &AnimationNodeStateMachineTransition::has_auto_advance);
ClassDB::bind_method(D_METHOD("set_advance_mode", "mode"), &AnimationNodeStateMachineTransition::set_advance_mode);
ClassDB::bind_method(D_METHOD("get_advance_mode"), &AnimationNodeStateMachineTransition::get_advance_mode);

ClassDB::bind_method(D_METHOD("set_advance_condition", "name"), &AnimationNodeStateMachineTransition::set_advance_condition);
ClassDB::bind_method(D_METHOD("get_advance_condition"), &AnimationNodeStateMachineTransition::get_advance_condition);
Expand All @@ -141,9 +132,6 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_xfade_curve", "curve"), &AnimationNodeStateMachineTransition::set_xfade_curve);
ClassDB::bind_method(D_METHOD("get_xfade_curve"), &AnimationNodeStateMachineTransition::get_xfade_curve);

ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &AnimationNodeStateMachineTransition::set_disabled);
ClassDB::bind_method(D_METHOD("is_disabled"), &AnimationNodeStateMachineTransition::is_disabled);

ClassDB::bind_method(D_METHOD("set_priority", "priority"), &AnimationNodeStateMachineTransition::set_priority);
ClassDB::bind_method(D_METHOD("get_priority"), &AnimationNodeStateMachineTransition::get_priority);

Expand All @@ -155,17 +143,19 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority");
ADD_GROUP("Switch", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,At End"), "set_switch_mode", "get_switch_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_advance"), "set_auto_advance", "has_auto_advance");
ADD_GROUP("Advance", "advance_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "advance_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Auto"), "set_advance_mode", "get_advance_mode");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "advance_condition"), "set_advance_condition", "get_advance_condition");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "advance_expression", PROPERTY_HINT_EXPRESSION, ""), "set_advance_expression", "get_advance_expression");
ADD_GROUP("Disabling", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");

BIND_ENUM_CONSTANT(SWITCH_MODE_IMMEDIATE);
BIND_ENUM_CONSTANT(SWITCH_MODE_SYNC);
BIND_ENUM_CONSTANT(SWITCH_MODE_AT_END);

BIND_ENUM_CONSTANT(ADVANCE_MODE_DISABLED);
BIND_ENUM_CONSTANT(ADVANCE_MODE_ENABLED);
BIND_ENUM_CONSTANT(ADVANCE_MODE_AUTO);

ADD_SIGNAL(MethodInfo("advance_condition_changed"));
}

Expand Down Expand Up @@ -234,7 +224,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta

//build open list
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
if (p_state_machine->transitions[i].transition->is_disabled()) {
if (p_state_machine->transitions[i].transition->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED) {
continue;
}

Expand Down Expand Up @@ -279,7 +269,7 @@ bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *p_sta
StringName transition = p_state_machine->transitions[least_cost_transition->get()].local_to;

for (int i = 0; i < p_state_machine->transitions.size(); i++) {
if (p_state_machine->transitions[i].transition->is_disabled()) {
if (p_state_machine->transitions[i].transition->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED) {
continue;
}

Expand Down Expand Up @@ -458,7 +448,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s

if (path.size()) {
for (int i = 0; i < p_state_machine->transitions.size(); i++) {
if (p_state_machine->transitions[i].transition->is_disabled()) {
if (p_state_machine->transitions[i].transition->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED) {
continue;
}

Expand All @@ -474,7 +464,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
int auto_advance_to = -1;

for (int i = 0; i < p_state_machine->transitions.size(); i++) {
if (p_state_machine->transitions[i].transition->is_disabled()) {
if (p_state_machine->transitions[i].transition->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED) {
continue;
}

Expand Down Expand Up @@ -542,7 +532,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
int auto_advance_to = -1;

for (int i = 0; i < prev_state_machine->transitions.size(); i++) {
if (prev_state_machine->transitions[i].transition->is_disabled()) {
if (prev_state_machine->transitions[i].transition->get_advance_mode() == AnimationNodeStateMachineTransition::ADVANCE_MODE_DISABLED) {
continue;
}

Expand Down Expand Up @@ -629,14 +619,14 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}

bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref<AnimationNodeStateMachine> state_machine, const Ref<AnimationNodeStateMachineTransition> transition) const {
if (transition->has_auto_advance()) {
return true;
if (transition->get_advance_mode() != AnimationNodeStateMachineTransition::ADVANCE_MODE_AUTO) {
return false;
}

StringName advance_condition_name = transition->get_advance_condition_name();

if (advance_condition_name != StringName() && bool(state_machine->get_parameter(advance_condition_name))) {
return true;
if (advance_condition_name != StringName() && !bool(state_machine->get_parameter(advance_condition_name))) {
return false;
}

if (transition->expression.is_valid()) {
Expand All @@ -646,20 +636,18 @@ bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref<Anima
NodePath advance_expression_base_node_path = tree_base->get_advance_expression_base_node();
Node *expression_base = tree_base->get_node_or_null(advance_expression_base_node_path);

WARN_PRINT_ONCE("Animation transition has a valid expression, but no expression base node was set on its AnimationTree.");

if (expression_base) {
Ref<Expression> exp = transition->expression;
bool ret = exp->execute(Array(), expression_base, false, Engine::get_singleton()->is_editor_hint()); // Avoids allowing the user to crash the system with an expression by only allowing const calls.
if (!exp->has_execute_failed()) {
if (ret) {
return true;
}
if (exp->has_execute_failed() || !ret) {
return false;
}
} else {
WARN_PRINT_ONCE("Animation transition has a valid expression, but no expression base node was set on its AnimationTree.");
}
}

return false;
return true;
}

void AnimationNodeStateMachinePlayback::_bind_methods() {
Expand Down
Loading

0 comments on commit 9b4888b

Please sign in to comment.