CARLA
Loading...
Searching...
No Matches
CarlaWheeledVehicle.h
Go to the documentation of this file.
1// Copyright (c) 2026 Computer Vision Center (CVC) at the Universitat Autonoma
2// de Barcelona (UAB).
3//
4// This work is licensed under the terms of the MIT license.
5// For a copy, see <https://opensource.org/licenses/MIT>.
6
7#pragma once
8
22
23//-----CARSIM--------------------------------
24#ifdef WITH_CARSIM
25#include "CarSimMovementComponent.h"
26#endif
27//-------------------------------------------
28
32
33#include <util/ue-header-guard-begin.h>
34#include "Components/SkeletalMeshComponent.h"
35#include "WheeledVehiclePawn.h"
36#include "PhysicsEngine/PhysicsConstraintComponent.h"
37#include "VehicleAnimationInstance.h"
38#include "FoliageInstancedStaticMeshComponent.h"
39#include "CoreMinimal.h"
40#include "WheeledVehiclePawn.h"
41#include <util/ue-header-guard-end.h>
42
43#include <utility>
44
45#include "CarlaWheeledVehicle.generated.h"
46
47
48
49class UBoxComponent;
50class UChaosVehicleWheel;
51
52UENUM()
53enum class EVehicleWheelLocation : uint8 {
54
55 FL_Wheel = 0,
56 FR_Wheel = 1,
57 BL_Wheel = 2,
58 BR_Wheel = 3,
59 ML_Wheel = 4,
60 MR_Wheel = 5,
61 //Use for bikes and bicycles
62 Front_Wheel = 0,
63 Back_Wheel = 1,
64};
65
66/// Type of door to open/close
67// When adding new door types, make sure that All is the last one.
68UENUM(BlueprintType)
69enum class EVehicleDoor : uint8 {
70 FL = 0,
71 FR = 1,
72 RL = 2,
73 RR = 3,
74 Hood = 4,
75 Trunk = 5,
76 All = 6
77};
78
79class FChaosScene;
80class FPhysScene_Chaos;
81
82/// Base class for CARLA wheeled vehicles.
83UCLASS()
85{
86 GENERATED_BODY()
87
88 // ===========================================================================
89 /// @name Constructor and destructor
90 // ===========================================================================
91 /// @{
92public:
93
94 ACarlaWheeledVehicle(const FObjectInitializer &ObjectInitializer);
95
97
98 /// @}
99 // ===========================================================================
100 /// @name Get functions
101 // ===========================================================================
102 /// @{
103public:
104
105 /// Get chaos wheeled vehicle movement component casted
106 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
107 UChaosWheeledVehicleMovementComponent* GetChaosWheeledVehicleMovementComponent() const
108 {
109 return Cast<UChaosWheeledVehicleMovementComponent>(GetVehicleMovement());
110 }
111
112 /// Vehicle control currently applied to this vehicle.
113 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
114 const FVehicleControl &GetVehicleControl() const
115 {
116 return LastAppliedControl;
117 }
118
119 /// Vehicle Ackermann control currently applied to this vehicle.
120 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
121 const FVehicleAckermannControl &GetVehicleAckermannControl() const
122 {
123 return LastAppliedAckermannControl;
124 }
125
126 /// Transform of the vehicle. Location is shifted so it matches center of the
127 /// vehicle bounds rather than the actor's location.
128 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
129 FTransform GetVehicleTransform() const
130 {
131 return GetActorTransform();
132 }
133
134 /// Forward speed in cm/s. Might be negative if goes backwards.
135 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
136 float GetVehicleForwardSpeed() const;
137
138 /// Orientation vector of the vehicle, pointing forward.
139 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
140 FVector GetVehicleOrientation() const;
141
142 /// Active gear of the vehicle.
143 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
144 int32 GetVehicleCurrentGear() const;
145
146 /// Transform of the vehicle's bounding box relative to the vehicle.
147 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
148 FTransform GetVehicleBoundingBoxTransform() const;
149
150 /// Extent of the vehicle's bounding box.
151 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
152 FVector GetVehicleBoundingBoxExtent() const;
153
154 /// Get vehicle's bounding box component.
155 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
156 UBoxComponent *GetVehicleBoundingBox() const
157 {
158 return VehicleBounds;
159 }
160
161 /// Get the maximum angle at which the front wheel can steer.
162 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
163 float GetMaximumSteerAngle() const;
164
165 /// @}
166 // ===========================================================================
167 /// @name AI debug state
168 // ===========================================================================
169 /// @{
170public:
171
172 /// @todo This function should be private to AWheeledVehicleAIController.
174 {
175 State = InState;
176 }
177
178 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
179 ECarlaWheeledVehicleState GetAIVehicleState() const
180 {
181 return State;
182 }
183
184 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
185 FVehiclePhysicsControl GetVehiclePhysicsControl() const;
186
187 /// Returns a snapshot of the vehicle's runtime telemetry: forward speed,
188 /// last applied control inputs, engine RPM, current gear, and per-wheel
189 /// lateral slip, longitudinal slip, and angular velocity sourced from
190 /// the Chaos vehicle physics state.
191 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
192 FVehicleTelemetryData GetVehicleTelemetryData() const;
193
194 FVector GetCenterOfMass(UChaosWheeledVehicleMovementComponent &VehicleMovComponent) const;
195
196 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
197 FAckermannControllerSettings GetAckermannControllerSettings() const {
198 return AckermannController.GetSettings();
199 }
200
201 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
202 void RestoreVehiclePhysicsControl();
203
204 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
205 FVehicleLightState GetVehicleLightState() const;
206
207 void ApplyVehiclePhysicsControl(const FVehiclePhysicsControl &PhysicsControl);
208
209 void SetCenterOfMass(UChaosWheeledVehicleMovementComponent &VehicleMovComponent, const FVehiclePhysicsControl &PhysicsControl);
210
211 void ApplyAckermannControllerSettings(const FAckermannControllerSettings &AckermannControllerSettings) {
212 return AckermannController.ApplySettings(AckermannControllerSettings);
213 }
214
215 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
216 void SetSimulatePhysics(bool enabled);
217
218 void SetVehicleLightState(const FVehicleLightState &LightState);
219
220 void SetFailureState(const carla::rpc::VehicleFailureState &FailureState);
221
222 UFUNCTION(BlueprintNativeEvent)
223 bool IsTwoWheeledVehicle();
224 virtual bool IsTwoWheeledVehicle_Implementation() {
225 return false;
226 }
227
228 /// Two-wheeled vehicles (bikes/motorcycles) embed their rider as a
229 /// SkeletalMeshComponent (named RiderMeshComponentName) meant to sit on
230 /// VehicleMeshSeatSocketName. That positioning is still driven by
231 /// Blueprint graph logic left over from the UE4-to-UE5 port, and it
232 /// drifts the rider backward off the seat while the vehicle is moving.
233 /// Rather than rewrite that graph, OnRiderBoneTransformsFinalized (see
234 /// below) snaps the rider back onto the seat socket every frame, after
235 /// this component's animation has finished evaluating for that frame --
236 /// nothing runs afterwards that could undo it before render, regardless
237 /// of what the Blueprint graph or its animation does. Only takes effect
238 /// when IsTwoWheeledVehicle() is true and both component names resolve
239 /// to real components on this actor.
240 ///
241 /// This does not fully fix the drift by itself -- the rider's own Anim
242 /// Blueprint separately queries this same seat socket in its own Event
243 /// BlueprintUpdateAnimation, and that read intermittently races ahead of
244 /// the vehicle mesh's physics update for the frame (confirmed by logging
245 /// both reads: on the frames it happens, the Blueprint's read exactly
246 /// matches the PREVIOUS frame's real seat transform -- a one-frame-stale
247 /// read on some frames but not others, which looks like a pop, not a
248 /// continuous drift). See GetCachedVehicleSocketWorldTransform() below,
249 /// which the Blueprint should read instead of querying the socket
250 /// itself -- for this one and every other vehicle-mesh socket its Data
251 /// Gather reads.
252 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CARLA Wheeled Vehicle|Rider")
253 FName RiderMeshComponentName = TEXT("SkeletalMesh");
254
255 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CARLA Wheeled Vehicle|Rider")
256 FName VehicleMeshSeatSocketName = TEXT("Seat");
257
258 /// Chaos suspension has small, real, high-frequency vertical jitter on
259 /// the vehicle mesh every frame (visible as the rider's torso/hip
260 /// bouncing, the seat popping the rider up) -- physically real, not a
261 /// bug, but the seat-lock's hard snap transmits it 1:1 to the rider with
262 /// zero smoothing. This is the speed (see FMath::FInterpTo) used to
263 /// low-pass just the VERTICAL (Z) component of the cached base transform
264 /// that GetCachedVehicleSocketWorldTransform() extrapolates from --
265 /// purely cosmetic, applied only to what the rider reads, never to the
266 /// vehicle's actual physics transform. X/Y and rotation are never
267 /// smoothed (see OnRiderBoneTransformsFinalized's comment -- smoothing
268 /// those trails a moving target by more the faster the vehicle goes,
269 /// which looked exactly like the original slide bug). Higher = snappier
270 /// (less smoothing); lower = smoother but more vertical lag.
271 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CARLA Wheeled Vehicle|Rider")
272 float RiderSeatSmoothingSpeed = 10.0f;
273
274 /// Cached, extrapolated world transform of ANY socket on the vehicle
275 /// mesh (Seat, HandlerLeftSocket, HandlerRightSocket, LeftPedalSocket,
276 /// RightPedalSocket, ...), captured from OnRiderBoneTransformsFinalized
277 /// (which fires late enough in the frame to reliably see this frame's
278 /// real, physics-updated position -- confirmed by logging it against
279 /// the vehicle's actual movement, always smooth, never stale).
280 ///
281 /// The rider's Anim Blueprint should read this instead of querying the
282 /// vehicle mesh's sockets itself (directly, or via a vehicle-side
283 /// variable computed that way) for EVERY seat/handler/pedal target used
284 /// in its Data Gather: that independent read, from the Blueprint's own
285 /// Event BlueprintUpdateAnimation, intermittently races ahead of the
286 /// physics update and returns last frame's transform instead --
287 /// confirmed first on the seat (fixed the torso pop) and then on the
288 /// same pattern showing up on the handler/pedal sockets driving the
289 /// arm/leg IK (hands/feet still popping to a stale pose after the torso
290 /// fix). This trades that flickering race for a lag that's constant
291 /// instead of intermittent, then extrapolates it away using the linear
292 /// velocity it was cached with -- all vehicle-mesh sockets share that
293 /// velocity, moving rigidly together, so one shared velocity/timestamp
294 /// covers every socket looked up here.
295 UFUNCTION(BlueprintPure, Category = "CARLA Wheeled Vehicle|Rider")
296 FTransform GetCachedVehicleSocketWorldTransform(FName SocketName) const
297 {
298 // Extrapolating each socket's own cached translation in a straight
299 // line (the first version of this function) ignores the vehicle's
300 // ANGULAR velocity -- it yaws/pitches/rolls a little essentially all
301 // the time (steering, suspension travel), even on flat road. Seat and
302 // the handler/elbow sockets sit close to the mesh's own origin, so
303 // that rotation barely displaces them and the straight-line
304 // approximation looked like a full fix. Sockets far from the origin
305 // -- the pedals, down at the sides -- get swept a lot further by the
306 // same small rotation (lever arm), which is exactly the "still
307 // sliding, constant, like before" the pedals kept showing after
308 // torso/hands were confirmed fixed.
309 // Fix: extrapolate the vehicle MESH's own rigid-body transform
310 // (translation + rotation, both from its cached linear/angular
311 // velocity) instead of each socket individually, then re-place every
312 // socket at its fixed (skeleton-rigid) offset from that predicted
313 // mesh transform -- this correctly sweeps far-out sockets through the
314 // rotation instead of leaving them on their own straight line.
315 //
316 // Extrapolating from SmoothedVehicleMeshTransform (see
317 // RiderSeatSmoothingSpeed's comment) here, not the raw
318 // CachedVehicleMeshTransform -- that raw value is still used below,
319 // unsmoothed, to compute each socket's RIGID offset (a static
320 // skeleton relationship, smoothing it would just introduce a phantom
321 // wobble between sockets instead of removing one).
322 FTransform PredictedMeshTransform = SmoothedVehicleMeshTransform;
323 if (const UWorld* World = GetWorld())
324 {
325 const float TimeSinceCache = World->GetTimeSeconds() - CachedVehicleTransformsTimeSeconds;
326 // X/Y (world space) zeroed for the same reason as the linear
327 // velocity's Z above -- Yaw is by definition rotation around the
328 // WORLD Z axis (true regardless of the vehicle's current heading),
329 // so keeping only the world-Z component of this angular velocity
330 // isolates exactly the yaw-rate contribution this extrapolation was
331 // meant to correct for (steering), while dropping the noisy
332 // Pitch/Roll rates that would otherwise reintroduce the same
333 // full-body rider bounce through every far-off socket (pedals,
334 // handlers) via their own lever arm.
335 FVector AngularVelocityRadians = FMath::DegreesToRadians(CachedVehicleWorldAngularVelocityDegrees);
336 AngularVelocityRadians.X = 0.0f;
337 AngularVelocityRadians.Y = 0.0f;
338 const FQuat DeltaRotation = FQuat::MakeFromRotationVector(AngularVelocityRadians * TimeSinceCache);
339 PredictedMeshTransform.SetRotation((DeltaRotation * PredictedMeshTransform.GetRotation()).GetNormalized());
340 // Z zeroed out here -- CachedVehicleWorldVelocity comes straight from
341 // physics (GetComponentVelocity(), unsmoothed) and its own vertical
342 // component is exactly as noisy as the position jitter this function
343 // is smoothing away above, so adding it back in would reintroduce
344 // the same uniform full-body bounce through every socket at once.
345 // Only X/Y matter for this extrapolation anyway (that's what the
346 // one-frame lag at real driving speed needed); vertical lag of a
347 // frame is imperceptible, unlike horizontal lag at speed.
348 FVector VelocityForExtrapolation = CachedVehicleWorldVelocity;
349 VelocityForExtrapolation.Z = 0.0f;
350 PredictedMeshTransform.AddToTranslation(VelocityForExtrapolation * TimeSinceCache);
351 }
352
353 // LeftPedalSocket/RightPedalSocket sit on LeftPedalGeo/RightPedalGeo,
354 // whose rotation isn't animated in the usual sense -- the AnimGraph's
355 // Transform (Modify) Bone on that bone is Component Space + Replace
356 // Existing, wired only on Rotation Y (Pitch) from Pedal Y (Roll/Yaw
357 // left at literal 0) -- meaning the bone's ENTIRE component-space
358 // rotation, every frame, IS exactly FRotator(+-PedalRotationAngle, 0, 0),
359 // nothing else composed in. A first attempt here tried to track "how
360 // much has the angle changed since the cache" and add that as a
361 // delta -- that's extra arithmetic (and an extra chance to get a sign
362 // or composition order wrong) for a value that doesn't need tracking
363 // at all: PedalRotationAngle itself is never stale (it's our own
364 // TickActor state, not read from a racing physics/Blueprint source),
365 // so the pivot's rotation can be reconstructed outright from it
366 // instead of extrapolated. Only the pivot's POSITION still needs the
367 // cached-and-extrapolated vehicle motion above -- its own spin
368 // doesn't move its origin, only the vehicle carrying it does.
369 static const FName LeftPedalSocket(TEXT("LeftPedalSocket"));
370 static const FName RightPedalSocket(TEXT("RightPedalSocket"));
371 static const FName LeftPedalPivotBone(TEXT("LeftPedalGeo"));
372 static const FName RightPedalPivotBone(TEXT("RightPedalGeo"));
373 if (SocketName == LeftPedalSocket || SocketName == RightPedalSocket)
374 {
375 const FName PivotBoneName = (SocketName == LeftPedalSocket) ? LeftPedalPivotBone : RightPedalPivotBone;
376 const FTransform* CachedSocket = CachedVehicleSocketTransforms.Find(SocketName);
377 const FTransform* CachedPivot = CachedVehicleSocketTransforms.Find(PivotBoneName);
378 if (CachedSocket && CachedPivot)
379 {
380 const FTransform SocketRelativeToPivot = CachedSocket->GetRelativeTransform(*CachedPivot);
381 FTransform PredictedPivot = CachedPivot->GetRelativeTransform(CachedVehicleMeshTransform) * PredictedMeshTransform;
382 // The AnimGraph feeds LeftPedalGeo the negative of Pedal Y and
383 // RightPedalGeo Pedal Y directly (see the Pedals block).
384 const float SignedPedalAngle = (SocketName == LeftPedalSocket) ? -PedalRotationAngle : PedalRotationAngle;
385 const FQuat ComponentSpaceRotation(FRotator(SignedPedalAngle, 0.0f, 0.0f));
386 PredictedPivot.SetRotation((ComponentSpaceRotation * PredictedMeshTransform.GetRotation()).GetNormalized());
387 return SocketRelativeToPivot * PredictedPivot;
388 }
389 }
390
391 if (const FTransform* CachedSocket = CachedVehicleSocketTransforms.Find(SocketName))
392 {
393 return CachedSocket->GetRelativeTransform(CachedVehicleMeshTransform) * PredictedMeshTransform;
394 }
395 return FTransform::Identity;
396 }
397
398 /// The rider mesh's rest pose isn't authored to sit flush with the
399 /// socket's own local axes -- its component template ships with this
400 /// exact relative rotation baked in (no translation), so the seat-lock
401 /// composes it on top of the socket's world transform instead of using
402 /// the bare socket transform, which twisted the pose.
403 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CARLA Wheeled Vehicle|Rider")
404 FRotator RiderSeatRelativeRotationOffset = FRotator(0.0f, -90.0f, 15.0f);
405
406 /// Two-wheeled vehicles spin a decorative pedal/crank bone (in the
407 /// vehicle mesh's own Anim Blueprint, unrelated to the rider) via a
408 /// looping Timeline whose curve doesn't start and end at the same
409 /// value -- every loop the Timeline restarts, the bone's rotation pops
410 /// instantly back to the curve's start value instead of continuing
411 /// smoothly, and the rider's foot IK (which targets a socket on that
412 /// bone) pops along with it. Play Rate scales with vehicle speed, so
413 /// the pops get more frequent the faster the vehicle goes, and their
414 /// size is capped at the curve's fixed peak value -- both are the
415 /// exact symptoms reported ("teleports", "there's a cap").
416 /// GetPedalRotation() replaces that Timeline: an angle continuously
417 /// accumulated from real vehicle speed and wrapped with Fmod, which
418 /// never pops (359.9 degrees to 0.1 degrees is the same rotation, not a
419 /// discontinuity) the way a Timeline restarting to an unrelated value
420 /// does. Bind it directly to whatever currently reads the Timeline's
421 /// output (Engine Speed -> Pedal Y) in the vehicle Blueprint's Anim
422 /// Blueprint instead.
423 UFUNCTION(BlueprintPure, Category = "CARLA Wheeled Vehicle|Rider")
424 float GetPedalRotation() const
425 {
426 return PedalRotationAngle;
427 }
428
429 /// Degrees the pedal bone rotates per centimeter of real vehicle
430 /// travel (see GetPedalRotation). Tune in-editor to match the desired
431 /// pedaling cadence -- no recompile needed.
432 UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "CARLA Wheeled Vehicle|Rider")
433 float PedalRotationDegreesPerCm = 0.5f;
434
435 void PrintROS2Message(const char* Message);
436
437 /// @}
438 // ===========================================================================
439 /// @name Vehicle input control
440 // ===========================================================================
441 /// @{
442public:
443
444 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
445 void ApplyVehicleControl(const FVehicleControl &Control, EVehicleInputPriority Priority);
446
447 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
448 void ApplyVehicleAckermannControl(const FVehicleAckermannControl &AckermannControl, EVehicleInputPriority Priority);
449
450 bool IsAckermannControlActive() const
451 {
452 return bAckermannControlActive;
453 }
454
455 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
456 void ActivateVelocityControl(const FVector &Velocity);
457
458 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
459 void DeactivateVelocityControl();
460
461 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
462 void ActivateAccelerationControl(const FVector &Acceleration);
463
464 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
465 void DeactivateAccelerationControl();
466
467 /// Apply control from Autoware /control/command/control_cmd (acceleration [m/s^2] + steering)
468 void ApplyVehicleAccelerationControl(float LongitudinalAccelerationMps2, float Steer, float SteerSpeed);
469
470 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
471 void ShowDebugTelemetry(bool Enabled);
472
473 /// @todo This function should be private to AWheeledVehicleAIController.
474 void FlushVehicleControl();
475
476 /// @}
477 // ===========================================================================
478 /// @name DEPRECATED Set functions
479 // ===========================================================================
480 /// @{
481public:
482
483 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
484 void SetThrottleInput(float Value);
485
486 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
487 void SetSteeringInput(float Value);
488
489 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
490 void SetBrakeInput(float Value);
491
492 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
493 void SetReverse(bool Value);
494
495 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
496 void ToggleReverse()
497 {
498 SetReverse(!LastAppliedControl.bReverse);
499 }
500
501 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
502 void SetHandbrakeInput(bool Value);
503
504 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
505 void HoldHandbrake()
506 {
507 SetHandbrakeInput(true);
508 }
509
510 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
511 void ReleaseHandbrake()
512 {
513 SetHandbrakeInput(false);
514 }
515
516 TArray<float> GetWheelsFrictionScale();
517
518 void SetWheelsFrictionScale(TArray<float> &WheelsFrictionScale);
519
520 void SetCarlaMovementComponent(UBaseCarlaMovementComponent* MoementComponent);
521
522 template<typename T = UBaseCarlaMovementComponent>
524 {
525 return Cast<T>(BaseMovementComponent);
526 }
527
528 /// @}
529 // ===========================================================================
530 /// @name Overriden from AActor
531 // ===========================================================================
532 /// @{
533
534protected:
535
536 virtual void BeginPlay() override;
537 virtual void TickActor(float DeltaTime, enum ELevelTick TickType, FActorTickFunction& ThisTickFunction) override;
538 virtual void EndPlay(const EEndPlayReason::Type EndPlayReason);
539
540 UFUNCTION(BlueprintImplementableEvent)
541 void RefreshLightState(const FVehicleLightState &VehicleLightState);
542
543 UFUNCTION(BlueprintCallable, CallInEditor)
544 void AdjustVehicleBounds();
545
546 UPROPERTY(Category="Door Animation", EditAnywhere, BlueprintReadWrite)
547 TArray<FName> ConstraintComponentNames;
548
549 UPROPERTY(Category="Door Animation", EditAnywhere, BlueprintReadWrite)
550 float DoorOpenStrength = 100.0f;
551
552 UFUNCTION(BlueprintCallable, CallInEditor)
553 void ResetConstraints();
554
555public:
556 struct
557 {
561 }
563
564private:
565
566 /// Current state of the vehicle controller (for debugging purposes).
567 UPROPERTY(Category = "AI Controller", VisibleAnywhere)
569
570 UPROPERTY(Category = "CARLA Wheeled Vehicle", EditAnywhere)
571 TObjectPtr<UVehicleVelocityControl> VelocityControl;
572
573 UPROPERTY(Category = "CARLA Wheeled Vehicle", EditAnywhere)
574 TObjectPtr<UVehicleAccelerationControl> AccelerationControl;
575
576
577 FVehicleControl LastAppliedControl;
578 FVehicleAckermannControl LastAppliedAckermannControl;
579 FVehiclePhysicsControl LastPhysicsControl;
580
581 /// Applies LastPhysicsControl.Wheels to the live Chaos wheels. Call it after
582 /// every RecreatePhysicsState(), which resets them to the class defaults.
583 void ApplyWheelPhysicsControl();
584
585 bool bAckermannControlActive = false;
586 FAckermannController AckermannController;
587
588 float RolloverBehaviorForce = 0.35;
589 int RolloverBehaviorTracker = 0;
590 float RolloverFlagTime = 5.0f;
591
592 carla::rpc::VehicleFailureState FailureState = carla::rpc::VehicleFailureState::None;
593
594 // Light Defaults tool integration: loaded once in
595 // ActivateVehicleLightComponents (BeginPlay), then reapplied by
596 // SetVehicleLightState on every light-state change -- see
597 // ULightDefaultsJsonUtils::ApplyVehicleLightsRuntimeState.
598 bool bHasSavedVehicleLightDefault = false;
599 float SavedVehicleLightIntensity = 1.0f;
600 TMap<FString, float> SavedVehicleLightGroupIntensity;
601 void ApplyVehicleLightDefaultsForCurrentState();
602
603 // Resolved once (BeginPlay) rather than looked up by name every tick.
604 // bRiderComponentsResolved distinguishes "looked up, none found" (a
605 // non-two-wheeled vehicle, or bad names) from "not looked up yet", so
606 // TickActor doesn't retry the lookup every frame for vehicles that
607 // simply don't have a rider.
608 UPROPERTY(Transient)
609 TObjectPtr<USkeletalMeshComponent> RiderMeshComponent = nullptr;
610
611 UPROPERTY(Transient)
612 TObjectPtr<USkeletalMeshComponent> VehicleMeshForRiderSeat = nullptr;
613
614 bool bRiderComponentsResolved = false;
615
616 void ResolveRiderComponentsIfNeeded();
617
618 // Bound (once, from ResolveRiderComponentsIfNeeded) to
619 // RiderMeshComponent's OnBoneTransformsFinalized -- see the comment on
620 // RiderMeshComponentName above for why this runs there and not from
621 // TickActor.
622 void OnRiderBoneTransformsFinalized();
623
624 // Backing values for GetCachedVehicleSocketWorldTransform() above --
625 // every vehicle-mesh socket's transform as of the last cache update, the
626 // vehicle mesh's own transform and linear/angular velocity at that same
627 // moment (to extrapolate the whole rigid body forward, not each socket
628 // independently), and the world time it was cached at (to know how far
629 // to extrapolate).
630 TMap<FName, FTransform> CachedVehicleSocketTransforms;
631 FTransform CachedVehicleMeshTransform;
632 FVector CachedVehicleWorldVelocity = FVector::ZeroVector;
633 FVector CachedVehicleWorldAngularVelocityDegrees = FVector::ZeroVector;
634 float CachedVehicleTransformsTimeSeconds = 0.0f;
635
636 // Backing value for RiderSeatSmoothingSpeed's low-pass filter -- see its
637 // comment. Updated in OnRiderBoneTransformsFinalized, read (as the
638 // extrapolation base) in GetCachedVehicleSocketWorldTransform().
639 FTransform SmoothedVehicleMeshTransform;
640 bool bSmoothedVehicleMeshTransformInitialized = false;
641
642 // Backing value for GetPedalRotation() -- accumulated from real vehicle
643 // speed each TickActor, wrapped to [0, 360) with Fmod. See the comment
644 // on GetPedalRotation() in the public Rider section above.
645 float PedalRotationAngle = 0.0f;
646
647public:
648 UPROPERTY(Category = "CARLA Wheeled Vehicle", EditDefaultsOnly)
649 float DetectionSize { 750.0f };
650
651 UPROPERTY(Category = "CARLA Wheeled Vehicle", VisibleAnywhere, BlueprintReadOnly)
652 FBox FoliageBoundingBox;
653
654 UPROPERTY(Category = "CARLA Wheeled Vehicle", EditAnywhere)
655 TObjectPtr<UBoxComponent> VehicleBounds;
656
657 UFUNCTION()
658 FBox GetDetectionBox() const;
659
660 UFUNCTION()
661 float GetDetectionSize() const;
662
663 UFUNCTION()
664 void UpdateDetectionBox();
665
666 UFUNCTION()
667 const TArray<int32> GetFoliageInstancesCloseToVehicle(const UInstancedStaticMeshComponent* Component) const;
668
669 UFUNCTION(BlueprintCallable)
670 void DrawFoliageBoundingBox() const;
671
672 UFUNCTION()
673 FBoxSphereBounds GetBoxSphereBounds() const;
674
675 UFUNCTION()
676 bool IsInVehicleRange(const FVector& Location) const;
677
678 /// Set the rotation of the car wheels indicated by the user
679 /// 0 = FL_VehicleWheel, 1 = FR_VehicleWheel, 2 = BL_VehicleWheel, 3 = BR_VehicleWheel
680 /// NOTE : This is purely aesthetic. It will not modify the physics of the car in any way
681 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
682 void SetWheelSteerDirection(EVehicleWheelLocation WheelLocation, float AngleInDeg);
683
684 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
685 float GetWheelSteerAngle(EVehicleWheelLocation WheelLocation);
686
687 /// Spin angle of a wheel, in degrees. Purely visual, wheel animation override only.
688 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
689 void SetWheelPitchAngle(EVehicleWheelLocation WheelLocation, float AngleInDeg);
690
691 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
692 float GetWheelPitchAngle(EVehicleWheelLocation WheelLocation);
693
694 /// Suspension compression of a wheel, in cm. Purely visual, wheel animation
695 /// override only.
696 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
697 void SetWheelSuspensionOffset(EVehicleWheelLocation WheelLocation, float OffsetInCm);
698
699 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
700 float GetWheelSuspensionOffset(EVehicleWheelLocation WheelLocation);
701
702 /// While the wheel animation is overridden the wheels show only the values
703 /// set through the wheel setters, whether or not Chaos simulates the vehicle.
704 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
705 void SetWheelAnimationOverride(bool bEnabled);
706
707 bool IsWheelAnimationOverridden() const { return bWheelAnimationOverridden; }
708
709 /// Indexed like the movement component's WheelSetups. Empty while the wheel
710 /// animation is not overridden.
711 const TArray<FWheelAnimationData> &GetOverriddenWheelPoses() const { return OverriddenWheelPoses; }
712
713 /// Whether the Chaos vehicle simulation is moving this vehicle.
714 bool IsSimulatedByChaos() const;
715
716 bool HasWheel(int32 WheelIndex) const;
717
718 /// Whether the animation blueprint derives from UCarlaVehicleAnimationInstance,
719 /// which is what draws the overridden wheel poses.
720 bool HasCarlaVehicleAnimation() const;
721
722 /// Radius of the wheel at WheelIndex, in cm, as last applied through the
723 /// physics control. Does not need the Chaos wheel instances to exist.
724 float GetWheelRadius(int32 WheelIndex) const;
725
726 /// Unit vector, component space, along which the wheel's suspension drops.
727 FVector GetWheelSuspensionAxis(int32 WheelIndex) const;
728
729 /// Wheel pose the animation instance drew this frame (else GetWheelPose); what secondaries must draw.
730 FWheelAnimationData GetDrawnWheelPose(int32 WheelIndex) const;
731
732 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
733 void OpenDoor(const EVehicleDoor DoorIdx);
734
735 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
736 void CloseDoor(const EVehicleDoor DoorIdx);
737
738 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
739 void OpenDoorPhys(const EVehicleDoor DoorIdx);
740
741 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
742 void CloseDoorPhys(const EVehicleDoor DoorIdx);
743
744 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
745 void RecordDoorChange(const EVehicleDoor DoorIdx, const bool bIsOpen);
746
747 /// Every door open now (a physics body of its own while open), by door
748 /// index, with its pose relative to the vehicle.
749 TArray<TPair<int32, FTransform>> GetOpenDoorPoses() const;
750
751 /// Multi-GPU secondary: shows these doors open, at these poses relative to
752 /// the vehicle, and every other door closed. A mirror's doors are posed,
753 /// never simulated.
754 void SetMirroredDoorPoses(const TArray<TPair<int32, FTransform>> &OpenDoors);
755
756 virtual FVector GetVelocity() const override;
757
758 /// Multi-GPU secondary: the primary's motion of the vehicle this one mirrors
759 /// (world frame, cm/s), which GetVelocity and GetVehicleForwardSpeed report
760 /// instead of this body's: a mirror never moves on its own.
761 void SetMirroredMotion(const FVector &Velocity, float ForwardSpeed);
762
763 void ClearMirroredMotion();
764
765 bool HasMirroredMotion() const
766 {
767 return bHasMirroredMotion;
768 }
769
770 /// Multi-GPU secondary: the primary's gear, which GetVehicleCurrentGear
771 /// reports instead of the idle drivetrain's, its manual gear shift, and
772 /// whether the primary's body simulates physics.
773 void SetMirroredDrivetrain(int32 Gear, bool bManualGearShift, bool bSimulatingPhysics)
774 {
775 bHasMirroredGear = true;
776 MirroredGear = Gear;
777 bMirroredManualGearShift = bManualGearShift;
778 bMirroredSimulatingPhysics = bSimulatingPhysics;
779 }
780
782 {
783 return bMirroredManualGearShift;
784 }
785
787 {
788 return bMirroredSimulatingPhysics;
789 }
790
791 UFUNCTION()
792 FPoseSnapshot GetWorldTransformedPose();
793
794//-----CARSIM--------------------------------
795 UPROPERTY(Category="CARLA Wheeled Vehicle", EditAnywhere)
796 float CarSimOriginOffset = 150.f;
797//-------------------------------------------
798
799 UPROPERTY(Category="CARLA Wheeled Vehicle", VisibleAnywhere)
800 bool bIsNWVehicle = false;
801
802 void SetRolloverFlag();
803
804 carla::rpc::VehicleFailureState GetFailureState() const;
805
806 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
807 static FRotator GetPhysicsConstraintAngle(UPhysicsConstraintComponent* Component);
808 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
809 static void SetPhysicsConstraintAngle(
810 UPhysicsConstraintComponent*Component, const FRotator &NewAngle);
811
812private:
813
814 bool bHasMirroredMotion = false;
815
816 FVector MirroredVelocity = FVector::ZeroVector;
817
818 float MirroredForwardSpeed = 0.0f;
819
820 bool bHasMirroredGear = false;
821
822 int32 MirroredGear = 0;
823
824 bool bMirroredManualGearShift = false;
825
826 bool bMirroredSimulatingPhysics = false;
827
828 UPROPERTY(Category="CARLA Wheeled Vehicle", VisibleAnywhere)
829 bool bPhysicsEnabled = true;
830
831 // Small workarround to allow optional CarSim plugin usage
832 UPROPERTY(Category="CARLA Wheeled Vehicle", VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
833 UBaseCarlaMovementComponent * BaseMovementComponent = nullptr;
834
835 UPROPERTY(Category="CARLA Wheeled Vehicle", VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
836 TArray<UPhysicsConstraintComponent*> ConstraintsComponents;
837
838 bool bWheelAnimationOverridden = false;
839
840 TArray<FWheelAnimationData> OverriddenWheelPoses;
841
842 /// The overridden pose for a setter to write, or nullptr, with a warning, if
843 /// it cannot be set.
844 FWheelAnimationData *GetOverriddenWheelPoseOrWarn(EVehicleWheelLocation WheelLocation, const TCHAR *Caller);
845
846 bool HasWheelOrWarn(int32 WheelIndex, const TCHAR *Caller) const;
847
848 /// The Chaos wheel, while Chaos simulates the vehicle and has output for it.
849 const UChaosVehicleWheel *GetSimulatedWheel(int32 WheelIndex) const;
850
851 /// The pose shown for a wheel: the overridden one, otherwise Chaos's,
852 /// otherwise the one last drawn.
853 FWheelAnimationData GetWheelPose(int32 WheelIndex) const;
854
855 /// GetWheelPose for every wheel.
856 TArray<FWheelAnimationData> GetWheelPoses() const;
857
858 /// Sets the Chaos wheels to these poses, so that they do not jump when Chaos
859 /// takes over the wheels again.
860 void SeedChaosWheels(const TArray<FWheelAnimationData> &Poses);
861
862 /// The next physics pre-tick copies in the output of the step before the
863 /// seed, so the seeded spins are written again when that frame's physics ends.
864 void ArmSeedRewrite(TArray<float> Spins);
865
866 void DisarmSeedRewrite();
867
868 void OnSeedPreTick(FPhysScene_Chaos *Scene, float DeltaSeconds);
869
870 void OnSeedPostTick(FChaosScene *Scene);
871
872 /// Degrees, as drawn.
873 TArray<float> PendingSeedSpins;
874
875 bool bSeedStepStarted = false;
876
877 FDelegateHandle SeedPreTickHandle;
878
879 FDelegateHandle SeedPostTickHandle;
880
881 const UChaosVehicleWheel *GetWheelDefaults(int32 WheelIndex) const;
882
883 UPROPERTY(Category="CARLA Wheeled Vehicle", VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
884 TMap<UPhysicsConstraintComponent*, UPrimitiveComponent*> ConstraintDoor;
885
886 // container of the initial transform of the door, used to reset its position
887 UPROPERTY(Category="CARLA Wheeled Vehicle", VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
888 TMap<UPrimitiveComponent*, FTransform> DoorComponentsTransform;
889
890 UPROPERTY(Category="CARLA Wheeled Vehicle", VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
891 TMap<UPrimitiveComponent*, UPhysicsConstraintComponent*> CollisionDisableConstraints;
892
893 /// Rollovers tend to have too much angular velocity, resulting in the vehicle doing a full 360ยบ flip.
894 /// This function progressively reduces the vehicle's angular velocity so that it ends up upside down instead.
895 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
896 void ApplyRolloverBehavior();
897
898 void CheckRollover(const float roll, const std::pair<float, float> threshold_roll);
899
900 void AddReferenceToManager();
901 void RemoveReferenceToManager();
902
903 /// Some vehicle Blueprints (Tesla, Mustang, DodgeCharger, ...) author real
904 /// Spot/Point light components for their headlights instead of relying on
905 /// emissive materials alone, but those components are not wrapped in a
906 /// UCarlaLight -- so they never got UE5's bAutoActivate=false activation
907 /// fix or the UE4-to-UE5 photometric intensity conversion (see CarlaLight.h).
908 /// Applies both fixes directly to this actor's light components.
909 void ActivateVehicleLightComponents();
910
911
912 FTimerHandle TimerHandler;
913public:
914 float SpeedAnim { 0.0f };
915 float RotationAnim { 0.0f };
916 FPoseSnapshot WorldTransformedPose;
917
918 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
919 float GetSpeedAnim() const { return SpeedAnim; }
920
921 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
922 void SetSpeedAnim(float Speed) { SpeedAnim = Speed; }
923
924 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
925 float GetRotationAnim() const { return RotationAnim; }
926
927 UFUNCTION(Category = "CARLA Wheeled Vehicle", BlueprintCallable)
928 void SetRotationAnim(float Rotation) { RotationAnim = Rotation; }
929};
ECarlaWheeledVehicleState
State of a ACarlaWheeledVehicle, to be displayed in editor for debugging purposes.
EVehicleWheelLocation
EVehicleDoor
Type of door to open/close.
EVehicleInputPriority
Base class for CARLA wheeled vehicles.
FVehicleLightState LightState
void SetAIVehicleState(ECarlaWheeledVehicleState InState)
FPoseSnapshot WorldTransformedPose
T * GetCarlaMovementComponent() const
bool GetMirroredManualGearShift() const
struct ACarlaWheeledVehicle::@1 InputControl
const TArray< FWheelAnimationData > & GetOverriddenWheelPoses() const
Indexed like the movement component's WheelSetups.
bool IsMirroredSimulatingPhysics() const
void ApplyAckermannControllerSettings(const FAckermannControllerSettings &AckermannControllerSettings)
void SetMirroredDrivetrain(int32 Gear, bool bManualGearShift, bool bSimulatingPhysics)
Multi-GPU secondary: the primary's gear, which GetVehicleCurrentGear reports instead of the idle driv...
This file contains definitions of common data structures used in traffic manager.