├── screenshots ├── .gdignore ├── mask_example.png └── masked_ui_example.png ├── godot.png ├── icon.png ├── .gitignore ├── new_gradient.tres ├── ActionRemapButton.tscn ├── MaskedMaterial.tres ├── MaskControl.gd ├── MaskUI.shader ├── icon.png.import ├── godot.png.import ├── ActionRemapButton.gd ├── LICENSE ├── README.md ├── project.godot └── InputRemapMenu.tscn /screenshots/.gdignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /godot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyuma/Godot-Control-Masking/HEAD/godot.png -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyuma/Godot-Control-Masking/HEAD/icon.png -------------------------------------------------------------------------------- /screenshots/mask_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyuma/Godot-Control-Masking/HEAD/screenshots/mask_example.png -------------------------------------------------------------------------------- /screenshots/masked_ui_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lyuma/Godot-Control-Masking/HEAD/screenshots/masked_ui_example.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Godot-specific ignores 3 | .import/ 4 | export.cfg 5 | export_presets.cfg 6 | 7 | # Mono-specific ignores 8 | .mono/ 9 | data_*/ 10 | -------------------------------------------------------------------------------- /new_gradient.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Gradient" format=2] 2 | 3 | [resource] 4 | offsets = PoolRealArray( -2.80515e+10, 0, 0.452381, 0.611111, 1 ) 5 | colors = PoolColorArray( 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 ) 6 | -------------------------------------------------------------------------------- /ActionRemapButton.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://ActionRemapButton.gd" type="Script" id=1] 4 | 5 | [node name="ActionRemapButton" type="Button"] 6 | margin_right = 90.0 7 | margin_bottom = 30.0 8 | toggle_mode = true 9 | script = ExtResource( 1 ) 10 | __meta__ = { 11 | "_edit_use_anchors_": false 12 | } 13 | -------------------------------------------------------------------------------- /MaskedMaterial.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="ShaderMaterial" load_steps=4 format=2] 2 | 3 | [ext_resource path="res://new_gradient.tres" type="Gradient" id=1] 4 | [ext_resource path="res://MaskUI.shader" type="Shader" id=2] 5 | 6 | [sub_resource type="GradientTexture" id=1] 7 | gradient = ExtResource( 1 ) 8 | 9 | [resource] 10 | shader = ExtResource( 2 ) 11 | shader_param/mask_transform = Transform( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ) 12 | shader_param/mask_texture = SubResource( 1 ) 13 | -------------------------------------------------------------------------------- /MaskControl.gd: -------------------------------------------------------------------------------- 1 | extends Control 2 | class_name MaskControl 3 | 4 | export (Array, Material) var materials: Array ; 5 | 6 | func _process(_time: float): 7 | var local_xform: Transform2D = Transform2D(Vector2(rect_size.x,0.0), Vector2(0.0,rect_size.y),Vector2(0.5,0.5)) 8 | var mask_to_world_xform: Transform2D = (get_viewport_transform() * get_global_transform() * local_xform) 9 | for material in materials: 10 | material.set_shader_param("mask_transform", mask_to_world_xform.affine_inverse()) 11 | -------------------------------------------------------------------------------- /MaskUI.shader: -------------------------------------------------------------------------------- 1 | shader_type canvas_item; 2 | render_mode blend_mix; 3 | 4 | uniform sampler2D mask_texture : hint_white; 5 | uniform mat4 mask_transform = mat4(0.0); 6 | varying vec2 mask_uv; 7 | 8 | void vertex() { 9 | mask_uv = (mask_transform * WORLD_MATRIX * vec4(VERTEX, 0.0, 1.0)).xy; 10 | } 11 | 12 | void fragment() { 13 | // * SCREEN_PIXEL_SIZE 14 | //.affine_inverse() 15 | COLOR = texture(TEXTURE, UV); 16 | if (length(mask_transform[0].xyz) > 0.00001) { 17 | vec2 adjusted_uv = ((mask_uv).xy); 18 | COLOR *= texture(mask_texture, adjusted_uv); 19 | if (any(greaterThan(abs(adjusted_uv - vec2(0.5)), vec2(0.5)))) { 20 | COLOR = vec4(0.0,0.0,0.0,0.0); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /godot.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/godot.png-5e0da45ed3d6786d5794553e04f58a8c.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://godot.png" 13 | dest_files=[ "res://.import/godot.png-5e0da45ed3d6786d5794553e04f58a8c.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=1 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 | -------------------------------------------------------------------------------- /ActionRemapButton.gd: -------------------------------------------------------------------------------- 1 | extends Button 2 | 3 | export(String) var action = "ui_up" 4 | 5 | func _ready(): 6 | assert(InputMap.has_action(action)) 7 | set_process_unhandled_key_input(false) 8 | display_current_key() 9 | 10 | 11 | func _toggled(button_pressed): 12 | set_process_unhandled_key_input(button_pressed) 13 | if button_pressed: 14 | text = "... Key" 15 | release_focus() 16 | else: 17 | display_current_key() 18 | 19 | 20 | func _unhandled_key_input(event): 21 | # Note that you can use the _input callback instead, especially if 22 | # you want to work with gamepads. 23 | remap_action_to(event) 24 | pressed = false 25 | 26 | 27 | func remap_action_to(event): 28 | InputMap.action_erase_events(action) 29 | InputMap.action_add_event(action, event) 30 | text = "%s Key" % event.as_text() 31 | 32 | 33 | func display_current_key(): 34 | var current_key = InputMap.get_action_list(action)[0].as_text() 35 | text = "%s Key" % current_key 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 lyuma 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Texture Masking Script and Shader 2 | 3 | Texture masking allows one node to mask another node or set of nodes with a texture. 4 | 5 | This is similar to a stencil, but does not actually restrict rendering. 6 | 7 | **NOTE**: This effect **will not show in editor**. You must run to see the mask in effect. 8 | 9 | To fix this limitation, this effect would have to be migrated to a tool script. 10 | 11 | To use texture masking: 12 | 13 | 1. first create a `ShaderMaterial` (or multiple materials). 14 | 2. Assign **`MaskUI.shader`** to the `ShaderMaterial`, and set a mask texture in `Shader Params`. 15 | 3. Create a Control node over the area you want masked, and add **`MaskControl.gd`** as a script. 16 | 4. Add all `ShaderMaterial` you created into the `Materials` section of the `Script` params. 17 | 5. Assign the `ShaderMaterial` you created earlier to every Control or Sprite you wish to be masked. 18 | 19 | An optimization to cull controls fully outside of the bounds of the mask should ideally be possib.e 20 | 21 | However, due to a combination of a lack of geometry shaders, poor design in the Control 22 | node implementation of mutating VERTEX position, rather than transform scale, and no easy way in 2D 23 | to inject custom vertex data for this purpose, it is impossible to determine the size of the control. 24 | 25 | This is an area may wish to continue exploring in the future. 26 | 27 | Another feature to consider adding is an inverse mask: hiding within the rect instead of outside. 28 | 29 | ## (from demo project) GUI Input Mapping 30 | 31 | A demo showing how to build an input key remapping screen. 32 | 33 | - Click the buttons to change the bound keys. 34 | - Persists the keys to disk, so they are preserved 35 | after the project is restarted. 36 | 37 | Language: GDScript 38 | 39 | Renderer: GLES 2 40 | 41 | ## Screenshots 42 | 43 | ![Screenshot](screenshots/masked_ui_example.png) 44 | 45 | ![Screenshot](screenshots/mask_example.png) 46 | -------------------------------------------------------------------------------- /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 | "base": "Control", 13 | "class": "MaskControl", 14 | "language": "GDScript", 15 | "path": "res://MaskControl.gd" 16 | } ] 17 | _global_script_class_icons={ 18 | "MaskControl": "" 19 | } 20 | 21 | [application] 22 | 23 | config/name="Control Texture Masking Demo" 24 | config/description="Forked from a demo showing how to build an input key remapping screen. 25 | 26 | - Click the buttons to change the bound keys. 27 | - Persists the keys to disk, so they are preserved 28 | after the project is restarted." 29 | run/main_scene="res://InputRemapMenu.tscn" 30 | config/icon="res://icon.png" 31 | 32 | [display] 33 | 34 | window/size/width=640 35 | window/size/height=480 36 | window/stretch/mode="2d" 37 | window/stretch/aspect="expand" 38 | 39 | [gdnative] 40 | 41 | singletons=[ ] 42 | 43 | [input] 44 | 45 | move_right={ 46 | "deadzone": 0.5, 47 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777233,"unicode":0,"echo":false,"script":null) 48 | ] 49 | } 50 | move_left={ 51 | "deadzone": 0.5, 52 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777231,"unicode":0,"echo":false,"script":null) 53 | ] 54 | } 55 | look_up={ 56 | "deadzone": 0.5, 57 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777232,"unicode":0,"echo":false,"script":null) 58 | ] 59 | } 60 | crouch={ 61 | "deadzone": 0.5, 62 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777234,"unicode":0,"echo":false,"script":null) 63 | ] 64 | } 65 | dash={ 66 | "deadzone": 0.5, 67 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777237,"unicode":0,"echo":false,"script":null) 68 | ] 69 | } 70 | 71 | [rendering] 72 | 73 | quality/driver/driver_name="GLES2" 74 | -------------------------------------------------------------------------------- /InputRemapMenu.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=6 format=2] 2 | 3 | [ext_resource path="res://ActionRemapButton.tscn" type="PackedScene" id=1] 4 | [ext_resource path="res://MaskedMaterial.tres" type="Material" id=2] 5 | [ext_resource path="res://MaskControl.gd" type="Script" id=3] 6 | 7 | [sub_resource type="ButtonGroup" id=1] 8 | 9 | [sub_resource type="Animation" id=2] 10 | resource_name = "movearound" 11 | loop = true 12 | tracks/0/type = "value" 13 | tracks/0/path = NodePath(".:anchor_left") 14 | tracks/0/interp = 2 15 | tracks/0/loop_wrap = true 16 | tracks/0/imported = false 17 | tracks/0/enabled = true 18 | tracks/0/keys = { 19 | "times": PoolRealArray( 0, 0.5, 1 ), 20 | "transitions": PoolRealArray( 1, 1, 1 ), 21 | "update": 0, 22 | "values": [ 0.5, 0.136, 0.5 ] 23 | } 24 | tracks/1/type = "value" 25 | tracks/1/path = NodePath(".:anchor_top") 26 | tracks/1/interp = 2 27 | tracks/1/loop_wrap = true 28 | tracks/1/imported = false 29 | tracks/1/enabled = true 30 | tracks/1/keys = { 31 | "times": PoolRealArray( 0, 0.5, 1 ), 32 | "transitions": PoolRealArray( 1, 1, 1 ), 33 | "update": 0, 34 | "values": [ 0.5, 0.811, 0.5 ] 35 | } 36 | tracks/2/type = "value" 37 | tracks/2/path = NodePath(".:anchor_right") 38 | tracks/2/interp = 2 39 | tracks/2/loop_wrap = true 40 | tracks/2/imported = false 41 | tracks/2/enabled = true 42 | tracks/2/keys = { 43 | "times": PoolRealArray( 0, 0.5, 1 ), 44 | "transitions": PoolRealArray( 1, 1, 1 ), 45 | "update": 0, 46 | "values": [ 0.5, 0.314, 0.5 ] 47 | } 48 | tracks/3/type = "value" 49 | tracks/3/path = NodePath(".:anchor_bottom") 50 | tracks/3/interp = 2 51 | tracks/3/loop_wrap = true 52 | tracks/3/imported = false 53 | tracks/3/enabled = true 54 | tracks/3/keys = { 55 | "times": PoolRealArray( 0, 0.5, 1 ), 56 | "transitions": PoolRealArray( 1, 1, 1 ), 57 | "update": 0, 58 | "values": [ 0.5, 0.72, 0.5 ] 59 | } 60 | 61 | [node name="InputRemapMenu" type="Control"] 62 | anchor_right = 1.0 63 | anchor_bottom = 1.0 64 | __meta__ = { 65 | "_edit_use_anchors_": false 66 | } 67 | 68 | [node name="Instructions" type="Label" parent="."] 69 | anchor_right = 1.0 70 | margin_top = 24.0 71 | margin_bottom = 55.0 72 | text = "Click on a button to reassign its action key." 73 | align = 1 74 | autowrap = true 75 | __meta__ = { 76 | "_edit_use_anchors_": false 77 | } 78 | 79 | [node name="RemapButtonGroup" type="Button" parent="."] 80 | anchor_left = 0.5 81 | anchor_top = 0.5 82 | anchor_right = 0.5 83 | anchor_bottom = 0.5 84 | margin_left = -160.0 85 | margin_top = -144.0 86 | margin_right = 160.0 87 | margin_bottom = 144.0 88 | disabled = true 89 | group = SubResource( 1 ) 90 | flat = true 91 | __meta__ = { 92 | "_edit_use_anchors_": false 93 | } 94 | 95 | [node name="MaskControl" type="Control" parent="RemapButtonGroup"] 96 | anchor_left = 0.5 97 | anchor_top = 0.5 98 | anchor_right = 0.5 99 | anchor_bottom = 0.5 100 | margin_left = -113.0 101 | margin_top = 127.0 102 | margin_right = 175.0 103 | margin_bottom = 363.0 104 | rect_rotation = -90.0 105 | script = ExtResource( 3 ) 106 | __meta__ = { 107 | "_edit_use_anchors_": false 108 | } 109 | materials = [ ExtResource( 2 ) ] 110 | 111 | [node name="AnimationPlayer" type="AnimationPlayer" parent="RemapButtonGroup/MaskControl"] 112 | autoplay = "movearound" 113 | anims/movearound = SubResource( 2 ) 114 | 115 | [node name="ShowBoundsRect" type="ColorRect" parent="RemapButtonGroup/MaskControl"] 116 | anchor_right = 1.0 117 | anchor_bottom = 1.0 118 | margin_top = -2.26276 119 | margin_bottom = -2.26276 120 | color = Color( 0.172549, 1, 0, 0.407843 ) 121 | __meta__ = { 122 | "_edit_use_anchors_": false 123 | } 124 | 125 | [node name="ActionsList" type="VBoxContainer" parent="RemapButtonGroup"] 126 | anchor_left = 0.5 127 | anchor_top = 0.5 128 | anchor_right = 0.5 129 | anchor_bottom = 0.5 130 | margin_left = -160.0 131 | margin_top = -140.0 132 | margin_right = 160.0 133 | margin_bottom = 140.0 134 | __meta__ = { 135 | "_edit_use_anchors_": false 136 | } 137 | 138 | [node name="ActionRemapRow" type="HBoxContainer" parent="RemapButtonGroup/ActionsList"] 139 | margin_right = 320.0 140 | margin_bottom = 52.0 141 | size_flags_horizontal = 3 142 | size_flags_vertical = 3 143 | 144 | [node name="ActionName" type="Label" parent="RemapButtonGroup/ActionsList/ActionRemapRow"] 145 | material = ExtResource( 2 ) 146 | margin_right = 158.0 147 | margin_bottom = 52.0 148 | size_flags_horizontal = 3 149 | size_flags_vertical = 3 150 | text = "Move Left" 151 | align = 1 152 | valign = 1 153 | 154 | [node name="RemapButton" parent="RemapButtonGroup/ActionsList/ActionRemapRow" instance=ExtResource( 1 )] 155 | material = ExtResource( 2 ) 156 | margin_left = 162.0 157 | margin_right = 320.0 158 | margin_bottom = 52.0 159 | size_flags_horizontal = 3 160 | size_flags_vertical = 3 161 | group = SubResource( 1 ) 162 | action = "move_left" 163 | 164 | [node name="ActionRemapRow2" type="HBoxContainer" parent="RemapButtonGroup/ActionsList"] 165 | margin_top = 56.0 166 | margin_right = 320.0 167 | margin_bottom = 109.0 168 | size_flags_horizontal = 3 169 | size_flags_vertical = 3 170 | 171 | [node name="ActionName" type="Label" parent="RemapButtonGroup/ActionsList/ActionRemapRow2"] 172 | material = ExtResource( 2 ) 173 | margin_right = 158.0 174 | margin_bottom = 53.0 175 | size_flags_horizontal = 3 176 | size_flags_vertical = 3 177 | text = "Move Right" 178 | align = 1 179 | valign = 1 180 | 181 | [node name="RemapButton" parent="RemapButtonGroup/ActionsList/ActionRemapRow2" instance=ExtResource( 1 )] 182 | material = ExtResource( 2 ) 183 | margin_left = 162.0 184 | margin_right = 320.0 185 | margin_bottom = 53.0 186 | size_flags_horizontal = 3 187 | size_flags_vertical = 3 188 | group = SubResource( 1 ) 189 | action = "move_right" 190 | 191 | [node name="ActionRemapRow3" type="HBoxContainer" parent="RemapButtonGroup/ActionsList"] 192 | margin_top = 113.0 193 | margin_right = 320.0 194 | margin_bottom = 166.0 195 | size_flags_horizontal = 3 196 | size_flags_vertical = 3 197 | 198 | [node name="ActionName" type="Label" parent="RemapButtonGroup/ActionsList/ActionRemapRow3"] 199 | material = ExtResource( 2 ) 200 | margin_right = 158.0 201 | margin_bottom = 53.0 202 | size_flags_horizontal = 3 203 | size_flags_vertical = 3 204 | text = "Look Up" 205 | align = 1 206 | valign = 1 207 | 208 | [node name="RemapButton" parent="RemapButtonGroup/ActionsList/ActionRemapRow3" instance=ExtResource( 1 )] 209 | material = ExtResource( 2 ) 210 | margin_left = 162.0 211 | margin_right = 320.0 212 | margin_bottom = 53.0 213 | size_flags_horizontal = 3 214 | size_flags_vertical = 3 215 | group = SubResource( 1 ) 216 | action = "look_up" 217 | 218 | [node name="ActionRemapRow4" type="HBoxContainer" parent="RemapButtonGroup/ActionsList"] 219 | margin_top = 170.0 220 | margin_right = 320.0 221 | margin_bottom = 223.0 222 | size_flags_horizontal = 3 223 | size_flags_vertical = 3 224 | 225 | [node name="ActionName" type="Label" parent="RemapButtonGroup/ActionsList/ActionRemapRow4"] 226 | material = ExtResource( 2 ) 227 | margin_right = 158.0 228 | margin_bottom = 53.0 229 | size_flags_horizontal = 3 230 | size_flags_vertical = 3 231 | text = "Crouch" 232 | align = 1 233 | valign = 1 234 | 235 | [node name="RemapButton" parent="RemapButtonGroup/ActionsList/ActionRemapRow4" instance=ExtResource( 1 )] 236 | material = ExtResource( 2 ) 237 | margin_left = 162.0 238 | margin_right = 320.0 239 | margin_bottom = 53.0 240 | size_flags_horizontal = 3 241 | size_flags_vertical = 3 242 | group = SubResource( 1 ) 243 | action = "crouch" 244 | 245 | [node name="ActionRemapRow5" type="HBoxContainer" parent="RemapButtonGroup/ActionsList"] 246 | margin_top = 227.0 247 | margin_right = 320.0 248 | margin_bottom = 280.0 249 | size_flags_horizontal = 3 250 | size_flags_vertical = 3 251 | 252 | [node name="ActionName" type="Label" parent="RemapButtonGroup/ActionsList/ActionRemapRow5"] 253 | material = ExtResource( 2 ) 254 | margin_right = 158.0 255 | margin_bottom = 53.0 256 | size_flags_horizontal = 3 257 | size_flags_vertical = 3 258 | text = "Dash" 259 | align = 1 260 | valign = 1 261 | 262 | [node name="RemapButton" parent="RemapButtonGroup/ActionsList/ActionRemapRow5" instance=ExtResource( 1 )] 263 | material = ExtResource( 2 ) 264 | margin_left = 162.0 265 | margin_right = 320.0 266 | margin_bottom = 53.0 267 | size_flags_horizontal = 3 268 | size_flags_vertical = 3 269 | group = SubResource( 1 ) 270 | action = "dash" 271 | --------------------------------------------------------------------------------