small racing game im working on
add
reset functionality [skip ci]
bendn 2023-03-03
parent dcac690 · commit 818f333
-rw-r--r--assets/cars/kenney_sedan/sedan.tscn4
-rw-r--r--assets/mats/shader_sky.tres2
-rw-r--r--cam.gd7
-rw-r--r--classes/car.gd6
-rw-r--r--classes/ghost_data.gd (renamed from classes/trackdata.gd)65
-rw-r--r--classes/timer.gd6
-rw-r--r--project.godot6
-rw-r--r--race.gd69
-rw-r--r--scenes/race_highlevel.gd10
-rw-r--r--scenes/ring_checkpoint.tscn2
-rw-r--r--scenes/track.tscn2
-rw-r--r--ui/hud.gd2
-rw-r--r--ui/map.gd2
13 files changed, 122 insertions, 61 deletions
diff --git a/assets/cars/kenney_sedan/sedan.tscn b/assets/cars/kenney_sedan/sedan.tscn
index 999f129..cbb5616 100644
--- a/assets/cars/kenney_sedan/sedan.tscn
+++ b/assets/cars/kenney_sedan/sedan.tscn
@@ -20,8 +20,8 @@
[ext_resource type="AudioStream" uid="uid://d3rhrhg8srpdg" path="res://assets/sounds/checkpoint.ogg" id="19_stkh0"]
[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_hs0do"]
-friction = 0.71
-bounce = 0.2
+friction = 0.7
+bounce = 0.7
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_8bpd4"]
resource_name = "plastic"
diff --git a/assets/mats/shader_sky.tres b/assets/mats/shader_sky.tres
index 52e822b..67e4b53 100644
--- a/assets/mats/shader_sky.tres
+++ b/assets/mats/shader_sky.tres
@@ -57,7 +57,7 @@ shader_parameter/cloud_scale_3 = 0.075
shader_parameter/cloud_distortion = 1.95
shader_parameter/cloud_strength = 1.0
shader_parameter/cloud_cutoff = 0.013
-shader_parameter/cloud_fuzziness = 1.0
+shader_parameter/cloud_fuzziness = 0.5
shader_parameter/cloud_noise1 = SubResource("NoiseTexture_uhc2h")
shader_parameter/cloud_noise2 = SubResource("NoiseTexture_vkbkn")
shader_parameter/day_bottom_color = Color(0.4, 1, 1, 1)
diff --git a/cam.gd b/cam.gd
index 3c60abc..dfcaedb 100644
--- a/cam.gd
+++ b/cam.gd
@@ -8,11 +8,14 @@ var follow_this: Node3D
var last_lookat: Vector3
func _ready():
+ reset()
+ far = 2000
+ near = .2
+
+func reset():
global_position = follow_this.global_position + (follow_this.global_transform.basis.z * target_distance)
look_at(follow_this.global_position)
last_lookat = follow_this.global_position
- far = 2000
- near = .2
func target() -> Vector3:
var delta_v := global_position - follow_this.global_position
diff --git a/classes/car.gd b/classes/car.gd
index 8d3a23c..e9acc26 100644
--- a/classes/car.gd
+++ b/classes/car.gd
@@ -42,12 +42,14 @@ func is_on_ground() -> bool:
func is_not_on_ground() -> bool:
return wheels.any(func(whl: VehicleWheel3D): return !whl.is_in_contact())
+func reset() -> void:
+ brake = 15
+ set_physics_process(false)
+
func _ready() -> void:
for whl in wheels:
particles.append(whl.get_node(^"particles"))
randomize()
- brake = 15
- set_physics_process(false)
func kph():
return (3 * PI * wheels[0].wheel_radius * rpm()) / 25;
diff --git a/classes/trackdata.gd b/classes/ghost_data.gd
index bac1297..59d1e9d 100644
--- a/classes/trackdata.gd
+++ b/classes/ghost_data.gd
@@ -1,17 +1,25 @@
-class_name TrackSaveableData
+class_name GhostData
extends Resource
var time: float
-var checkpoints: Array#[PackedFloat32Array]
-var positional := {
- origins = PackedVector3Array(),
- rotations = PackedVector3Array(),
- steering = PackedFloat32Array(),
- snaps = 0,
-}
+var checkpoints: Array
+var positions: PackedVector3Array = []
+var rotations: PackedVector3Array = []
+var steering: PackedFloat32Array = []
+var snaps := 0
+
+func snapshot(obj: Car):
+ positions.append(obj.global_position)
+ rotations.append(obj.global_rotation)
+ steering.append(snappedf(obj.steering, .1)) # FastLZ benefits from repetition
+ snaps += 1
+
+## returns [position, rotation, steering]
+func load_snap(i: int) -> Array:
+ return [positions[i], rotations[i], steering[i]]
func save(path: String) -> void:
- _save_file(path, {checkpoints = checkpoints, positional = positional, time = time})
+ _save_file(path, {checkpoints = checkpoints, positions = positions, rotations = rotations, steering = steering, time = time, snaps = snaps})
func _init(num_checkpoints := 0, laps := 0) -> void:
for i in laps:
@@ -20,6 +28,15 @@ func _init(num_checkpoints := 0, laps := 0) -> void:
arr.fill(-1)
checkpoints.append(arr)
+func clear() -> void:
+ for lap in checkpoints:
+ lap.fill(-1)
+ positions = []
+ rotations = []
+ steering = []
+ time = 0
+ snaps = 0
+
func collect(lap: int, cp: int, now: float) -> void:
if lap == len(checkpoints) - 1 && cp == -1:
checkpoints[lap][cp] = now
@@ -27,29 +44,22 @@ func collect(lap: int, cp: int, now: float) -> void:
else:
checkpoints[lap][cp] = now
+func has_collected(lap: int, cp: int) -> bool:
+ return checkpoints[lap][cp] != -1
+
func get_time(lap: int, cp: int) -> float:
return checkpoints[lap][cp]
-func snapshot(obj: Car) -> void:
- positional.origins.append(obj.global_position)
- positional.rotations.append(obj.global_rotation)
- positional.steering.append(snappedf(obj.steering, .1)) # FastLZ benefits from repetition
- positional.snaps += 1
-
-func loadshot(frame: int) -> Array:
- return [positional.origins[frame], positional.rotations[frame], positional.steering[frame]]
-
-func snaps() -> int:
- return positional.snaps
-
-static func from_d(d: Dictionary) -> TrackSaveableData:
- var obj := TrackSaveableData.new(0)
+static func from_d(d: Dictionary) -> GhostData:
+ var obj := GhostData.new()
obj.time = d.time
obj.checkpoints = d.checkpoints
- obj.positional = d.positional
+ obj.positions = d.positions
+ obj.rotations = d.rotations
+ obj.steering = d.steering
+ obj.snaps = d.snaps
return obj
-
## Saves a basic dictionary to a path.
static func _save_file(path: String, data: Dictionary) -> void:
var file := FileAccess.open_compressed(path, FileAccess.WRITE, FileAccess.COMPRESSION_FASTLZ)
@@ -67,8 +77,9 @@ static func _load_file(path: String) -> Dictionary:
_save_file(path, {}) # create file if it doesn't exist
return {}
-static func _load(path: String) -> TrackSaveableData:
+## Creates a [GhostData] from a file
+static func _load(path: String) -> GhostData:
var res := _load_file(path)
if res.is_empty():
return null
- return TrackSaveableData.from_d(res)
+ return GhostData.from_d(res)
diff --git a/classes/timer.gd b/classes/timer.gd
index 01684b5..a193c7f 100644
--- a/classes/timer.gd
+++ b/classes/timer.gd
@@ -12,6 +12,10 @@ func start() -> void:
func stop() -> void:
set_process(false)
+func reset() -> void:
+ stop()
+ elapsed_time = 0.0
+
func now() -> float:
return elapsed_time
@@ -23,4 +27,4 @@ func fmt_now() -> String:
return GameTimer.format(elapsed_time)
func _process(delta: float) -> void:
- elapsed_time += delta \ No newline at end of file
+ elapsed_time += delta
diff --git a/project.godot b/project.godot
index f60912d..41ea384 100644
--- a/project.godot
+++ b/project.godot
@@ -97,6 +97,12 @@ shift_up={
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":2,"pressure":0.0,"pressed":false,"script":null)
]
}
+reset={
+"deadzone": 0.5,
+"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194308,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null)
+, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":6,"pressure":0.0,"pressed":false,"script":null)
+]
+}
[layer_names]
diff --git a/race.gd b/race.gd
index 2848ed0..69aeba5 100644
--- a/race.gd
+++ b/race.gd
@@ -7,8 +7,8 @@ var track_loader_scene: PackedScene
var ghost_scene: PackedScene
var track_res: TrackResource
var track: TrackLoader
-var data: TrackSaveableData
-var best_time_data: TrackSaveableData
+var data: GhostData
+var best_time_data: GhostData
var car: Car
var ghost: GhostCar
var start_frame: int
@@ -18,13 +18,14 @@ var playing := false
var timer := GameTimer.new()
const SaveLoad := preload("res://addons/@bendn/remap/private/SaveLoadUtils.gd")
-const saves := "user://%s.trackdata"
+const saves := "user://%s.ghost"
signal next_lap
signal created_car(car: Car)
signal created_ghost(ghost: GhostCar)
signal finished
signal split(time: float, prev_time: float)
+signal reset
func _init(t: TrackResource, _car_scene, _ghost_scene, _track_loader_scene) -> void:
car_scene = _car_scene
@@ -37,40 +38,64 @@ func mkghost() -> void:
g.set_script(load("res://classes/ghost.gd"))
ghost = g
add_child(ghost)
- ghost.global_position = best_time_data.loadshot(0)[0]
- ghost.global_rotation = best_time_data.loadshot(0)[1]
- ghost.hide()
+ reset_ghost()
created_ghost.emit(ghost)
+func reset_ghost() -> void:
+ if best_time_data:
+ ghost.global_position = best_time_data.load_snap(0)[0]
+ ghost.global_rotation = best_time_data.load_snap(0)[1]
+ ghost.hide()
+
+func reset_car() -> void:
+ await get_tree().physics_frame
+ car.rotation = track.start_rot + Vector3(0, PI, -PI/2)
+ car.global_position = track.start_pos + Vector3(0, 2, 0) - (car.global_transform.basis.z * 2) # bump forward a teensy bit
+ car.engine_force = 0
+ car.current_gear = 0
+ car.linear_velocity = Vector3.ZERO
+ car.angular_velocity = Vector3.ZERO
+ car.steering = 0
+ for p in car.particles:
+ p.emitting = false
+ car.reset()
+
func mkcar() -> void:
car = HumanCar.attach(car_scene)
- car.visible = false
add_child(car)
- car.rotation = track.start_rot + Vector3(0, PI, -PI/2)
- car.global_position = track.start_pos + Vector3(0, 2, 0) - (car.global_transform.basis.z * 2) # bump forward a teensy bit
- car.visible = true
+ reset_car()
created_car.emit(car)
- print("car created")
func _ready() -> void:
set_physics_process(false)
track = track_loader_scene.instantiate()
track.track = track_res
add_child(track)
- data = TrackSaveableData.new(track_res.checkpoints.size(), track_res.laps)
- best_time_data = TrackSaveableData._load(saves % track_res.name)
+ data = GhostData.new(track_res.checkpoints.size(), track_res.laps)
+ best_time_data = GhostData._load(saves % track_res.name)
mkcar()
- if best_time_data:
- mkghost()
+ mkghost()
connect_checkpoints()
add_child(timer)
+func _input(event: InputEvent) -> void:
+ if event.is_action("reset") and playing:
+ playing = false
+ if best_time_data:
+ if ghost:
+ reset_ghost()
+ await reset_car()
+ set_physics_process(false)
+ data.clear()
+ timer.reset()
+ reset.emit()
+
func connect_checkpoints() -> void:
for i in len(track.checkpoints):
track.checkpoints[i].collected.connect(passed_cp.bind(i))
track.finish.collected.connect(passed_finish)
-func passed_cp(cp: int) -> void: if playing and data.checkpoints[current_lap][cp] < 0: collect(cp)
+func passed_cp(cp: int) -> void: if playing and !data.has_collected(current_lap, cp): collect(cp)
func passed_finish() -> void:
if !playing: return
@@ -92,13 +117,13 @@ func passed_finish() -> void:
func _physics_process(_delta: float) -> void:
data.snapshot(car)
- if ghost:
- if best_time_data.snaps() - 1 < Engine.get_physics_frames() - start_frame:
- print("ran out of snaps, removing ghost")
- ghost.queue_free()
- ghost = null
+ if best_time_data:
+ if best_time_data.snaps - 1 < Engine.get_physics_frames() - start_frame:
+ if ghost.visible:
+ print("ran out of snaps, hiding ghost")
+ ghost.hide()
return
- var shot := best_time_data.loadshot(Engine.get_physics_frames() - start_frame)
+ var shot := best_time_data.load_snap(Engine.get_physics_frames() - start_frame)
ghost.update(shot[0], shot[1], shot[2])
ghost.visible = (ghost.global_position.distance_squared_to(car.global_position) > 10)
diff --git a/scenes/race_highlevel.gd b/scenes/race_highlevel.gd
index 00c84cb..ba14ac3 100644
--- a/scenes/race_highlevel.gd
+++ b/scenes/race_highlevel.gd
@@ -13,6 +13,7 @@ var huds: Array[HUD]
func _ready() -> void:
race = Race.new(Globals.playing, car_scene, ghost_scene, track_loader_scene)
+ race.reset.connect(count_in)
add_child(race)
add_player()
super()
@@ -29,4 +30,11 @@ func add_player() -> void:
v.add_child(hud)
race.split.connect(hud.splits.update)
race.next_lap.connect(hud.laps.increment)
- i_cam.finished.connect(func(): var countdown := countdown_scene.instantiate(); v.add_child(countdown); countdown.finished.connect(func(): race.start()))
+ huds.append(hud)
+ i_cam.finished.connect(count_in)
+ race.reset.connect(c_cam.reset)
+
+func count_in():
+ var countdown := countdown_scene.instantiate()
+ huds[0].add_child(countdown)
+ countdown.finished.connect(race.start)
diff --git a/scenes/ring_checkpoint.tscn b/scenes/ring_checkpoint.tscn
index c85070c..afd0d47 100644
--- a/scenes/ring_checkpoint.tscn
+++ b/scenes/ring_checkpoint.tscn
@@ -18,6 +18,8 @@ script = ExtResource("1_cmmpp")
[node name="Ring" type="MeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 0)
+visibility_range_end_margin = 1000.0
+visibility_range_fade_mode = 1
mesh = ExtResource("2_xys6y")
[node name="Collision" type="CSGMesh3D" parent="Ring"]
diff --git a/scenes/track.tscn b/scenes/track.tscn
index da89693..db51c13 100644
--- a/scenes/track.tscn
+++ b/scenes/track.tscn
@@ -58,7 +58,7 @@ path_u_distance = 1.0
path_joined = true
[node name="Support" type="CSGPolygon3D" parent="."]
-polygon = PackedVector2Array(-22, -1, 22, -1, 25.1, -2, -25, -2)
+polygon = PackedVector2Array(-22, -1, 22, -1, 25, -2, -25, -2)
mode = 2
path_node = NodePath("..")
path_interval_type = 0
diff --git a/ui/hud.gd b/ui/hud.gd
index 3dcdff5..708bdd6 100644
--- a/ui/hud.gd
+++ b/ui/hud.gd
@@ -5,4 +5,4 @@ signal assigned(car: Car, ghost: GhostCar, timer: GameTimer, track: TrackLoader)
signal next_lap
@export var splits: Splits
-@export var laps: LapCounter \ No newline at end of file
+@export var laps: LapCounter
diff --git a/ui/map.gd b/ui/map.gd
index 2f3bf10..4dd93f5 100644
--- a/ui/map.gd
+++ b/ui/map.gd
@@ -28,7 +28,7 @@ func _ready() -> void:
add_point(p)
path.curve = curve
add_child(path)
- global_position = -box.position
+ position = -box.position
scale = vec((add((get_parent() as Container).get_rect().size) / add(box.size))/2)
mkfollower(track.finish, finish_indicator, Color.WHITE, false, true)