Merge pull request #61196 from V-Sekai/animtree-advance-expressions

This commit is contained in:
Rémi Verschelde 2022-06-28 21:40:29 +02:00 committed by GitHub
commit fed5ebb24b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 109 additions and 3 deletions

View File

@ -19,6 +19,12 @@
[/csharp]
[/codeblocks]
</member>
<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="advance_expression_base_node" type="NodePath" setter="set_advance_expression_base_node" getter="get_advance_expression_base_node" default="NodePath(&quot;&quot;)">
The path to the [Node] used to evaluate an [Expression] if one is not explictly specified internally.
</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>

View File

@ -37,6 +37,9 @@
<member name="active" type="bool" setter="set_active" getter="is_active" default="false">
If [code]true[/code], the [AnimationTree] will be processing.
</member>
<member name="advance_expression_base_node" type="NodePath" setter="set_advance_expression_base_node" getter="get_advance_expression_base_node" default="NodePath(&quot;.&quot;)">
The path to the [Node] used to evaluate the AnimationNode [Expression] if one is not explictly specified internally.
</member>
<member name="anim_player" type="NodePath" setter="set_animation_player" getter="get_animation_player" default="NodePath(&quot;&quot;)">
The path to the [AnimationPlayer] used for animating.
</member>

View File

@ -68,6 +68,34 @@ StringName AnimationNodeStateMachineTransition::get_advance_condition_name() con
return advance_condition_name;
}
void AnimationNodeStateMachineTransition::set_advance_expression(const String &p_expression) {
advance_expression = p_expression;
String advance_expression_stripped = advance_expression.strip_edges();
if (advance_expression_stripped == String()) {
expression.unref();
return;
}
if (expression.is_null()) {
expression.instantiate();
}
expression->parse(advance_expression_stripped);
}
String AnimationNodeStateMachineTransition::get_advance_expression() const {
return advance_expression;
}
void AnimationNodeStateMachineTransition::set_advance_expression_base_node(const NodePath &p_expression_base_node) {
advance_expression_base_node = p_expression_base_node;
}
NodePath AnimationNodeStateMachineTransition::get_advance_expression_base_node() const {
return advance_expression_base_node;
}
void AnimationNodeStateMachineTransition::set_xfade_time(float p_xfade) {
ERR_FAIL_COND(p_xfade < 0);
xfade = p_xfade;
@ -115,11 +143,22 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_priority", "priority"), &AnimationNodeStateMachineTransition::set_priority);
ClassDB::bind_method(D_METHOD("get_priority"), &AnimationNodeStateMachineTransition::get_priority);
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_PROPERTY(PropertyInfo(Variant::STRING_NAME, "advance_condition"), "set_advance_condition", "get_advance_condition");
ClassDB::bind_method(D_METHOD("set_advance_expression", "text"), &AnimationNodeStateMachineTransition::set_advance_expression);
ClassDB::bind_method(D_METHOD("get_advance_expression"), &AnimationNodeStateMachineTransition::get_advance_expression);
ClassDB::bind_method(D_METHOD("set_advance_expression_base_node", "path"), &AnimationNodeStateMachineTransition::set_advance_expression_base_node);
ClassDB::bind_method(D_METHOD("get_advance_expression_base_node"), &AnimationNodeStateMachineTransition::get_advance_expression_base_node);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01,suffix:s"), "set_xfade_time", "get_xfade_time");
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::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_PROPERTY(PropertyInfo(Variant::NODE_PATH, "advance_expression_base_node"), "set_advance_expression_base_node", "get_advance_expression_base_node");
ADD_GROUP("Disabling", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
BIND_ENUM_CONSTANT(SWITCH_MODE_IMMEDIATE);
@ -577,6 +616,29 @@ bool AnimationNodeStateMachinePlayback::_check_advance_condition(const Ref<Anima
return true;
}
if (transition->expression.is_valid()) {
AnimationTree *tree_base = state_machine->get_animation_tree();
ERR_FAIL_COND_V(tree_base == nullptr, false);
NodePath advance_expression_base_node_path;
if (!transition->advance_expression_base_node.is_empty()) {
advance_expression_base_node_path = transition->advance_expression_base_node;
} else {
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);
if (expression_base) {
Ref<Expression> exp = transition->expression;
bool ret = exp->execute(Array(), tree_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;
}
}
}
}
return false;
}

View File

@ -31,6 +31,7 @@
#ifndef ANIMATION_NODE_STATE_MACHINE_H
#define ANIMATION_NODE_STATE_MACHINE_H
#include "core/math/expression.h"
#include "scene/animation/animation_tree.h"
class AnimationNodeStateMachineTransition : public Resource {
@ -51,6 +52,11 @@ private:
float xfade = 0.0;
bool disabled = false;
int priority = 1;
String advance_expression;
NodePath advance_expression_base_node;
friend class AnimationNodeStateMachinePlayback;
Ref<Expression> expression;
protected:
static void _bind_methods();
@ -67,6 +73,12 @@ public:
StringName get_advance_condition_name() const;
void set_advance_expression(const String &p_expression);
String get_advance_expression() const;
void set_advance_expression_base_node(const NodePath &p_expression_base_node);
NodePath get_advance_expression_base_node() const;
void set_xfade_time(float p_xfade);
float get_xfade_time() const;

View File

@ -136,6 +136,11 @@ double AnimationNode::_pre_process(const StringName &p_base_path, AnimationNode
return t;
}
AnimationTree *AnimationNode::get_animation_tree() const {
ERR_FAIL_COND_V(!state, nullptr);
return state->tree;
}
void AnimationNode::make_invalid(const String &p_reason) {
ERR_FAIL_COND(!state);
state->valid = false;
@ -1704,6 +1709,14 @@ NodePath AnimationTree::get_animation_player() const {
return animation_player;
}
void AnimationTree::set_advance_expression_base_node(const NodePath &p_advance_expression_base_node) {
advance_expression_base_node = p_advance_expression_base_node;
}
NodePath AnimationTree::get_advance_expression_base_node() const {
return advance_expression_base_node;
}
bool AnimationTree::is_state_invalid() const {
return !state.valid;
}
@ -1899,6 +1912,9 @@ void AnimationTree::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_animation_player", "root"), &AnimationTree::set_animation_player);
ClassDB::bind_method(D_METHOD("get_animation_player"), &AnimationTree::get_animation_player);
ClassDB::bind_method(D_METHOD("set_advance_expression_base_node", "node"), &AnimationTree::set_advance_expression_base_node);
ClassDB::bind_method(D_METHOD("get_advance_expression_base_node"), &AnimationTree::get_advance_expression_base_node);
ClassDB::bind_method(D_METHOD("set_root_motion_track", "path"), &AnimationTree::set_root_motion_track);
ClassDB::bind_method(D_METHOD("get_root_motion_track"), &AnimationTree::get_root_motion_track);
@ -1912,6 +1928,8 @@ void AnimationTree::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode"), "set_tree_root", "get_tree_root");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player");
ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "advance_expression_base_node", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node"), "set_advance_expression_base_node", "get_advance_expression_base_node");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle,Manual"), "set_process_callback", "get_process_callback");
ADD_GROUP("Root Motion", "root_motion_");

View File

@ -107,6 +107,7 @@ protected:
double blend_input(int p_input, double p_time, bool p_seek, bool p_seek_root, real_t p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
void make_invalid(const String &p_reason);
AnimationTree *get_animation_tree() const;
static void _bind_methods();
@ -270,6 +271,7 @@ private:
HashSet<TrackCache *> playing_caches;
Ref<AnimationNode> root;
NodePath advance_expression_base_node = NodePath(String("."));
AnimationProcessCallback process_callback = ANIMATION_PROCESS_IDLE;
bool active = false;
@ -332,6 +334,9 @@ public:
void set_animation_player(const NodePath &p_player);
NodePath get_animation_player() const;
void set_advance_expression_base_node(const NodePath &p_advance_expression_base_node);
NodePath get_advance_expression_base_node() const;
TypedArray<String> get_configuration_warnings() const override;
bool is_state_invalid() const;