Skip to content

Commit dc924d9

Browse files
committed
Progress on GDScript integration
Have two approches, C++ class and GDScript wrapper class `HexMapCellId` and `HexMapCellIdRef`, or unified class in `HexMapIterAxial`. The unified approach requires an upstream change[^1] to allow stack allocation of `godot::Object` classes. `HexMapIterAxial` being an Object means all the doctest tests are now broken, as it cannot find the `godot::String` implementation pointers. The tests crash currently because of this. We do have tests in the godot demo project, using GUT. They confirm that `HexMapCellId.get_neighbors()` is properly working from GDScript. I'm not sure which direction I'll go in the long term. I'd like to be able to unit test in C++ and GDScript, but I don't want to have to maintain a pile of pass-through proxy methods in the wrapper class. I made one suggestion[^2] in godot-cpp to allow binding methods to class instance methods, but I don't even know if that's possible. [^1]: godotengine/godot-cpp#1585 [^2]: godotengine/godot-cpp#1446 (comment)
1 parent 1ee88cd commit dc924d9

File tree

128 files changed

+16545
-75
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

128 files changed

+16545
-75
lines changed

SConstruct

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ env = SConscript("godot-cpp/SConstruct")
1616

1717
# tweak this if you want to use different folders, or more folders, to store your source code in.
1818
env.Append(CPPPATH=["src/"])
19+
env.Append(CPPDEFINES = [ 'DISABLE_OBJECT_BINDING_GUARD' ] )
1920
sources = Glob("src/*.cpp")
2021
sources += Glob("src/hex_map/*.cpp")
2122
sources += Glob("src/hex_map/editor/*.cpp")
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
extends Camera3D
2+
3+
@export var hex_map: HexMap
4+
@export var label_cells := true
5+
@export var marker: Node3D
6+
7+
@onready var raycast: RayCast3D = $RayCast3D
8+
@onready var label: Label = %DebugLabel
9+
10+
var map: AStar3D;
11+
var points: Dictionary;
12+
var cursor_cell;
13+
var lines = [];
14+
15+
func _ready() -> void:
16+
map = AStar3D.new()
17+
points = {};
18+
19+
var id := 0;
20+
for cell in hex_map.get_used_cells():
21+
map.add_point(id, cell)
22+
points[cell] = id
23+
id += 1
24+
25+
for cell in hex_map.get_used_cells():
26+
var cell_id = points[cell]
27+
for neighbor in hex_map.get_cell_neighbors(cell):
28+
var neighbor_id = points.get(neighbor)
29+
if neighbor_id != null:
30+
map.connect_points(cell_id, neighbor_id)
31+
32+
33+
# label all of the cells
34+
if label_cells == true:
35+
for cell in hex_map.get_used_cells():
36+
var above = cell
37+
above.y += 1
38+
if hex_map.get_cell_item(above) != -1:
39+
continue
40+
41+
var coords = hex_map.map_to_local(cell);
42+
var clabel := Label3D.new();
43+
clabel.no_depth_test = true
44+
clabel.text = str(cell)
45+
coords.y += 0.5
46+
clabel.position = coords
47+
clabel.billboard = BaseMaterial3D.BILLBOARD_ENABLED
48+
clabel.font_size = 64
49+
clabel.outline_size = 0
50+
clabel.modulate = Color.BLACK
51+
get_parent().add_child.call_deferred(clabel)
52+
53+
func _process(_delta: float) -> void:
54+
var mouse_pos: Vector2 = get_viewport().get_mouse_position()
55+
raycast.target_position = project_local_ray_normal(mouse_pos) * 100.0
56+
raycast.force_raycast_update()
57+
58+
if !raycast.is_colliding():
59+
label.text = "not colliding"
60+
cursor_cell = null
61+
return
62+
63+
var collider = raycast.get_collider()
64+
if collider is not HexMap:
65+
return
66+
67+
var collison_point = raycast.get_collision_point()
68+
var local = collider.to_local(collison_point)
69+
var cell = hex_map.local_to_map(local)
70+
if cell == cursor_cell:
71+
return
72+
cursor_cell = cell;
73+
74+
var item = hex_map.get_cell_item(cell)
75+
var cell2 = hex_map.local_to_cell_id(collison_point)
76+
label.text = "collision %s\nlocal %s\ncell %s %s\nitem %d" % [ collison_point, local, cell, cell2, item]
77+
78+
var point = points.get(cell)
79+
if point == null:
80+
return
81+
82+
var path := map.get_point_path(marker_point(), points.get(cell))
83+
label.text += "\npath: %s" % [path]
84+
draw_path(path)
85+
86+
func marker_point() -> int:
87+
# grab the marker y and search down until we find a cell
88+
var pos = marker.position
89+
while true:
90+
# XXX infinite loop during shutdown
91+
var cell = hex_map.local_to_map(pos)
92+
var point = points.get(cell)
93+
if point:
94+
return point
95+
pos.y -= 1.0
96+
return 0
97+
98+
func draw_path(path: PackedVector3Array):
99+
for line in lines:
100+
line.queue_free()
101+
lines.clear()
102+
103+
print("drawing", path)
104+
for i in path.size()-1:
105+
var a = path[i]
106+
var b = path[i+1]
107+
var line = draw_line(hex_map.map_to_local(a) + Vector3(0, 2, 0), hex_map.map_to_local(b) + Vector3(0, 2, 0))
108+
hex_map.add_child(line)
109+
lines.append(line)
110+
111+
func draw_line(pos1: Vector3, pos2: Vector3) -> MeshInstance3D:
112+
var mesh_instance := MeshInstance3D.new()
113+
var immediate_mesh := ImmediateMesh.new()
114+
var material := ORMMaterial3D.new()
115+
116+
mesh_instance.mesh = immediate_mesh
117+
mesh_instance.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
118+
119+
immediate_mesh.surface_begin(Mesh.PRIMITIVE_LINES, material)
120+
immediate_mesh.surface_add_vertex(pos1)
121+
immediate_mesh.surface_add_vertex(pos2)
122+
immediate_mesh.surface_end()
123+
124+
material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
125+
material.albedo_color = Color.DARK_RED
126+
127+
return mesh_instance
128+
129+
130+
func _input(event: InputEvent) -> void:
131+
if event is InputEventPanGesture:
132+
fov = clampf(fov + event.delta.y, 15.0, 75.0);
133+
elif event.is_action("zoom_in"):
134+
fov = clampf(fov - 2.0, 15.0, 75.0);
135+
elif event.is_action("zoom_out"):
136+
fov = clampf(fov + 2.0, 15.0, 75.0);
137+
elif event.is_action("place_marker") && cursor_cell:
138+
var pos = hex_map.map_to_local(cursor_cell)
139+
pos.y += 1.5
140+
marker.position = pos
141+
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
[gd_scene load_steps=4 format=3 uid="uid://wcaukixhlbo2"]
2+
3+
[ext_resource type="Script" path="res://RayPickerCamera/ray_picker_camera.gd" id="1_1mxd7"]
4+
5+
[sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_d0hky"]
6+
7+
[sub_resource type="LabelSettings" id="LabelSettings_y822c"]
8+
font_size = 18
9+
outline_size = 3
10+
outline_color = Color(0, 0, 0, 1)
11+
shadow_size = 0
12+
13+
[node name="RayPickerCamera" type="Camera3D"]
14+
script = ExtResource("1_1mxd7")
15+
16+
[node name="RayCast3D" type="RayCast3D" parent="."]
17+
18+
[node name="Label3D" type="Label3D" parent="."]
19+
transform = Transform3D(1, -6.64895e-16, 1.47403e-16, 1.47404e-16, 0.422618, 0.906308, -6.64895e-16, -0.906308, 0.422618, 2.08165e-12, 14.5, 9.5)
20+
top_level = true
21+
visible = false
22+
no_depth_test = true
23+
text = "test"
24+
font_size = 96
25+
horizontal_alignment = 0
26+
vertical_alignment = 0
27+
28+
[node name="HBoxContainer" type="HBoxContainer" parent="."]
29+
anchors_preset = 15
30+
anchor_right = 1.0
31+
anchor_bottom = 1.0
32+
grow_horizontal = 2
33+
grow_vertical = 2
34+
size_flags_horizontal = 0
35+
36+
[node name="MarginContainer" type="MarginContainer" parent="HBoxContainer"]
37+
layout_mode = 2
38+
size_flags_horizontal = 3
39+
theme_override_constants/margin_left = 10
40+
theme_override_constants/margin_top = 10
41+
42+
[node name="DebugLabel" type="Label" parent="HBoxContainer/MarginContainer"]
43+
unique_name_in_owner = true
44+
material = SubResource("CanvasItemMaterial_d0hky")
45+
layout_mode = 2
46+
size_flags_horizontal = 0
47+
size_flags_vertical = 0
48+
text = "Test"
49+
label_settings = SubResource("LabelSettings_y822c")

demo/addons/gut/GutScene.gd

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
extends Node2D
2+
# ##############################################################################
3+
# This is a wrapper around the normal and compact gui controls and serves as
4+
# the interface between gut.gd and the gui. The GutRunner creates an instance
5+
# of this and then this takes care of managing the different GUI controls.
6+
# ##############################################################################
7+
@onready var _normal_gui = $Normal
8+
@onready var _compact_gui = $Compact
9+
10+
var gut = null :
11+
set(val):
12+
gut = val
13+
_set_gut(val)
14+
15+
16+
func _ready():
17+
_normal_gui.switch_modes.connect(use_compact_mode.bind(true))
18+
_compact_gui.switch_modes.connect(use_compact_mode.bind(false))
19+
20+
_normal_gui.set_title("GUT")
21+
_compact_gui.set_title("GUT")
22+
23+
_normal_gui.align_right()
24+
_compact_gui.to_bottom_right()
25+
26+
use_compact_mode(false)
27+
28+
if(get_parent() == get_tree().root):
29+
_test_running_setup()
30+
31+
func _test_running_setup():
32+
set_font_size(100)
33+
_normal_gui.get_textbox().text = "hello world, how are you doing?"
34+
35+
# ------------------------
36+
# Private
37+
# ------------------------
38+
func _set_gut(val):
39+
if(_normal_gui.get_gut() == val):
40+
return
41+
_normal_gui.set_gut(val)
42+
_compact_gui.set_gut(val)
43+
44+
val.start_run.connect(_on_gut_start_run)
45+
val.end_run.connect(_on_gut_end_run)
46+
val.start_pause_before_teardown.connect(_on_gut_pause)
47+
val.end_pause_before_teardown.connect(_on_pause_end)
48+
49+
func _set_both_titles(text):
50+
_normal_gui.set_title(text)
51+
_compact_gui.set_title(text)
52+
53+
54+
# ------------------------
55+
# Events
56+
# ------------------------
57+
func _on_gut_start_run():
58+
_set_both_titles('Running')
59+
60+
func _on_gut_end_run():
61+
_set_both_titles('Finished')
62+
63+
func _on_gut_pause():
64+
_set_both_titles('-- Paused --')
65+
66+
func _on_pause_end():
67+
_set_both_titles('Running')
68+
69+
70+
# ------------------------
71+
# Public
72+
# ------------------------
73+
func get_textbox():
74+
return _normal_gui.get_textbox()
75+
76+
77+
func set_font_size(new_size):
78+
var rtl = _normal_gui.get_textbox()
79+
80+
rtl.set('theme_override_font_sizes/bold_italics_font_size', new_size)
81+
rtl.set('theme_override_font_sizes/bold_font_size', new_size)
82+
rtl.set('theme_override_font_sizes/italics_font_size', new_size)
83+
rtl.set('theme_override_font_sizes/normal_font_size', new_size)
84+
85+
86+
func set_font(font_name):
87+
_set_all_fonts_in_rtl(_normal_gui.get_textbox(), font_name)
88+
89+
90+
func _set_font(rtl, font_name, custom_name):
91+
if(font_name == null):
92+
rtl.remove_theme_font_override(custom_name)
93+
else:
94+
var dyn_font = FontFile.new()
95+
dyn_font.load_dynamic_font('res://addons/gut/fonts/' + font_name + '.ttf')
96+
rtl.add_theme_font_override(custom_name, dyn_font)
97+
98+
99+
func _set_all_fonts_in_rtl(rtl, base_name):
100+
if(base_name == 'Default'):
101+
_set_font(rtl, null, 'normal_font')
102+
_set_font(rtl, null, 'bold_font')
103+
_set_font(rtl, null, 'italics_font')
104+
_set_font(rtl, null, 'bold_italics_font')
105+
else:
106+
_set_font(rtl, base_name + '-Regular', 'normal_font')
107+
_set_font(rtl, base_name + '-Bold', 'bold_font')
108+
_set_font(rtl, base_name + '-Italic', 'italics_font')
109+
_set_font(rtl, base_name + '-BoldItalic', 'bold_italics_font')
110+
111+
112+
func set_default_font_color(color):
113+
_normal_gui.get_textbox().set('custom_colors/default_color', color)
114+
115+
116+
func set_background_color(color):
117+
_normal_gui.set_bg_color(color)
118+
119+
120+
func use_compact_mode(should=true):
121+
_compact_gui.visible = should
122+
_normal_gui.visible = !should
123+
124+
125+
func set_opacity(val):
126+
_normal_gui.modulate.a = val
127+
_compact_gui.modulate.a = val
128+
129+
func set_title(text):
130+
_set_both_titles(text)

demo/addons/gut/GutScene.tscn

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[gd_scene load_steps=4 format=3 uid="uid://m28heqtswbuq"]
2+
3+
[ext_resource type="Script" path="res://addons/gut/GutScene.gd" id="1_b4m8y"]
4+
[ext_resource type="PackedScene" uid="uid://duxblir3vu8x7" path="res://addons/gut/gui/NormalGui.tscn" id="2_j6ywb"]
5+
[ext_resource type="PackedScene" uid="uid://cnqqdfsn80ise" path="res://addons/gut/gui/MinGui.tscn" id="3_3glw1"]
6+
7+
[node name="GutScene" type="Node2D"]
8+
script = ExtResource("1_b4m8y")
9+
10+
[node name="Normal" parent="." instance=ExtResource("2_j6ywb")]
11+
12+
[node name="Compact" parent="." instance=ExtResource("3_3glw1")]
13+
offset_left = 5.0
14+
offset_top = 273.0
15+
offset_right = 265.0
16+
offset_bottom = 403.0

demo/addons/gut/LICENSE.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
The MIT License (MIT)
2+
=====================
3+
4+
Copyright (c) 2018 Tom "Butch" Wesley
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in
14+
all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
THE SOFTWARE.

0 commit comments

Comments
 (0)