Loose shopping trolleys in the Woolies carpark

18 kg of chrome chaos each: procedural basket + handle + castors on a
low-friction RigidBody, so they roll away when nudged and FLY when hit at
speed. Four strays around every trolley bay plus five rogues claiming
parking spots, as is tradition.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
m3ultra 2026-07-30 13:58:02 +10:00
parent 52f7705e13
commit 30d5cfd846

View File

@ -46,6 +46,12 @@ func _ready() -> void:
_planter_island(Vector3(x, 0, -60.0 + j * 30.0), rng)
for p in [Vector3(-52, 0, 44), Vector3(52, 0, 44), Vector3(-52, 0, -46)]:
_trolley_bay(p)
for i in 4: # strays that never made it back to the bay
_trolley(p + Vector3(rng.randf_range(-7.0, 7.0), 0, rng.randf_range(-6.0, 6.0)),
rng.randf() * TAU)
for i in 5: # rogue trolleys claiming parking spots, as is tradition
_trolley(Vector3(rng.randf_range(-60.0, 60.0), 0, rng.randf_range(-40.0, 40.0)),
rng.randf() * TAU)
_race_path()
func _mat(tex: String, scale: float, rough := 0.9) -> StandardMaterial3D:
@ -179,6 +185,64 @@ func _trolley_bay(pos: Vector3) -> void:
for j in 5: # the trolleys themselves
_slab(pos + Vector3(0, 0.5, -3.2 + j * 1.6), Vector3(0.9, 1.0, 1.2), steel, false)
func _trolley(pos: Vector3, yaw: float) -> void:
## A loose shopping trolley: 18 kg of chrome chaos. Light enough to FLY when
## clipped at speed, low-friction so it rolls away like the real thing.
var b := RigidBody3D.new()
b.mass = 18.0
var pm := PhysicsMaterial.new()
pm.friction = 0.25
pm.bounce = 0.25
b.physics_material_override = pm
var cs := CollisionShape3D.new()
var bs := BoxShape3D.new()
bs.size = Vector3(0.58, 0.95, 0.92)
cs.shape = bs
cs.position = Vector3(0, 0.5, 0)
b.add_child(cs)
var steel := _flat(Color(0.62, 0.63, 0.66), 0.35)
steel.metallic = 0.7
# basket: floor + four walls, wider at the top like the real thing
var basket := [
[Vector3(0, 0.42, 0), Vector3(0.50, 0.03, 0.84)], # floor
[Vector3(0, 0.66, 0.44), Vector3(0.54, 0.45, 0.03)], # back
[Vector3(0, 0.66, -0.42), Vector3(0.50, 0.45, 0.03)], # front
[Vector3(0.27, 0.66, 0), Vector3(0.03, 0.45, 0.86)], # right
[Vector3(-0.27, 0.66, 0), Vector3(0.03, 0.45, 0.86)], # left
]
for spec in basket:
var mi := MeshInstance3D.new()
var bm := BoxMesh.new()
bm.size = spec[1]
bm.material = steel
mi.mesh = bm
mi.position = spec[0]
b.add_child(mi)
var handle := MeshInstance3D.new()
var hm := BoxMesh.new()
hm.size = Vector3(0.58, 0.04, 0.04)
hm.material = steel
handle.mesh = hm
handle.position = Vector3(0, 0.97, 0.50)
b.add_child(handle)
for sx in [-0.22, 0.22]:
for sz in [-0.36, 0.36]:
var c := MeshInstance3D.new()
var cm := CylinderMesh.new()
cm.top_radius = 0.05
cm.bottom_radius = 0.05
cm.height = 0.04
cm.radial_segments = 8
cm.material = _flat(Color(0.12, 0.12, 0.13), 0.7)
c.mesh = cm
c.rotation_degrees = Vector3(0, 0, 90)
c.position = Vector3(sx, 0.05, sz)
b.add_child(c)
add_child(b)
b.position = pos + Vector3.UP * 0.05
b.rotate_y(yaw)
b.sleeping = true
func _race_path() -> void:
# perimeter loop road around the parking rows -- race line + traffic route
var path := Path3D.new()