2018-08-29 20:38:13 +00:00
/*************************************************************************/
/* animation_node_state_machine.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2022-01-03 20:27:34 +00:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2018-08-29 20:38:13 +00:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-06-25 19:21:57 +00:00
# include "animation_node_state_machine.h"
/////////////////////////////////////////////////
void AnimationNodeStateMachineTransition : : set_switch_mode ( SwitchMode p_mode ) {
switch_mode = p_mode ;
}
AnimationNodeStateMachineTransition : : SwitchMode AnimationNodeStateMachineTransition : : get_switch_mode ( ) const {
return switch_mode ;
}
void AnimationNodeStateMachineTransition : : set_auto_advance ( bool p_enable ) {
auto_advance = p_enable ;
}
bool AnimationNodeStateMachineTransition : : has_auto_advance ( ) const {
return auto_advance ;
}
2018-08-20 16:38:18 +00:00
void AnimationNodeStateMachineTransition : : set_advance_condition ( const StringName & p_condition ) {
String cs = p_condition ;
2022-02-03 16:03:38 +00:00
ERR_FAIL_COND ( cs . contains ( " / " ) | | cs . contains ( " : " ) ) ;
2018-08-20 16:38:18 +00:00
advance_condition = p_condition ;
2021-12-09 09:42:46 +00:00
if ( ! cs . is_empty ( ) ) {
2018-08-20 16:38:18 +00:00
advance_condition_name = " conditions/ " + cs ;
} else {
advance_condition_name = StringName ( ) ;
}
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " advance_condition_changed " ) ) ;
2018-08-20 16:38:18 +00:00
}
StringName AnimationNodeStateMachineTransition : : get_advance_condition ( ) const {
return advance_condition ;
}
StringName AnimationNodeStateMachineTransition : : get_advance_condition_name ( ) const {
return advance_condition_name ;
}
2022-04-20 10:36:54 +00:00
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 ;
}
2018-06-25 19:21:57 +00:00
void AnimationNodeStateMachineTransition : : set_xfade_time ( float p_xfade ) {
ERR_FAIL_COND ( p_xfade < 0 ) ;
2022-07-28 08:31:23 +00:00
xfade_time = p_xfade ;
2018-06-25 19:21:57 +00:00
emit_changed ( ) ;
}
float AnimationNodeStateMachineTransition : : get_xfade_time ( ) const {
2022-07-28 08:31:23 +00:00
return xfade_time ;
}
void AnimationNodeStateMachineTransition : : set_xfade_curve ( const Ref < Curve > & p_curve ) {
xfade_curve = p_curve ;
}
Ref < Curve > AnimationNodeStateMachineTransition : : get_xfade_curve ( ) const {
return xfade_curve ;
2018-06-25 19:21:57 +00:00
}
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 ( ) ;
}
int AnimationNodeStateMachineTransition : : get_priority ( ) const {
return priority ;
}
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 ) ;
2018-08-20 16:38:18 +00:00
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 ) ;
2018-06-25 19:21:57 +00:00
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 ) ;
2022-07-28 08:31:23 +00:00
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 ) ;
2018-06-25 19:21:57 +00:00
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 ) ;
2022-04-20 10:36:54 +00:00
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 " ) ;
2022-07-28 08:31:23 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " xfade_curve " , PROPERTY_HINT_RESOURCE_TYPE , " Curve " ) , " set_xfade_curve " , " get_xfade_curve " ) ;
2022-04-20 10:36:54 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " priority " , PROPERTY_HINT_RANGE , " 0,32,1 " ) , " set_priority " , " get_priority " ) ;
ADD_GROUP ( " Switch " , " " ) ;
2021-05-22 02:30:58 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " switch_mode " , PROPERTY_HINT_ENUM , " Immediate,Sync,At End " ) , " set_switch_mode " , " get_switch_mode " ) ;
2018-06-25 19:21:57 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " auto_advance " ) , " set_auto_advance " , " has_auto_advance " ) ;
2022-04-20 10:36:54 +00:00
ADD_GROUP ( " Advance " , " advance_ " ) ;
2020-02-20 21:58:05 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING_NAME , " advance_condition " ) , " set_advance_condition " , " get_advance_condition " ) ;
2022-04-20 10:36:54 +00:00
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 " , " " ) ;
2018-06-25 19:21:57 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " disabled " ) , " set_disabled " , " is_disabled " ) ;
2018-06-21 12:47:07 +00:00
BIND_ENUM_CONSTANT ( SWITCH_MODE_IMMEDIATE ) ;
BIND_ENUM_CONSTANT ( SWITCH_MODE_SYNC ) ;
BIND_ENUM_CONSTANT ( SWITCH_MODE_AT_END ) ;
2018-08-20 16:38:18 +00:00
ADD_SIGNAL ( MethodInfo ( " advance_condition_changed " ) ) ;
2018-06-25 19:21:57 +00:00
}
AnimationNodeStateMachineTransition : : AnimationNodeStateMachineTransition ( ) {
}
2018-08-20 16:38:18 +00:00
////////////////////////////////////////////////////////
void AnimationNodeStateMachinePlayback : : travel ( const StringName & p_state ) {
start_request_travel = true ;
start_request = p_state ;
stop_request = false ;
}
void AnimationNodeStateMachinePlayback : : start ( const StringName & p_state ) {
start_request_travel = false ;
start_request = p_state ;
stop_request = false ;
}
2020-05-14 12:29:06 +00:00
2018-08-20 16:38:18 +00:00
void AnimationNodeStateMachinePlayback : : stop ( ) {
stop_request = true ;
}
2020-05-14 12:29:06 +00:00
2018-08-20 16:38:18 +00:00
bool AnimationNodeStateMachinePlayback : : is_playing ( ) const {
return playing ;
}
2020-05-14 12:29:06 +00:00
2018-08-20 16:38:18 +00:00
StringName AnimationNodeStateMachinePlayback : : get_current_node ( ) const {
return current ;
}
2020-05-14 12:29:06 +00:00
2018-08-20 16:38:18 +00:00
StringName AnimationNodeStateMachinePlayback : : get_blend_from_node ( ) const {
return fading_from ;
}
2020-05-14 12:29:06 +00:00
2018-08-20 16:38:18 +00:00
Vector < StringName > AnimationNodeStateMachinePlayback : : get_travel_path ( ) const {
return path ;
}
2020-05-14 12:29:06 +00:00
2018-08-20 16:38:18 +00:00
float AnimationNodeStateMachinePlayback : : get_current_play_pos ( ) const {
return pos_current ;
}
2020-05-14 12:29:06 +00:00
2018-08-20 16:38:18 +00:00
float AnimationNodeStateMachinePlayback : : get_current_length ( ) const {
return len_current ;
}
2019-06-26 13:08:25 +00:00
bool AnimationNodeStateMachinePlayback : : _travel ( AnimationNodeStateMachine * p_state_machine , const StringName & p_travel ) {
2018-08-20 16:38:18 +00:00
ERR_FAIL_COND_V ( ! playing , false ) ;
2019-06-26 13:08:25 +00:00
ERR_FAIL_COND_V ( ! p_state_machine - > states . has ( p_travel ) , false ) ;
ERR_FAIL_COND_V ( ! p_state_machine - > states . has ( current ) , false ) ;
2018-08-20 16:38:18 +00:00
path . clear ( ) ; //a new one will be needed
2020-05-14 14:41:43 +00:00
if ( current = = p_travel ) {
2018-08-20 16:38:18 +00:00
return true ; //nothing to do
2020-05-14 14:41:43 +00:00
}
2018-08-20 16:38:18 +00:00
2019-06-26 13:08:25 +00:00
Vector2 current_pos = p_state_machine - > states [ current ] . position ;
Vector2 target_pos = p_state_machine - > states [ p_travel ] . position ;
2018-08-20 16:38:18 +00:00
2022-05-13 13:04:37 +00:00
HashMap < StringName , AStarCost > cost_map ;
2018-08-20 16:38:18 +00:00
List < int > open_list ;
//build open list
2019-06-26 13:08:25 +00:00
for ( int i = 0 ; i < p_state_machine - > transitions . size ( ) ; i + + ) {
2018-12-16 14:43:20 +00:00
if ( p_state_machine - > transitions [ i ] . transition - > is_disabled ( ) ) {
continue ;
}
if ( p_state_machine - > transitions [ i ] . local_from = = current ) {
2018-08-20 16:38:18 +00:00
open_list . push_back ( i ) ;
2018-12-16 14:43:20 +00:00
float cost = p_state_machine - > states [ p_state_machine - > transitions [ i ] . local_to ] . position . distance_to ( current_pos ) ;
2019-06-26 13:08:25 +00:00
cost * = p_state_machine - > transitions [ i ] . transition - > get_priority ( ) ;
2018-08-20 16:38:18 +00:00
AStarCost ap ;
ap . prev = current ;
ap . distance = cost ;
2018-12-16 14:43:20 +00:00
cost_map [ p_state_machine - > transitions [ i ] . local_to ] = ap ;
2018-08-20 16:38:18 +00:00
2018-12-16 14:43:20 +00:00
if ( p_state_machine - > transitions [ i ] . local_to = = p_travel ) { //prematurely found it! :D
2018-08-20 16:38:18 +00:00
path . push_back ( p_travel ) ;
return true ;
}
}
}
//begin astar
bool found_route = false ;
while ( ! found_route ) {
if ( open_list . size ( ) = = 0 ) {
return false ; //no path found
}
//find the last cost transition
2020-04-01 23:20:12 +00:00
List < int > : : Element * least_cost_transition = nullptr ;
2018-08-20 16:38:18 +00:00
float least_cost = 1e20 ;
for ( List < int > : : Element * E = open_list . front ( ) ; E ; E = E - > next ( ) ) {
2018-12-16 14:43:20 +00:00
float cost = cost_map [ p_state_machine - > transitions [ E - > get ( ) ] . local_to ] . distance ;
cost + = p_state_machine - > states [ p_state_machine - > transitions [ E - > get ( ) ] . local_to ] . position . distance_to ( target_pos ) ;
2018-08-20 16:38:18 +00:00
if ( cost < least_cost ) {
least_cost_transition = E ;
2019-08-06 21:27:25 +00:00
least_cost = cost ;
2018-08-20 16:38:18 +00:00
}
}
2018-12-16 14:43:20 +00:00
StringName transition_prev = p_state_machine - > transitions [ least_cost_transition - > get ( ) ] . local_from ;
StringName transition = p_state_machine - > transitions [ least_cost_transition - > get ( ) ] . local_to ;
2018-08-20 16:38:18 +00:00
2019-06-26 13:08:25 +00:00
for ( int i = 0 ; i < p_state_machine - > transitions . size ( ) ; i + + ) {
2018-12-16 14:43:20 +00:00
if ( p_state_machine - > transitions [ i ] . transition - > is_disabled ( ) ) {
continue ;
}
if ( p_state_machine - > transitions [ i ] . local_from ! = transition | | p_state_machine - > transitions [ i ] . local_to = = transition_prev ) {
2018-08-20 16:38:18 +00:00
continue ; //not interested on those
}
2018-12-16 14:43:20 +00:00
float distance = p_state_machine - > states [ p_state_machine - > transitions [ i ] . local_from ] . position . distance_to ( p_state_machine - > states [ p_state_machine - > transitions [ i ] . local_to ] . position ) ;
2019-06-26 13:08:25 +00:00
distance * = p_state_machine - > transitions [ i ] . transition - > get_priority ( ) ;
2018-12-16 14:43:20 +00:00
distance + = cost_map [ p_state_machine - > transitions [ i ] . local_from ] . distance ;
2018-08-20 16:38:18 +00:00
2018-12-16 14:43:20 +00:00
if ( cost_map . has ( p_state_machine - > transitions [ i ] . local_to ) ) {
2018-08-20 16:38:18 +00:00
//oh this was visited already, can we win the cost?
2018-12-16 14:43:20 +00:00
if ( distance < cost_map [ p_state_machine - > transitions [ i ] . local_to ] . distance ) {
cost_map [ p_state_machine - > transitions [ i ] . local_to ] . distance = distance ;
cost_map [ p_state_machine - > transitions [ i ] . local_to ] . prev = p_state_machine - > transitions [ i ] . local_from ;
2018-08-20 16:38:18 +00:00
}
} else {
//add to open list
AStarCost ac ;
2018-12-16 14:43:20 +00:00
ac . prev = p_state_machine - > transitions [ i ] . local_from ;
2018-08-20 16:38:18 +00:00
ac . distance = distance ;
2018-12-16 14:43:20 +00:00
cost_map [ p_state_machine - > transitions [ i ] . local_to ] = ac ;
2018-08-20 16:38:18 +00:00
open_list . push_back ( i ) ;
2018-12-16 14:43:20 +00:00
if ( p_state_machine - > transitions [ i ] . local_to = = p_travel ) {
2018-08-20 16:38:18 +00:00
found_route = true ;
break ;
}
}
}
if ( found_route ) {
break ;
}
open_list . erase ( least_cost_transition ) ;
}
//make path
StringName at = p_travel ;
while ( at ! = current ) {
path . push_back ( at ) ;
at = cost_map [ at ] . prev ;
}
2021-03-14 07:21:32 +00:00
path . reverse ( ) ;
2018-08-20 16:38:18 +00:00
return true ;
}
2022-05-04 18:53:48 +00:00
double AnimationNodeStateMachinePlayback : : process ( AnimationNodeStateMachine * p_state_machine , double p_time , bool p_seek , bool p_seek_root ) {
2022-08-03 00:09:33 +00:00
if ( p_time = = - 1 ) {
Ref < AnimationNodeStateMachine > anodesm = p_state_machine - > states [ current ] . node ;
if ( anodesm . is_valid ( ) ) {
p_state_machine - > blend_node ( current , p_state_machine - > states [ current ] . node , - 1 , p_seek , p_seek_root , 0 , AnimationNode : : FILTER_IGNORE , true ) ;
}
playing = false ;
return 0 ;
}
2018-08-20 16:38:18 +00:00
//if not playing and it can restart, then restart
if ( ! playing & & start_request = = StringName ( ) ) {
2019-06-26 13:08:25 +00:00
if ( ! stop_request & & p_state_machine - > start_node ) {
start ( p_state_machine - > start_node ) ;
2018-08-20 16:38:18 +00:00
} else {
return 0 ;
}
}
if ( playing & & stop_request ) {
stop_request = false ;
playing = false ;
return 0 ;
}
bool play_start = false ;
if ( start_request ! = StringName ( ) ) {
if ( start_request_travel ) {
if ( ! playing ) {
2019-11-30 01:41:40 +00:00
if ( ! stop_request & & p_state_machine - > start_node ) {
// can restart, just postpone traveling
path . clear ( ) ;
current = p_state_machine - > start_node ;
playing = true ;
play_start = true ;
} else {
// stopped, invalid state
String node_name = start_request ;
start_request = StringName ( ) ; //clear start request
2021-02-14 00:57:03 +00:00
ERR_FAIL_V_MSG ( 0 , " Can't travel to ' " + node_name + " ' if state machine is not playing. Maybe you need to enable Autoplay on Load for one of the nodes in your state machine or call .start() first? " ) ;
2019-11-30 01:41:40 +00:00
}
} else {
if ( ! _travel ( p_state_machine , start_request ) ) {
// can't travel, then teleport
path . clear ( ) ;
current = start_request ;
}
start_request = StringName ( ) ; //clear start request
2018-08-20 16:38:18 +00:00
}
} else {
2019-11-30 01:41:40 +00:00
// teleport to start
2021-01-05 14:56:06 +00:00
if ( p_state_machine - > states . has ( start_request ) ) {
path . clear ( ) ;
current = start_request ;
playing = true ;
play_start = true ;
start_request = StringName ( ) ; //clear start request
} else {
StringName node = start_request ;
start_request = StringName ( ) ; //clear start request
ERR_FAIL_V_MSG ( 0 , " No such node: ' " + node + " ' " ) ;
}
2018-08-20 16:38:18 +00:00
}
}
bool do_start = ( p_seek & & p_time = = 0 ) | | play_start | | current = = StringName ( ) ;
if ( do_start ) {
2021-10-31 03:29:00 +00:00
if ( p_state_machine - > start_node ! = StringName ( ) & & p_seek & & p_time = = 0 & & current = = StringName ( ) ) {
2019-06-26 13:08:25 +00:00
current = p_state_machine - > start_node ;
2018-08-20 16:38:18 +00:00
}
2022-06-29 06:35:29 +00:00
len_current = p_state_machine - > blend_node ( current , p_state_machine - > states [ current ] . node , 0 , true , p_seek_root , 1.0 , AnimationNode : : FILTER_IGNORE , true ) ;
2018-08-20 16:38:18 +00:00
pos_current = 0 ;
}
2019-06-26 13:08:25 +00:00
if ( ! p_state_machine - > states . has ( current ) ) {
2018-08-20 16:38:18 +00:00
playing = false ; //current does not exist
current = StringName ( ) ;
return 0 ;
}
float fade_blend = 1.0 ;
if ( fading_from ! = StringName ( ) ) {
2019-06-26 13:08:25 +00:00
if ( ! p_state_machine - > states . has ( fading_from ) ) {
2018-08-20 16:38:18 +00:00
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 ( ) ;
}
}
}
2022-07-28 08:31:23 +00:00
if ( current_curve . is_valid ( ) ) {
2022-07-24 16:47:57 +00:00
fade_blend = current_curve - > sample ( fade_blend ) ;
2022-07-28 08:31:23 +00:00
}
2022-06-29 06:35:29 +00:00
float rem = p_state_machine - > blend_node ( current , p_state_machine - > states [ current ] . node , p_time , p_seek , p_seek_root , fade_blend , AnimationNode : : FILTER_IGNORE , true ) ;
2018-08-20 16:38:18 +00:00
if ( fading_from ! = StringName ( ) ) {
2022-06-29 06:35:29 +00:00
p_state_machine - > blend_node ( fading_from , p_state_machine - > states [ fading_from ] . node , p_time , p_seek , p_seek_root , 1.0 - fade_blend , AnimationNode : : FILTER_IGNORE , true ) ;
2018-08-20 16:38:18 +00:00
}
//guess playback position
if ( rem > len_current ) { // weird but ok
len_current = rem ;
}
{ //advance and loop check
float next_pos = len_current - rem ;
2022-04-14 18:28:42 +00:00
end_loop = next_pos < pos_current ;
2018-08-20 16:38:18 +00:00
pos_current = next_pos ; //looped
}
//find next
StringName next ;
2021-02-09 17:24:36 +00:00
float next_xfade = 0.0 ;
2018-08-20 16:38:18 +00:00
AnimationNodeStateMachineTransition : : SwitchMode switch_mode = AnimationNodeStateMachineTransition : : SWITCH_MODE_IMMEDIATE ;
if ( path . size ( ) ) {
2019-06-26 13:08:25 +00:00
for ( int i = 0 ; i < p_state_machine - > transitions . size ( ) ; i + + ) {
2018-12-16 14:43:20 +00:00
if ( p_state_machine - > transitions [ i ] . transition - > is_disabled ( ) ) {
continue ;
}
if ( p_state_machine - > transitions [ i ] . local_from = = current & & p_state_machine - > transitions [ i ] . local_to = = path [ 0 ] ) {
2019-06-26 13:08:25 +00:00
next_xfade = p_state_machine - > transitions [ i ] . transition - > get_xfade_time ( ) ;
2022-07-28 08:31:23 +00:00
current_curve = p_state_machine - > transitions [ i ] . transition - > get_xfade_curve ( ) ;
2019-06-26 13:08:25 +00:00
switch_mode = p_state_machine - > transitions [ i ] . transition - > get_switch_mode ( ) ;
2018-08-20 16:38:18 +00:00
next = path [ 0 ] ;
}
}
} else {
float priority_best = 1e20 ;
int auto_advance_to = - 1 ;
2018-12-16 14:43:20 +00:00
2019-06-26 13:08:25 +00:00
for ( int i = 0 ; i < p_state_machine - > transitions . size ( ) ; i + + ) {
2018-12-16 14:43:20 +00:00
if ( p_state_machine - > transitions [ i ] . transition - > is_disabled ( ) ) {
continue ;
2018-08-20 16:38:18 +00:00
}
2018-12-16 14:43:20 +00:00
// handles end_node: when end_node is reached in a sub state machine, find and activate the current_transition
if ( force_auto_advance ) {
if ( p_state_machine - > transitions [ i ] . from = = current_transition . from & & p_state_machine - > transitions [ i ] . to = = current_transition . to ) {
auto_advance_to = i ;
force_auto_advance = false ;
break ;
}
2018-08-20 16:38:18 +00:00
}
2018-12-16 14:43:20 +00:00
// handles start_node: if previous state machine is pointing to a node inside the current state machine, starts the current machine from start_node to prev_local_to
if ( p_state_machine - > start_node = = current & & p_state_machine - > transitions [ i ] . local_from = = current ) {
2022-06-08 02:06:45 +00:00
if ( p_state_machine - > prev_state_machine ! = nullptr ) {
2022-08-03 00:09:33 +00:00
Ref < AnimationNodeStateMachinePlayback > prev_playback = p_state_machine - > prev_state_machine - > get_parameter ( p_state_machine - > playback ) ;
2018-12-16 14:43:20 +00:00
if ( prev_playback . is_valid ( ) ) {
StringName prev_local_to = String ( prev_playback - > current_transition . next ) . replace_first ( String ( p_state_machine - > state_machine_name ) + " / " , " " ) ;
if ( p_state_machine - > transitions [ i ] . to = = prev_local_to ) {
auto_advance_to = i ;
prev_playback - > current_transition . next = StringName ( ) ;
break ;
}
}
}
}
if ( p_state_machine - > transitions [ i ] . from = = current & & _check_advance_condition ( p_state_machine , p_state_machine - > transitions [ i ] . transition ) ) {
2019-06-26 13:08:25 +00:00
if ( p_state_machine - > transitions [ i ] . transition - > get_priority ( ) < = priority_best ) {
priority_best = p_state_machine - > transitions [ i ] . transition - > get_priority ( ) ;
2018-08-20 16:38:18 +00:00
auto_advance_to = i ;
}
}
}
if ( auto_advance_to ! = - 1 ) {
2018-12-16 14:43:20 +00:00
next = p_state_machine - > transitions [ auto_advance_to ] . local_to ;
Transition tr ;
tr . from = String ( p_state_machine - > state_machine_name ) + " / " + String ( p_state_machine - > transitions [ auto_advance_to ] . from ) ;
tr . to = String ( p_state_machine - > transitions [ auto_advance_to ] . to ) . replace_first ( " ../ " , " " ) ;
tr . next = p_state_machine - > transitions [ auto_advance_to ] . to ;
current_transition = tr ;
2022-07-28 08:31:23 +00:00
current_curve = p_state_machine - > transitions [ auto_advance_to ] . transition - > get_xfade_curve ( ) ;
2019-06-26 13:08:25 +00:00
next_xfade = p_state_machine - > transitions [ auto_advance_to ] . transition - > get_xfade_time ( ) ;
switch_mode = p_state_machine - > transitions [ auto_advance_to ] . transition - > get_switch_mode ( ) ;
2018-08-20 16:38:18 +00:00
}
}
2018-12-16 14:43:20 +00:00
if ( next = = p_state_machine - > end_node ) {
2022-06-08 02:06:45 +00:00
AnimationNodeStateMachine * prev_state_machine = p_state_machine - > prev_state_machine ;
2018-12-16 14:43:20 +00:00
2022-06-08 02:06:45 +00:00
if ( prev_state_machine ! = nullptr ) {
2022-08-03 00:09:33 +00:00
Ref < AnimationNodeStateMachinePlayback > prev_playback = prev_state_machine - > get_parameter ( p_state_machine - > playback ) ;
2018-12-16 14:43:20 +00:00
if ( prev_playback . is_valid ( ) ) {
if ( next_xfade ) {
prev_playback - > current_transition = current_transition ;
prev_playback - > force_auto_advance = true ;
return rem ;
}
float priority_best = 1e20 ;
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 ( ) ) {
continue ;
}
if ( current_transition . next = = prev_state_machine - > end_node & & _check_advance_condition ( prev_state_machine , prev_state_machine - > transitions [ i ] . transition ) ) {
if ( prev_state_machine - > transitions [ i ] . transition - > get_priority ( ) < = priority_best ) {
priority_best = prev_state_machine - > transitions [ i ] . transition - > get_priority ( ) ;
auto_advance_to = i ;
}
}
}
if ( auto_advance_to ! = - 1 ) {
if ( prev_state_machine - > transitions [ auto_advance_to ] . transition - > get_xfade_time ( ) ) {
return rem ;
}
}
}
}
}
2018-08-20 16:38:18 +00:00
//if next, see when to transition
if ( next ! = StringName ( ) ) {
bool goto_next = false ;
2019-03-17 13:12:27 +00:00
if ( switch_mode = = AnimationNodeStateMachineTransition : : SWITCH_MODE_AT_END ) {
2022-04-14 18:28:42 +00:00
goto_next = next_xfade > = ( len_current - pos_current ) | | end_loop ;
if ( end_loop ) {
2018-08-20 16:38:18 +00:00
next_xfade = 0 ;
}
2019-03-17 13:12:27 +00:00
} else {
goto_next = fading_from = = StringName ( ) ;
2018-08-20 16:38:18 +00:00
}
2022-04-14 18:28:42 +00:00
if ( goto_next ) { //end_loop should be used because fade time may be too small or zero and animation may have looped
2018-08-20 16:38:18 +00:00
if ( next_xfade ) {
//time to fade, baby
fading_from = current ;
fading_time = next_xfade ;
fading_pos = 0 ;
} else {
fading_from = StringName ( ) ;
fading_pos = 0 ;
}
if ( path . size ( ) ) { //if it came from path, remove path
2021-07-03 22:17:03 +00:00
path . remove_at ( 0 ) ;
2018-08-20 16:38:18 +00:00
}
2022-08-03 00:09:33 +00:00
{ // if the current node is a state machine, update the "playing" variable to false by passing -1 in p_time
Ref < AnimationNodeStateMachine > anodesm = p_state_machine - > states [ current ] . node ;
if ( anodesm . is_valid ( ) ) {
p_state_machine - > blend_node ( current , p_state_machine - > states [ current ] . node , - 1 , p_seek , p_seek_root , 0 , AnimationNode : : FILTER_IGNORE , true ) ;
}
}
2018-08-20 16:38:18 +00:00
current = next ;
2022-08-03 00:09:33 +00:00
2018-08-20 16:38:18 +00:00
if ( switch_mode = = AnimationNodeStateMachineTransition : : SWITCH_MODE_SYNC ) {
2022-06-29 06:35:29 +00:00
len_current = p_state_machine - > blend_node ( current , p_state_machine - > states [ current ] . node , 0 , true , p_seek_root , 0 , AnimationNode : : FILTER_IGNORE , true ) ;
2018-08-20 16:38:18 +00:00
pos_current = MIN ( pos_current , len_current ) ;
2022-06-29 06:35:29 +00:00
p_state_machine - > blend_node ( current , p_state_machine - > states [ current ] . node , pos_current , true , p_seek_root , 0 , AnimationNode : : FILTER_IGNORE , true ) ;
2018-08-20 16:38:18 +00:00
} else {
2022-06-29 06:35:29 +00:00
len_current = p_state_machine - > blend_node ( current , p_state_machine - > states [ current ] . node , 0 , true , p_seek_root , 0 , AnimationNode : : FILTER_IGNORE , true ) ;
2018-08-20 16:38:18 +00:00
pos_current = 0 ;
}
rem = len_current ; //so it does not show 0 on transition
}
}
2022-08-03 00:09:33 +00:00
if ( current ! = p_state_machine - > end_node ) {
rem = 1 ; // the time remaining must always be 1 because there is no way to predict how long it takes for the entire state machine to complete
2018-12-16 14:43:20 +00:00
} else {
2022-08-09 19:55:07 +00:00
if ( p_state_machine - > prev_state_machine ! = nullptr ) {
Ref < AnimationNodeStateMachinePlayback > prev_playback = p_state_machine - > prev_state_machine - > get_parameter ( p_state_machine - > playback ) ;
2018-12-16 14:43:20 +00:00
2022-08-09 19:55:07 +00:00
if ( prev_playback . is_valid ( ) ) {
prev_playback - > current_transition = current_transition ;
prev_playback - > force_auto_advance = true ;
}
2018-12-16 14:43:20 +00:00
}
2018-08-20 16:38:18 +00:00
}
return rem ;
}
2018-12-16 14:43:20 +00:00
bool AnimationNodeStateMachinePlayback : : _check_advance_condition ( const Ref < AnimationNodeStateMachine > state_machine , const Ref < AnimationNodeStateMachineTransition > transition ) const {
if ( transition - > has_auto_advance ( ) ) {
return true ;
}
StringName advance_condition_name = transition - > get_advance_condition_name ( ) ;
if ( advance_condition_name ! = StringName ( ) & & bool ( state_machine - > get_parameter ( advance_condition_name ) ) ) {
return true ;
}
2022-04-20 10:36:54 +00:00
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 ;
2022-07-20 11:44:08 +00:00
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.
2022-04-20 10:36:54 +00:00
if ( ! exp - > has_execute_failed ( ) ) {
if ( ret ) {
return true ;
}
}
}
}
2018-12-16 14:43:20 +00:00
return false ;
}
2018-08-20 16:38:18 +00:00
void AnimationNodeStateMachinePlayback : : _bind_methods ( ) {
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 ) ;
2020-09-19 02:59:25 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_current_play_position " ) , & AnimationNodeStateMachinePlayback : : get_current_play_pos ) ;
ClassDB : : bind_method ( D_METHOD ( " get_current_length " ) , & AnimationNodeStateMachinePlayback : : get_current_length ) ;
2018-08-20 16:38:18 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_travel_path " ) , & AnimationNodeStateMachinePlayback : : get_travel_path ) ;
}
AnimationNodeStateMachinePlayback : : AnimationNodeStateMachinePlayback ( ) {
2021-06-17 22:03:09 +00:00
set_local_to_scene ( true ) ; //only one per instantiated scene
2018-08-20 16:38:18 +00:00
}
2018-06-25 19:21:57 +00:00
///////////////////////////////////////////////////////
2018-08-20 16:38:18 +00:00
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 ( ) ;
2020-04-01 23:20:12 +00:00
if ( ac ! = StringName ( ) & & advance_conditions . find ( ac ) = = nullptr ) {
2018-08-20 16:38:18 +00:00
advance_conditions . push_back ( ac ) ;
}
}
advance_conditions . sort_custom < StringName : : AlphCompare > ( ) ;
2021-07-24 13:46:25 +00:00
for ( const StringName & E : advance_conditions ) {
2021-07-16 03:45:57 +00:00
r_list - > push_back ( PropertyInfo ( Variant : : BOOL , E ) ) ;
2018-08-20 16:38:18 +00:00
}
}
Variant AnimationNodeStateMachine : : get_parameter_default_value ( const StringName & p_parameter ) const {
if ( p_parameter = = playback ) {
Ref < AnimationNodeStateMachinePlayback > p ;
2021-06-17 22:03:09 +00:00
p . instantiate ( ) ;
2018-08-20 16:38:18 +00:00
return p ;
} else {
return false ; //advance condition
}
}
void AnimationNodeStateMachine : : add_node ( const StringName & p_name , Ref < AnimationNode > p_node , const Vector2 & p_position ) {
2018-06-25 19:21:57 +00:00
ERR_FAIL_COND ( states . has ( p_name ) ) ;
ERR_FAIL_COND ( p_node . is_null ( ) ) ;
2022-02-03 16:03:38 +00:00
ERR_FAIL_COND ( String ( p_name ) . contains ( " / " ) ) ;
2018-06-25 19:21:57 +00:00
2018-08-20 16:38:18 +00:00
State state ;
state . node = p_node ;
state . position = p_position ;
states [ p_name ] = state ;
2018-06-25 19:21:57 +00:00
2018-12-16 14:43:20 +00:00
Ref < AnimationNodeStateMachine > anodesm = p_node ;
if ( anodesm . is_valid ( ) ) {
anodesm - > state_machine_name = p_name ;
2022-06-08 02:06:45 +00:00
anodesm - > prev_state_machine = this ;
2018-12-16 14:43:20 +00:00
}
2018-06-25 19:21:57 +00:00
emit_changed ( ) ;
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " tree_changed " ) ) ;
2018-08-20 16:38:18 +00:00
2022-07-28 20:56:41 +00:00
p_node - > connect ( " tree_changed " , callable_mp ( this , & AnimationNodeStateMachine : : _tree_changed ) , CONNECT_REFERENCE_COUNTED ) ;
2018-06-25 19:21:57 +00:00
}
2020-03-16 16:42:29 +00:00
void AnimationNodeStateMachine : : replace_node ( const StringName & p_name , Ref < AnimationNode > p_node ) {
ERR_FAIL_COND ( states . has ( p_name ) = = false ) ;
ERR_FAIL_COND ( p_node . is_null ( ) ) ;
2022-02-03 16:03:38 +00:00
ERR_FAIL_COND ( String ( p_name ) . contains ( " / " ) ) ;
2020-03-16 16:42:29 +00:00
{
Ref < AnimationNode > node = states [ p_name ] . node ;
if ( node . is_valid ( ) ) {
2020-12-05 21:01:27 +00:00
node - > disconnect ( " tree_changed " , callable_mp ( this , & AnimationNodeStateMachine : : _tree_changed ) ) ;
2020-03-16 16:42:29 +00:00
}
}
states [ p_name ] . node = p_node ;
emit_changed ( ) ;
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " tree_changed " ) ) ;
2020-03-16 16:42:29 +00:00
2022-07-28 20:56:41 +00:00
p_node - > connect ( " tree_changed " , callable_mp ( this , & AnimationNodeStateMachine : : _tree_changed ) , CONNECT_REFERENCE_COUNTED ) ;
2020-03-16 16:42:29 +00:00
}
2018-12-16 14:43:20 +00:00
bool AnimationNodeStateMachine : : can_edit_node ( const StringName & p_name ) const {
if ( states . has ( p_name ) ) {
return ! ( states [ p_name ] . node - > is_class ( " AnimationNodeStartState " ) | | states [ p_name ] . node - > is_class ( " AnimationNodeEndState " ) ) ;
}
return true ;
}
2018-06-25 19:21:57 +00:00
Ref < AnimationNode > AnimationNodeStateMachine : : get_node ( const StringName & p_name ) const {
ERR_FAIL_COND_V ( ! states . has ( p_name ) , Ref < AnimationNode > ( ) ) ;
2018-08-20 16:38:18 +00:00
return states [ p_name ] . node ;
2018-06-25 19:21:57 +00:00
}
StringName AnimationNodeStateMachine : : get_node_name ( const Ref < AnimationNode > & p_node ) const {
2021-08-09 20:13:42 +00:00
for ( const KeyValue < StringName , State > & E : states ) {
if ( E . value . node = = p_node ) {
return E . key ;
2018-06-25 19:21:57 +00:00
}
}
2018-08-20 16:38:18 +00:00
ERR_FAIL_V ( StringName ( ) ) ;
}
void AnimationNodeStateMachine : : get_child_nodes ( List < ChildNode > * r_child_nodes ) {
Vector < StringName > nodes ;
2021-08-09 20:13:42 +00:00
for ( const KeyValue < StringName , State > & E : states ) {
nodes . push_back ( E . key ) ;
2018-08-20 16:38:18 +00:00
}
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 ) ;
}
2018-06-25 19:21:57 +00:00
}
bool AnimationNodeStateMachine : : has_node ( const StringName & p_name ) const {
return states . has ( p_name ) ;
}
2020-05-14 12:29:06 +00:00
2018-06-25 19:21:57 +00:00
void AnimationNodeStateMachine : : remove_node ( const StringName & p_name ) {
ERR_FAIL_COND ( ! states . has ( p_name ) ) ;
2018-12-16 14:43:20 +00:00
if ( ! can_edit_node ( p_name ) ) {
return ;
2018-06-25 19:21:57 +00:00
}
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
2018-12-16 14:43:20 +00:00
if ( transitions [ i ] . local_from = = p_name | | transitions [ i ] . local_to = = p_name ) {
remove_transition_by_index ( i ) ;
2018-06-25 19:21:57 +00:00
i - - ;
}
}
2018-12-16 14:43:20 +00:00
{
Ref < AnimationNode > node = states [ p_name ] . node ;
ERR_FAIL_COND ( node . is_null ( ) ) ;
node - > disconnect ( " tree_changed " , callable_mp ( this , & AnimationNodeStateMachine : : _tree_changed ) ) ;
2018-06-25 19:21:57 +00:00
}
2018-12-16 14:43:20 +00:00
states . erase ( p_name ) ;
2018-08-20 16:38:18 +00:00
2018-06-25 19:21:57 +00:00
emit_changed ( ) ;
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " tree_changed " ) ) ;
2018-06-25 19:21:57 +00:00
}
void AnimationNodeStateMachine : : rename_node ( const StringName & p_name , const StringName & p_new_name ) {
ERR_FAIL_COND ( ! states . has ( p_name ) ) ;
ERR_FAIL_COND ( states . has ( p_new_name ) ) ;
2018-12-16 14:43:20 +00:00
ERR_FAIL_COND ( ! can_edit_node ( p_name ) ) ;
2018-06-25 19:21:57 +00:00
states [ p_new_name ] = states [ p_name ] ;
states . erase ( p_name ) ;
2018-12-16 14:43:20 +00:00
Ref < AnimationNodeStateMachine > anodesm = states [ p_new_name ] . node ;
if ( anodesm . is_valid ( ) ) {
anodesm - > state_machine_name = p_new_name ;
}
2022-07-31 21:47:19 +00:00
_rename_transitions ( p_name , p_new_name ) ;
2018-06-25 19:21:57 +00:00
2018-12-16 14:43:20 +00:00
emit_signal ( " tree_changed " ) ;
}
2018-06-25 19:21:57 +00:00
2022-07-31 21:47:19 +00:00
void AnimationNodeStateMachine : : _rename_transitions ( const StringName & p_name , const StringName & p_new_name ) {
2018-12-16 14:43:20 +00:00
if ( updating_transitions ) {
return ;
2018-06-25 19:21:57 +00:00
}
2018-12-16 14:43:20 +00:00
updating_transitions = true ;
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
if ( transitions [ i ] . from = = p_name ) {
Vector < String > path = String ( transitions [ i ] . to ) . split ( " / " ) ;
if ( path . size ( ) > 1 ) {
if ( path [ 0 ] = = " .. " ) {
2022-07-31 21:47:19 +00:00
prev_state_machine - > _rename_transitions ( String ( state_machine_name ) + " / " + p_name , String ( state_machine_name ) + " / " + p_new_name ) ;
2018-12-16 14:43:20 +00:00
} else {
2022-07-31 21:47:19 +00:00
( ( Ref < AnimationNodeStateMachine > ) states [ transitions [ i ] . local_to ] . node ) - > _rename_transitions ( " ../ " + p_name , " ../ " + p_new_name ) ;
2018-12-16 14:43:20 +00:00
}
}
2022-07-31 21:47:19 +00:00
if ( transitions [ i ] . local_from = = p_name ) {
transitions . write [ i ] . local_from = p_new_name ;
}
2018-12-16 14:43:20 +00:00
transitions . write [ i ] . from = p_new_name ;
}
2018-06-25 19:21:57 +00:00
2018-12-16 14:43:20 +00:00
if ( transitions [ i ] . to = = p_name ) {
Vector < String > path = String ( transitions [ i ] . from ) . split ( " / " ) ;
if ( path . size ( ) > 1 ) {
if ( path [ 0 ] = = " .. " ) {
2022-07-31 21:47:19 +00:00
prev_state_machine - > _rename_transitions ( String ( state_machine_name ) + " / " + p_name , String ( state_machine_name ) + " / " + p_new_name ) ;
2018-12-16 14:43:20 +00:00
} else {
2022-07-31 21:47:19 +00:00
( ( Ref < AnimationNodeStateMachine > ) states [ transitions [ i ] . local_from ] . node ) - > _rename_transitions ( " ../ " + p_name , " ../ " + p_new_name ) ;
2018-12-16 14:43:20 +00:00
}
}
2022-07-31 21:47:19 +00:00
if ( transitions [ i ] . local_to = = p_name ) {
transitions . write [ i ] . local_to = p_new_name ;
}
2018-12-16 14:43:20 +00:00
transitions . write [ i ] . to = p_new_name ;
}
updating_transitions = false ;
}
2018-06-25 19:21:57 +00:00
}
void AnimationNodeStateMachine : : get_node_list ( List < StringName > * r_nodes ) const {
List < StringName > nodes ;
2021-08-09 20:13:42 +00:00
for ( const KeyValue < StringName , State > & E : states ) {
nodes . push_back ( E . key ) ;
2018-06-25 19:21:57 +00:00
}
nodes . sort_custom < StringName : : AlphCompare > ( ) ;
2021-07-24 13:46:25 +00:00
for ( const StringName & E : nodes ) {
2021-07-16 03:45:57 +00:00
r_nodes - > push_back ( E ) ;
2018-06-25 19:21:57 +00:00
}
}
2022-06-08 02:06:45 +00:00
AnimationNodeStateMachine * AnimationNodeStateMachine : : get_prev_state_machine ( ) const {
2018-12-16 14:43:20 +00:00
return prev_state_machine ;
}
2018-06-25 19:21:57 +00:00
bool AnimationNodeStateMachine : : has_transition ( const StringName & p_from , const StringName & p_to ) const {
2018-12-16 14:43:20 +00:00
StringName from = _get_shortest_path ( p_from ) ;
StringName to = _get_shortest_path ( p_to ) ;
2018-06-25 19:21:57 +00:00
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
2018-12-16 14:43:20 +00:00
if ( transitions [ i ] . from = = from & & transitions [ i ] . to = = to ) {
2018-06-25 19:21:57 +00:00
return true ;
2020-05-14 14:41:43 +00:00
}
2018-06-25 19:21:57 +00:00
}
return false ;
}
int AnimationNodeStateMachine : : find_transition ( const StringName & p_from , const StringName & p_to ) const {
2018-12-16 14:43:20 +00:00
StringName from = _get_shortest_path ( p_from ) ;
StringName to = _get_shortest_path ( p_to ) ;
2018-06-25 19:21:57 +00:00
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
2018-12-16 14:43:20 +00:00
if ( transitions [ i ] . from = = from & & transitions [ i ] . to = = to ) {
2018-06-25 19:21:57 +00:00
return i ;
2020-05-14 14:41:43 +00:00
}
2018-06-25 19:21:57 +00:00
}
return - 1 ;
}
2022-06-08 02:06:45 +00:00
bool AnimationNodeStateMachine : : _can_connect ( const StringName & p_name , Vector < AnimationNodeStateMachine * > p_parents ) {
2018-12-16 14:43:20 +00:00
if ( p_parents . is_empty ( ) ) {
2022-06-08 02:06:45 +00:00
AnimationNodeStateMachine * prev = this ;
while ( prev ! = nullptr ) {
2018-12-16 14:43:20 +00:00
p_parents . push_back ( prev ) ;
prev = prev - > prev_state_machine ;
}
}
if ( states . has ( p_name ) ) {
Ref < AnimationNodeStateMachine > anodesm = states [ p_name ] . node ;
2022-06-08 02:06:45 +00:00
if ( anodesm . is_valid ( ) & & p_parents . find ( anodesm . ptr ( ) ) ! = - 1 ) {
2018-12-16 14:43:20 +00:00
return false ;
}
return true ;
}
String name = p_name ;
Vector < String > path = name . split ( " / " ) ;
if ( path . size ( ) < 2 ) {
return false ;
}
if ( path [ 0 ] = = " .. " ) {
2022-06-08 02:06:45 +00:00
if ( prev_state_machine ! = nullptr ) {
2018-12-16 14:43:20 +00:00
return prev_state_machine - > _can_connect ( name . replace_first ( " ../ " , " " ) , p_parents ) ;
}
} else if ( states . has ( path [ 0 ] ) ) {
Ref < AnimationNodeStateMachine > anodesm = states [ path [ 0 ] ] . node ;
if ( anodesm . is_valid ( ) ) {
return anodesm - > _can_connect ( name . replace_first ( path [ 0 ] + " / " , " " ) , p_parents ) ;
}
}
return false ;
}
StringName AnimationNodeStateMachine : : _get_shortest_path ( const StringName & p_path ) const {
// If p_path is something like StateMachine/../StateMachine2/State1,
// the result will be StateMachine2/State1. This avoid duplicate
// transitions when using add_transition. eg, this two calls is the same:
//
// add_transition("State1", "StateMachine/../State2", tr)
// add_transition("State1", "State2", tr)
//
// but the second call must be invalid because the transition already exists
Vector < String > path = String ( p_path ) . split ( " / " ) ;
Vector < String > new_path ;
for ( int i = 0 ; i < path . size ( ) ; i + + ) {
if ( i > 0 & & path [ i ] = = " .. " & & new_path [ i - 1 ] ! = " .. " ) {
new_path . remove_at ( i - 1 ) ;
} else {
new_path . push_back ( path [ i ] ) ;
}
}
String result ;
for ( int i = 0 ; i < new_path . size ( ) ; i + + ) {
result + = new_path [ i ] + " / " ;
}
result . remove_at ( result . length ( ) - 1 ) ;
return result ;
}
2018-06-25 19:21:57 +00:00
void AnimationNodeStateMachine : : add_transition ( const StringName & p_from , const StringName & p_to , const Ref < AnimationNodeStateMachineTransition > & p_transition ) {
2018-12-16 14:43:20 +00:00
if ( updating_transitions ) {
return ;
}
StringName from = _get_shortest_path ( p_from ) ;
StringName to = _get_shortest_path ( p_to ) ;
Vector < String > path_from = String ( from ) . split ( " / " ) ;
Vector < String > path_to = String ( to ) . split ( " / " ) ;
ERR_FAIL_COND ( from = = end_node | | to = = start_node ) ;
ERR_FAIL_COND ( from = = to ) ;
ERR_FAIL_COND ( ! _can_connect ( from ) ) ;
ERR_FAIL_COND ( ! _can_connect ( to ) ) ;
2018-06-25 19:21:57 +00:00
ERR_FAIL_COND ( p_transition . is_null ( ) ) ;
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
2018-12-16 14:43:20 +00:00
ERR_FAIL_COND ( transitions [ i ] . from = = from & & transitions [ i ] . to = = to ) ;
2018-06-25 19:21:57 +00:00
}
2018-12-16 14:43:20 +00:00
if ( path_from . size ( ) > 1 | | path_to . size ( ) > 1 ) {
ERR_FAIL_COND ( path_from [ 0 ] = = path_to [ 0 ] ) ;
}
updating_transitions = true ;
StringName local_from = String ( from ) . get_slicec ( ' / ' , 0 ) ;
StringName local_to = String ( to ) . get_slicec ( ' / ' , 0 ) ;
local_from = local_from = = " .. " ? " Start " : local_from ;
local_to = local_to = = " .. " ? " End " : local_to ;
2018-06-25 19:21:57 +00:00
Transition tr ;
2018-12-16 14:43:20 +00:00
tr . from = from ;
tr . to = to ;
tr . local_from = local_from ;
tr . local_to = local_to ;
2018-06-25 19:21:57 +00:00
tr . transition = p_transition ;
2022-07-28 20:56:41 +00:00
tr . transition - > connect ( " advance_condition_changed " , callable_mp ( this , & AnimationNodeStateMachine : : _tree_changed ) , CONNECT_REFERENCE_COUNTED ) ;
2018-08-20 16:38:18 +00:00
2018-06-25 19:21:57 +00:00
transitions . push_back ( tr ) ;
2018-12-16 14:43:20 +00:00
// do recursive
if ( path_from . size ( ) > 1 ) {
StringName local_path = String ( from ) . replace_first ( path_from [ 0 ] + " / " , " " ) ;
if ( path_from [ 0 ] = = " .. " ) {
prev_state_machine - > add_transition ( local_path , String ( state_machine_name ) + " / " + to , p_transition ) ;
} else {
( ( Ref < AnimationNodeStateMachine > ) states [ path_from [ 0 ] ] . node ) - > add_transition ( local_path , " ../ " + to , p_transition ) ;
}
}
if ( path_to . size ( ) > 1 ) {
StringName local_path = String ( to ) . replace_first ( path_to [ 0 ] + " / " , " " ) ;
if ( path_to [ 0 ] = = " .. " ) {
prev_state_machine - > add_transition ( String ( state_machine_name ) + " / " + from , local_path , p_transition ) ;
} else {
( ( Ref < AnimationNodeStateMachine > ) states [ path_to [ 0 ] ] . node ) - > add_transition ( " ../ " + from , local_path , p_transition ) ;
}
}
updating_transitions = false ;
2018-06-25 19:21:57 +00:00
}
Ref < AnimationNodeStateMachineTransition > AnimationNodeStateMachine : : get_transition ( int p_transition ) const {
ERR_FAIL_INDEX_V ( p_transition , transitions . size ( ) , Ref < AnimationNodeStateMachineTransition > ( ) ) ;
return transitions [ p_transition ] . transition ;
}
2020-05-14 12:29:06 +00:00
2018-06-25 19:21:57 +00:00
StringName AnimationNodeStateMachine : : get_transition_from ( int p_transition ) const {
ERR_FAIL_INDEX_V ( p_transition , transitions . size ( ) , StringName ( ) ) ;
return transitions [ p_transition ] . from ;
}
2020-05-14 12:29:06 +00:00
2018-06-25 19:21:57 +00:00
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 ( ) ;
}
2020-05-14 12:29:06 +00:00
2018-06-25 19:21:57 +00:00
void AnimationNodeStateMachine : : remove_transition ( const StringName & p_from , const StringName & p_to ) {
2018-12-16 14:43:20 +00:00
StringName from = _get_shortest_path ( p_from ) ;
StringName to = _get_shortest_path ( p_to ) ;
2018-06-25 19:21:57 +00:00
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
2018-12-16 14:43:20 +00:00
if ( transitions [ i ] . from = = from & & transitions [ i ] . to = = to ) {
remove_transition_by_index ( i ) ;
2018-06-25 19:21:57 +00:00
return ;
}
}
}
2018-12-16 14:43:20 +00:00
void AnimationNodeStateMachine : : remove_transition_by_index ( const int p_transition ) {
2018-08-20 16:38:18 +00:00
ERR_FAIL_INDEX ( p_transition , transitions . size ( ) ) ;
2018-12-16 14:43:20 +00:00
Transition tr = transitions [ p_transition ] ;
2020-02-21 17:28:45 +00:00
transitions . write [ p_transition ] . transition - > disconnect ( " advance_condition_changed " , callable_mp ( this , & AnimationNodeStateMachine : : _tree_changed ) ) ;
2021-07-03 22:17:03 +00:00
transitions . remove_at ( p_transition ) ;
2018-06-25 19:21:57 +00:00
2018-12-16 14:43:20 +00:00
Vector < String > path_from = String ( tr . from ) . split ( " / " ) ;
Vector < String > path_to = String ( tr . to ) . split ( " / " ) ;
2018-06-25 19:21:57 +00:00
2018-12-16 14:43:20 +00:00
List < Vector < String > > paths ;
paths . push_back ( path_from ) ;
paths . push_back ( path_to ) ;
2018-06-25 19:21:57 +00:00
2018-12-16 14:43:20 +00:00
for ( List < Vector < String > > : : Element * E = paths . front ( ) ; E ; E = E - > next ( ) ) {
if ( E - > get ( ) [ 0 ] . size ( ) > 1 ) {
if ( E - > get ( ) [ 0 ] = = " .. " ) {
prev_state_machine - > _remove_transition ( tr . transition ) ;
} else if ( states . has ( E - > get ( ) [ 0 ] ) ) {
Ref < AnimationNodeStateMachine > anodesm = states [ E - > get ( ) [ 0 ] ] . node ;
if ( anodesm . is_valid ( ) ) {
anodesm - > _remove_transition ( tr . transition ) ;
}
}
}
}
2018-06-25 19:21:57 +00:00
}
2018-12-16 14:43:20 +00:00
void AnimationNodeStateMachine : : _remove_transition ( const Ref < AnimationNodeStateMachineTransition > p_transition ) {
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
if ( transitions [ i ] . transition = = p_transition ) {
remove_transition_by_index ( i ) ;
return ;
}
}
2018-06-25 19:21:57 +00:00
}
void AnimationNodeStateMachine : : set_graph_offset ( const Vector2 & p_offset ) {
graph_offset = p_offset ;
}
Vector2 AnimationNodeStateMachine : : get_graph_offset ( ) const {
return graph_offset ;
}
2022-05-04 18:53:48 +00:00
double AnimationNodeStateMachine : : process ( double p_time , bool p_seek , bool p_seek_root ) {
2018-08-20 16:38:18 +00:00
Ref < AnimationNodeStateMachinePlayback > playback = get_parameter ( this - > playback ) ;
ERR_FAIL_COND_V ( playback . is_null ( ) , 0.0 ) ;
2018-06-25 19:21:57 +00:00
2022-05-04 18:53:48 +00:00
return playback - > process ( this , p_time , p_seek , p_seek_root ) ;
2018-06-25 19:21:57 +00:00
}
String AnimationNodeStateMachine : : get_caption ( ) const {
return " StateMachine " ;
}
2018-12-16 14:43:20 +00:00
bool AnimationNodeStateMachine : : has_local_transition ( const StringName & p_from , const StringName & p_to ) const {
StringName from = _get_shortest_path ( p_from ) ;
StringName to = _get_shortest_path ( p_to ) ;
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
if ( transitions [ i ] . local_from = = from & & transitions [ i ] . local_to = = to ) {
return true ;
}
}
return false ;
}
2018-08-20 16:38:18 +00:00
Ref < AnimationNode > AnimationNodeStateMachine : : get_child_by_name ( const StringName & p_name ) {
return get_node ( p_name ) ;
2018-06-25 19:21:57 +00:00
}
bool AnimationNodeStateMachine : : _set ( const StringName & p_name , const Variant & p_value ) {
String name = p_name ;
if ( name . begins_with ( " states/ " ) ) {
String node_name = name . get_slicec ( ' / ' , 1 ) ;
String what = name . get_slicec ( ' / ' , 2 ) ;
if ( what = = " node " ) {
Ref < AnimationNode > anode = p_value ;
if ( anode . is_valid ( ) ) {
add_node ( node_name , p_value ) ;
}
return true ;
}
if ( what = = " position " ) {
if ( states . has ( node_name ) ) {
2018-08-20 16:38:18 +00:00
states [ node_name ] . position = p_value ;
2018-06-25 19:21:57 +00:00
}
return true ;
}
} else if ( name = = " transitions " ) {
Array trans = p_value ;
ERR_FAIL_COND_V ( trans . size ( ) % 3 ! = 0 , false ) ;
for ( int i = 0 ; i < trans . size ( ) ; i + = 3 ) {
add_transition ( trans [ i ] , trans [ i + 1 ] , trans [ i + 2 ] ) ;
}
return true ;
} else if ( name = = " graph_offset " ) {
set_graph_offset ( p_value ) ;
return true ;
}
return false ;
}
bool AnimationNodeStateMachine : : _get ( const StringName & p_name , Variant & r_ret ) const {
String name = p_name ;
if ( name . begins_with ( " states/ " ) ) {
String node_name = name . get_slicec ( ' / ' , 1 ) ;
String what = name . get_slicec ( ' / ' , 2 ) ;
if ( what = = " node " ) {
2018-12-16 14:43:20 +00:00
if ( states . has ( node_name ) & & can_edit_node ( node_name ) ) {
2018-08-20 16:38:18 +00:00
r_ret = states [ node_name ] . node ;
2018-06-25 19:21:57 +00:00
return true ;
}
}
if ( what = = " position " ) {
if ( states . has ( node_name ) ) {
2018-08-20 16:38:18 +00:00
r_ret = states [ node_name ] . position ;
2018-06-25 19:21:57 +00:00
return true ;
}
}
} else if ( name = = " transitions " ) {
Array trans ;
for ( int i = 0 ; i < transitions . size ( ) ; i + + ) {
2018-12-16 14:43:20 +00:00
String from = transitions [ i ] . from ;
String to = transitions [ i ] . to ;
if ( from . get_slicec ( ' / ' , 0 ) = = " .. " | | to . get_slicec ( ' / ' , 0 ) = = " .. " ) {
continue ;
}
trans . push_back ( from ) ;
trans . push_back ( to ) ;
trans . push_back ( transitions [ i ] . transition ) ;
2018-06-25 19:21:57 +00:00
}
r_ret = trans ;
return true ;
} else if ( name = = " graph_offset " ) {
r_ret = get_graph_offset ( ) ;
return true ;
}
return false ;
}
2020-05-14 12:29:06 +00:00
2018-06-25 19:21:57 +00:00
void AnimationNodeStateMachine : : _get_property_list ( List < PropertyInfo > * p_list ) const {
List < StringName > names ;
2021-08-09 20:13:42 +00:00
for ( const KeyValue < StringName , State > & E : states ) {
names . push_back ( E . key ) ;
2018-06-25 19:21:57 +00:00
}
names . sort_custom < StringName : : AlphCompare > ( ) ;
2021-07-24 13:46:25 +00:00
for ( const StringName & name : names ) {
2021-11-03 22:06:17 +00:00
p_list - > push_back ( PropertyInfo ( Variant : : OBJECT , " states/ " + name + " /node " , PROPERTY_HINT_RESOURCE_TYPE , " AnimationNode " , PROPERTY_USAGE_NO_EDITOR ) ) ;
p_list - > push_back ( PropertyInfo ( Variant : : VECTOR2 , " states/ " + name + " /position " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR ) ) ;
2018-06-25 19:21:57 +00:00
}
2021-11-03 22:06:17 +00:00
p_list - > push_back ( PropertyInfo ( Variant : : ARRAY , " transitions " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR ) ) ;
p_list - > push_back ( PropertyInfo ( Variant : : VECTOR2 , " graph_offset " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NO_EDITOR ) ) ;
2018-06-25 19:21:57 +00:00
}
2021-02-11 17:18:45 +00:00
void AnimationNodeStateMachine : : reset_state ( ) {
states . clear ( ) ;
transitions . clear ( ) ;
playback = " playback " ;
2018-12-16 14:43:20 +00:00
start_node = " Start " ;
end_node = " End " ;
2021-02-11 17:18:45 +00:00
graph_offset = Vector2 ( ) ;
2018-12-16 14:43:20 +00:00
Ref < AnimationNodeStartState > s ;
s . instantiate ( ) ;
State start ;
start . node = s ;
start . position = Vector2 ( 200 , 100 ) ;
states [ start_node ] = start ;
Ref < AnimationNodeEndState > e ;
e . instantiate ( ) ;
State end ;
end . node = e ;
end . position = Vector2 ( 900 , 100 ) ;
states [ end_node ] = end ;
2021-02-11 17:18:45 +00:00
emit_changed ( ) ;
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " tree_changed " ) ) ;
2021-02-11 17:18:45 +00:00
}
2018-08-20 16:38:18 +00:00
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 ( ) {
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " tree_changed " ) ) ;
2018-08-20 16:38:18 +00:00
}
2018-06-25 19:21:57 +00:00
void AnimationNodeStateMachine : : _bind_methods ( ) {
2018-08-20 16:38:18 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_node " , " name " , " node " , " position " ) , & AnimationNodeStateMachine : : add_node , DEFVAL ( Vector2 ( ) ) ) ;
2020-03-16 16:42:29 +00:00
ClassDB : : bind_method ( D_METHOD ( " replace_node " , " name " , " node " ) , & AnimationNodeStateMachine : : replace_node ) ;
2018-06-25 19:21:57 +00:00
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 ) ;
2018-08-20 16:38:18 +00:00
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 ) ;
2018-08-20 13:58:53 +00:00
ClassDB : : bind_method ( D_METHOD ( " has_transition " , " from " , " to " ) , & AnimationNodeStateMachine : : has_transition ) ;
2018-06-25 19:21:57 +00:00
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 ) ;
ClassDB : : bind_method ( D_METHOD ( " get_transition_to " , " idx " ) , & AnimationNodeStateMachine : : get_transition_to ) ;
ClassDB : : bind_method ( D_METHOD ( " get_transition_count " ) , & AnimationNodeStateMachine : : get_transition_count ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_transition_by_index " , " idx " ) , & AnimationNodeStateMachine : : remove_transition_by_index ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_transition " , " from " , " to " ) , & AnimationNodeStateMachine : : remove_transition ) ;
2019-04-07 11:21:31 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_graph_offset " , " offset " ) , & AnimationNodeStateMachine : : set_graph_offset ) ;
2018-06-25 19:21:57 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_graph_offset " ) , & AnimationNodeStateMachine : : get_graph_offset ) ;
}
AnimationNodeStateMachine : : AnimationNodeStateMachine ( ) {
2018-12-16 14:43:20 +00:00
Ref < AnimationNodeStartState > s ;
s . instantiate ( ) ;
State start ;
start . node = s ;
start . position = Vector2 ( 200 , 100 ) ;
states [ start_node ] = start ;
Ref < AnimationNodeEndState > e ;
e . instantiate ( ) ;
State end ;
end . node = e ;
end . position = Vector2 ( 900 , 100 ) ;
states [ end_node ] = end ;
2018-06-25 19:21:57 +00:00
}