diff options
Diffstat (limited to 'scene/animation/animation_node_state_machine.cpp')
-rw-r--r-- | scene/animation/animation_node_state_machine.cpp | 837 |
1 files changed, 495 insertions, 342 deletions
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index f478112a36..09c36eb081 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -20,6 +20,26 @@ bool AnimationNodeStateMachineTransition::has_auto_advance() const { return auto_advance; } +void AnimationNodeStateMachineTransition::set_advance_condition(const StringName &p_condition) { + String cs = p_condition; + ERR_FAIL_COND(cs.find("/") != -1 || cs.find(":") != -1); + advance_condition = p_condition; + if (cs != String()) { + advance_condition_name = "conditions/" + cs; + } else { + advance_condition_name = StringName(); + } + emit_signal("advance_condition_changed"); +} + +StringName AnimationNodeStateMachineTransition::get_advance_condition() const { + return advance_condition; +} + +StringName AnimationNodeStateMachineTransition::get_advance_condition_name() const { + return advance_condition_name; +} + void AnimationNodeStateMachineTransition::set_xfade_time(float p_xfade) { ERR_FAIL_COND(p_xfade < 0); @@ -56,6 +76,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() { 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_condition", "name"), &AnimationNodeStateMachineTransition::set_advance_condition); + ClassDB::bind_method(D_METHOD("get_advance_condition"), &AnimationNodeStateMachineTransition::get_advance_condition); + ClassDB::bind_method(D_METHOD("set_xfade_time", "secs"), &AnimationNodeStateMachineTransition::set_xfade_time); ClassDB::bind_method(D_METHOD("get_xfade_time"), &AnimationNodeStateMachineTransition::get_xfade_time); @@ -67,6 +90,7 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,AtEnd"), "set_switch_mode", "get_switch_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_advance"), "set_auto_advance", "has_auto_advance"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "advance_condition"), "set_advance_condition", "get_advance_condition"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01"), "set_xfade_time", "get_xfade_time"); ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); @@ -74,6 +98,8 @@ void AnimationNodeStateMachineTransition::_bind_methods() { BIND_ENUM_CONSTANT(SWITCH_MODE_IMMEDIATE); BIND_ENUM_CONSTANT(SWITCH_MODE_SYNC); BIND_ENUM_CONSTANT(SWITCH_MODE_AT_END); + + ADD_SIGNAL(MethodInfo("advance_condition_changed")); } AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() { @@ -85,277 +111,240 @@ AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() { priority = 1; } -/////////////////////////////////////////////////////// -void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<AnimationNode> p_node) { - - ERR_FAIL_COND(states.has(p_name)); - ERR_FAIL_COND(p_node.is_null()); - ERR_FAIL_COND(p_node->get_parent().is_valid()); - ERR_FAIL_COND(p_node->get_tree() != NULL); - ERR_FAIL_COND(String(p_name).find("/") != -1); - states[p_name] = p_node; +//////////////////////////////////////////////////////// - p_node->set_parent(this); - p_node->set_tree(get_tree()); +void AnimationNodeStateMachinePlayback::travel(const StringName &p_state) { - emit_changed(); + start_request_travel = true; + start_request = p_state; + stop_request = false; } -Ref<AnimationNode> AnimationNodeStateMachine::get_node(const StringName &p_name) const { - - ERR_FAIL_COND_V(!states.has(p_name), Ref<AnimationNode>()); - - return states[p_name]; +void AnimationNodeStateMachinePlayback::start(const StringName &p_state) { + start_request_travel = false; + start_request = p_state; + stop_request = false; } +void AnimationNodeStateMachinePlayback::stop() { -StringName AnimationNodeStateMachine::get_node_name(const Ref<AnimationNode> &p_node) const { - for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) { - if (E->get() == p_node) { - return E->key(); - } - } - - ERR_FAIL_V(StringName()); + stop_request = true; } - -bool AnimationNodeStateMachine::has_node(const StringName &p_name) const { - return states.has(p_name); +bool AnimationNodeStateMachinePlayback::is_playing() const { + return playing; +} +StringName AnimationNodeStateMachinePlayback::get_current_node() const { + return current; +} +StringName AnimationNodeStateMachinePlayback::get_blend_from_node() const { + return fading_from; +} +Vector<StringName> AnimationNodeStateMachinePlayback::get_travel_path() const { + return path; +} +float AnimationNodeStateMachinePlayback::get_current_play_pos() const { + return pos_current; +} +float AnimationNodeStateMachinePlayback::get_current_length() const { + return len_current; } -void AnimationNodeStateMachine::remove_node(const StringName &p_name) { - - ERR_FAIL_COND(!states.has(p_name)); - - { - //erase node connections - Ref<AnimationNode> node = states[p_name]; - for (int i = 0; i < node->get_input_count(); i++) { - node->set_input_connection(i, StringName()); - } - node->set_parent(NULL); - node->set_tree(NULL); - } - states.erase(p_name); - path.erase(p_name); +bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *sm, const StringName &p_travel) { - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_name || transitions[i].to == p_name) { - transitions.remove(i); - i--; - } - } + ERR_FAIL_COND_V(!playing, false); + ERR_FAIL_COND_V(!sm->states.has(p_travel), false); + ERR_FAIL_COND_V(!sm->states.has(current), false); - if (start_node == p_name) { - start_node = StringName(); - } + path.clear(); //a new one will be needed - if (end_node == p_name) { - end_node = StringName(); - } + if (current == p_travel) + return true; //nothing to do - if (playing && current == p_name) { - stop(); - } - emit_changed(); -} + loops_current = 0; // reset loops, so fade does not happen immediately -void AnimationNodeStateMachine::rename_node(const StringName &p_name, const StringName &p_new_name) { + Vector2 current_pos = sm->states[current].position; + Vector2 target_pos = sm->states[p_travel].position; - ERR_FAIL_COND(!states.has(p_name)); - ERR_FAIL_COND(states.has(p_new_name)); + Map<StringName, AStarCost> cost_map; - states[p_new_name] = states[p_name]; - states.erase(p_name); + List<int> open_list; - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_name) { - transitions.write[i].from = p_new_name; - } + //build open list + for (int i = 0; i < sm->transitions.size(); i++) { + if (sm->transitions[i].from == current) { + open_list.push_back(i); + float cost = sm->states[sm->transitions[i].to].position.distance_to(current_pos); + cost *= sm->transitions[i].transition->get_priority(); + AStarCost ap; + ap.prev = current; + ap.distance = cost; + cost_map[sm->transitions[i].to] = ap; - if (transitions[i].to == p_name) { - transitions.write[i].to = p_new_name; + if (sm->transitions[i].to == p_travel) { //prematurely found it! :D + path.push_back(p_travel); + return true; + } } } - if (start_node == p_name) { - start_node = p_new_name; - } + //begin astar + bool found_route = false; + while (!found_route) { - if (end_node == p_name) { - end_node = p_new_name; - } + if (open_list.size() == 0) { + return false; //no path found + } - if (playing && current == p_name) { - current = p_new_name; - } + //find the last cost transition + List<int>::Element *least_cost_transition = NULL; + float least_cost = 1e20; - path.clear(); //clear path -} + for (List<int>::Element *E = open_list.front(); E; E = E->next()) { -void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const { + float cost = cost_map[sm->transitions[E->get()].to].distance; + cost += sm->states[sm->transitions[E->get()].to].position.distance_to(target_pos); - List<StringName> nodes; - for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) { - nodes.push_back(E->key()); - } - nodes.sort_custom<StringName::AlphCompare>(); + if (cost < least_cost) { + least_cost_transition = E; + } + } - for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) { - r_nodes->push_back(E->get()); - } -} + StringName transition_prev = sm->transitions[least_cost_transition->get()].from; + StringName transition = sm->transitions[least_cost_transition->get()].to; -bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const StringName &p_to) const { + for (int i = 0; i < sm->transitions.size(); i++) { + if (sm->transitions[i].from != transition || sm->transitions[i].to == transition_prev) { + continue; //not interested on those + } - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_from && transitions[i].to == p_to) - return true; - } - return false; -} + float distance = sm->states[sm->transitions[i].from].position.distance_to(sm->states[sm->transitions[i].to].position); + distance *= sm->transitions[i].transition->get_priority(); + distance += cost_map[sm->transitions[i].from].distance; -int AnimationNodeStateMachine::find_transition(const StringName &p_from, const StringName &p_to) const { + if (cost_map.has(sm->transitions[i].to)) { + //oh this was visited already, can we win the cost? + if (distance < cost_map[sm->transitions[i].to].distance) { + cost_map[sm->transitions[i].to].distance = distance; + cost_map[sm->transitions[i].to].prev = sm->transitions[i].from; + } + } else { + //add to open list + AStarCost ac; + ac.prev = sm->transitions[i].from; + ac.distance = distance; + cost_map[sm->transitions[i].to] = ac; - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_from && transitions[i].to == p_to) - return i; - } - return -1; -} + open_list.push_back(i); -void AnimationNodeStateMachine::add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition) { + if (sm->transitions[i].to == p_travel) { + found_route = true; + break; + } + } + } - ERR_FAIL_COND(p_from == p_to); - ERR_FAIL_COND(!states.has(p_from)); - ERR_FAIL_COND(!states.has(p_to)); - ERR_FAIL_COND(p_transition.is_null()); + if (found_route) { + break; + } - for (int i = 0; i < transitions.size(); i++) { - ERR_FAIL_COND(transitions[i].from == p_from && transitions[i].to == p_to); + open_list.erase(least_cost_transition); } - Transition tr; - tr.from = p_from; - tr.to = p_to; - tr.transition = p_transition; - - transitions.push_back(tr); -} - -Ref<AnimationNodeStateMachineTransition> AnimationNodeStateMachine::get_transition(int p_transition) const { - ERR_FAIL_INDEX_V(p_transition, transitions.size(), Ref<AnimationNodeStateMachineTransition>()); - return transitions[p_transition].transition; -} -StringName AnimationNodeStateMachine::get_transition_from(int p_transition) const { + //make path + StringName at = p_travel; + while (at != current) { + path.push_back(at); + at = cost_map[at].prev; + } - ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName()); - return transitions[p_transition].from; -} -StringName AnimationNodeStateMachine::get_transition_to(int p_transition) const { + path.invert(); - ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName()); - return transitions[p_transition].to; + return true; } -int AnimationNodeStateMachine::get_transition_count() const { - - return transitions.size(); -} -void AnimationNodeStateMachine::remove_transition(const StringName &p_from, const StringName &p_to) { +float AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *sm, float p_time, bool p_seek) { - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_from && transitions[i].to == p_to) { - transitions.remove(i); - return; + //if not playing and it can restart, then restart + if (!playing && start_request == StringName()) { + if (!stop_request && sm->start_node) { + start(sm->start_node); + } else { + return 0; } } - if (playing) { - path.clear(); - } -} - -void AnimationNodeStateMachine::remove_transition_by_index(int p_transition) { - - transitions.remove(p_transition); - if (playing) { - path.clear(); + if (playing && stop_request) { + stop_request = false; + playing = false; + return 0; } -} - -void AnimationNodeStateMachine::set_start_node(const StringName &p_node) { - - ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); - start_node = p_node; -} - -String AnimationNodeStateMachine::get_start_node() const { - - return start_node; -} - -void AnimationNodeStateMachine::set_end_node(const StringName &p_node) { - - ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); - end_node = p_node; -} - -String AnimationNodeStateMachine::get_end_node() const { - - return end_node; -} -void AnimationNodeStateMachine::set_graph_offset(const Vector2 &p_offset) { - graph_offset = p_offset; -} + bool play_start = false; -Vector2 AnimationNodeStateMachine::get_graph_offset() const { - return graph_offset; -} + if (start_request != StringName()) { -float AnimationNodeStateMachine::process(float p_time, bool p_seek) { + if (start_request_travel) { + if (!playing) { + start_request = StringName(); + ERR_EXPLAIN("Can't travel to '" + String(start_request) + "' if state machine is not active."); + ERR_FAIL_V(0); + } - //if not playing and it can restart, then restart - if (!playing) { - if (start_node) { - start(start_node); + if (!_travel(sm, start_request)) { + //cant travel, then teleport + path.clear(); + current = start_request; + } } else { - return 0; + path.clear(); + current = start_request; + playing = true; + play_start = true; } + + start_request = StringName(); //clear start request } bool do_start = (p_seek && p_time == 0) || play_start || current == StringName(); if (do_start) { - if (start_node != StringName() && p_seek && p_time == 0) { - current = start_node; + if (sm->start_node != StringName() && p_seek && p_time == 0) { + current = sm->start_node; } - len_current = blend_node(states[current], 0, true, 1.0, FILTER_IGNORE, false); + len_current = sm->blend_node(current, sm->states[current].node, 0, true, 1.0, AnimationNode::FILTER_IGNORE, false); pos_current = 0; loops_current = 0; play_start = false; } + if (!sm->states.has(current)) { + playing = false; //current does not exist + current = StringName(); + return 0; + } float fade_blend = 1.0; if (fading_from != StringName()) { - if (!p_seek) { - fading_pos += p_time; - } - fade_blend = MIN(1.0, fading_pos / fading_time); - if (fade_blend >= 1.0) { + if (!sm->states.has(fading_from)) { fading_from = StringName(); + } else { + if (!p_seek) { + fading_pos += p_time; + } + fade_blend = MIN(1.0, fading_pos / fading_time); + if (fade_blend >= 1.0) { + fading_from = StringName(); + } } } - float rem = blend_node(states[current], p_time, p_seek, fade_blend, FILTER_IGNORE, false); + float rem = sm->blend_node(current, sm->states[current].node, p_time, p_seek, fade_blend, AnimationNode::FILTER_IGNORE, false); if (fading_from != StringName()) { - blend_node(states[fading_from], p_time, p_seek, 1.0 - fade_blend, FILTER_IGNORE, false); + sm->blend_node(current, sm->states[fading_from].node, p_time, p_seek, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, false); } //guess playback position @@ -380,29 +369,39 @@ float AnimationNodeStateMachine::process(float p_time, bool p_seek) { if (path.size()) { - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == current && transitions[i].to == path[0]) { - next_xfade = transitions[i].transition->get_xfade_time(); - switch_mode = transitions[i].transition->get_switch_mode(); + for (int i = 0; i < sm->transitions.size(); i++) { + if (sm->transitions[i].from == current && sm->transitions[i].to == path[0]) { + next_xfade = sm->transitions[i].transition->get_xfade_time(); + switch_mode = sm->transitions[i].transition->get_switch_mode(); next = path[0]; } } } else { float priority_best = 1e20; int auto_advance_to = -1; - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == current && transitions[i].transition->has_auto_advance()) { + for (int i = 0; i < sm->transitions.size(); i++) { + + bool auto_advance = false; + if (sm->transitions[i].transition->has_auto_advance()) { + auto_advance = true; + } + StringName advance_condition_name = sm->transitions[i].transition->get_advance_condition_name(); + if (advance_condition_name != StringName() && bool(sm->get_parameter(advance_condition_name))) { + auto_advance = true; + } + + if (sm->transitions[i].from == current && sm->transitions[i].transition->has_auto_advance()) { - if (transitions[i].transition->get_priority() < priority_best) { + if (sm->transitions[i].transition->get_priority() < priority_best) { auto_advance_to = i; } } } if (auto_advance_to != -1) { - next = transitions[auto_advance_to].to; - next_xfade = transitions[auto_advance_to].transition->get_xfade_time(); - switch_mode = transitions[auto_advance_to].transition->get_switch_mode(); + next = sm->transitions[auto_advance_to].to; + next_xfade = sm->transitions[auto_advance_to].transition->get_xfade_time(); + switch_mode = sm->transitions[auto_advance_to].transition->get_switch_mode(); } } @@ -437,12 +436,12 @@ float AnimationNodeStateMachine::process(float p_time, bool p_seek) { } current = next; if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) { - len_current = blend_node(states[current], 0, true, 0, FILTER_IGNORE, false); + len_current = sm->blend_node(current, sm->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false); pos_current = MIN(pos_current, len_current); - blend_node(states[current], pos_current, true, 0, FILTER_IGNORE, false); + sm->blend_node(current, sm->states[current].node, pos_current, true, 0, AnimationNode::FILTER_IGNORE, false); } else { - len_current = blend_node(states[current], 0, true, 0, FILTER_IGNORE, false); + len_current = sm->blend_node(current, sm->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false); pos_current = 0; } @@ -453,169 +452,321 @@ float AnimationNodeStateMachine::process(float p_time, bool p_seek) { //compute time left for transitions by using the end node - if (end_node != StringName() && end_node != current) { + if (sm->end_node != StringName() && sm->end_node != current) { - rem = blend_node(states[end_node], 0, true, 0, FILTER_IGNORE, false); + rem = sm->blend_node(current, sm->states[sm->end_node].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false); } return rem; } -bool AnimationNodeStateMachine::travel(const StringName &p_state) { - ERR_FAIL_COND_V(!playing, false); - ERR_FAIL_COND_V(!states.has(p_state), false); - ERR_FAIL_COND_V(!states.has(current), false); +void AnimationNodeStateMachinePlayback::_bind_methods() { - path.clear(); //a new one will be needed + ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachinePlayback::travel); + ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachinePlayback::start); + ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeStateMachinePlayback::stop); + ClassDB::bind_method(D_METHOD("is_playing"), &AnimationNodeStateMachinePlayback::is_playing); + ClassDB::bind_method(D_METHOD("get_current_node"), &AnimationNodeStateMachinePlayback::get_current_node); + ClassDB::bind_method(D_METHOD("get_travel_path"), &AnimationNodeStateMachinePlayback::get_travel_path); +} - if (current == p_state) - return true; //nothing to do +AnimationNodeStateMachinePlayback::AnimationNodeStateMachinePlayback() { + set_local_to_scene(true); //only one per instanced scene - loops_current = 0; // reset loops, so fade does not happen immediately + playing = false; + len_current = 0; + fading_time = 0; + stop_request = false; +} - Vector2 current_pos = states[current]->get_position(); - Vector2 target_pos = states[p_state]->get_position(); +/////////////////////////////////////////////////////// - Map<StringName, AStarCost> cost_map; +void AnimationNodeStateMachine::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::OBJECT, playback, PROPERTY_HINT_RESOURCE_TYPE, "AnimationNodeStateMachinePlayback", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + List<StringName> advance_conditions; + for (int i = 0; i < transitions.size(); i++) { + StringName ac = transitions[i].transition->get_advance_condition_name(); + if (ac != StringName() && advance_conditions.find(ac) == NULL) { + advance_conditions.push_back(ac); + } + } - List<int> open_list; + advance_conditions.sort_custom<StringName::AlphCompare>(); + for (List<StringName>::Element *E = advance_conditions.front(); E; E = E->next()) { + r_list->push_back(PropertyInfo(Variant::BOOL, E->get())); + } +} - //build open list - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == current) { - open_list.push_back(i); - float cost = states[transitions[i].to]->get_position().distance_to(current_pos); - cost *= transitions[i].transition->get_priority(); - AStarCost ap; - ap.prev = current; - ap.distance = cost; - cost_map[transitions[i].to] = ap; +Variant AnimationNodeStateMachine::get_parameter_default_value(const StringName &p_parameter) const { - if (transitions[i].to == p_state) { //prematurely found it! :D - path.push_back(p_state); - return true; - } - } + if (p_parameter == playback) { + Ref<AnimationNodeStateMachinePlayback> p; + p.instance(); + return p; + } else { + return false; //advance condition } +} - //begin astar - bool found_route = false; - while (!found_route) { +void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position) { - if (open_list.size() == 0) { - return false; //no path found + ERR_FAIL_COND(states.has(p_name)); + ERR_FAIL_COND(p_node.is_null()); + ERR_FAIL_COND(String(p_name).find("/") != -1); + + State state; + state.node = p_node; + state.position = p_position; + + states[p_name] = state; + + emit_changed(); + emit_signal("tree_changed"); + + p_node->connect("tree_changed", this, "_tree_changed", varray(), CONNECT_REFERENCE_COUNTED); +} + +Ref<AnimationNode> AnimationNodeStateMachine::get_node(const StringName &p_name) const { + + ERR_FAIL_COND_V(!states.has(p_name), Ref<AnimationNode>()); + + return states[p_name].node; +} + +StringName AnimationNodeStateMachine::get_node_name(const Ref<AnimationNode> &p_node) const { + for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { + if (E->get().node == p_node) { + return E->key(); } + } - //find the last cost transition - List<int>::Element *least_cost_transition = NULL; - float least_cost = 1e20; + ERR_FAIL_V(StringName()); +} - for (List<int>::Element *E = open_list.front(); E; E = E->next()) { +void AnimationNodeStateMachine::get_child_nodes(List<ChildNode> *r_child_nodes) { + Vector<StringName> nodes; - float cost = cost_map[transitions[E->get()].to].distance; - cost += states[transitions[E->get()].to]->get_position().distance_to(target_pos); + for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { + nodes.push_back(E->key()); + } - if (cost < least_cost) { - least_cost_transition = E; - } + nodes.sort_custom<StringName::AlphCompare>(); + + for (int i = 0; i < nodes.size(); i++) { + ChildNode cn; + cn.name = nodes[i]; + cn.node = states[cn.name].node; + r_child_nodes->push_back(cn); + } +} + +bool AnimationNodeStateMachine::has_node(const StringName &p_name) const { + return states.has(p_name); +} +void AnimationNodeStateMachine::remove_node(const StringName &p_name) { + + ERR_FAIL_COND(!states.has(p_name)); + + { + Ref<AnimationNode> node = states[p_name].node; + node->disconnect("tree_changed", this, "_tree_changed"); + } + + states.erase(p_name); + //path.erase(p_name); + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_name || transitions[i].to == p_name) { + transitions.write[i].transition->disconnect("advance_condition_changed", this, "_tree_changed"); + transitions.remove(i); + i--; } + } - StringName transition_prev = transitions[least_cost_transition->get()].from; - StringName transition = transitions[least_cost_transition->get()].to; + if (start_node == p_name) { + start_node = StringName(); + } - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from != transition || transitions[i].to == transition_prev) { - continue; //not interested on those - } + if (end_node == p_name) { + end_node = StringName(); + } - float distance = states[transitions[i].from]->get_position().distance_to(states[transitions[i].to]->get_position()); - distance *= transitions[i].transition->get_priority(); - distance += cost_map[transitions[i].from].distance; + /*if (playing && current == p_name) { + stop(); + }*/ - if (cost_map.has(transitions[i].to)) { - //oh this was visited already, can we win the cost? - if (distance < cost_map[transitions[i].to].distance) { - cost_map[transitions[i].to].distance = distance; - cost_map[transitions[i].to].prev = transitions[i].from; - } - } else { - //add to open list - AStarCost ac; - ac.prev = transitions[i].from; - ac.distance = distance; - cost_map[transitions[i].to] = ac; + emit_changed(); + emit_signal("tree_changed"); +} - open_list.push_back(i); +void AnimationNodeStateMachine::rename_node(const StringName &p_name, const StringName &p_new_name) { - if (transitions[i].to == p_state) { - found_route = true; - break; - } - } + ERR_FAIL_COND(!states.has(p_name)); + ERR_FAIL_COND(states.has(p_new_name)); + + states[p_new_name] = states[p_name]; + states.erase(p_name); + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_name) { + transitions.write[i].from = p_new_name; } - if (found_route) { - break; + if (transitions[i].to == p_name) { + transitions.write[i].to = p_new_name; } + } - open_list.erase(least_cost_transition); + if (start_node == p_name) { + start_node = p_new_name; } - //make path - StringName at = p_state; - while (at != current) { - path.push_back(at); - at = cost_map[at].prev; + if (end_node == p_name) { + end_node = p_new_name; } - path.invert(); + /*if (playing && current == p_name) { + current = p_new_name; + }*/ - return true; + //path.clear(); //clear path + emit_signal("tree_changed"); } -void AnimationNodeStateMachine::start(const StringName &p_state) { +void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const { - ERR_FAIL_COND(!states.has(p_state)); - path.clear(); - current = p_state; - playing = true; - play_start = true; + List<StringName> nodes; + for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { + nodes.push_back(E->key()); + } + nodes.sort_custom<StringName::AlphCompare>(); + + for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) { + r_nodes->push_back(E->get()); + } } -void AnimationNodeStateMachine::stop() { - playing = false; - play_start = false; - current = StringName(); + +bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const StringName &p_to) const { + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_from && transitions[i].to == p_to) + return true; + } + return false; } -bool AnimationNodeStateMachine::is_playing() const { - return playing; +int AnimationNodeStateMachine::find_transition(const StringName &p_from, const StringName &p_to) const { + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_from && transitions[i].to == p_to) + return i; + } + return -1; } -StringName AnimationNodeStateMachine::get_current_node() const { - if (!playing) { - return StringName(); + +void AnimationNodeStateMachine::add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition) { + + ERR_FAIL_COND(p_from == p_to); + ERR_FAIL_COND(!states.has(p_from)); + ERR_FAIL_COND(!states.has(p_to)); + ERR_FAIL_COND(p_transition.is_null()); + + for (int i = 0; i < transitions.size(); i++) { + ERR_FAIL_COND(transitions[i].from == p_from && transitions[i].to == p_to); } - return current; + Transition tr; + tr.from = p_from; + tr.to = p_to; + tr.transition = p_transition; + + tr.transition->connect("advance_condition_changed", this, "_tree_changed", varray(), CONNECT_REFERENCE_COUNTED); + + transitions.push_back(tr); +} + +Ref<AnimationNodeStateMachineTransition> AnimationNodeStateMachine::get_transition(int p_transition) const { + ERR_FAIL_INDEX_V(p_transition, transitions.size(), Ref<AnimationNodeStateMachineTransition>()); + return transitions[p_transition].transition; } +StringName AnimationNodeStateMachine::get_transition_from(int p_transition) const { + + ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName()); + return transitions[p_transition].from; +} +StringName AnimationNodeStateMachine::get_transition_to(int p_transition) const { + + ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName()); + return transitions[p_transition].to; +} + +int AnimationNodeStateMachine::get_transition_count() const { + + return transitions.size(); +} +void AnimationNodeStateMachine::remove_transition(const StringName &p_from, const StringName &p_to) { -StringName AnimationNodeStateMachine::get_blend_from_node() const { - if (!playing) { - return StringName(); + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_from && transitions[i].to == p_to) { + transitions.write[i].transition->disconnect("advance_condition_changed", this, "_tree_changed"); + transitions.remove(i); + return; + } } - return fading_from; + /*if (playing) { + path.clear(); + }*/ } -float AnimationNodeStateMachine::get_current_play_pos() const { - return pos_current; +void AnimationNodeStateMachine::remove_transition_by_index(int p_transition) { + + ERR_FAIL_INDEX(p_transition, transitions.size()); + transitions.write[p_transition].transition->disconnect("advance_condition_changed", this, "_tree_changed"); + transitions.remove(p_transition); + /*if (playing) { + path.clear(); + }*/ } -float AnimationNodeStateMachine::get_current_length() const { - return len_current; + +void AnimationNodeStateMachine::set_start_node(const StringName &p_node) { + + ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); + start_node = p_node; } -Vector<StringName> AnimationNodeStateMachine::get_travel_path() const { - return path; +String AnimationNodeStateMachine::get_start_node() const { + + return start_node; +} + +void AnimationNodeStateMachine::set_end_node(const StringName &p_node) { + + ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); + end_node = p_node; +} + +String AnimationNodeStateMachine::get_end_node() const { + + return end_node; +} + +void AnimationNodeStateMachine::set_graph_offset(const Vector2 &p_offset) { + graph_offset = p_offset; +} + +Vector2 AnimationNodeStateMachine::get_graph_offset() const { + return graph_offset; +} + +float AnimationNodeStateMachine::process(float p_time, bool p_seek) { + + Ref<AnimationNodeStateMachinePlayback> playback = get_parameter(this->playback); + ERR_FAIL_COND_V(playback.is_null(), 0.0); + + return playback->process(this, p_time, p_seek); } + String AnimationNodeStateMachine::get_caption() const { return "StateMachine"; } @@ -623,14 +774,8 @@ String AnimationNodeStateMachine::get_caption() const { void AnimationNodeStateMachine::_notification(int p_what) { } -void AnimationNodeStateMachine::set_tree(AnimationTree *p_player) { - - AnimationNode::set_tree(p_player); - - for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) { - Ref<AnimationRootNode> node = E->get(); - node->set_tree(p_player); - } +Ref<AnimationNode> AnimationNodeStateMachine::get_child_by_name(const StringName &p_name) { + return get_node(p_name); } bool AnimationNodeStateMachine::_set(const StringName &p_name, const Variant &p_value) { @@ -651,7 +796,7 @@ bool AnimationNodeStateMachine::_set(const StringName &p_name, const Variant &p_ if (what == "position") { if (states.has(node_name)) { - states[node_name]->set_position(p_value); + states[node_name].position = p_value; } return true; } @@ -687,7 +832,7 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c if (what == "node") { if (states.has(node_name)) { - r_ret = states[node_name]; + r_ret = states[node_name].node; return true; } } @@ -695,7 +840,7 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c if (what == "position") { if (states.has(node_name)) { - r_ret = states[node_name]->get_position(); + r_ret = states[node_name].position; return true; } } @@ -727,14 +872,14 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) const { List<StringName> names; - for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) { + for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { names.push_back(E->key()); } names.sort_custom<StringName::AlphCompare>(); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { String name = E->get(); - p_list->push_back(PropertyInfo(Variant::OBJECT, "states/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "states/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::VECTOR2, "states/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } @@ -744,16 +889,34 @@ void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) c p_list->push_back(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } +void AnimationNodeStateMachine::set_node_position(const StringName &p_name, const Vector2 &p_position) { + ERR_FAIL_COND(!states.has(p_name)); + states[p_name].position = p_position; +} + +Vector2 AnimationNodeStateMachine::get_node_position(const StringName &p_name) const { + + ERR_FAIL_COND_V(!states.has(p_name), Vector2()); + return states[p_name].position; +} + +void AnimationNodeStateMachine::_tree_changed() { + emit_signal("tree_changed"); +} + void AnimationNodeStateMachine::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_node", "name", "node"), &AnimationNodeStateMachine::add_node); + ClassDB::bind_method(D_METHOD("add_node", "name", "node", "position"), &AnimationNodeStateMachine::add_node, DEFVAL(Vector2())); ClassDB::bind_method(D_METHOD("get_node", "name"), &AnimationNodeStateMachine::get_node); ClassDB::bind_method(D_METHOD("remove_node", "name"), &AnimationNodeStateMachine::remove_node); ClassDB::bind_method(D_METHOD("rename_node", "name", "new_name"), &AnimationNodeStateMachine::rename_node); ClassDB::bind_method(D_METHOD("has_node", "name"), &AnimationNodeStateMachine::has_node); ClassDB::bind_method(D_METHOD("get_node_name", "node"), &AnimationNodeStateMachine::get_node_name); - ClassDB::bind_method(D_METHOD("has_transition", "from", "to"), &AnimationNodeStateMachine::add_transition); + ClassDB::bind_method(D_METHOD("set_node_position", "name", "position"), &AnimationNodeStateMachine::set_node_position); + ClassDB::bind_method(D_METHOD("get_node_position", "name"), &AnimationNodeStateMachine::get_node_position); + + ClassDB::bind_method(D_METHOD("has_transition", "from", "to"), &AnimationNodeStateMachine::has_transition); ClassDB::bind_method(D_METHOD("add_transition", "from", "to", "transition"), &AnimationNodeStateMachine::add_transition); ClassDB::bind_method(D_METHOD("get_transition", "idx"), &AnimationNodeStateMachine::get_transition); ClassDB::bind_method(D_METHOD("get_transition_from", "idx"), &AnimationNodeStateMachine::get_transition_from); @@ -771,20 +934,10 @@ void AnimationNodeStateMachine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_graph_offset", "name"), &AnimationNodeStateMachine::set_graph_offset); ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeStateMachine::get_graph_offset); - ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachine::travel); - ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachine::start); - ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeStateMachine::stop); - ClassDB::bind_method(D_METHOD("is_playing"), &AnimationNodeStateMachine::is_playing); - ClassDB::bind_method(D_METHOD("get_current_node"), &AnimationNodeStateMachine::get_current_node); - ClassDB::bind_method(D_METHOD("get_travel_path"), &AnimationNodeStateMachine::get_travel_path); + ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeStateMachine::_tree_changed); } AnimationNodeStateMachine::AnimationNodeStateMachine() { - play_start = false; - - playing = false; - len_current = 0; - - fading_time = 0; + playback = "playback"; } |