├── .import ├── icon.png-487276ed1e3a0c39cad0279d744ee560.md5 └── icon.png-487276ed1e3a0c39cad0279d744ee560.stex ├── LICENSE ├── Outline_Shader.tres ├── README.md ├── Skill_Tree_Line.tscn ├── Skill_Tree_Node.gd ├── Skill_Tree_Node.tscn ├── Skill_Tree_Test.tscn ├── default_env.tres ├── icon.png ├── icon.png.import └── project.godot /.import/icon.png-487276ed1e3a0c39cad0279d744ee560.md5: -------------------------------------------------------------------------------- 1 | source_md5="47313fa4c47a9963fddd764e1ec6e4a8" 2 | dest_md5="2ded9e7f9060e2b530aab678b135fc5b" 3 | 4 | -------------------------------------------------------------------------------- /.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Reneator/godot-skill-tree/80ec34159b2762ea08f83bfcfb6996365c327f72/.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Reneator 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Outline_Shader.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="ShaderMaterial" load_steps=2 format=2] 2 | 3 | [sub_resource type="Shader" id=1] 4 | code = "shader_type canvas_item; 5 | render_mode blend_premul_alpha; 6 | 7 | // This shader only works properly with premultiplied alpha blend mode 8 | uniform float aura_width = 2.0; 9 | uniform vec4 aura_color : hint_color; 10 | 11 | void fragment() { 12 | vec4 col = texture(TEXTURE, UV); 13 | vec2 ps = TEXTURE_PIXEL_SIZE; 14 | float a; 15 | float maxa = col.a; 16 | float mina = col.a; 17 | 18 | a = texture(TEXTURE, UV + vec2(0.0, -aura_width) * ps).a; 19 | maxa = max(a, maxa); 20 | mina = min(a, mina); 21 | 22 | a = texture(TEXTURE, UV + vec2(0.0, aura_width) * ps).a; 23 | maxa = max(a, maxa); 24 | mina = min(a, mina); 25 | 26 | a = texture(TEXTURE, UV + vec2(-aura_width, 0.0) * ps).a; 27 | maxa = max(a, maxa); 28 | mina = min(a, mina); 29 | 30 | a = texture(TEXTURE, UV + vec2(aura_width, 0.0) * ps).a; 31 | maxa = max(a, maxa); 32 | mina = min(a, mina); 33 | 34 | col.rgb *= col.a; 35 | 36 | COLOR = col; 37 | COLOR.rgb += aura_color.rgb * (maxa - mina); 38 | } 39 | " 40 | 41 | [resource] 42 | shader = SubResource( 1 ) 43 | shader_param/aura_width = 2.0 44 | shader_param/aura_color = null 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # godot-skill-tree 2 | This is just quickly made skill-tree in godot using tool-scripts. It refreshes the connections as you make changes to the nodes in the skill-tree 3 | 4 | If you want to see any kind of feature regarding the skill-tree, just go ahead and create an issues ticket. Really interested in your ideas. And when implemented i can go ahead and try to explain how it works, if anyone is interested. 5 | 6 | 7 | As corner-points: tool makes it run in the editor, and by defining set-methods for the variables i can set behaviour for whenever the value gets changed. 8 | Additionally i have the line's second point reset every frame as long as its in the editor, so this way the Line will always remain connected with the previous node. 9 | 10 | As soon as you start the scene, the line wont be updated anymore, as its not necessary anymore. 11 | -------------------------------------------------------------------------------- /Skill_Tree_Line.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene format=2] 2 | 3 | [node name="Node2D" type="Line2D"] 4 | show_behind_parent = true 5 | points = PoolVector2Array( 0, 0 ) 6 | -------------------------------------------------------------------------------- /Skill_Tree_Node.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends TextureButton 3 | 4 | export (Array, NodePath) var previous_nodes_paths setget set_previous_nodes_paths 5 | var previous_nodes 6 | export (PackedScene) var tree_node_line_scene 7 | enum PREVIOUS_NODES_TYPE {ANY,ALL} 8 | export (PREVIOUS_NODES_TYPE) var previous_nodes_type #any -> one skill being learned unlocks it, all -> all previous nodes have to be learned 9 | 10 | export (String) var talent_row_id 11 | var talent_row_nodes = [] 12 | export (int) var max_row_selections = 1 13 | var current_row_selections_count = 0 14 | 15 | export (Texture) var texture setget set_texture 16 | export (ShaderMaterial) var on_learned_shader 17 | export (String) var tooltip_text 18 | export (bool) var unlocked = false setget set_unlocked 19 | export (bool) var learned = false 20 | 21 | export (Color) var active_tint = Color(1,1,1,1) 22 | export (Color) var inactive_tint = Color(0.6,0.6,0.6,1) 23 | 24 | var active = false 25 | 26 | signal on_learned(node) 27 | signal on_unlocked() 28 | 29 | func set_previous_nodes_paths(_paths): 30 | previous_nodes_paths = _paths 31 | initialize_previous_nodes_connection() 32 | refresh_lines() 33 | 34 | func _init(): 35 | texture_normal = texture 36 | 37 | func _ready(): 38 | initialize_talent_row() 39 | initialize_previous_nodes_connection() 40 | refresh_lines() 41 | 42 | func initialize_talent_row(): 43 | if Engine.is_editor_hint(): 44 | return 45 | if not talent_row_id: 46 | return 47 | var group_name = "talent_row_%s" % talent_row_id 48 | add_to_group(group_name) 49 | var talent_row_skill_nodes = get_tree().get_nodes_in_group(group_name) 50 | for node in talent_row_skill_nodes: 51 | node.add_talent_row_node(self) 52 | add_talent_row_node(node) 53 | 54 | func add_talent_row_node(node): 55 | if node == self: 56 | return 57 | if not node in talent_row_nodes: 58 | talent_row_nodes.append(node) 59 | node.connect("on_learned",self, "on_talent_row_learned") 60 | 61 | func on_talent_row_learned(node): 62 | current_row_selections_count += 1 63 | if max_row_selections <= current_row_selections_count: 64 | set_unlocked(false) 65 | 66 | func initialize_previous_nodes_connection(): 67 | previous_nodes = [] 68 | for previous_node_path in previous_nodes_paths: 69 | previous_nodes.append(get_node_or_null(previous_node_path)) 70 | 71 | for previous_node in previous_nodes: 72 | if not previous_node: 73 | return 74 | previous_node.connect("on_learned", self, "on_learned") 75 | 76 | # unlocked = previous_nodes.empty() 77 | 78 | func on_learned(previous_node): 79 | check_unlocked() 80 | 81 | func check_unlocked(): 82 | match previous_nodes_type: 83 | PREVIOUS_NODES_TYPE.ANY: 84 | for previous_node in previous_nodes: 85 | if previous_node.learned: 86 | set_unlocked(true) 87 | return 88 | return 89 | PREVIOUS_NODES_TYPE.ALL: 90 | var unlocked = true 91 | for previous_node in previous_nodes: 92 | unlocked = unlocked && previous_node.learned 93 | set_unlocked(unlocked) 94 | 95 | func set_unlocked(_unlocked): 96 | unlocked = _unlocked 97 | disabled = !unlocked 98 | set_active(unlocked) 99 | emit_signal("on_unlocked") 100 | print("Skill_Node %s unlocked" % name) 101 | 102 | func set_active(_active): 103 | active = _active 104 | if active: 105 | modulate = active_tint 106 | else: 107 | modulate = inactive_tint 108 | 109 | func set_texture(_texture): 110 | texture = _texture 111 | texture_normal = texture 112 | 113 | var lines #previous_node : line2D 114 | 115 | func refresh_lines(): #this sets the line2d to be connected with the previous_nodes 116 | if not lines: 117 | lines = {} 118 | if Engine.is_editor_hint(): # deletes old lines if we are in the editor 119 | for node in lines: 120 | var line = lines[node] 121 | print("line freed") 122 | line.queue_free() 123 | lines = {} 124 | 125 | for previous_node in previous_nodes: 126 | refresh_line(previous_node) 127 | 128 | 129 | #for the line to properly show behind the line2d the lowest nodes have to be the lowest in the tree 130 | func refresh_line(previous_node): 131 | if not previous_node: 132 | return 133 | var line = lines.get(previous_node) 134 | if not line: 135 | print("Line added") 136 | line = tree_node_line_scene.instance() 137 | line.position = rect_size / 2 138 | lines[previous_node] = line 139 | add_child(line) 140 | 141 | var target_pos = previous_node.rect_global_position 142 | var line_point = target_pos - rect_global_position 143 | var points = line.points 144 | if points.size() > 1: 145 | points.remove(1) 146 | points.append(line_point) 147 | line.points = points 148 | 149 | func get_tooltip_text(): 150 | return tooltip_text 151 | 152 | func _on_TextureButton_pressed(): 153 | if not unlocked: 154 | return 155 | if learned: 156 | return 157 | learned = true 158 | emit_signal("on_learned", self) 159 | $LearnedColor.show() 160 | print("Skill_Node %s learned" % name) 161 | -------------------------------------------------------------------------------- /Skill_Tree_Node.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=2] 2 | 3 | [ext_resource path="res://Skill_Tree_Node.gd" type="Script" id=1] 4 | [ext_resource path="res://Outline_Shader.tres" type="Material" id=2] 5 | [ext_resource path="res://icon.png" type="Texture" id=3] 6 | [ext_resource path="res://Skill_Tree_Line.tscn" type="PackedScene" id=4] 7 | 8 | [node name="TextureButton" type="TextureButton"] 9 | modulate = Color( 0.6, 0.6, 0.6, 1 ) 10 | margin_right = 40.0 11 | margin_bottom = 40.0 12 | rect_min_size = Vector2( 40, 40 ) 13 | disabled = true 14 | texture_normal = ExtResource( 3 ) 15 | expand = true 16 | script = ExtResource( 1 ) 17 | __meta__ = { 18 | "_edit_use_anchors_": false 19 | } 20 | tree_node_line_scene = ExtResource( 4 ) 21 | max_row_selections = 0 22 | texture = ExtResource( 3 ) 23 | on_learned_shader = ExtResource( 2 ) 24 | 25 | [node name="LearnedColor" type="ColorRect" parent="."] 26 | visible = false 27 | modulate = Color( 0.192157, 0.32549, 0.219608, 1 ) 28 | show_behind_parent = true 29 | margin_left = -5.0 30 | margin_top = -4.0 31 | margin_right = 44.0 32 | margin_bottom = 44.0 33 | __meta__ = { 34 | "_edit_use_anchors_": false 35 | } 36 | 37 | [connection signal="pressed" from="." to="." method="_on_TextureButton_pressed"] 38 | -------------------------------------------------------------------------------- /Skill_Tree_Test.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://Skill_Tree_Node.tscn" type="PackedScene" id=1] 4 | [ext_resource path="res://Outline_Shader.tres" type="Material" id=2] 5 | 6 | [node name="Control" type="Control"] 7 | __meta__ = { 8 | "_edit_use_anchors_": false 9 | } 10 | 11 | [node name="CanvasLayer" type="CanvasLayer" parent="."] 12 | 13 | [node name="Thingy" parent="CanvasLayer" instance=ExtResource( 1 )] 14 | margin_left = 614.973 15 | margin_top = 194.384 16 | margin_right = 654.973 17 | margin_bottom = 234.384 18 | previous_nodes_paths = [ NodePath("../Arrow"), NodePath("../Ray") ] 19 | previous_nodes_type = 1 20 | max_row_selections = 0 21 | 22 | [node name="Arrow" parent="CanvasLayer" instance=ExtResource( 1 )] 23 | margin_left = 667.165 24 | margin_top = 303.177 25 | margin_right = 707.165 26 | margin_bottom = 343.177 27 | previous_nodes_paths = [ NodePath("../Lock") ] 28 | max_row_selections = 0 29 | 30 | [node name="Ray" parent="CanvasLayer" instance=ExtResource( 1 )] 31 | margin_left = 517.511 32 | margin_top = 300.124 33 | margin_right = 557.511 34 | margin_bottom = 340.124 35 | previous_nodes_paths = [ NodePath("../Beam"), NodePath("../Lock") ] 36 | max_row_selections = 0 37 | 38 | [node name="Time" parent="CanvasLayer" instance=ExtResource( 1 )] 39 | margin_left = 398.293 40 | margin_top = 301.151 41 | margin_right = 438.293 42 | margin_bottom = 341.151 43 | previous_nodes_paths = [ NodePath("../Beam") ] 44 | max_row_selections = 0 45 | 46 | [node name="Lock" parent="CanvasLayer" instance=ExtResource( 1 )] 47 | margin_left = 606.52 48 | margin_top = 410.303 49 | margin_right = 646.52 50 | margin_bottom = 450.303 51 | previous_nodes_paths = [ NodePath("../Prison") ] 52 | talent_row_id = "1" 53 | 54 | [node name="Beam" parent="CanvasLayer" instance=ExtResource( 1 )] 55 | margin_left = 468.881 56 | margin_top = 402.558 57 | margin_right = 508.881 58 | margin_bottom = 442.558 59 | previous_nodes_paths = [ NodePath("../Prison") ] 60 | talent_row_id = "1" 61 | 62 | [node name="Prison" parent="CanvasLayer" instance=ExtResource( 1 )] 63 | modulate = Color( 1, 1, 1, 1 ) 64 | material = ExtResource( 2 ) 65 | margin_left = 539.077 66 | margin_top = 523.134 67 | margin_right = 579.077 68 | margin_bottom = 563.134 69 | disabled = false 70 | max_row_selections = 0 71 | unlocked = true 72 | -------------------------------------------------------------------------------- /default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | 5 | [resource] 6 | background_mode = 2 7 | background_sky = SubResource( 1 ) 8 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Reneator/godot-skill-tree/80ec34159b2762ea08f83bfcfb6996365c327f72/icon.png -------------------------------------------------------------------------------- /icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://icon.png" 13 | dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=4 10 | 11 | _global_script_classes=[ ] 12 | _global_script_class_icons={ 13 | 14 | } 15 | 16 | [application] 17 | 18 | config/name="Godot-Skill-Tree" 19 | run/main_scene="res://Skill_Tree_Test.tscn" 20 | config/icon="res://icon.png" 21 | 22 | [rendering] 23 | 24 | environment/default_environment="res://default_env.tres" 25 | --------------------------------------------------------------------------------