small racing game im working on
add
reset functionality [skip ci]
| -rw-r--r-- | assets/cars/kenney_sedan/sedan.tscn | 4 | ||||
| -rw-r--r-- | assets/mats/shader_sky.tres | 2 | ||||
| -rw-r--r-- | cam.gd | 7 | ||||
| -rw-r--r-- | classes/car.gd | 6 | ||||
| -rw-r--r-- | classes/ghost_data.gd (renamed from classes/trackdata.gd) | 65 | ||||
| -rw-r--r-- | classes/timer.gd | 6 | ||||
| -rw-r--r-- | project.godot | 6 | ||||
| -rw-r--r-- | race.gd | 69 | ||||
| -rw-r--r-- | scenes/race_highlevel.gd | 10 | ||||
| -rw-r--r-- | scenes/ring_checkpoint.tscn | 2 | ||||
| -rw-r--r-- | scenes/track.tscn | 2 | ||||
| -rw-r--r-- | ui/hud.gd | 2 | ||||
| -rw-r--r-- | ui/map.gd | 2 |
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) @@ -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] @@ -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 @@ -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 @@ -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) |