--[[ diag_forward.luau -- settle which end of the car is the NOSE. Contradiction to resolve: the chassis places its FRONT axle at local -Z (WHEELBASE_HALF = 5.9, frontZ = -5.90) but Body.LookVector is +Z. Those disagree: the part the code calls "front" is BEHIND the direction the body looks. One of the two conventions is wrong, and the shell is being aligned to the wrong one -- which is why the car renders backwards even though shell and body agree (yawDelta 0). Ground truth used here, in order of reliability: 1. VehicleSeat.LookVector -- in Roblox a seated driver faces the seat's +Z look direction and positive throttle drives that way. This is the direction the car ACTUALLY moves, so it defines "forward" for gameplay. 2. Steering: which wheels have a steering constraint. Only the FRONT wheels steer, so a steered wheel's local Z tells us where the front is. 3. Shell art extents: the visual mesh bounding box relative to its pivot. Reports each independently so a disagreement is visible rather than assumed. ]] local out = {} local function log(...) local parts = {} for i = 1, select("#", ...) do parts[#parts + 1] = tostring((select(i, ...))) end table.insert(out, table.concat(parts, " ")) end local cars = {} for _, d in ipairs(workspace:GetDescendants()) do if d:IsA("Model") and d.Name:match("^Car_") then cars[#cars + 1] = d end end if #cars == 0 then return "No live Car_* models." end for _, model in ipairs(cars) do log("=== " .. model.Name .. " ===") local body = model:FindFirstChild("Body") if not (body and body:IsA("BasePart")) then log(" no Body part") continue end local bodyCF = body.CFrame local inv = bodyCF:Inverse() log( string.format( " bodyLook (world) = (%.2f, %.2f, %.2f)", bodyCF.LookVector.X, bodyCF.LookVector.Y, bodyCF.LookVector.Z ) ) -- 1. VehicleSeat: the direction the car actually drives. local seat = nil for _, d in ipairs(model:GetDescendants()) do if d:IsA("VehicleSeat") then seat = d break end end if seat then local seatLocalZ = (inv * seat.CFrame).Z local dot = bodyCF.LookVector:Dot(seat.CFrame.LookVector) log( string.format( " [1] VehicleSeat: localZ=%.2f, seatLook.dot(bodyLook)=%.2f -> seat faces %s body", seatLocalZ, dot, (dot > 0) and "SAME as" or "OPPOSITE to" ) ) log(string.format(" => car drives toward body %s Z", (dot > 0) and "+" or "-")) else log(" [1] no VehicleSeat found") end -- 2. Which wheels steer? Only the front axle has a steering servo. local steered, plain = {}, {} for _, d in ipairs(model:GetDescendants()) do if d:IsA("BasePart") and d.Name:match("^Wheel_") then local z = (inv * d.CFrame).Z local hasServo = false for _, c in ipairs(d:GetDescendants()) do if c:IsA("HingeConstraint") and c.ActuatorType == Enum.ActuatorType.Servo then hasServo = true break end end -- servo may live on the attachment's parent chain instead if not hasServo and d.Parent then for _, c in ipairs(d.Parent:GetChildren()) do if c:IsA("HingeConstraint") and c.ActuatorType == Enum.ActuatorType.Servo then local a1, a0 = c.Attachment1, c.Attachment0 if (a1 and a1:IsDescendantOf(d)) or (a0 and a0:IsDescendantOf(d)) then hasServo = true break end end end end table.insert(hasServo and steered or plain, { name = d.Name, z = z }) end end local function meanZ(t) if #t == 0 then return 0 / 0 end local s = 0 for _, v in ipairs(t) do s = s + v.z end return s / #t end local sz, pz = meanZ(steered), meanZ(plain) log(string.format(" [2] steered wheels=%d meanZ=%.2f | unsteered=%d meanZ=%.2f", #steered, sz, #plain, pz)) if #steered > 0 and #plain > 0 then log(string.format(" => FRONT (steered) axle is at body %s Z", (sz < pz) and "-" or "+")) end for _, v in ipairs(steered) do log(string.format(" steered: %-14s z=%.2f", v.name, v.z)) end for _, v in ipairs(plain) do log(string.format(" plain : %-14s z=%.2f", v.name, v.z)) end -- 3. Shell art: where is the visual mass, and is it symmetric? local shell = nil for _, c in ipairs(model:GetChildren()) do if c:IsA("Model") then shell = c break end end if shell then local cf, size = shell:GetBoundingBox() local localCentre = (inv * cf.Position) log( string.format( " [3] shell bbox size=(%.1f, %.1f, %.1f) centre localZ=%.2f", size.X, size.Y, size.Z, localCentre.Z ) ) -- named bodywork gives an independent nose/tail cue for _, d in ipairs(shell:GetDescendants()) do local n = d.Name:lower() if d:IsA("BasePart") and ( n:match("hood") or n:match("bonnet") or n:match("boot") or n:match("trunk") or n:match("spoiler") or n:match("windscreen") or n:match("bumper") ) then log(string.format(" %-16s localZ=%.2f", d.Name, (inv * d.CFrame).Z)) end end else log(" [3] no shell model child") end log("") end return table.concat(out, "\n")