feat: parked cars across the city (Kenney Car Kit, CC0)

Streets are now populated: seeded parking lanes hug the curbs of every
N-S street with sedans, taxis, vans, SUVs, hatchbacks, delivery trucks
and the occasional police car (~100+ across the city).

- Wheels attach at the models' wheel-socket empties at spawn
- Metal-tagged box colliders: cars are cover that sounds like cover
- Toonified + darkened base tint (kit albedo clips under the map light)
  with per-car hue jitter for varied paint jobs
- Slight rotation jitter so parking reads natural, not stamped

Verified via street capture (colored cars parked down the avenue, no
clipping/glow); movement 11/11, smoke 0 failures, acoustics ALL PASSED.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
Nicholas Butzke
2026-07-19 02:39:35 -04:00
co-authored by Claude Fable 5
parent 04d91dea31
commit 56db9eea59
11 changed files with 82 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
Car Kit (3.1)
Created/distributed by Kenney (www.kenney.nl)
Creation date: 02-04-2026 14:03
------------------------------
License: (Creative Commons Zero, CC0)
http://creativecommons.org/publicdomain/zero/1.0/
You can use this content for personal, educational, and commercial purposes.
Support by crediting 'Kenney' or 'www.kenney.nl' (this is not a requirement)
------------------------------
• Website : www.kenney.nl
• Donate : www.kenney.nl/donate
• Patreon : patreon.com/kenney
Follow on social media for updates:
• Twitter: twitter.com/KenneyNL
• BlueSky: kenney.bsky.social
• Instagram: instagram.com/kenney_nl
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -270,6 +270,7 @@ func _build_geometry() -> void:
_build_rail_line() _build_rail_line()
_build_streets() _build_streets()
_build_avenues() _build_avenues()
_build_parked_cars()
if _spawn_points.is_empty(): if _spawn_points.is_empty():
_spawn_points.append(Vector3(0, 2, 0)) _spawn_points.append(Vector3(0, 2, 0))
@@ -665,6 +666,59 @@ func _build_avenues() -> void:
Vector3(x, 0, z2), 0.0 if side > 0 else 180.0, lamp_scale) Vector3(x, 0, z2), 0.0 if side > 0 else 180.0, lamp_scale)
# ── Parked cars (Kenney Car Kit) ─────────────────────────────────────────────
const CAR_KEYS := ["sedan", "sedan", "taxi", "van", "suv", "hatchback-sports",
"delivery", "sedan-sports", "police"]
const CAR_SCALE := 1.8
## A parked car: metal-tagged collider, kit body, wheels attached to the
## model's wheel sockets, slight per-car tint.
func _parked_car(pos: Vector3, yaw_deg: float) -> void:
var key: String = CAR_KEYS[_rng.randi() % CAR_KEYS.size()]
var body := StaticBody3D.new()
body.name = "Car_%d" % _rng.randi()
body.set_meta("acoustic_material", "metal")
add_child(body)
body.global_position = pos
body.rotation_degrees.y = yaw_deg + _rng.randf_range(-3.0, 3.0)
var shape := CollisionShape3D.new()
var bs := BoxShape3D.new()
bs.size = Vector3(2.8, 2.6, 5.0)
shape.shape = bs
shape.position = Vector3(0, 1.3, 0)
body.add_child(shape)
var inst: Node3D = _kit_scene_at("res://assets/props/cars/" + key + ".glb").instantiate()
inst.scale = Vector3.ONE * CAR_SCALE
inst.position.y = 0.55 # wheel contact at ground after scaling
body.add_child(inst)
var wheel_scene := _kit_scene_at("res://assets/props/cars/wheel-default.glb")
for n in inst.find_children("wheel*", "Node3D", true, false):
if n.get_child_count() == 0 and not (n is MeshInstance3D):
var wheel: Node3D = wheel_scene.instantiate()
n.add_child(wheel)
LevelMaterials.apply_toon_recursive(inst, 0.0)
# Darken under the map lighting (kit albedo clips) + per-car hue
_tint_recursive(inst, Color(0.62, 0.62, 0.66) * Color(1, 1, 1).lerp(Color.from_hsv(_rng.randf(), 0.6, 1.0), 0.35))
## Seeded parking lanes hugging the curbs of the N-S streets.
func _build_parked_cars() -> void:
for x in _street_lines():
for row in GRID:
_rng.seed = hash(Vector2i(int(x), row)) + 913
if _rng.randf() < 0.45:
continue
var z_base := (float(row) - float(GRID - 1) * 0.5) * PITCH
var cars := 1 + _rng.randi() % 3
for i in cars:
var side := 1.0 if _rng.randi() % 2 == 0 else -1.0
var cx := x + side * (ROAD_W * 0.5 - 1.8)
var cz := z_base + _rng.randf_range(-18.0, 18.0)
_parked_car(Vector3(cx, 0.02, cz), 0.0 if _rng.randi() % 2 == 0 else 180.0)
func _kit_prop_path(path: String, pos: Vector3, yaw_deg: float, scale_f: float) -> void: func _kit_prop_path(path: String, pos: Vector3, yaw_deg: float, scale_f: float) -> void:
var inst: Node3D = _kit_scene_at(path).instantiate() var inst: Node3D = _kit_scene_at(path).instantiate()
inst.scale = Vector3.ONE * scale_f inst.scale = Vector3.ONE * scale_f