Skip to content

Commit d6b20fd

Browse files
committed
Merge pull request #98402 from vaner-org/highlight-node-neighbours
Reduce opacity of nodes with no shared transition when selecting an AnimationNodeStateMachine node
2 parents 463c479 + 366fa9f commit d6b20fd

5 files changed

+174
-16
lines changed

editor/plugins/animation_state_machine_editor.cpp

+144-15
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ void AnimationNodeStateMachineEditor::edit(const Ref<AnimationNode> &p_node) {
7171
selected_transition_index = -1;
7272
selected_node = StringName();
7373
selected_nodes.clear();
74+
connected_nodes.clear();
7475
_update_mode();
7576
_update_graph();
7677
}
@@ -214,6 +215,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
214215
}
215216

216217
selected_nodes.insert(selected_node);
218+
_update_connected_nodes(selected_node);
217219

218220
Ref<AnimationNode> anode = state_machine->get_node(selected_node);
219221
EditorNode::get_singleton()->push_item(anode.ptr(), "", true);
@@ -277,6 +279,11 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
277279
selected_transition_to = transition_lines[closest].to_node;
278280
selected_transition_index = closest;
279281

282+
// Update connected_nodes for the selected transition.
283+
connected_nodes.clear();
284+
connected_nodes.insert(selected_transition_from);
285+
connected_nodes.insert(selected_transition_to);
286+
280287
Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(closest);
281288
if (!state_machine->is_transition_across_group(closest)) {
282289
EditorNode::get_singleton()->push_item(tr.ptr(), "", true);
@@ -377,6 +384,48 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv
377384
_update_mode();
378385
}
379386

387+
if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
388+
StringName clicked_node;
389+
for (int i = node_rects.size() - 1; i >= 0; i--) {
390+
if (node_rects[i].node.has_point(mb->get_position())) {
391+
clicked_node = node_rects[i].node_name;
392+
break;
393+
}
394+
}
395+
396+
if (clicked_node != StringName()) {
397+
if (selected_nodes.has(clicked_node) && mb->is_shift_pressed()) {
398+
selected_nodes.erase(clicked_node);
399+
} else {
400+
if (!mb->is_shift_pressed()) {
401+
selected_nodes.clear();
402+
}
403+
selected_nodes.insert(clicked_node);
404+
}
405+
selected_node = clicked_node;
406+
} else {
407+
// Clicked on empty space.
408+
selected_nodes.clear();
409+
selected_node = StringName();
410+
}
411+
412+
_update_connected_nodes(selected_node);
413+
state_machine_draw->queue_redraw();
414+
_update_mode();
415+
416+
if (clicked_node != StringName()) {
417+
Ref<AnimationNode> anode = state_machine->get_node(clicked_node);
418+
EditorNode::get_singleton()->push_item(anode.ptr(), "", true);
419+
dragging_selected_attempt = true;
420+
dragging_selected = false;
421+
drag_from = mb->get_position();
422+
snap_x = StringName();
423+
snap_y = StringName();
424+
}
425+
426+
return;
427+
}
428+
380429
Ref<InputEventMouseMotion> mm = p_event;
381430

382431
// Pan window
@@ -860,11 +909,15 @@ void AnimationNodeStateMachineEditor::_add_transition(const bool p_nested_action
860909
connecting = false;
861910
}
862911

863-
void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, float p_fade_ratio, bool p_auto_advance, bool p_is_across_group) {
912+
void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, float p_fade_ratio, bool p_auto_advance, bool p_is_across_group, float p_opacity) {
864913
Color line_color = p_enabled ? theme_cache.transition_color : theme_cache.transition_disabled_color;
865914
Color icon_color = p_enabled ? theme_cache.transition_icon_color : theme_cache.transition_icon_disabled_color;
866915
Color highlight_color = p_enabled ? theme_cache.highlight_color : theme_cache.highlight_disabled_color;
867916

917+
line_color.a *= p_opacity;
918+
icon_color.a *= p_opacity;
919+
highlight_color.a *= p_opacity;
920+
868921
if (p_travel) {
869922
line_color = highlight_color;
870923
}
@@ -877,6 +930,7 @@ void AnimationNodeStateMachineEditor::_connection_draw(const Vector2 &p_from, co
877930
if (p_fade_ratio > 0.0) {
878931
Color fade_line_color = highlight_color;
879932
fade_line_color.set_hsv(1.0, fade_line_color.get_s(), fade_line_color.get_v());
933+
fade_line_color.a *= p_opacity;
880934
state_machine_draw->draw_line(p_from, p_from.lerp(p_to, p_fade_ratio), fade_line_color, 2);
881935
}
882936

@@ -921,6 +975,25 @@ void AnimationNodeStateMachineEditor::_clip_dst_line_to_rect(const Vector2 &p_fr
921975
}
922976
}
923977

978+
Ref<StyleBox> AnimationNodeStateMachineEditor::_adjust_stylebox_opacity(Ref<StyleBox> p_style, float p_opacity) {
979+
Ref<StyleBox> style = p_style->duplicate();
980+
if (style->is_class("StyleBoxFlat")) {
981+
Ref<StyleBoxFlat> flat_style = style;
982+
Color bg_color = flat_style->get_bg_color();
983+
Color border_color = flat_style->get_border_color();
984+
Color shadow_color = flat_style->get_shadow_color();
985+
986+
bg_color.a *= p_opacity;
987+
border_color.a *= p_opacity;
988+
shadow_color.a *= p_opacity;
989+
990+
flat_style->set_bg_color(bg_color);
991+
flat_style->set_border_color(border_color);
992+
flat_style->set_shadow_color(shadow_color);
993+
}
994+
return style;
995+
}
996+
924997
void AnimationNodeStateMachineEditor::_state_machine_draw() {
925998
AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();
926999
if (!tree) {
@@ -1112,7 +1185,30 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
11121185
for (int i = 0; i < transition_lines.size(); i++) {
11131186
TransitionLine tl = transition_lines[i];
11141187
if (!tl.hidden) {
1115-
_connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, tl.selected, tl.travel, tl.fade_ratio, tl.auto_advance, tl.is_across_group);
1188+
float opacity = 0.2; // Default to reduced opacity.
1189+
1190+
if (selected_transition_from != StringName() && selected_transition_to != StringName()) {
1191+
// A transition is selected.
1192+
if ((tl.from_node == selected_transition_from && tl.to_node == selected_transition_to) || (tl.from_node == selected_transition_to && tl.to_node == selected_transition_from)) {
1193+
opacity = 1.0; // Full opacity for the selected transition pair.
1194+
}
1195+
} else if (!connected_nodes.is_empty()) {
1196+
// A node is selected.
1197+
if (connected_nodes.has(selected_node)) {
1198+
// Only keep full opacity for transitions directly connected to the selected node.
1199+
if (tl.from_node == selected_node || tl.to_node == selected_node) {
1200+
opacity = 1.0;
1201+
}
1202+
} else {
1203+
// If no node is selected, all transitions are at full opacity.
1204+
opacity = 1.0;
1205+
}
1206+
} else {
1207+
// If nothing is selected, all transitions are at full opacity.
1208+
opacity = 1.0;
1209+
}
1210+
1211+
_connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, tl.selected, tl.travel, tl.fade_ratio, tl.auto_advance, tl.is_across_group, opacity);
11161212
}
11171213
}
11181214

@@ -1129,39 +1225,55 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
11291225
Vector2 offset = nr.node.position;
11301226
int h = nr.node.size.height;
11311227

1132-
//prepre rect
1228+
float opacity = 1.0;
1229+
if (selected_transition_from != StringName() && selected_transition_to != StringName()) {
1230+
// A transition is selected.
1231+
if (name != selected_transition_from && name != selected_transition_to) {
1232+
opacity = 0.2;
1233+
}
1234+
} else if (!connected_nodes.is_empty() && !connected_nodes.has(name)) {
1235+
// A node is selected.
1236+
opacity = 0.2;
1237+
}
11331238

1134-
//now scroll it to draw
1135-
Ref<StyleBox> node_frame_style = is_selected ? theme_cache.node_frame_selected : theme_cache.node_frame;
1136-
state_machine_draw->draw_style_box(node_frame_style, nr.node);
1239+
Ref<StyleBox> original_style = is_selected ? theme_cache.node_frame_selected : theme_cache.node_frame;
1240+
Ref<StyleBox> node_style = _adjust_stylebox_opacity(original_style, opacity);
1241+
1242+
state_machine_draw->draw_style_box(node_style, nr.node);
11371243

11381244
if (!is_selected && AnimationNodeStateMachine::START_NODE == name) {
1139-
state_machine_draw->draw_style_box(theme_cache.node_frame_start, nr.node);
1245+
Ref<StyleBox> start_style = _adjust_stylebox_opacity(theme_cache.node_frame_start, opacity);
1246+
state_machine_draw->draw_style_box(start_style, nr.node);
11401247
}
11411248
if (!is_selected && AnimationNodeStateMachine::END_NODE == name) {
1142-
state_machine_draw->draw_style_box(theme_cache.node_frame_end, nr.node);
1249+
Ref<StyleBox> end_style = _adjust_stylebox_opacity(theme_cache.node_frame_end, opacity);
1250+
state_machine_draw->draw_style_box(end_style, nr.node);
11431251
}
11441252
if (playing && (blend_from == name || current == name || travel_path.has(name))) {
1145-
state_machine_draw->draw_style_box(theme_cache.node_frame_playing, nr.node);
1253+
Ref<StyleBox> playing_style = _adjust_stylebox_opacity(theme_cache.node_frame_playing, opacity);
1254+
state_machine_draw->draw_style_box(playing_style, nr.node);
11461255
}
11471256

1148-
offset.x += node_frame_style->get_offset().x;
1257+
offset.x += original_style->get_offset().x;
11491258

11501259
nr.play.position = offset + Vector2(0, (h - theme_cache.play_node->get_height()) / 2).floor();
11511260
nr.play.size = theme_cache.play_node->get_size();
11521261

1262+
Color color_mod = Color(1, 1, 1, opacity);
11531263
if (hovered_node_name == name && hovered_node_area == HOVER_NODE_PLAY) {
1154-
state_machine_draw->draw_texture(theme_cache.play_node, nr.play.position, theme_cache.highlight_color);
1264+
state_machine_draw->draw_texture(theme_cache.play_node, nr.play.position, theme_cache.highlight_color * color_mod);
11551265
} else {
1156-
state_machine_draw->draw_texture(theme_cache.play_node, nr.play.position);
1266+
state_machine_draw->draw_texture(theme_cache.play_node, nr.play.position, color_mod);
11571267
}
11581268

11591269
offset.x += sep + theme_cache.play_node->get_width();
11601270

11611271
nr.name.position = offset + Vector2(0, (h - theme_cache.node_title_font->get_height(theme_cache.node_title_font_size)) / 2).floor();
11621272
nr.name.size = Vector2(name_string_size, theme_cache.node_title_font->get_height(theme_cache.node_title_font_size));
11631273

1164-
state_machine_draw->draw_string(theme_cache.node_title_font, nr.name.position + Vector2(0, theme_cache.node_title_font->get_ascent(theme_cache.node_title_font_size)), name, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.node_title_font_size, theme_cache.node_title_font_color);
1274+
Color font_color = theme_cache.node_title_font_color;
1275+
font_color.a *= opacity;
1276+
state_machine_draw->draw_string(theme_cache.node_title_font, nr.name.position + Vector2(0, theme_cache.node_title_font->get_ascent(theme_cache.node_title_font_size)), name, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.node_title_font_size, font_color);
11651277
offset.x += name_string_size + sep;
11661278

11671279
nr.can_edit = needs_editor;
@@ -1170,9 +1282,9 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
11701282
nr.edit.size = theme_cache.edit_node->get_size();
11711283

11721284
if (hovered_node_name == name && hovered_node_area == HOVER_NODE_EDIT) {
1173-
state_machine_draw->draw_texture(theme_cache.edit_node, nr.edit.position, theme_cache.highlight_color);
1285+
state_machine_draw->draw_texture(theme_cache.edit_node, nr.edit.position, theme_cache.highlight_color * color_mod);
11741286
} else {
1175-
state_machine_draw->draw_texture(theme_cache.edit_node, nr.edit.position);
1287+
state_machine_draw->draw_texture(theme_cache.edit_node, nr.edit.position, color_mod);
11761288
}
11771289
}
11781290
}
@@ -1201,6 +1313,23 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() {
12011313
state_machine_play_pos->queue_redraw();
12021314
}
12031315

1316+
void AnimationNodeStateMachineEditor::_update_connected_nodes(const StringName &p_node) {
1317+
connected_nodes.clear();
1318+
if (p_node != StringName()) {
1319+
connected_nodes.insert(p_node);
1320+
1321+
Vector<StringName> nodes_to = state_machine->get_nodes_with_transitions_to(p_node);
1322+
for (const StringName &node_to : nodes_to) {
1323+
connected_nodes.insert(node_to);
1324+
}
1325+
1326+
Vector<StringName> nodes_from = state_machine->get_nodes_with_transitions_from(p_node);
1327+
for (const StringName &node_from : nodes_from) {
1328+
connected_nodes.insert(node_from);
1329+
}
1330+
}
1331+
}
1332+
12041333
void AnimationNodeStateMachineEditor::_state_machine_pos_draw_individual(const String &p_name, float p_ratio) {
12051334
AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_animation_tree();
12061335
if (!tree) {

editor/plugins/animation_state_machine_editor.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin {
131131
static AnimationNodeStateMachineEditor *singleton;
132132

133133
void _state_machine_gui_input(const Ref<InputEvent> &p_event);
134-
void _connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, float p_fade_ratio, bool p_auto_advance, bool p_is_across_group);
134+
void _connection_draw(const Vector2 &p_from, const Vector2 &p_to, AnimationNodeStateMachineTransition::SwitchMode p_mode, bool p_enabled, bool p_selected, bool p_travel, float p_fade_ratio, bool p_auto_advance, bool p_is_across_group, float p_opacity = 1.0);
135135

136136
void _state_machine_draw();
137137

@@ -288,6 +288,11 @@ class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin {
288288
MENU_LOAD_FILE_CONFIRM = 1002
289289
};
290290

291+
HashSet<StringName> connected_nodes;
292+
void _update_connected_nodes(const StringName &p_node);
293+
294+
Ref<StyleBox> _adjust_stylebox_opacity(Ref<StyleBox> p_style, float p_opacity);
295+
291296
protected:
292297
void _notification(int p_what);
293298
static void _bind_methods();

editor/themes/editor_theme_manager.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -2499,6 +2499,7 @@ void EditorThemeManager::_populate_editor_styles(const Ref<EditorTheme> &p_theme
24992499
Ref<StyleBoxFlat> sm_node_playing_style = sm_node_selected_style->duplicate();
25002500
sm_node_playing_style->set_border_color(p_config.warning_color);
25012501
sm_node_playing_style->set_shadow_color(p_config.warning_color * Color(1, 1, 1, 0.2));
2502+
sm_node_playing_style->set_draw_center(false);
25022503

25032504
p_theme->set_stylebox("node_frame", "GraphStateMachine", sm_node_style);
25042505
p_theme->set_stylebox("node_frame_selected", "GraphStateMachine", sm_node_selected_style);

scene/animation/animation_node_state_machine.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -1841,6 +1841,26 @@ void AnimationNodeStateMachine::_bind_methods() {
18411841
BIND_ENUM_CONSTANT(STATE_MACHINE_TYPE_GROUPED);
18421842
}
18431843

1844+
Vector<StringName> AnimationNodeStateMachine::get_nodes_with_transitions_from(const StringName &p_node) const {
1845+
Vector<StringName> result;
1846+
for (const Transition &transition : transitions) {
1847+
if (transition.from == p_node) {
1848+
result.push_back(transition.to);
1849+
}
1850+
}
1851+
return result;
1852+
}
1853+
1854+
Vector<StringName> AnimationNodeStateMachine::get_nodes_with_transitions_to(const StringName &p_node) const {
1855+
Vector<StringName> result;
1856+
for (const Transition &transition : transitions) {
1857+
if (transition.to == p_node) {
1858+
result.push_back(transition.from);
1859+
}
1860+
}
1861+
return result;
1862+
}
1863+
18441864
AnimationNodeStateMachine::AnimationNodeStateMachine() {
18451865
START_NODE = "Start";
18461866
END_NODE = "End";

scene/animation/animation_node_state_machine.h

+3
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ class AnimationNodeStateMachine : public AnimationRootNode {
223223
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
224224
#endif
225225

226+
Vector<StringName> get_nodes_with_transitions_from(const StringName &p_node) const;
227+
Vector<StringName> get_nodes_with_transitions_to(const StringName &p_node) const;
228+
226229
AnimationNodeStateMachine();
227230
};
228231

0 commit comments

Comments
 (0)