// Copyright Epic Games, Inc. All Rights Reserved. /* * Component to handle the vehicle simulation for an actor */ #pragma once #include "CoreMinimal.h" #include "UObject/ObjectMacros.h" #include "UObject/Object.h" #include "UObject/ScriptMacros.h" #include "EngineDefines.h" #include "SimpleVehicle.h" #include "VehicleUtility.h" #if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_4 #include "Engine/HitResult.h" #endif // UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_4 #include "Curves/CurveFloat.h" #include "ChaosVehicleWheel.generated.h" class UPhysicalMaterial; class FChaosVehicleManager; class UChaosWheeledVehicleMovementComponent; UENUM() enum class ESweepShape : uint8 { /** Use ray to determine suspension length to ground - fastest */ Raycast UMETA(DisplayName = "Raycast"), /** Use sphere to determine suspension length to ground */ Spherecast UMETA(DisplayName = "Spherecast"), /** Use wheel collision shape to determine suspension length to ground - Slowest */ Shapecast UMETA(DisplayName = "Shapecast") }; UENUM() enum class ESweepType : uint8 { /** Sweeps against simple geometry only */ SimpleSweep UMETA(DisplayName = "SimpleSweep"), /** Sweeps against complex geometry only */ ComplexSweep UMETA(DisplayName = "ComplexSweep") }; UENUM() enum class EAxleType : uint8 { Undefined = 0, Front, Rear }; UENUM() enum class ETorqueCombineMethod : uint8 { /** External torque value has no effect - default **/ None = 0, /** completely replace existing torques, can set and forget will apply same torque every frame until zeroed */ Override, /** Combine external torque with existing torques, must set external torque every frame */ Additive }; UCLASS(BlueprintType, Blueprintable) class CHAOSVEHICLES_API UChaosVehicleWheel : public UObject { GENERATED_UCLASS_BODY() /** * Static mesh with collision setup for wheel, will be used to create wheel shape * (if empty, sphere will be added as wheel shape, check bDontCreateShape flag) */ UPROPERTY(EditDefaultsOnly, Category = Shape) TObjectPtr CollisionMesh; /** If left undefined then the bAffectedByEngine value is used, if defined then bAffectedByEngine is ignored and the differential setup on the vehicle defines which wheels get power from the engine */ UPROPERTY(EditAnywhere, Category = Wheel) EAxleType AxleType; /** * If BoneName is specified, offset the wheel from the bone's location. * Otherwise this offsets the wheel from the vehicle's origin. */ UPROPERTY(EditAnywhere, Category = Wheel) FVector Offset; /** Radius of the wheel */ UPROPERTY(EditAnywhere, Category = Wheel, meta = (ClampMin = "0.01", UIMin = "0.01")) float WheelRadius; /** Width of the wheel */ UPROPERTY(EditAnywhere, Category = Wheel, meta = (ClampMin = "0.01", UIMin = "0.01")) float WheelWidth; /** Mass of the wheel Kg */ UPROPERTY(EditAnywhere, Category = Wheel, meta = (ClampMin = "0.01", UIMin = "0.01")) float WheelMass; /** Tyre Cornering Ability */ UPROPERTY(EditAnywhere, Category = Wheel) float CorneringStiffness; /** Friction Force Multiplier */ UPROPERTY(EditAnywhere, Category = Wheel) float FrictionForceMultiplier; /** Wheel Lateral Skid Grip Loss, lower number less grip on skid */ UPROPERTY(EditAnywhere, Category = Wheel, meta = (ClampMin = "0.0", UIMin = "0.0", ClampMax = "1.0", UIMax = "1.0")) float SideSlipModifier; /** Wheel Longitudinal Slip Threshold */ UPROPERTY(EditAnywhere, Category = Wheel, meta = (ClampMin = "0.0", UIMin = "0.0")) float SlipThreshold; /** Wheel Lateral Skid Threshold */ UPROPERTY(EditAnywhere, Category = Wheel, meta = (ClampMin = "0.0", UIMin = "0.0")) float SkidThreshold; // steer angle in degrees for this wheel UPROPERTY(EditAnywhere, Category = WheelsSetup) float MaxSteerAngle; /** Whether steering should affect this wheel */ UPROPERTY(EditAnywhere, Category = WheelsSetup) bool bAffectedBySteering; /** Whether brake should affect this wheel */ UPROPERTY(EditAnywhere, Category = Wheel) bool bAffectedByBrake; /** Whether handbrake should affect this wheel */ UPROPERTY(EditAnywhere, Category = Wheel) bool bAffectedByHandbrake; /** Whether engine should power this wheel */ UPROPERTY(EditAnywhere, Category = Wheel) bool bAffectedByEngine; /** Advanced Braking System Enabled */ UPROPERTY(EditAnywhere, Category = Wheel) bool bABSEnabled; /** Straight Line Traction Control Enabled */ UPROPERTY(EditAnywhere, Category = Wheel) bool bTractionControlEnabled; /** Max Wheelspin rotation rad/sec */ UPROPERTY(EditAnywhere, Category = Wheel) float MaxWheelspinRotation; /** Determines how the SetDriveTorque/SetBrakeTorque inputs are combined with the internal torques */ UPROPERTY(EditAnywhere, Category = Wheel) ETorqueCombineMethod ExternalTorqueCombineMethod; UPROPERTY(EditAnywhere, Category = Setup) FRuntimeFloatCurve LateralSlipGraph; /** Local body direction in which where suspension forces are applied (typically along -Z-axis) */ UPROPERTY(EditAnywhere, Category = Suspension) FVector SuspensionAxis; /** Vertical offset from where suspension forces are applied (along Z-axis) */ UPROPERTY(EditAnywhere, Category = Suspension) FVector SuspensionForceOffset; /** How far the wheel can go above the resting position */ UPROPERTY(EditAnywhere, Category = Suspension) float SuspensionMaxRaise; /** How far the wheel can drop below the resting position */ UPROPERTY(EditAnywhere, Category = Suspension) float SuspensionMaxDrop; /** Suspension damping, larger value causes the suspension to come to rest faster [range 0 to 1] */ UPROPERTY(EditAnywhere, Category = Suspension) float SuspensionDampingRatio; /** * Amount wheel load effects wheel friction. At 0 wheel friction is completely independent of the loading on the wheel (This is artificial as it always assumes even balance between all wheels) At 1 wheel friction is based on the force pressing wheel into the ground. This is more realistic. Lower value cures lift off over-steer, generally makes vehicle easier to handle under extreme motions. */ UPROPERTY(EditAnywhere, Category = Suspension, meta = (ClampMin = "0.0", UIMin = "0.0", ClampMax = "1.0", UIMax = "1.0")) float WheelLoadRatio; /** Spring Force (N/m) */ UPROPERTY(EditAnywhere, Category = Suspension) float SpringRate; /** Spring Preload (N/m) */ UPROPERTY(EditAnywhere, Category = Suspension) float SpringPreload; /** Smooth suspension [0-off, 10-max] - Warning might cause momentary visual inter-penetration of the wheel against objects/terrain */ UPROPERTY(EditAnywhere, Category = Suspension, meta = (ClampMin = "0.0", UIMin = "0", ClampMax = "10.0", UIMax = "10")) int SuspensionSmoothing; /** Anti-roll effect */ UPROPERTY(EditAnywhere, Category = Suspension, meta = (ClampMin = "0.0", UIMin = "0", ClampMax = "1.0", UIMax = "1")) float RollbarScaling; /** Wheel suspension trace type, defaults to ray trace */ UPROPERTY(EditAnywhere, Category = Suspension) ESweepShape SweepShape; /** Whether wheel suspension considers simple, complex */ UPROPERTY(EditAnywhere, Category = Suspension) ESweepType SweepType; /** max brake torque for this wheel (Nm) */ UPROPERTY(EditAnywhere, Category = Brakes) float MaxBrakeTorque; /** * Max handbrake brake torque for this wheel (Nm). A handbrake should have a stronger brake torque * than the brake. This will be ignored for wheels that are not affected by the handbrake. */ UPROPERTY(EditAnywhere, Category = Brakes) float MaxHandBrakeTorque; /** The vehicle that owns us */ UPROPERTY(transient) TObjectPtr VehicleComponent; // Our index in the vehicle's (and setup's) wheels array UPROPERTY(transient) int32 WheelIndex; // Longitudinal slip experienced by the wheel UPROPERTY(transient) float DebugLongSlip; // Lateral slip experienced by the wheel UPROPERTY(transient) float DebugLatSlip; // How much force the tire experiences at rest divided by how much force it is experiencing now UPROPERTY(transient) float DebugNormalizedTireLoad; //How much force the tire is experiencing now float DebugTireLoad; // Wheel torque UPROPERTY(transient) float DebugWheelTorque; // Longitudinal force the wheel is applying to the chassis UPROPERTY(transient) float DebugLongForce; // Lateral force the wheel is applying to the chassis UPROPERTY(transient) float DebugLatForce; // Worldspace location of this wheel UPROPERTY(transient) FVector Location; // Worldspace location of this wheel last frame UPROPERTY(transient) FVector OldLocation; // Current velocity of the wheel center (change in location over time) UPROPERTY(transient) FVector Velocity; UFUNCTION(BlueprintCallable, Category = "Game|Components|WheeledVehicleMovement") float GetSteerAngle() const; UFUNCTION(BlueprintCallable, Category = "Game|Components|WheeledVehicleMovement") float GetRotationAngle() const; UFUNCTION(BlueprintCallable, Category = "Game|Components|WheeledVehicleMovement") float GetRotationAngularVelocity() const; UFUNCTION(BlueprintCallable, Category = "Game|Components|WheeledVehicleMovement") float GetSuspensionOffset() const; UFUNCTION(BlueprintCallable, Category = "Game|Components|WheeledVehicleMovement") float GetWheelRadius() const; UFUNCTION(BlueprintCallable, Category = "Game|Components|WheeledVehicleMovement") float GetWheelAngularVelocity() const; UFUNCTION(BlueprintCallable, Category = "Game|Components|WheeledVehicleMovement") FVector GetSuspensionAxis() const; UFUNCTION(BlueprintCallable, Category = "Game|Components|WheeledVehicleMovement") bool IsInAir() const; UFUNCTION(BlueprintCallable, Category = "Game|Components|WheeledVehicleMovement") EAxleType GetAxleType() { return AxleType; } /** * Initialize this wheel instance */ virtual void Init(class UChaosWheeledVehicleMovementComponent* InVehicleSim, int32 InWheelIndex); /** * Notify this wheel it will be removed from the scene */ virtual void Shutdown(); /** * Get the Axle setup we were created from */ struct FChaosWheelSetup& GetWheelSetup(); /** * Tick this class when the vehicle ticks */ virtual void Tick(float DeltaTime); #if WITH_EDITOR /** * Respond to a property change in editor */ virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override; #endif //WITH_EDITOR protected: /** * Get the wheel's location in physics land */ FVector GetPhysicsLocation(); private: FChaosVehicleManager* GetVehicleManager() const; void FillWheelSetup() { // Perform any unit conversions here; between editable property and simulation system PWheelConfig.Offset = this->Offset; //PWheelConfig.WheelMass = this->WheelMass; PWheelConfig.WheelRadius = this->WheelRadius; PWheelConfig.WheelWidth = this->WheelWidth; PWheelConfig.WheelMass = this->WheelMass; PWheelConfig.MaxSteeringAngle = this->MaxSteerAngle; PWheelConfig.MaxBrakeTorque = this->MaxBrakeTorque; PWheelConfig.HandbrakeTorque = this->MaxHandBrakeTorque; PWheelConfig.SteeringEnabled = this->bAffectedBySteering; PWheelConfig.BrakeEnabled = this->bAffectedByBrake; PWheelConfig.HandbrakeEnabled = this->bAffectedByHandbrake; PWheelConfig.EngineEnabled = this->bAffectedByEngine; // may be modified later by vehicle differential override setup PWheelConfig.TorqueRatio = 0.f; // calculated later after all wheel info is known PWheelConfig.ABSEnabled = this->bABSEnabled; PWheelConfig.TractionControlEnabled = this->bTractionControlEnabled; PWheelConfig.MaxSpinRotation = this->MaxWheelspinRotation; PWheelConfig.AxleType = static_cast(this->AxleType); PWheelConfig.FrictionMultiplier = this->FrictionForceMultiplier; PWheelConfig.CorneringStiffness = this->CorneringStiffness * 10000.0f; PWheelConfig.SideSlipModifier = this->SideSlipModifier; PWheelConfig.SlipThreshold = this->SlipThreshold; PWheelConfig.SkidThreshold = this->SkidThreshold; PWheelConfig.ExternalTorqueCombineMethod = static_cast(this->ExternalTorqueCombineMethod); PWheelConfig.LateralSlipGraph.Empty(); float NumSamples = 20; float MinTime = 0.f, MaxTime = 0.f; this->LateralSlipGraph.GetRichCurveConst()->GetTimeRange(MinTime, MaxTime); if (MaxTime > 0.0f) { for (float X = 0; X <= MaxTime; X += (MaxTime / NumSamples)) { float MinVal = 0.f, MaxVal = 0.f; this->LateralSlipGraph.GetRichCurveConst()->GetValueRange(MinVal, MaxVal); float Y = this->LateralSlipGraph.GetRichCurveConst()->Eval(X) * 10000.0f; PWheelConfig.LateralSlipGraph.Add(Chaos::FVec2(X, Y)); } } } void FillSuspensionSetup() { PSuspensionConfig.SuspensionAxis = this->SuspensionAxis; PSuspensionConfig.SuspensionForceOffset = this->SuspensionForceOffset; PSuspensionConfig.SuspensionMaxRaise = this->SuspensionMaxRaise; PSuspensionConfig.SuspensionMaxDrop = this->SuspensionMaxDrop; PSuspensionConfig.SpringRate = Chaos::MToCm(this->SpringRate); PSuspensionConfig.SpringPreload = Chaos::MToCm(this->SpringPreload); PSuspensionConfig.DampingRatio = this->SuspensionDampingRatio; PSuspensionConfig.WheelLoadRatio = this->WheelLoadRatio; PSuspensionConfig.SuspensionSmoothing = this->SuspensionSmoothing; // These are calculated later from the PSuspensionConfig.DampingRatio // PSuspensionConfig.ReboundDamping // PSuspensionConfig.CompressionDamping } Chaos::FSimpleWheelConfig PWheelConfig; Chaos::FSimpleSuspensionConfig PSuspensionConfig; public: const Chaos::FSimpleWheelConfig& GetPhysicsWheelConfig() { FillWheelSetup(); return PWheelConfig; } const Chaos::FSimpleSuspensionConfig& GetPhysicsSuspensionConfig() { FillSuspensionSetup(); return PSuspensionConfig; } /** Get contact surface material */ UPhysicalMaterial* GetContactSurfaceMaterial(); };