small racing game im working on
thumbnails
bendn 2023-03-08
parent 2d022f8 · commit d521916
-rw-r--r--.vscode/settings.json3
-rw-r--r--classes/car_vars.gd40
-rw-r--r--classes/resources/car_vars.gd40
-rw-r--r--classes/resources/ghost_data.gd (renamed from classes/ghost_data.gd)0
-rw-r--r--init_classes.py33
-rw-r--r--lib/activation.gd9
-rw-r--r--lib/matrix.gd148
-rw-r--r--lib/neural_network.gd127
-rw-r--r--start.tscn6
-rw-r--r--tracks/multilap_test.tres2
-rw-r--r--tracks/test.tres2
-rw-r--r--tracks/the fallen tramps.tres2
-rw-r--r--ui/hud.tscn3
-rw-r--r--ui/intro_cam.gd17
-rw-r--r--ui/track_button.tscn74
-rw-r--r--ui/trackbutton.gd39
-rw-r--r--ui/tracks.gd2
17 files changed, 188 insertions, 359 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index d773226..c9596d0 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,3 +1,4 @@
{
- "godot_tools.gdscript_lsp_server_port": 6005
+ "godot_tools.gdscript_lsp_server_port": 6005,
+ "python.formatting.provider": "black"
}
diff --git a/classes/car_vars.gd b/classes/car_vars.gd
deleted file mode 100644
index dff1820..0000000
--- a/classes/car_vars.gd
+++ /dev/null
@@ -1,40 +0,0 @@
-class_name CarVars
-extends Resource
-
-var throttle: float
-var brake: float
-var steering: float
-var engine_force: float
-var engine_rpm: float
-var wheel_rpm: float
-var wheel_skidinfo: Array
-var wheel_contact: Array
-var wheel_position: Array
-var current_gear: int
-var position: Vector3
-var rotation: Vector3
-var kph: float
-
-func _init(from: Car) -> void:
- throttle = from.throttle
- brake = from.brake
- steering = from.steering
- engine_force = from.engine_force
- engine_rpm = from.engine_rpm
- position = from.global_position
- rotation = from.global_rotation
- kph = from.kph()
- for i in from.wheels.size():
- var wheel := from.wheels[i]
- wheel_skidinfo.append(wheel.get_skidinfo())
- wheel_contact.append(wheel.is_in_contact())
- wheel_position.append(wheel.global_position)
-
-func to_dict(): # RIP memory
- return {
- throttle = throttle, brake = brake, steering = steering,
- engine_force = engine_force, engine_rpm = engine_rpm,
- position = position, rotation = rotation, kph = kph,
- wheel_skidinfo = wheel_skidinfo, wheel_contact = wheel_contact,
- wheel_position = wheel_position,
- }
diff --git a/classes/resources/car_vars.gd b/classes/resources/car_vars.gd
new file mode 100644
index 0000000..2854034
--- /dev/null
+++ b/classes/resources/car_vars.gd
@@ -0,0 +1,40 @@
+class_name CarVars
+extends Resource
+
+var throttle: float
+var brake: float
+var steering: float
+var engine_force: float
+var engine_rpm: float
+var wheel_rpm: float
+var wheel_skidinfo: Array
+var wheel_contact: Array
+var wheel_position: Array
+var current_gear: int
+var position: Vector3
+var rotation: Vector3
+var kph: float
+
+func _init(from: Car) -> void:
+ throttle = from.throttle
+ brake = from.brake
+ steering = from.steering
+ engine_force = from.engine_force
+ engine_rpm = from.engine_rpm
+ position = from.global_position
+ rotation = from.global_rotation
+ kph = from.kph()
+ for i in from.wheels.size():
+ var wheel := from.wheels[i]
+ wheel_skidinfo.append(wheel.get_skidinfo())
+ wheel_contact.append(wheel.is_in_contact())
+ wheel_position.append(wheel.global_position)
+
+func to_dict(): # RIP memory
+ return {
+ throttle = throttle, brake = brake, steering = steering,
+ engine_force = engine_force, engine_rpm = engine_rpm,
+ position = position, rotation = rotation, kph = kph,
+ wheel_skidinfo = wheel_skidinfo, wheel_contact = wheel_contact,
+ wheel_position = wheel_position,
+ }
diff --git a/classes/ghost_data.gd b/classes/resources/ghost_data.gd
index 7be0426..7be0426 100644
--- a/classes/ghost_data.gd
+++ b/classes/resources/ghost_data.gd
diff --git a/init_classes.py b/init_classes.py
new file mode 100644
index 0000000..29d1487
--- /dev/null
+++ b/init_classes.py
@@ -0,0 +1,33 @@
+import re
+from glob import glob
+
+find_class = re.compile("class_name\s+(.+)")
+find_base = re.compile("extends\s+(.+)")
+classes = [] # { file, class name, base class name }
+
+for file in glob("**/*.gd", recursive=True):
+ with open(file, "r") as f:
+ text = f"{f.readline()}\n{f.readline()}\n{f.readline()}" # 3 lines: extend, class, tool
+ m = find_class.search(text)
+ if not m:
+ continue
+ class_name = m.groups()[0]
+ m = find_base.search(text)
+ base = m.groups()[0]
+ classes.append(
+ {"class_name": class_name, "base_class_name": base, "file": file}
+ )
+
+first = True
+print("list=[", end="")
+for c in classes:
+ if not first:
+ print(", ", end="")
+ first = False
+ print("{")
+ print(f'"base": "{c["base_class_name"]}",')
+ print(f'"class": &"{c["class_name"]}",')
+ print('"language": &"GDScript",')
+ print(f'"path": "res://{c["file"]}"')
+ print("}", end="")
+print("]")
diff --git a/lib/activation.gd b/lib/activation.gd
deleted file mode 100644
index 1db7637..0000000
--- a/lib/activation.gd
+++ /dev/null
@@ -1,9 +0,0 @@
-class_name Activation
-
-
-static func sigmoid(value: float, _row: int, _col: int) -> float:
- return 1 / (1 + exp(-value))
-
-
-static func dsigmoid(value: float, _row: int, _col: int) -> float:
- return value * (1 - value)
diff --git a/lib/matrix.gd b/lib/matrix.gd
deleted file mode 100644
index f33103a..0000000
--- a/lib/matrix.gd
+++ /dev/null
@@ -1,148 +0,0 @@
-## Matrix calculations.
-##
-## This class allows to perform all the basic operations with matrices.
-## (add, sub, multiply, scalar, dot product)
-## It is also possible to import and export matrices from an array.
-class_name Matrix
-
-var rows: int
-var cols: int
-
-var data = []
-
-
-func _init(_rows: int,_cols: int,value: float = 0.0):
- randomize()
- rows = _rows
- cols = _cols
- for row in range(rows):
- data.insert(row , [])
- for col in range(cols):
- data[row].insert(col, value)
-
-
-static func from_array(arr: Array[float]) -> Matrix:
- var result = Matrix.new(arr.size(), 1)
- for row in range(result.rows):
- result.data[row][0] = arr[row]
-
- return result
-
-
-static func to_array(matrix: Matrix) -> Array[float]:
- var result = []
- for row in range(matrix.rows):
- for col in range(matrix.cols):
- result.append(matrix.data[row][col])
-
- return result
-
-
-static func rand(matrix: Matrix) -> Matrix:
- randomize()
-
- var result = Matrix.new(matrix.rows, matrix.cols)
-
- for row in range(result.rows):
- for col in range(result.cols):
- result.data[row][col] = randf_range(-1, 1)
-
- return result
-
-
-static func add(a: Matrix, b: Matrix) -> Matrix:
- assert(a.rows == b.rows and a.cols == b.cols)
-
- var result = Matrix.new(a.rows, a.cols)
-
- for row in range(result.rows):
- for col in range(result.cols):
- result.data[row][col] = a.data[row][col] + b.data[row][col]
-
- return result
-
-
-static func subtract(a: Matrix, b: Matrix) -> Matrix:
- assert(a.rows == b.rows and a.cols == b.cols)
-
- var result = Matrix.new(a.rows, a.cols)
-
- for row in range(result.rows):
- for col in range(result.cols):
- result.data[row][col] = a.data[row][col] - b.data[row][col]
-
- return result
-
-
-static func scalar(matrix: Matrix, value: float) -> Matrix:
- var result = Matrix.new(matrix.rows, matrix.cols)
-
- for row in range(result.rows):
- for col in range(result.cols):
- result.data[row][col] = matrix.data[row][col] * value
-
- return result
-
-
-static func product(a: Matrix, b: Matrix) -> Matrix:
- assert(a.cols == b.rows)
-
- var result = Matrix.new(a.rows, b.cols)
-
- for row in range(result.rows):
- for col in range(result.cols):
- result.data[row][col] = 0.0
- for k in range(a.cols):
- result.data[row][col] += a.data[row][k] * b.data[k][col]
-
- return result
-
-
-static func multiply(a: Matrix, b: Matrix) -> Matrix:
- assert(a.rows == b.rows and a.cols == b.cols)
-
- var result = Matrix.new(a.rows, a.cols)
-
- for row in range(result.rows):
- for col in range(result.cols):
- result.data[row][col] = a.data[row][col] * b.data[row][col]
-
- return result
-
-
-static func random(a: Matrix, b: Matrix) -> Matrix:
- randomize()
- var result = Matrix.new(a.rows, a.cols)
- for row in range(result.rows):
- for col in range(result.cols):
- result.data[row][col] = a.data[row][col] if randf_range(0, 1) > 0.5 else b.data[row][col]
-
- return result
-
-
-static func transpose(matrix: Matrix) -> Matrix:
- var result = Matrix.new(matrix.cols, matrix.rows)
-
- for row in range(result.rows):
- for col in range(result.cols):
- result.data[row][col] = matrix.data[col][row]
-
- return result
-
-
-static func copy(matrix: Matrix) -> Matrix:
- var result = Matrix.new(matrix.rows, matrix.cols)
- for row in range(result.rows):
- for col in range(result.cols):
- result.data[row][col] = matrix.data[row][col]
- return result
-
-
-static func map(matrix: Matrix, callback) -> Matrix:
- var result = Matrix.new(matrix.rows, matrix.cols)
-
- for row in range(result.rows):
- for col in range(result.cols):
- result.data[row][col] = callback.call(matrix.data[row][col], row, col)
-
- return result
diff --git a/lib/neural_network.gd b/lib/neural_network.gd
deleted file mode 100644
index 70c49f6..0000000
--- a/lib/neural_network.gd
+++ /dev/null
@@ -1,127 +0,0 @@
-## Neural network
-##
-## It is a basic neural network with three layers (input, hidden and output).
-## The network is randomly initialized, it is possible to train it by passing it
-## the expected result for some input.
-class_name NeuralNetwork
-
-var input_nodes: int
-var hidden_nodes: int
-var output_nodes: int
-
-var weights_input_hidden: Matrix
-var weights_hidden_output: Matrix
-
-var bias_hidden: Matrix
-var bias_output: Matrix
-
-var learning_rate: float
-
-var activation_function: Callable
-var activation_dfunction: Callable
-
-
-func _init(_input_nodes: int,_hidden_nodes: int,_output_nodes: int):
- input_nodes = _input_nodes;
- hidden_nodes = _hidden_nodes;
- output_nodes = _output_nodes;
-
- weights_input_hidden = Matrix.rand(Matrix.new(hidden_nodes, input_nodes))
- weights_hidden_output = Matrix.rand(Matrix.new(output_nodes, hidden_nodes));
-
- bias_hidden = Matrix.rand(Matrix.new(hidden_nodes, 1));
- bias_output = Matrix.rand(Matrix.new(output_nodes, 1));
-
- set_learning_rate()
- set_activation_function()
-
-
-func set_learning_rate(_learning_rate: float = 0.1) -> void:
- learning_rate = _learning_rate
-
-
-func set_activation_function(callback: Callable = Callable(Activation, "sigmoid"), dcallback: Callable = Callable(Activation, "dsigmoid")) -> void:
- activation_function = callback
- activation_dfunction = dcallback
-
-
-func predict(input_array: Array[float]) -> Array[float]:
- var inputs := Matrix.from_array(input_array)
-
- var hidden := Matrix.product(weights_input_hidden, inputs)
- hidden = Matrix.add(hidden, bias_hidden)
- hidden = Matrix.map(hidden, activation_function)
-
- var output = Matrix.product(weights_hidden_output, hidden)
- output = Matrix.add(output, bias_output)
- output = Matrix.map(output, activation_function)
-
- return Matrix.to_array(output)
-
-
-func train(input_array: Array[float], target_array: Array[float]):
- var inputs := Matrix.from_array(input_array)
- var targets := Matrix.from_array(target_array)
-
- var hidden := Matrix.product(weights_input_hidden, inputs);
- hidden = Matrix.add(hidden, bias_hidden)
- hidden = Matrix.map(hidden, activation_function)
-
- var outputs := Matrix.product(weights_hidden_output, hidden)
- outputs = Matrix.add(outputs, bias_output)
- outputs = Matrix.map(outputs, activation_function)
-
- var output_errors = Matrix.subtract(targets, outputs)
-
- var gradients = Matrix.map(outputs, activation_dfunction)
- gradients = Matrix.multiply(gradients, output_errors)
- gradients = Matrix.scalar(gradients, learning_rate)
-
- var hidden_t = Matrix.transpose(hidden)
- var weight_ho_deltas = Matrix.product(gradients, hidden_t)
-
- weights_hidden_output = Matrix.add(weights_hidden_output, weight_ho_deltas)
- bias_output = Matrix.add(bias_output, gradients)
-
- var weights_hidden_output_t = Matrix.transpose(weights_hidden_output)
- var hidden_errors = Matrix.product(weights_hidden_output_t, output_errors)
-
- var hidden_gradient = Matrix.map(hidden, activation_dfunction)
- hidden_gradient = Matrix.multiply(hidden_gradient, hidden_errors)
- hidden_gradient = Matrix.scalar(hidden_gradient, learning_rate)
-
- var inputs_t = Matrix.transpose(inputs)
- var weight_ih_deltas = Matrix.product(hidden_gradient, inputs_t)
-
- weights_input_hidden = Matrix.add(weights_input_hidden, weight_ih_deltas)
-
- bias_hidden = Matrix.add(bias_hidden, hidden_gradient)
-
-
-static func reproduce(a: NeuralNetwork, b: NeuralNetwork) -> NeuralNetwork:
- var result = NeuralNetwork.new(a.input_nodes, a.hidden_nodes, a.output_nodes)
-
- result.weights_input_hidden = Matrix.random(a.weights_input_hidden, b.weights_input_hidden)
- result.weights_hidden_output = Matrix.random(a.weights_hidden_output, b.weights_hidden_output)
- result.bias_hidden = Matrix.random(a.bias_hidden, b.bias_hidden)
- result.bias_output = Matrix.random(a.bias_output, b.bias_output)
-
- return result
-
-
-static func mutate(nn: NeuralNetwork, callback: Callable) -> NeuralNetwork:
- var result = NeuralNetwork.new(nn.input_nodes, nn.hidden_nodes, nn.output_nodes)
- result.weights_input_hidden = Matrix.map(nn.weights_input_hidden, callback)
- result.weights_hidden_output = Matrix.map(nn.weights_hidden_output, callback)
- result.bias_hidden = Matrix.map(nn.bias_hidden, callback)
- result.bias_output = Matrix.map(nn.bias_output, callback)
- return result
-
-
-static func copy(nn : NeuralNetwork) -> NeuralNetwork:
- var result = NeuralNetwork.new(nn.input_nodes, nn.hidden_nodes, nn.output_nodes)
- result.weights_input_hidden = Matrix.copy(nn.weights_input_hidden)
- result.weights_hidden_output = Matrix.copy(nn.weights_hidden_output)
- result.bias_hidden = Matrix.copy(nn.bias_hidden)
- result.bias_output = Matrix.copy(nn.bias_output)
- return result
diff --git a/start.tscn b/start.tscn
index efc242d..6e99a13 100644
--- a/start.tscn
+++ b/start.tscn
@@ -2,10 +2,10 @@
[ext_resource type="Theme" uid="uid://cru1d7n2ftrfm" path="res://ui/theme.tres" id="1_gm0ws"]
[ext_resource type="Script" path="res://ui/tracks.gd" id="2_po2ce"]
-[ext_resource type="Resource" uid="uid://crye0ijvmtsyb" path="res://tracks/multilap_test.tres" id="3_0yjp1"]
-[ext_resource type="Resource" uid="uid://de46bcu1ivmtq" path="res://tracks/test.tres" id="4_3xqvr"]
+[ext_resource type="Resource" path="res://tracks/multilap_test.tres" id="3_0yjp1"]
+[ext_resource type="Resource" path="res://tracks/test.tres" id="4_3xqvr"]
[ext_resource type="PackedScene" uid="uid://dhiei0g5tr74s" path="res://scenes/race_high.tscn" id="5_m5kci"]
-[ext_resource type="Resource" uid="uid://crtutccbf1sxh" path="res://tracks/the fallen tramps.tres" id="5_qwie6"]
+[ext_resource type="Resource" path="res://tracks/the fallen tramps.tres" id="5_qwie6"]
[ext_resource type="PackedScene" uid="uid://dfvtugujgcjcw" path="res://ui/track_button.tscn" id="7_pchkj"]
[node name="start" type="Control"]
diff --git a/tracks/multilap_test.tres b/tracks/multilap_test.tres
index 298fba4..1f66712 100644
--- a/tracks/multilap_test.tres
+++ b/tracks/multilap_test.tres
@@ -1,4 +1,4 @@
-[gd_resource type="Resource" script_class="TrackResource" load_steps=6 format=3 uid="uid://crye0ijvmtsyb"]
+[gd_resource type="Resource" script_class="TrackResource" load_steps=6 format=3]
[ext_resource type="Script" path="res://classes/track.gd" id="1_c5h3o"]
[ext_resource type="PackedScene" uid="uid://d4a3e1w62m3ck" path="res://scenes/ring_checkpoint.tscn" id="1_ehf5p"]
diff --git a/tracks/test.tres b/tracks/test.tres
index b30bdca..a22f705 100644
--- a/tracks/test.tres
+++ b/tracks/test.tres
@@ -1,4 +1,4 @@
-[gd_resource type="Resource" script_class="TrackResource" load_steps=6 format=3 uid="uid://de46bcu1ivmtq"]
+[gd_resource type="Resource" script_class="TrackResource" load_steps=6 format=3]
[ext_resource type="PackedScene" uid="uid://d4a3e1w62m3ck" path="res://scenes/ring_checkpoint.tscn" id="1_ra7ed"]
[ext_resource type="PackedScene" uid="uid://t8ywjcjgw322" path="res://scenes/ring_finish.tscn" id="2_e22em"]
diff --git a/tracks/the fallen tramps.tres b/tracks/the fallen tramps.tres
index ed4cfb6..e87daa0 100644
--- a/tracks/the fallen tramps.tres
+++ b/tracks/the fallen tramps.tres
@@ -1,4 +1,4 @@
-[gd_resource type="Resource" script_class="TrackResource" load_steps=6 format=3 uid="uid://crtutccbf1sxh"]
+[gd_resource type="Resource" script_class="TrackResource" load_steps=6 format=3]
[ext_resource type="PackedScene" uid="uid://d4a3e1w62m3ck" path="res://scenes/ring_checkpoint.tscn" id="1_i8s2b"]
[ext_resource type="PackedScene" uid="uid://t8ywjcjgw322" path="res://scenes/ring_finish.tscn" id="2_107h2"]
diff --git a/ui/hud.tscn b/ui/hud.tscn
index d6ccf3e..8ac3f5b 100644
--- a/ui/hud.tscn
+++ b/ui/hud.tscn
@@ -151,7 +151,8 @@ ghost_indicator = ExtResource("8_yfv7r")
[node name="Splits" parent="." instance=ExtResource("9_gtkqi")]
layout_mode = 1
-offset_right = 25.0
+offset_top = -403.0
+offset_bottom = -303.0
[connection signal="assigned" from="." to="Dashboard/laps" method="assigned"]
[connection signal="assigned" from="." to="MiniMapContainer/MiniMap" method="assigned"]
diff --git a/ui/intro_cam.gd b/ui/intro_cam.gd
index c8371ac..ee2d7f5 100644
--- a/ui/intro_cam.gd
+++ b/ui/intro_cam.gd
@@ -6,11 +6,13 @@ signal finished
var track: TrackResource
var main_cam: Camera3D
+# if main_cam null, dont animate
func _init(_track: TrackResource, _main_cam: Camera3D):
far = 4000
near = .2
track = _track
main_cam = _main_cam
+ attributes = CameraAttributesPractical.new()
func _ready() -> void:
make_current()
@@ -21,10 +23,11 @@ func _ready() -> void:
var top_center := Vector3(box_center.x, track.overview_height, box_center.z)
global_position = top_center
global_rotation_degrees.x = -90
- await get_tree().create_timer(2).timeout
- var tween := get_tree().create_tween().set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
- tween.tween_property(self, ^"global_position", main_cam.global_position, 2)
- tween.tween_property(self, ^"global_rotation", main_cam.global_rotation, 1)
- await tween.finished
- finished.emit()
- main_cam.make_current()
+ if is_instance_valid(main_cam):
+ await get_tree().create_timer(2).timeout
+ var tween := get_tree().create_tween().set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
+ tween.tween_property(self, ^"global_position", main_cam.global_position, 2)
+ tween.tween_property(self, ^"global_rotation", main_cam.global_rotation, 1)
+ await tween.finished
+ finished.emit()
+ main_cam.make_current()
diff --git a/ui/track_button.tscn b/ui/track_button.tscn
index e4652cb..a54c48e 100644
--- a/ui/track_button.tscn
+++ b/ui/track_button.tscn
@@ -1,43 +1,89 @@
-[gd_scene load_steps=6 format=3 uid="uid://dfvtugujgcjcw"]
+[gd_scene load_steps=8 format=3 uid="uid://dfvtugujgcjcw"]
+[ext_resource type="Theme" uid="uid://cru1d7n2ftrfm" path="res://ui/theme.tres" id="1_noykn"]
[ext_resource type="Script" path="res://ui/trackbutton.gd" id="2_bcpuy"]
[ext_resource type="FontVariation" uid="uid://ba8ab6dti2fvo" path="res://ui/boldsans.tres" id="2_gctvu"]
[ext_resource type="SystemFont" uid="uid://d2klp6vxh5l2d" path="res://ui/cascadiabold.tres" id="3_suph6"]
+[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_fymg3"]
+bg_color = Color(0.137255, 0.137255, 0.137255, 1)
+
[sub_resource type="LabelSettings" id="LabelSettings_sa0e6"]
font = ExtResource("2_gctvu")
-font_size = 45
+font_size = 25
font_color = Color(0.933333, 0.909804, 0.835294, 1)
[sub_resource type="LabelSettings" id="LabelSettings_7u0yx"]
font = ExtResource("3_suph6")
-font_size = 45
+font_size = 25
font_color = Color(0.933333, 0.909804, 0.835294, 1)
-[node name="trackbutton" type="Button"]
-custom_minimum_size = Vector2(450, 150)
+[node name="trackbutton" type="PanelContainer"]
+custom_minimum_size = Vector2(450, 243)
offset_right = 200.0
offset_bottom = 100.0
+theme = ExtResource("1_noykn")
+theme_override_styles/panel = SubResource("StyleBoxFlat_fymg3")
script = ExtResource("2_bcpuy")
-[node name="VBoxContainer" type="VBoxContainer" parent="."]
-layout_mode = 1
-anchors_preset = 15
-anchor_right = 1.0
-anchor_bottom = 1.0
-grow_horizontal = 2
-grow_vertical = 2
+[node name="port" type="SubViewport" parent="."]
+unique_name_in_owner = true
+msaa_3d = 3
+screen_space_aa = 1
+size = Vector2i(450, 200)
+render_target_update_mode = 1
+
+[node name="v" type="VBoxContainer" parent="."]
+layout_mode = 2
+
+[node name="h" type="HBoxContainer" parent="v"]
+layout_mode = 2
mouse_filter = 2
+theme_override_constants/separation = 10
alignment = 1
-[node name="name" type="Label" parent="VBoxContainer"]
+[node name="name" type="Label" parent="v/h"]
+unique_name_in_owner = true
layout_mode = 2
text = "trambler 42"
label_settings = SubResource("LabelSettings_sa0e6")
horizontal_alignment = 1
-[node name="time" type="Label" parent="VBoxContainer"]
+[node name="bar" type="Label" parent="v/h"]
+layout_mode = 2
+text = "|"
+label_settings = SubResource("LabelSettings_sa0e6")
+horizontal_alignment = 1
+
+[node name="time" type="Label" parent="v/h"]
+unique_name_in_owner = true
layout_mode = 2
text = "no time set"
label_settings = SubResource("LabelSettings_7u0yx")
horizontal_alignment = 1
+
+[node name="thumb" type="TextureRect" parent="v"]
+unique_name_in_owner = true
+custom_minimum_size = Vector2(450, 200)
+layout_mode = 2
+stretch_mode = 2
+
+[node name="h2" type="HBoxContainer" parent="."]
+layout_mode = 2
+size_flags_vertical = 8
+
+[node name="play" type="Button" parent="h2"]
+layout_mode = 2
+theme_override_font_sizes/font_size = 35
+text = " "
+
+[node name="spacer" type="Control" parent="h2"]
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="watch" type="Button" parent="h2"]
+layout_mode = 2
+theme_override_font_sizes/font_size = 35
+text = "󰿎 "
+
+[connection signal="pressed" from="h2/play" to="." method="emit_signal" binds= [&"pressed"]]
diff --git a/ui/trackbutton.gd b/ui/trackbutton.gd
index c046098..35f211f 100644
--- a/ui/trackbutton.gd
+++ b/ui/trackbutton.gd
@@ -1,8 +1,37 @@
extends Control
-func init(t: float, n: String) -> void:
- $VBoxContainer/name.text = n
- if t < 0:
- $VBoxContainer/time.text = "no time set"
+const trackloader_scn = preload("res://scenes/track.tscn")
+const thumbnail_path = "user://%s.thumb"
+
+signal pressed
+
+func init(t: TrackResource, g: GhostData) -> void:
+ %name.text = t.name
+ if g == null:
+ %time.text = "no time set"
+ else:
+ %time.text = GameTimer.format_precise(g.time)
+ var p: String = thumbnail_path % t.name
+ if FileAccess.file_exists(p) and Time.get_unix_time_from_system() - FileAccess.get_modified_time(p) < 40000: # ~5days
+ print("loading thumb")
+ var f := FileAccess.open(p, FileAccess.READ)
+ var img := Image.new()
+ var e := img.load_png_from_buffer(f.get_buffer(f.get_length()))
+ if e != OK:
+ print("error: ", e)
+ return
+ (%thumb as TextureRect).texture = ImageTexture.create_from_image(img)
else:
- $VBoxContainer/time.text = GameTimer.format_precise(t)
+ print("making thumb")
+ %port.add_child(IntroCam.new(t, null))
+ var trackloader: TrackLoader = trackloader_scn.instantiate()
+ trackloader.track = t
+ %port.add_child(trackloader)
+ await RenderingServer.frame_post_draw
+ trackloader.queue_free()
+ var img: Image = %port.get_texture().get_image()
+ img.save_png(p)
+ (%thumb as TextureRect).texture = ImageTexture.create_from_image(img)
+
+
+
diff --git a/ui/tracks.gd b/ui/tracks.gd
index 1b23df2..f2ea755 100644
--- a/ui/tracks.gd
+++ b/ui/tracks.gd
@@ -9,7 +9,7 @@ func _ready() -> void:
var button := trackbutton.instantiate()
add_child(button)
var ghost := GhostData._load(Globals.SAVES % track.name)
- button.init(ghost.time if ghost else -1, track.name)
+ await button.init(track, ghost)
button.pressed.connect(track_selected.bind(track))
func track_selected(track: TrackResource) -> void: