From 5dd53ac30bf0f7cc64fe40e71c2a6c4fdbe68dd6 Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Mon, 9 Nov 2026 15:19:56 +0100 Subject: [PATCH] fix(vehicle): one chassis body, so the car sits on the road The vehicle floated 3.7 m above the street because the physics asset had a body per bone and the four wheel bodies intersected the chassis body, pushing the whole thing up. It is now a single chassis body on the root bone (452 x 205 x 40 cm) - the same shape Unreal's vehicle template uses, because Chaos simulates the wheels itself and they must not be separate rigid bodies. Settle check, same spawn point: before: settled at Z 494 cm after: settled at Z 119 cm Added -drivetest, which holds full throttle for 3 s and reports speed and distance. It fails, and the failure names its own cause: BeginPlay logs '0 wheel objects after register' while the component reports 4 wheel setups, so no wheels exist to receive torque. Recorded in TODO.md with the next step - check the skeleton bone names against the setups, then give the front and rear pairs their own wheel subclasses instead of the base class. --- .../Hollywood/Vehicle/PA_Vehicle_Sedan.uasset | 4 +- .../GigLine/Source/GigLine/GigLineVehicle.cpp | 49 ++++++++++++++++- Game/GigLine/Source/GigLine/GigLineVehicle.h | 4 ++ .../Source/GigLine/GigLineVehicleBuilder.cpp | 54 ++++++++++--------- TODO.md | 9 ++-- 5 files changed, 86 insertions(+), 34 deletions(-) diff --git a/Game/GigLine/Content/Hollywood/Vehicle/PA_Vehicle_Sedan.uasset b/Game/GigLine/Content/Hollywood/Vehicle/PA_Vehicle_Sedan.uasset index 825260d..8bd39c9 100644 --- a/Game/GigLine/Content/Hollywood/Vehicle/PA_Vehicle_Sedan.uasset +++ b/Game/GigLine/Content/Hollywood/Vehicle/PA_Vehicle_Sedan.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca7b0c16d20886e7304e99b708b69d1c3523032d5c8c7f635f75d9fe6e472f75 -size 11876 +oid sha256:9f7dfd8f321f79a8132f2f79476db3fd80a65c15ae868cb782e42d220d004328 +size 2621 diff --git a/Game/GigLine/Source/GigLine/GigLineVehicle.cpp b/Game/GigLine/Source/GigLine/GigLineVehicle.cpp index ebb475c..5e58e1d 100644 --- a/Game/GigLine/Source/GigLine/GigLineVehicle.cpp +++ b/Game/GigLine/Source/GigLine/GigLineVehicle.cpp @@ -91,7 +91,25 @@ void AGigLineVehicle::ConfigureWheels() void AGigLineVehicle::BeginPlay() { Super::BeginPlay(); - ConfigureWheels(); + + // Wheel objects do not exist until the component is registered, so the per-wheel + // flags set in the constructor went nowhere - which is why the drive test measured + // zero speed with throttle held. Apply them again now that they exist, and give the + // engine a torque curve to actually make force. + if (UChaosWheeledVehicleMovementComponent* Movement = + Cast(GetVehicleMovementComponent())) + { + ConfigureWheels(); + Movement->EngineSetup.MaxTorque = 500.0f; + Movement->EngineSetup.MaxRPM = 6000.0f; + Movement->EngineSetup.EngineIdleRPM = 900.0f; + Movement->TransmissionSetup.bUseAutomaticGears = true; + Movement->TransmissionSetup.FinalRatio = 3.5f; + Movement->SetThrottleInput(0.0f); + UE_LOG(LogTemp, Warning, + TEXT("GigLine vehicle: %d wheel objects after register, torque %.0f"), + Movement->Wheels.Num(), Movement->EngineSetup.MaxTorque); + } UE_LOG(LogTemp, Warning, TEXT("GigLine vehicle: spawned at (%.0f, %.0f, %.0f), %d wheel setups"), GetActorLocation().X, GetActorLocation().Y, GetActorLocation().Z, GetVehicleMovementComponent() @@ -107,6 +125,35 @@ void AGigLineVehicle::Tick(float DeltaSeconds) // One settled-state report: proves the physics body exists, is simulating, and has // come to rest on the road rather than falling through it. Everything else about the // driving behaviour is measured later; this is the "is it real" check. + // `-drivetest`: hold full throttle from 1 s to 4 s and report the result. A vehicle + // that spawns is not a vehicle that drives. + if (FParse::Param(FCommandLine::Get(), TEXT("drivetest"))) + { + DriveElapsed += DeltaSeconds; + if (UChaosVehicleMovementComponent* Movement = GetVehicleMovementComponent()) + { + if (DriveElapsed > 1.0f && DriveElapsed < 4.0f) + { + Movement->SetThrottleInput(1.0f); + } + else if (DriveElapsed >= 4.0f && !bDriveReported) + { + Movement->SetThrottleInput(0.0f); + bDriveReported = true; + const float SpeedCm = Movement->GetForwardSpeed(); + const float TravelledM = FVector::Dist2D(GetActorLocation(), DriveStart) / 100.f; + UE_LOG(LogTemp, Warning, + TEXT("GigLine vehicle DRIVETEST speed=%.0f cm/s (%.1f km/h), travelled %.1f m -> %s"), + SpeedCm, SpeedCm * 0.036f, TravelledM, + (SpeedCm > 200.f && TravelledM > 1.0f) ? TEXT("PASS") : TEXT("FAIL")); + } + } + } + if (DriveStart.IsZero() && DriveElapsed > 1.0f) + { + DriveStart = GetActorLocation(); + } + SettleElapsed += DeltaSeconds; if (!bSettleReported && SettleElapsed > 3.0f) { diff --git a/Game/GigLine/Source/GigLine/GigLineVehicle.h b/Game/GigLine/Source/GigLine/GigLineVehicle.h index 7020a17..9d38fe8 100644 --- a/Game/GigLine/Source/GigLine/GigLineVehicle.h +++ b/Game/GigLine/Source/GigLine/GigLineVehicle.h @@ -36,6 +36,10 @@ private: /** Configure the Chaos wheels from the modelled dimensions. */ void ConfigureWheels(); + float DriveElapsed = 0.f; + bool bDriveReported = false; + FVector DriveStart = FVector::ZeroVector; + float SettleElapsed = 0.f; bool bSettleReported = false; }; diff --git a/Game/GigLine/Source/GigLine/GigLineVehicleBuilder.cpp b/Game/GigLine/Source/GigLine/GigLineVehicleBuilder.cpp index 42c8a79..fbbfd2f 100644 --- a/Game/GigLine/Source/GigLine/GigLineVehicleBuilder.cpp +++ b/Game/GigLine/Source/GigLine/GigLineVehicleBuilder.cpp @@ -5,6 +5,10 @@ #include "Engine/SkeletalMesh.h" #include "PhysicsAssetUtils.h" #include "PhysicsEngine/PhysicsAsset.h" +#include "PhysicsEngine/BodySetup.h" +#include "PhysicsEngine/SkeletalBodySetup.h" +#include "Engine/SkeletalMesh.h" + #include "UObject/Package.h" #endif @@ -28,34 +32,32 @@ UPhysicsAsset* UGigLineVehicleBuilder::BuildPhysicsAsset(USkeletalMesh* Mesh, } UPhysicsAsset* PhysicsAsset = NewObject(Package, *AssetName, RF_Public | RF_Standalone | RF_Transactional); - if (!PhysicsAsset) - { - UE_LOG(LogTemp, Warning, TEXT("GigLine vehicle: could not create %s"), *FullName); - return nullptr; - } - // Boxes on the five bones: a root box and four wheel capsules is what a car needs; - // per-bone hulls from a 500-triangle mesh would add cost for nothing. - FPhysAssetCreateParams Params; - Params.MinBoneSize = 1.0f; - Params.GeomType = EFG_Box; - Params.bCreateConstraints = true; - // Every bone gets a body: the vehicle has exactly five bones (root plus four - // wheels) and each one needs collision, so filtering by bone size only produced a - // single root body. - Params.bBodyForAll = true; - Params.bWalkPastSmall = false; - Params.bDisableCollisionsByDefault = true; + // ONE body on the chassis bone, which is what Unreal's own vehicle template uses. + // The first attempt let FPhysicsAssetUtils build a body per bone and the car settled + // 3.7 m above the road: the four wheel bodies were intersecting the chassis body and + // pushing the whole vehicle up. Chaos drives the wheels itself, so they must not be + // separate rigid bodies here. + const FBoxSphereBounds Bounds = Mesh->GetBounds(); + const FVector Extent = Bounds.BoxExtent; + const FName ChassisBone(TEXT("root")); - FText Error; - const bool bOk = FPhysicsAssetUtils::CreateFromSkeletalMesh( - PhysicsAsset, Mesh, Params, Error, /*bSetToMesh=*/true, /*bShowProgress=*/false); - if (!bOk) - { - UE_LOG(LogTemp, Warning, TEXT("GigLine vehicle: physics asset build failed: %s"), - *Error.ToString()); - return nullptr; - } + USkeletalBodySetup* Body = NewObject(PhysicsAsset, ChassisBone, + RF_Public | RF_Transactional); + Body->BoneName = ChassisBone; + // A box slightly under the mesh bounds: a body that is exactly the bounds sits on the + // ground plane and reads as floating because the mesh has wheels below the sills. + FKBoxElem Box; + Box.X = Extent.X * 1.9f; + Box.Y = Extent.Y * 1.8f; + Box.Z = Extent.Z * 0.55f; + Box.Center = FVector(0.f, 0.f, -Extent.Z * 0.25f); + Body->AggGeom.BoxElems.Add(Box); + PhysicsAsset->SkeletalBodySetups.Add(Body); + + UE_LOG(LogTemp, Warning, + TEXT("GigLine vehicle: chassis body %.0f x %.0f x %.0f cm on bone '%s'"), + Box.X, Box.Y, Box.Z, *ChassisBone.ToString()); PhysicsAsset->MarkPackageDirty(); UE_LOG(LogTemp, Warning, TEXT("GigLine vehicle: physics asset %s with %d bodies"), diff --git a/TODO.md b/TODO.md index cda6b29..3acf57b 100644 --- a/TODO.md +++ b/TODO.md @@ -16,11 +16,10 @@ Actionable task list for this project. This file is the single, central place fo - [A] Model one vehicle in Blender with collision meshes; export to Unreal. **Model done** (`Tools/vehicle_build.py` → `Art/blender/vehicle/gigline-sedan.blend`, preview in `Art/blender/vehicle/preview.png`): 4.72 x 1.80 x 1.44 m, 2.78 m wheelbase, 1.62 m track, 0.33 m wheels, 252 triangles, four `UCX_` convex hulls. Dimensions come from real sedan figures and are logged to the report, so the vehicle config can read them rather than restating them. - **Imported into Unreal** (`Tools/ue_vehicle_import.py` → `/Game/Hollywood/Vehicle`), split per the vehicle-artist object split: `SM_Vehicle_Body` (3 slots, 4.76 x 1.76 x 1.28 m), `SM_Vehicle_Glass`, `SM_Vehicle_Light_Front`, `SM_Vehicle_Light_Rear`, `SM_Vehicle_Mirrors`, and one `SM_Vehicle_Wheel` (0.22 x 0.66 x 0.66 m) reused four times. The body FBX carries the `UCX_` hulls as simple collision. - **LODs cut for real:** decimating the duplicate gives LOD1 at 38 triangles and LOD2 at 14, against 132 for LOD0. The first attempt duplicated the shell unchanged and produced 84-triangle LODs that saved nothing - worth remembering as the failure mode of a "LOD" step nobody measures. - **LOD group wiring is open:** `StaticMeshEditorSubsystem.import_lod` is unavailable in a headless commandlet (`get_editor_subsystem` returns None), so the LOD meshes import as their own assets and the group still needs an editor session. -- [~] Set up Chaos Vehicle with wheel and physics configuration. **The vehicle spawns, simulates and has its four wheel setups; it does not yet sit on the road.** - - **Physics asset generated from script.** `unreal.PhysicsAssetFactory` exposes no mesh property, so `UGigLineVehicleBuilder::BuildPhysicsAsset` (C++, editor-only) calls `FPhysicsAssetUtils::CreateFromSkeletalMesh`. Result: `PA_Vehicle_Sedan` with **6 bodies**, assigned to `SK_Vehicle_Sedan`. - - **Vehicle class in place.** `AGigLineVehicle` (Chaos wheeled pawn) configures four wheel setups from the modelled dimensions — radius 33 cm, wheelbase 278 cm, track 156 cm — and is staged in DemoMap as `Demo_Vehicle_Sedan`. A run logs `spawned at (-12950, -900, 120), 4 wheel setups`. - - **Defect:** it settles at **Z 494 cm** instead of road level (spawned at 120). The physics bodies do not match the mesh, or the car is resting on its own collision boxes — 3.7 m of lift is exactly the kind of thing `bBodyForAll` with generated boxes produces when the boxes are built from the wrong bone extents. - - **Next:** inspect `PA_Vehicle_Sedan`'s bodies against the mesh (root box should be ~4.7 x 1.8 x 0.5 m, wheels ~0.66 m), then fix the creation params or replace the auto bodies with two explicit boxes plus four wheel spheres. +- [~] Set up Chaos Vehicle with wheel and physics configuration. **It sits on the road now; it does not drive yet.** + - **Fixed the floating:** the physics asset had a body per bone, and the four wheel bodies intersected the chassis body and pushed the car 3.7 m up. It is now a **single chassis body** on the `root` bone (452 x 205 x 40 cm), which is what Unreal's own vehicle template uses - Chaos simulates the wheels itself, they must not be separate rigid bodies. Settle check: **Z 119 cm** at a spawn of 120 (was 494). + - **Drive test fails, with a specific cause:** `-drivetest` holds full throttle for 3 s and reports speed and distance. Result: `speed=0 cm/s, travelled 2.2 m -> FAIL`, and BeginPlay logs **`0 wheel objects after register`** while the component reports 4 wheel setups. The setups exist but no wheels are created from them, so there is nothing to apply torque to. + - **Next:** confirm the skeleton's bone names match the setups (`Wheel_FL`, `Wheel_FR`, `Wheel_RL`, `Wheel_RR`) - a name mismatch silently produces zero wheels - and give the front and rear pairs their own `UChaosVehicleWheel` subclasses rather than the base class, which is what the setups point at today. - **Also open:** LOD group wiring, enter/exit, driving route, handling. - [ ] Implement enter/exit interaction. - [ ] Build a closed driving route with boundaries.