Development Log: physic-based arcade driving system
Goal: Create a realistic but arcade-style cart driving system that’s physically responsive, intuitive to control, and above all, fun.
This post walks through my development journey from raw implementation to iterative refinement, bugs, breakthroughs, and a major game pivot, from grounded cart racing to an isometric shopping carts & snake experience.
Technical Summary
Engine: Unity 2020.0.23f1
Physics Base: Rigidbody + Custom Suspension & Force Application
Key Systems:
Raycast-based suspension system using Hooke’s Law
Procedural steering and drift control using grip curves
Dynamic torque scaling with
AnimationCurve
Top-down isometric camera re-mapping
⚠️ If any image in this post has a red filter or noise filter, it was a bug of Unity 6 with Linux.
Day 1: Initial Setup
Started with a fresh, empty scene to test the driving system setup.
• Decided to follow a tutorial's modular approach: a cart body with 4 wheels. At first glance, the structure made sense – forces on each wheel (suspension, torque, acceleration/braking) – but honestly, I wasn’t sure how these pieces would interact. Are the wheels just visual? Is raycasting involved?
• Used ProBuilder to create the body and wheels. ProBuilder’s mesh colliders caused issues because Unity considers them concave, making them incompatible with physics calculations.
After some trial and error, I replaced the body’s collider with a box collider. And Set the wheels to use convex mesh colliders. They’re less precise, but it’s the only reasonable alternative I could think of for cylinders.
• Reflection: I’m still wondering if there’s a better way to handle wheel colliders for accuracy without sacrificing performance.
Day 2: Suspension System and Raycasting
Jumped into the suspension system. Based on research, we can use raycasting to simulate spring behaviour. Use OnDrawGizmos for visualizing rays, which was helpful as always.
• Added adjustable offsets for ray origins since each wheel’s position can differ.
Immediate Problems:
Rays went rogue without a maximum distance, making the cart act like it had rockets attached.
Self-detection issues arose because the raycast hit the cart itself. A layer mask fixed this.
Wheels behaving as physical objects messed with the calculations. Are they just visual, or should they interact with physics? This question bugged me.
• Plan: Simplify by hardcoding ray origins to the cart’s corners. Treat wheels purely as visual objects, detached from physics calculations.
Day 3: Refining Suspension Calculations
Dropped reliance on rayStartPosition calculations. Hardcoding felt cleaner, but not perfect.
• Added Gizmos for debugging forces. Seeing the force vectors in action helped clarify issues, though the green lines were thin and messy – maybe numbers would’ve been clearer.
• Big Realization: Offset calculations were wrong! Hooke’s Law (F = -kΔx) made me realize I’d flipped the logic. Instead of hit.distance - rest, it should be rest - hit.distance. This way, the spring compresses correctly, and forces align with displacement.
Here is the final codes:
Cart finally bounced as expected! But flipping became an issue. I thought forcing the spring’s direction to world Y would help, but that’s wrong. It has to align with wheel.transform.up. Why is the flipping happening, though? Still investigating. (Answer: The four ray casts were not evenly placed on the rigidbody)
Day 4: Steering System
Implemented steering by simulating lateral grip forces. Surprisingly straightforward, but testing revealed gaps.
Added basic acceleration and braking for testing. They worked, but felt too basic.
• Observations:
Steering rotation didn’t reset when I let go of the input. Not sure why yet.
Drift didn’t feel dynamic. A grip factor based on lateral velocity might fix this.
Day 5: Research and Learn from other titles
Played "Horizon Chase Turbo" for inspiration.
Observed:
A/D keys smoothly rotate the car and snap back when released.
Left joystick handles rotation only; acceleration/braking use separate inputs. • Adjusted code for smoother snapping:
if (input != 0) { currentRotationAngle = Mathf.Clamp(currentRotationAngle + input * rotationSpeed * Time.deltaTime, maxRotationAngle, maxRotationAngle); }
else { currentRotationAngle = Mathf.MoveTowards(currentRotationAngle, 0f, rotationSpeed * Time.deltaTime); }
Day 6: More updates
Added grip curves for front and rear wheels. Rear wheels lose grip earlier, making drifting possible. Also improve the feeling of accelration and slowing down.
I am still working on translating my dev log, originally on my Google Doc, here.
But the current system handles
Remapped input control for isometric view
Different input schemes via Unity New Input System
Real-time dynamic snake body movement and management (spawn via event or deattach at any length)
Lastly, huge credit to this Unity Tutorial on how to make a physics based driving system. https://www.youtube.com/watch?v=CdPYlj5uZeI&t=981s