FGear Vehicle Physics (v1.4)

this is how it works in general:

bone position = forward offset + spacer offset + vertical offset + suspension travel
wireframe position = bone position + rim offset

bone position represents the visual of the wheel, wireframe position is the physical wheel(there is no physical wheel but it’s the origin of the ray/volume queries). the difference between rim and spacer offset is that rim offset only moves physical wheel but spacer moves both.

will check your json for the hillholder case.

yet again I don’t see a problem with hill-holder but it’s clear there is a bug with auto-clutch(the rear gear case).
you’re not directly using the standard input, I guess there is a problem with how you apply inputs.

My input is customized to fit the SimpleController plugin, unfortunately the Hill-holder assist is breaking my game, would it be very difficult for me to re-implement the “Allow Wheel Rotation Against Engine”? (actually only the part of the code that denies this function, working as standard)

In my case I would need to move only the physical wheel, and have feedback from the wireframe if my calculation for adjusting the wheel rotation in relation to the kingpin as a pivot is correct, would there be a chance for you to implement a forward offset that works like the rim offset?

reimplementing “Allow Wheel Rotation Against Engine” is quite easy if you’re building from source, you just need to add 2 lines to the wheel class, if you’re up to it drop a mail.

the origin of the wheel bone can be shifted on Y axis so I have added the rimoffset feature to quickly fix mismatches between physical wheel and the wheel mesh. it’s unlikely that the other axis’ will be shifted so the other offsets are not required and at the end of the day you can do that shift yourself in the anim bp.

Comparing FGearWheel.cpp from 1.8.3 with 1.8.2, I saw that these lines below were removed, should I just copy them back?

820 bool allowReverse = mVehicle->getAllowReverse();
831 //if overflow is not the same sign as limit, then set tire speed to 0
832 else if (!allowReverse) mRealSpeed = FMath::Lerp(mRealSpeed, 0.0f, clutch);
844 else if (!allowReverse) mSpeedOverflow = 1.0f;

Since I’m going to have to recompile the code, is there any simple change I can make so that the wireframe follows the forward and spacer offsets?

for allowReverse just get rid of the conditions like:

else /*if (!allowReverse)*/ mRealSpeed = FMath::Lerp(mRealSpeed, 0.0f, clutch);
...
else /*if (!allowReverse)*/ mSpeedOverflow = 1.0f;

since you want the behavior where it’s false.

wireframe code is a bit more involved, i’ll take a look and paste here when I get the time.

Thank you, everything is working properly again!
You mentioned that using “Allow Wheel Rotation Against Engine” causes a conflict with another feature that causes a bug, what is the feature and the bug? That way I can avoid using it.

I’m trying to make a replay system using a vehicle proxy, I can record things like the vehicle’s position, wheel rotations, suspension height, etc., but how could I record the moment the dust FX is played on the wheels? Is there a variable that is provided by the wheels (and exposed in BP) that when it exceeds a certain value means that the particle has been emitted?

here is the post that mentions that bug: FGear Vehicle Physics (v1.4) - #1683 by Davit_Masia
I didn’t look for the cause since I wanted to remove the option.

and about replay system, you can take a look at the provided replay system in fgear, basically we do not save any FX data but expect a similar FX to be generated based on inputs and the transform. if you want to roll your own system you can take a look at the UFGearEffects::effectUpdate function where we compile some wheel slip data to trigger the effects.

and for the offsets you could try the below code (didn’t test it)

I don’t think it’s a bug, it’s normal for a vehicle with an automatic transmission in D to move forward slowly even without pressing the accelerator. I tested it here on a clean project and the car doesn’t move when in N or with the clutch pressed.
I think you should keep both systems, the “Hill-holder assist” for arcade vehicles with automatic transmission and the “Allow Wheel Rotation Against Engine” for vehicles with manual transmissions. (In “Hill-holder assist”, it might be better to remove the “On Brake State Changed” when the vehicle is on a flat surface, it’s strange to see the brake light turn on without pressing the brake.)

Thanks for the code!
It worked! I finally managed to make the physical wheel follow exactly the rotation arc of the visible wheel in relation to the kingpin (for an offroad game with rock crawling it is very important that the wheel has perfect contact even with the smallest rocks).

About the replay, I wanted to do something super light, using a vehicle proxy without physics or anything, then I should be able to record and reproduce everything related to the vehicle and the wheels, I can also record things like the brake light and backfire through the events, the only thing missing would be the moments where the dust fx was activated. Would it be too much work to expose a boolean variable indicating whether the fx is active or not? (I believe it would also be useful to reproduce the skidding sound)

1 Like

Hello!

It seems that the keyboard controls have some kind of problem, when i use the directional keys to turn the car around sometimes there is latency or seems like there is a force fiighting the input direction. For some reason is more common/pronouonced turning left. Gamepad controls doesnt seem to be affected.

Tested with same results in UE 5.4 and UE 5.6

keyboard controls

there is no change in input code since last year.

looking at your video there seems to be some interference with your input, it’s visible at around 13.-14. second. the steering bar at the bottom twitches before moving to the left. I suggest unplugging other input devices and try again with the keyboard.

Trying to achieve that on reverse, if press accel = wheels starts spinning forward, without reduction to 0 first.

It seems the problem is at: UFGearWheel::updateWheelSpeed on //ground correction, if (mHasContact).

Because doing some changes i get that fixed, but then, the overall accel/braking not works as previous.

Any idea? Or maybe there is another way to fix that better?

EDIT: What im trying inside (mHasContact is:
if (groundDelta < 0.0f && mVehicle->getEngine()->getThrottle() > 0.0f)
= Spin the wheels
else = as original

that portion of the code is critical and easy to brake, so be careful.

can you try setting “Optimum Slip Weight” to 1.0 after making that fix.

the acceleration/braking behavior is mostly tied on wheel slip and using this param will make sure you get an optimal value at all times.

All should get fixed without edit “Optimum Slip Weight”, i just want avoid that reduction on reverse, rest the same. Im gonna sleep now, tomorrow i will try with an else if and clone the code, so when reverse + accel will try to change or avoid some code will see.

I think got it, let me know what you think:

  1. Changed condition to use getStandardInput()->getThrottleRawInput()
    Because with previous one also triggers on braking-reverse.

  2. Using getKMHSpeed() < 0.0f, instead of groundDelta.

  3. After some debug and testing seems this does what i was looking:
    bool caseFIX = getKMHSpeed() < -0.1f && mVehicle->getStandardInput()->getThrottleRawInput() > 0.0f;
    mRealSpeed += caseFIX ? 1.0f : deltaV;

Still playing around and testing all scenarios but after some hours no weird behaviours found.

@lazybitgames , can you please share some insight on how you pooled the vehicles? I am having consitent crashes. I need to pool my vehicles as spawning a lot of vehicles and destroying them tanks my perf.


I have attached my pooling and depooling functions. Sleep is called when entering the pool. Pooling function is bound to Sleep delegate. This works fine. The problem is exiting the pool. I can calling the depooling function before waking the vehicle and I get this crash

Assertion failed: ProxyBaseIn->GetMarkedDeleted() == false [File:D:\build++UE5\Sync\Engine\Source\Runtime\Experimental\Chaos\Public\Chaos\Framework\PhysicsSolverBase.h] [Line: 330]

UnrealEditor_Core!FDebug::CheckVerifyFailedImpl2() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Private\Misc\AssertionMacros.cpp:728]
UnrealEditor_PhysicsCore!Chaos::FPhysicsSolverBase::AddDirtyProxy() [D:\build++UE5\Sync\Engine\Source\Runtime\Experimental\Chaos\Public\Chaos\Framework\PhysicsSolverBase.h:330]
UnrealEditor_PhysicsCore!Chaos::FRigidBodyHandle_External::SetV() [D:\build++UE5\Sync\Engine\Source\Runtime\Experimental\Chaos\Public\PhysicsProxy\SingleParticlePhysicsProxy.h:1173]
UnrealEditor_PhysicsCore!FChaosEngineInterface::SetLinearVelocity_AssumesLocked() [D:\build++UE5\Sync\Engine\Source\Runtime\PhysicsCore\Private\ChaosEngineInterface.cpp:633]
UnrealEditor_Engine!Chaos::TAABBTree<int,Chaos::TAABBTreeLeafArray<int,1,double>,1,double,Chaos::TDefaultAABBTreeStorageTraits<int> >::ProgressCopyTimeSliced'::2’::<lambda_1>::operator()() [D:\build++UE5\Sync\Engine\Source\Runtime\Engine\Private\PhysicsEngine\BodyInstance.cpp:3562]
UnrealEditor_Engine!FBodyInstance::SetLinearVelocity() [D:\build++UE5\Sync\Engine\Source\Runtime\Engine\Private\PhysicsEngine\BodyInstance.cpp:3549]
UnrealEditor_FGearPlugin!AFGearVehicle::handleWakeUp() [D:\build\U5M\Sync\LocalBuilds\PluginTemp\HostProject\Plugins\FGearPlugin\Source\FGearPlugin\Private\FGearVehicle.cpp:1531]
UnrealEditor_CoreUObject!UFunction::Invoke() [D:\build++UE5\Sync\Engine\Source\Runtime\CoreUObject\Private\UObject\Class.cpp:7453]
UnrealEditor_CoreUObject!UObject::CallFunction() [D:\build++UE5\Sync\Engine\Source\Runtime\CoreUObject\Private\UObject\ScriptCore.cpp:1175]
UnrealEditor_CoreUObject!ProcessLocalScriptFunction() [D:\build++UE5\Sync\Engine\Source\Runtime\CoreUObject\Private\UObject\ScriptCore.cpp:1242]
UnrealEditor_CoreUObject!ProcessScriptFunction<void (__cdecl*)(UObject *,FFrame &,void *)>() [D:\build++UE5\Sync\Engine\Source\Runtime\CoreUObject\Private\UObject\ScriptCore.cpp:1070]
UnrealEditor_CoreUObject!ProcessLocalFunction'::2’::<lambda_1>::operator()() [D:\build++UE5\Sync\Engine\Source\Runtime\CoreUObject\Private\UObject\ScriptCore.cpp:1320]
UnrealEditor_CoreUObject!ProcessLocalFunction() [D:\build++UE5\Sync\Engine\Source\Runtime\CoreUObject\Private\UObject\ScriptCore.cpp:1339]
UnrealEditor_CoreUObject!ProcessLocalScriptFunction() [D:\build++UE5\Sync\Engine\Source\Runtime\CoreUObject\Private\UObject\ScriptCore.cpp:1242]
UnrealEditor_CoreUObject!UObject::ProcessInternal() [D:\build++UE5\Sync\Engine\Source\Runtime\CoreUObject\Private\UObject\ScriptCore.cpp:1371]
UnrealEditor_CoreUObject!UFunction::Invoke() [D:\build++UE5\Sync\Engine\Source\Runtime\CoreUObject\Private\UObject\Class.cpp:7453]
UnrealEditor_CoreUObject!UObject::ProcessEvent() [D:\build++UE5\Sync\Engine\Source\Runtime\CoreUObject\Private\UObject\ScriptCore.cpp:2211]
UnrealEditor_Engine!AActor::ProcessEvent() [D:\build++UE5\Sync\Engine\Source\Runtime\Engine\Private\Actor.cpp:1456]
UnrealEditor_MassActors!UMassActorSpawnerSubsystem::SpawnOrRetrieveFromPool() [D:\build++UE5\Sync\Engine\Plugins\Runtime\MassGameplay\Source\MassActors\Private\MassActorSpawnerSubsystem.cpp:245]
UnrealEditor_MassActors!UMassActorSpawnerSubsystem::ProcessSpawnRequest() [D:\build++UE5\Sync\Engine\Plugins\Runtime\MassGameplay\Source\MassActors\Private\MassActorSpawnerSubsystem.cpp:399]
UnrealEditor_MassActors!UMassActorSpawnerSubsystem::ProcessPendingSpawningRequest() [D:\build++UE5\Sync\Engine\Plugins\Runtime\MassGameplay\Source\MassActors\Private\MassActorSpawnerSubsystem.cpp:364]
UnrealEditor_MassActors!UMassActorSpawnerSubsystem::OnPrePhysicsPhaseStarted() [D:\build++UE5\Sync\Engine\Plugins\Runtime\MassGameplay\Source\MassActors\Private\MassActorSpawnerSubsystem.cpp:513]
UnrealEditor_MassActors!V::TBaseUObjectMethodDelegateInstance::ExecuteIfSafe() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Public\Delegates\DelegateInstancesImpl.h:689]
UnrealEditor_MassEntity!TMulticastDelegate<void __cdecl(float),FDefaultDelegateUserPolicy>::Broadcast() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Public\Delegates\DelegateSignatureImpl.inl:1080]
UnrealEditor_MassEntity!FMassProcessingPhase::ExecuteTick() [D:\build++UE5\Sync\Engine\Source\Runtime\MassEntity\Private\MassProcessingPhaseManager.cpp:74]
UnrealEditor_Engine!TGraphTask::ExecuteTask() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Public\Async\TaskGraphInterfaces.h:706]
UnrealEditor_Core!UE::Tasks::Private::FTaskBase::TryExecuteTask() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Public\Tasks\TaskPrivate.h:527]
UnrealEditor_Core!FNamedTaskThread::ProcessTasksNamedThread() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Private\Async\TaskGraph.cpp:779]
UnrealEditor_Core!FNamedTaskThread::ProcessTasksUntilIdle() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Private\Async\TaskGraph.cpp:679]
UnrealEditor_Core!FTaskGraphCompatibilityImplementation::ProcessUntilTasksComplete() [D:\build++UE5\Sync\Engine\Source\Runtime\Core\Private\Async\TaskGraph.cpp:1592]
UnrealEditor_Engine!FTickTaskSequencer::ReleaseTickGroup() [D:\build++UE5\Sync\Engine\Source\Runtime\Engine\Private\TickTaskManager.cpp:986]
UnrealEditor_Engine!FTickTaskManager::RunTickGroup() [D:\build++UE5\Sync\Engine\Source\Runtime\Engine\Private\TickTaskManager.cpp:2079]
UnrealEditor_Engine!UWorld::RunTickGroup() [D:\build++UE5\Sync\Engine\Source\Runtime\Engine\Private\LevelTick.cpp:786]
UnrealEditor_Engine!UWorld::Tick() [D:\build++UE5\Sync\Engine\Source\Runtime\Engine\Private\LevelTick.cpp:1511]
UnrealEditor_UnrealEd!UEditorEngine::Tick() [D:\build++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\EditorEngine.cpp:2151]
UnrealEditor_UnrealEd!UUnrealEdEngine::Tick() [D:\build++UE5\Sync\Engine\Source\Editor\UnrealEd\Private\UnrealEdEngine.cpp:533]
UnrealEditor!FEngineLoop::Tick() [D:\build++UE5\Sync\Engine\Source\Runtime\Launch\Private\LaunchEngineLoop.cpp:5625]
UnrealEditor!GuardedMain() [D:\build++UE5\Sync\Engine\Source\Runtime\Launch\Private\Launch.cpp:187]
UnrealEditor!GuardedMainWrapper() [D:\build++UE5\Sync\Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:128]
UnrealEditor!LaunchWindowsStartup() [D:\build++UE5\Sync\Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:282]
UnrealEditor!WinMain() [D:\build++UE5\Sync\Engine\Source\Runtime\Launch\Private\Windows\LaunchWindows.cpp:339]
UnrealEditor!__scrt_common_main_seh() [D:\a_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288]
kernel32
ntdll

it looks like somehow your physics body is marked to be deleted, idk how that happened though, I tried a similar BP code and didn’t encounter such a problem.

on the other hand my approach to actor activation/deactivation was rather simple, sth. like below code:

void setActorActive(AActor* actor, bool active)
{
	//set actor states
	actor->SetActorHiddenInGame(!active);
	actor->SetActorEnableCollision(active);
	actor->SetActorTickEnabled(active);

	//set component tick states
	TInlineComponentArray<UActorComponent*> components;
	actor->GetComponents(components);
	for (int i = 0; i < components.Num(); i++) 
	{ 
		components[i]->SetComponentTickEnabled(active);
	}

	//...
}

@lazybitgames How do you handle the suspension not being reinstated when reactivating the actor?

Because upon reactivating, the wheels fall through the ground

maybe try calling vehicle.reset before reactivating.

@lazybitgames Is “UFGearArcadeAssists::updateAntiRollBars” correct?

Checking the code says:
//apply front , if (flCompress > frCompress), but uses -rearAntiRoll
&
//apply rear, if (rlCompress > rrCompress)|, but uses -frontAntiRoll

They are switched, is a bug, or is done like that for some reason ¿?.