Add debug draw addon, implement basic input event

This commit is contained in:
Nitwel 2023-11-22 23:59:46 +01:00
parent 58000dcc1f
commit 2433845df7
36 changed files with 554 additions and 41 deletions

View File

@ -120,6 +120,7 @@ Thus I've decided to use a custom event system that is similar to the one used i
| Group | Description | | Group | Description |
| -- | -- | | -- | -- |
| `ui_focus` | The element can be focused | | `ui_focus` | The element can be focused |
| `ui_focus_skip` | The focus will not be reset. Useful for keyboard |
### Functions ### Functions

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 DmitriySalnikov
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, andor 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.

View File

@ -0,0 +1,183 @@
![icon](/images/icon.png)
# Debug drawing utility for Godot
This is an add-on for debug drawing in 3D and for some 2D overlays, which is written in `C++` and can be used with `GDScript` or `C#`.
Based on my previous addon, which was developed only for C# https://github.com/DmitriySalnikov/godot_debug_draw_cs, and which was inspired by Zylann's GDScript addon https://github.com/Zylann/godot_debug_draw
## [Godot 3 version](https://github.com/DmitriySalnikov/godot_debug_draw_3d/tree/godot_3)
## Support me
Your support adds motivation to develop my public projects.
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/I2I53VZ2D)
[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://paypal.me/dmitriysalnikov)
[<img src="https://upload.wikimedia.org/wikipedia/commons/8/8f/QIWI_logo.svg" alt="qiwi" width=90px/>](https://qiwi.com/n/DMITRIYSALNIKOV)
## Features
3D:
* Arrow
* Billboard opaque square
* Box
* Camera Frustum
* Cylinder
* Gizmo
* Grid
* Line
* Line Path
* Line with Arrow
* Points
* Position 3D (3 crossing axes)
* Sphere
2D:
* **[Work in progress]**
Overlay:
* Text (with grouping and coloring)
* FPS Graph
* Custom Graphs
Precompiled for:
* Windows
* Linux
* macOS
* Android
## Download
To download, use the [Godot Asset Library](https://godotengine.org/asset-library/asset/1766) or download the archive by clicking the button at the top of the main repository page: `Code -> Download ZIP`, then unzip it to your project folder. Or use one of the stable versions from the [GitHub Releases](https://github.com/DmitriySalnikov/godot_debug_draw_3d/releases) page (just download one of the `Source Codes` in assets).
## Usage
* Close editor
* Copy `addons/debug_draw_3d` to your `addons` folder, create it if the folder doesn't exist
* Launch editor
### C\#
When you start the engine for the first time, bindings for `C#` will be generated automatically. If this does not happen, you can manually generate them through the `Project - Tools - Debug Draw` menu.
![project_tools_menu](/images/project_tools_menu.png)
## Examples
More examples can be found in the `examples_dd3d/` folder.
Simple test:
```gdscript
func _process(delta: float) -> void:
var _time = Time.get_ticks_msec() / 1000.0
var box_pos = Vector3(0, sin(_time * 4), 0)
var line_begin = Vector3(-1, sin(_time * 4), 0)
var line_end = Vector3(1, cos(_time * 4), 0)
DebugDraw3D.draw_box(box_pos, Vector3(1, 2, 1), Color(0, 1, 0))
DebugDraw3D.draw_line(line_begin, line_end, Color(1, 1, 0))
DebugDraw2D.set_text("Time", _time)
DebugDraw2D.set_text("Frames drawn", Engine.get_frames_drawn())
DebugDraw2D.set_text("FPS", Engine.get_frames_per_second())
DebugDraw2D.set_text("delta", delta)
```
```csharp
public override void _Process(float delta)
{
var _time = Time.GetTicksMsec() / 1000.0f;
var box_pos = new Vector3(0, Mathf.Sin(_time * 4f), 0);
var line_begin = new Vector3(-1, Mathf.Sin(_time * 4f), 0);
var line_end = new Vector3(1, Mathf.Cos(_time * 4f), 0);
DebugDraw3D.DrawBox(box_pos, new Vector3(1, 2, 1), new Color(0, 1, 0));
DebugDraw3D.DrawLine(line_begin, line_end, new Color(1, 1, 0));
DebugDraw2D.SetText("Time", _time);
DebugDraw2D.SetText("Frames drawn", Engine.GetFramesDrawn());
DebugDraw2D.SetText("FPS", Engine.GetFramesPerSecond());
DebugDraw2D.SetText("delta", delta);
}
```
![screenshot_1](/images/screenshot_1.png)
## API
A list of all functions is available in the documentation inside the editor.
![screenshot_4](/images/screenshot_4.png)
Besides `DebugDraw2D/3D`, you can also use `Dbg2/3`.
```gdscript
DebugDraw3D.draw_box_xf(Transform3D(), Color.GREEN)
Dbg3.draw_box_xf(Transform3D(), Color.GREEN)
DebugDraw2D.set_text("delta", delta)
Dbg2.set_text("delta", delta)
```
But unfortunately at the moment `GDExtension` does not support adding documentation.
## Exporting a project
Most likely, when exporting a release version of a game, you don't want to export the debug library along with it. But since there is still no `Conditional Compilation` in `GDScript`, so I decided to create a `dummy` library that has the same API as a regular library, but has minimal impact on performance, even if calls to its methods occur. The `dummy` library is used by default in the release version. However if you need to use debug rendering in the release version, then you can add the `forced_dd3d` feature when exporting. In this case, the release library with all the functionality will be used.
![export_features](/images/export_features.png)
In C#, these tags are not taken into account at compile time, so the Release build will use Runtime checks to disable draw calls. If you want to avoid this, you can manually specify the `FORCED_DD3D` symbol.
![csharp_compilation_symbols](/images/csharp_compilation_symbols.png)
## Known issues and limitations
Enabling occlusion culing can lower fps instead of increasing it. At the moment I do not know how to speed up the calculation of the visibility of objects.
The text in the keys and values of a text group cannot contain multi-line strings.
The entire text overlay can only be placed in one corner, unlike `DataGraphs`.
[Frustum of Camera3D does not take into account the window size from ProjectSettings](https://github.com/godotengine/godot/issues/70362).
**The version for Godot 4.0 requires explicitly specifying the exact data types, otherwise errors may occur.**
## More screenshots
`DebugDrawDemoScene.tscn` in editor
![screenshot_2](/images/screenshot_2.png)
`DebugDrawDemoScene.tscn` in play mode
![screenshot_3](/images/screenshot_3.png)
## Build
As well as for the engine itself, you will need to configure the [environment](https://docs.godotengine.org/en/4.1/contributing/development/compiling/index.html).
And also you need to apply several patches:
```bash
cd godot-cpp
git apply --ignore-space-change --ignore-whitespace ../patches/always_build_fix.patch
git apply --ignore-space-change --ignore-whitespace ../patches/1165.patch
# Optional
## Build only the necessary classes
git apply --ignore-space-change --ignore-whitespace ../patches/godot_cpp_exclude_unused_classes.patch
## Faster build
git apply --ignore-space-change --ignore-whitespace ../patches/unity_build.patch
```
Then you can just run scons as usual:
```bash
# build for the current system.
# target=editor is used for both the editor and the debug template.
scons target=editor dev_build=yes debug_symbols=yes
# build for the android. ANDROID_NDK_ROOT is required in your environment variables.
scons platform=android target=template_release arch=arm64v8
```

View File

@ -0,0 +1,55 @@
[configuration]
entry_symbol = "debug_draw_3d_library_init"
compatibility_minimum = "4.1"
[dependencies]
# example.x86_64 = { "relative or absolute path to the dependency" : "the path relative to the exported project", }
macos = { }
windows.x86_64 = { }
linux.x86_64 = { }
android.arm32 = { }
android.arm64 = { }
android.x86_32 = { }
android.x86_64 = { }
macos.template_release = { }
windows.template_release.x86_64 = { }
linux.template_release.x86_64 = { }
android.template_release.arm32 = { }
android.template_release.arm64 = { }
android.template_release.x86_32 = { }
android.template_release.x86_64 = { }
[libraries]
macos = "libs/libdd3d.macos.editor.universal.dylib"
windows.x86_64 = "libs/libdd3d.windows.editor.x86_64.dll"
linux.x86_64 = "libs/libdd3d.linux.editor.x86_64.so"
android.arm32 = "libs/libdd3d.android.template_debug.arm32.so"
android.arm64 = "libs/libdd3d.android.template_debug.arm64.so"
android.x86_32 = "libs/libdd3d.android.template_debug.x86_32.so"
android.x86_64 = "libs/libdd3d.android.template_debug.x86_64.so"
macos.template_release = "libs/libdd3d.macos.template_release.universal.dylib"
windows.template_release.x86_64 = "libs/libdd3d.windows.template_release.x86_64.dll"
linux.template_release.x86_64 = "libs/libdd3d.linux.template_release.x86_64.so"
android.template_release.arm32 = "libs/libdd3d.android.template_release.arm32.so"
android.template_release.arm64 = "libs/libdd3d.android.template_release.arm64.so"
android.template_release.x86_32 = "libs/libdd3d.android.template_release.x86_32.so"
android.template_release.x86_64 = "libs/libdd3d.android.template_release.x86_64.so"
macos.template_release.forced_dd3d = "libs/libdd3d.macos.template_release.universal.enabled.dylib"
windows.template_release.x86_64.forced_dd3d = "libs/libdd3d.windows.template_release.x86_64.enabled.dll"
linux.template_release.x86_64.forced_dd3d = "libs/libdd3d.linux.template_release.x86_64.enabled.so"
android.template_release.arm32.forced_dd3d = "libs/libdd3d.android.template_release.arm32.enabled.so"
android.template_release.arm64.forced_dd3d = "libs/libdd3d.android.template_release.arm64.enabled.so"
android.template_release.x86_32.forced_dd3d = "libs/libdd3d.android.template_release.x86_32.enabled.so"
android.template_release.x86_64.forced_dd3d = "libs/libdd3d.android.template_release.x86_64.enabled.so"

View File

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0fb41898f762f555f379bc351a0d2d82bc9cbf3b701a1f9fe8035646ddad44b5
size 2928328

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1dfdb52ffe2e428ea0a0b9ff951b986546291f383f37c957d7de4c367f79892f
size 2993032

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5c580b7b4d44342c56d1990ab09358b63fa60cf99846a355c86ceb2cb5023b14
size 3057172

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b6c2b95269938e60e709539c8166cd9e5f450bd3fbd2b6bdfdf652fca3f594db
size 2977744

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1c40f93bd53bc4b132d075f18814b1198e71df6b9f2601c11f0cfbc2494698cf
size 1804756

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0181cc7c1a08ecc39de983b1b1e35f66572479da533f744190fa803cbde44612
size 2001576

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:24497f5a2ad8a7d99e2acb62d6c526017d7c365f8fd7fd6ae3042d96bd6e980a
size 1996072

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6eab3ff00fc2d7347a6b166e67a3800d6117fa84bcfa3317429d6e0bff896654
size 2028472

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9b39c1ca40f0a22e66e742daba8e050884caa7a3ff959824b0c99a7e061fd869
size 3806144

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4bfbec2037276c6858cbfc4019bb0381eb9bd83cdcdfcfa3cc802bfc0d5f427b
size 3072224

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7efcb3de6de7d5a878c4a221931a94a74f3741a6aeb7d7e9a4f19fb07bf1115c
size 2193840

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:818647a1234354725573918ce71972193812dd19d9ffb4bbe35f4a17ff48341f
size 5739144

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5bbf87c77f9c9fe6643948257e91f271825a2874172c97cdbd915a6203a6eff3
size 3339680

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b0f280b00b057266534e477e4b33e476f108b41d1927d4db39e8d1e3a781adc5
size 4689496

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ac3ac3647171c53e2f47564df2e25eff19fbfab8bf763d0078fe1c82476d8761
size 1291264

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3d3113daaa53fe431ca8ce55f127c0e41ef4243ab5aadd359dc6d2c63b31f057
size 680448

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:af5ae3a80d09e6212d21e4cf608aa38f711d230914918d0f2f427bdcc5488e24
size 862208

View File

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:55d5f30db336a8f8f7633743c23e41077d5c1a1b900c475a6d3811746eba3d1b
size 141

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:57f8bfca31acba3de3a15a2b9a88b8afd12f8db9c356f14d906840dcee48d6f6
size 1021

View File

@ -33,13 +33,13 @@ script = ExtResource("1_uvrd4")
[node name="XROrigin3D" type="XROrigin3D" parent="."] [node name="XROrigin3D" type="XROrigin3D" parent="."]
[node name="XRCamera3D" type="XRCamera3D" parent="XROrigin3D"] [node name="XRCamera3D" type="XRCamera3D" parent="XROrigin3D"]
transform = Transform3D(1, 2.18865e-10, 3.7835e-10, 3.85836e-11, 1, 2.08752e-08, -2.91038e-11, 8.6402e-12, 1, 0.0356344, 0.79808, 0.202806) transform = Transform3D(1, 1.8976e-10, 4.07454e-10, 6.76872e-11, 1, 2.08738e-08, -5.82077e-11, 1.04592e-11, 1, 0.0356618, 0.71033, 0.00564247)
[node name="XRControllerLeft" parent="XROrigin3D" instance=ExtResource("2_2lraw")] [node name="XRControllerLeft" parent="XROrigin3D" instance=ExtResource("2_2lraw")]
transform = Transform3D(0.999999, -1.39633e-11, 0, 9.48075e-12, 1, 0, 0, 0, 1, -0.355145, 0.550439, -0.477945) transform = Transform3D(0.999999, -1.39633e-11, 0, 9.48075e-12, 1, 0, 0, 0, 1, -0.355145, 0.550439, -0.477945)
[node name="XRControllerRight" type="XRController3D" parent="XROrigin3D"] [node name="XRControllerRight" type="XRController3D" parent="XROrigin3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.488349, 0.559219, -0.2988) transform = Transform3D(0.999999, -1.39633e-11, 0, 9.48075e-12, 1, 0, 0, 0, 1, 0.272616, 0.559282, -0.468369)
tracker = &"right_hand" tracker = &"right_hand"
pose = &"aim" pose = &"aim"
@ -66,4 +66,4 @@ transform = Transform3D(0.404247, 0.000180645, 0.914648, 0.00017221, 0.999999, -
visible = false visible = false
[node name="Keyboard" parent="." instance=ExtResource("9_e5n3p")] [node name="Keyboard" parent="." instance=ExtResource("9_e5n3p")]
transform = Transform3D(0.5, -6.98186e-12, 0, 4.73988e-12, 0.5, 0, 0, 0, 0.5, -0.00874073, 0.253873, -0.357135) transform = Transform3D(0.499999, -6.98142e-12, 0, 4.74065e-12, 0.5, -2.27374e-13, 0, 2.27374e-13, 0.5, -0.125313, 0.424282, -0.263966)

View File

@ -6,6 +6,7 @@ const button_scene = preload("res://content/ui/components/button/button.tscn")
@onready var keys = $Keys @onready var keys = $Keys
@onready var caps_button = $Caps @onready var caps_button = $Caps
@onready var backspace_button = $Backspace @onready var backspace_button = $Backspace
@onready var paste_button = $Paste
var key_list = [ var key_list = [
[KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_ASCIITILDE], [KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_ASCIITILDE],
[KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_SLASH], [KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_SLASH],
@ -57,6 +58,15 @@ func _ready():
_emit_event("key_up", KEY_CAPSLOCK) _emit_event("key_up", KEY_CAPSLOCK)
) )
paste_button.on_button_down.connect(func():
# There is no KEY_PASTE obviously, so we use KEY_INSERT for now
_emit_event("key_down", KEY_INSERT)
)
paste_button.on_button_up.connect(func():
_emit_event("key_up", KEY_INSERT)
)
func create_key(key: Key): func create_key(key: Key):
var button = button_scene.instantiate() var button = button_scene.instantiate()
@ -68,7 +78,7 @@ func create_key(key: Key):
label.add_to_group("button_label") label.add_to_group("button_label")
button.set_meta("key", key) button.set_meta("key", key)
button.add_to_group("ui_focus_skip")
button.add_child(label) button.add_child(label)
return button return button

View File

@ -12,7 +12,7 @@ size = Vector3(0.744504, 0.0402036, 0.296009)
transform = Transform3D(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0, 0) transform = Transform3D(0.5, 0, 0, 0, 0.5, 0, 0, 0, 0.5, 0, 0, 0)
script = ExtResource("1_maojw") script = ExtResource("1_maojw")
[node name="Backspace" parent="." instance=ExtResource("1_xdpwr")] [node name="Backspace" parent="." groups=["ui_focus_skip"] instance=ExtResource("1_xdpwr")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.66, 0, 0.02) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.66, 0, 0.02)
metadata/key = 4194308 metadata/key = 4194308
@ -21,7 +21,7 @@ transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.0
pixel_size = 0.001 pixel_size = 0.001
text = "back" text = "back"
[node name="Caps" parent="." instance=ExtResource("1_xdpwr")] [node name="Caps" parent="." groups=["ui_focus_skip"] instance=ExtResource("1_xdpwr")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.06, 0, 0.15) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.06, 0, 0.15)
toggleable = true toggleable = true
@ -30,6 +30,14 @@ transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.0
pixel_size = 0.001 pixel_size = 0.001
text = "caps" text = "caps"
[node name="Paste" parent="." groups=["ui_focus_skip"] instance=ExtResource("1_xdpwr")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.66, 0, 0.18)
[node name="Label3D" type="Label3D" parent="Paste"]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0.012, 0)
pixel_size = 0.001
text = "paste"
[node name="Keys" type="Node3D" parent="."] [node name="Keys" type="Node3D" parent="."]
script = ExtResource("3_mx544") script = ExtResource("3_mx544")
columns = 11 columns = 11

View File

@ -1,4 +1,3 @@
@tool
extends StaticBody3D extends StaticBody3D
class_name Button3D class_name Button3D

View File

@ -1,13 +1,11 @@
[gd_scene load_steps=8 format=3 uid="uid://bsjqdvkt0u87c"] [gd_scene load_steps=8 format=3 uid="uid://bsjqdvkt0u87c"]
[ext_resource type="Script" path="res://content/ui/components/button/button.gd" id="1_74x7g"] [ext_resource type="Script" path="res://content/ui/components/button/button.gd" id="1_74x7g"]
[ext_resource type="Material" uid="uid://bujy3egn1oqac" path="res://assets/materials/ui_element.material" id="2_h7ln4"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_peqek"]
albedo_color = Color(0.151534, 0.211909, 0.619523, 1)
[sub_resource type="BoxMesh" id="BoxMesh_jwpm5"] [sub_resource type="BoxMesh" id="BoxMesh_jwpm5"]
resource_local_to_scene = true resource_local_to_scene = true
material = SubResource("StandardMaterial3D_peqek") material = ExtResource("2_h7ln4")
size = Vector3(0.05, 0.02, 0.05) size = Vector3(0.05, 0.02, 0.05)
[sub_resource type="ConvexPolygonShape3D" id="ConvexPolygonShape3D_o4j7g"] [sub_resource type="ConvexPolygonShape3D" id="ConvexPolygonShape3D_o4j7g"]

View File

@ -0,0 +1,85 @@
extends StaticBody3D
@onready var caret: MeshInstance3D = $Label/Caret
@onready var label: Label3D = $Label
@onready var animation: AnimationPlayer = $AnimationPlayer
var text: String = "Hello World":
set(value):
var old_text = text
text = value
label.text = value
gap_offsets = _calculate_text_gaps()
caret_position += text.length() - old_text.length()
var keyboard_input: bool = false
var gap_offsets = []
var caret_position: int = 3:
set(value):
caret_position = clampi(value, 0, text.length())
caret.position.x = gap_offsets[caret_position]
func _ready():
EventSystem.on_key_down.connect(func(event):
if EventSystem.is_focused(self) == false:
return
text = EventKey.key_to_string(event.key, event.shift_pressed, text.substr(0, caret_position)) + text.substr(caret_position, text.length())
)
func _input(event):
if event is InputEventKey && EventSystem.is_focused(self) && event.pressed:
if event.keycode == KEY_F1:
keyboard_input = !keyboard_input
return
if keyboard_input:
text = EventKey.key_to_string(event.keycode, event.shift_pressed, text.substr(0, caret_position)) + text.substr(caret_position, text.length())
func _process(_delta):
if get_tree().debug_collisions_hint && OS.get_name() != "Android":
_draw_debug_text_gaps()
func _calculate_caret_pos(click_pos_x: float):
for i in range(1, gap_offsets.size()):
var left = gap_offsets[i]
if click_pos_x < left:
return i - 1
return gap_offsets.size() - 1
func _on_focus_in(event):
gap_offsets = _calculate_text_gaps()
var pos_x = label.to_local(event.ray.get_collision_point()).x
caret_position = _calculate_caret_pos(pos_x)
animation.play("blink")
func _on_focus_out(_event):
animation.stop()
caret.hide()
func _calculate_text_gaps():
var font = label.get_font()
var offsets = [0.0]
var offset = 0.0
for i in range(text.length()):
var character = text[i]
var size = font.get_string_size(character, HORIZONTAL_ALIGNMENT_CENTER, -1, label.font_size)
offset += size.x * label.pixel_size
offsets.append(offset)
return offsets
func _draw_debug_text_gaps():
for offset in gap_offsets:
DebugDraw3D.draw_line(
label.to_global(Vector3(offset, -0.01, 0)),
label.to_global(Vector3(offset, 0.01, 0)),
Color(1, 0, 0),
)

View File

@ -0,0 +1,80 @@
[gd_scene load_steps=10 format=3 uid="uid://blrhy2uccrdn4"]
[ext_resource type="Material" uid="uid://bujy3egn1oqac" path="res://assets/materials/ui_element.material" id="1_0kd7r"]
[ext_resource type="Script" path="res://content/ui/components/input/input.gd" id="1_uml3t"]
[sub_resource type="BoxMesh" id="BoxMesh_kjbca"]
size = Vector3(0.2, 0.006, 0.03)
[sub_resource type="BoxShape3D" id="BoxShape3D_x4yp8"]
size = Vector3(0.2, 0.006, 0.03)
[sub_resource type="SystemFont" id="SystemFont_nbea0"]
[sub_resource type="BoxMesh" id="BoxMesh_2736g"]
size = Vector3(0.001, 0.02, 0.001)
[sub_resource type="Animation" id="Animation_65tpe"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Label/Caret:visible")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [true]
}
[sub_resource type="Animation" id="Animation_8ny1h"]
resource_name = "blink"
loop_mode = 1
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Label/Caret:visible")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.5, 1),
"transitions": PackedFloat32Array(1, 1, 1),
"update": 0,
"values": [true, false, true]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_1sy4t"]
_data = {
"RESET": SubResource("Animation_65tpe"),
"blink": SubResource("Animation_8ny1h")
}
[node name="Input" type="StaticBody3D" groups=["ui_focus"]]
script = ExtResource("1_uml3t")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.003, 0)
material_override = ExtResource("1_0kd7r")
mesh = SubResource("BoxMesh_kjbca")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.003, 0)
shape = SubResource("BoxShape3D_x4yp8")
[node name="Label" type="Label3D" parent="."]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, -0.096, 0.00618291, 0)
pixel_size = 0.0004
text = "Hello World"
font = SubResource("SystemFont_nbea0")
horizontal_alignment = 0
[node name="Caret" type="MeshInstance3D" parent="Label"]
mesh = SubResource("BoxMesh_2736g")
skeleton = NodePath("../..")
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
libraries = {
"": SubResource("AnimationLibrary_1sy4t")
}

View File

@ -24,6 +24,7 @@ font_size = 36
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.19, 0.01, 0.27) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.19, 0.01, 0.27)
[node name="NextPageButton" parent="Buttons" instance=ExtResource("4_tvimg")] [node name="NextPageButton" parent="Buttons" instance=ExtResource("4_tvimg")]
focusable = true
[node name="Decal" type="Decal" parent="Buttons/NextPageButton"] [node name="Decal" type="Decal" parent="Buttons/NextPageButton"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.02, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.02, 0)
@ -35,6 +36,7 @@ script = ExtResource("6_pf8jy")
[node name="PreviousPageButton" parent="Buttons" instance=ExtResource("4_tvimg")] [node name="PreviousPageButton" parent="Buttons" instance=ExtResource("4_tvimg")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.08, 0, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.08, 0, 0)
focusable = true
[node name="Decal" type="Decal" parent="Buttons/PreviousPageButton"] [node name="Decal" type="Decal" parent="Buttons/PreviousPageButton"]
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0.02, 0) transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0.02, 0)

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=16 format=3 uid="uid://c3kdssrmv84kv"] [gd_scene load_steps=17 format=3 uid="uid://c3kdssrmv84kv"]
[ext_resource type="Script" path="res://content/ui/menu/menu.gd" id="1_ng4u3"] [ext_resource type="Script" path="res://content/ui/menu/menu.gd" id="1_ng4u3"]
[ext_resource type="PackedScene" uid="uid://crrb0l3ekuotj" path="res://content/ui/menu/edit/edit_menu.tscn" id="4_r2raj"] [ext_resource type="PackedScene" uid="uid://crrb0l3ekuotj" path="res://content/ui/menu/edit/edit_menu.tscn" id="4_r2raj"]
@ -11,6 +11,7 @@
[ext_resource type="Texture2D" uid="uid://dyh0ax51xqp8n" path="res://assets/icons/settings_white_24dp.svg" id="9_mel13"] [ext_resource type="Texture2D" uid="uid://dyh0ax51xqp8n" path="res://assets/icons/settings_white_24dp.svg" id="9_mel13"]
[ext_resource type="PackedScene" uid="uid://c01gkeldvjwtr" path="res://content/ui/menu/room/room_menu.tscn" id="10_u4i1x"] [ext_resource type="PackedScene" uid="uid://c01gkeldvjwtr" path="res://content/ui/menu/room/room_menu.tscn" id="10_u4i1x"]
[ext_resource type="PackedScene" uid="uid://c6r4higceibif" path="res://content/ui/menu/settings/settings_menu.tscn" id="11_7wm6b"] [ext_resource type="PackedScene" uid="uid://c6r4higceibif" path="res://content/ui/menu/settings/settings_menu.tscn" id="11_7wm6b"]
[ext_resource type="PackedScene" uid="uid://blrhy2uccrdn4" path="res://content/ui/components/input/input.tscn" id="12_ov3em"]
[sub_resource type="BoxMesh" id="BoxMesh_08du6"] [sub_resource type="BoxMesh" id="BoxMesh_08du6"]
size = Vector3(0.3, 0.01, 0.3) size = Vector3(0.3, 0.01, 0.3)
@ -236,3 +237,6 @@ mesh = ExtResource("7_f4u4o")
libraries = { libraries = {
"": SubResource("AnimationLibrary_s30cd") "": SubResource("AnimationLibrary_s30cd")
} }
[node name="Input" parent="." instance=ExtResource("12_ov3em")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.19, 0, -0.04)

View File

@ -1,5 +1,6 @@
extends Event extends Event
class_name EventFocus class_name EventFocus
var ray: RayCast3D
var target: Node var target: Node
var previous_target: Node var previous_target: Node

View File

@ -4,13 +4,19 @@ class_name EventKey
var key: Key var key: Key
var echo: bool var echo: bool
static func key_to_string(key: Key, caps: bool = false) -> String: static func key_to_string(key: Key, caps: bool = false, apply_to: String = "") -> String:
match key: match key:
KEY_ASCIITILDE: return "~" KEY_INSERT: apply_to += DisplayServer.clipboard_get()
KEY_SLASH: return "/" KEY_BACKSPACE: apply_to = apply_to.substr(0, apply_to.length() - 1)
KEY_BACKSLASH: return "\\" KEY_SPACE: apply_to += " "
KEY_COLON: return ";" KEY_ASCIITILDE: apply_to += "~"
KEY_COMMA: return "," KEY_SLASH: apply_to += "/"
KEY_PERIOD: return "." KEY_BACKSLASH: apply_to += "\\"
KEY_MINUS: return "-" KEY_COLON: apply_to += ";"
_: return OS.get_keycode_string(key).to_upper() if caps else OS.get_keycode_string(key).to_lower() KEY_COMMA: apply_to += ","
KEY_PERIOD: apply_to += "."
KEY_MINUS: apply_to += "-"
KEY_CAPSLOCK: return apply_to
_: apply_to += OS.get_keycode_string(key).to_upper() if caps else OS.get_keycode_string(key).to_lower()
return apply_to

View File

@ -20,32 +20,41 @@ signal on_key_up(event: EventKey)
signal on_focus_in(event: EventFocus) signal on_focus_in(event: EventFocus)
signal on_focus_out(event: EventFocus) signal on_focus_out(event: EventFocus)
var active_node: Node = null var _active_node: Node = null
func emit(type: String, event: Event): func emit(type: String, event: Event):
if event is EventBubble: if event is EventBubble:
_bubble_call(type, event.target, event) _bubble_call(type, event.target, event)
if event.target.is_in_group("ui_focus"): if type == "press_down":
_handle_focus(event.target) _handle_focus(event)
else:
_handle_focus(null)
else: else:
_root_call(type, event) _root_call(type, event)
func _handle_focus(node: Node): func is_focused(node: Node):
var event = EventFocus.new() return _active_node == node
event.previous_target = active_node
event.target = node
if active_node != null && active_node.has_method(FN_PREFIX + "focus_in"): func _handle_focus(event: EventRay):
active_node.call(FN_PREFIX + "focus_out", event) if event.target != null && event.target.is_in_group("ui_focus_skip"):
on_focus_out.emit(event) return
active_node = node var event_focus = EventFocus.new()
event_focus.previous_target = _active_node
event_focus.target = event.target
event_focus.ray = event.ray
if active_node != null: if _active_node != null && _active_node.has_method(FN_PREFIX + "focus_out"):
active_node.call(FN_PREFIX + "focus_in", event) _active_node.call(FN_PREFIX + "focus_out", event_focus)
on_focus_in.emit(event) on_focus_out.emit(event_focus)
if event.target == null || event.target.is_in_group("ui_focus") == false:
_active_node = null
return
_active_node = event.target
if _active_node != null && _active_node.has_method(FN_PREFIX + "focus_in"):
_active_node.call(FN_PREFIX + "focus_in", event_focus)
on_focus_in.emit(event_focus)
func _bubble_call(type: String, target: Variant, event: EventBubble): func _bubble_call(type: String, target: Variant, event: EventBubble):
if target == null: if target == null: