The Re-Inventing the Wheel Thread

Couple of issue here:

  1. You need to take into account rotation of the wheel when you calculate relative velocity. Without it it’s like wheels are not spinning at all. For simplicity let’s assume that now you always have them on brakes or something :slight_smile:
    But if you want to take it into account then do it before this:


float velF = FVector::DotProduct(BodyForwardVector, TempVel.GetSafeNormal());


so that if car is moving forward but your wheels are spinning and you are steering them at the same time, then relative velocity vector won’t be aligned with direction in which car is moving. Which is important, otherwise it won’t turn.

  1. There is a bug with projection of force, you need to project TempFrictionForce on vector, not on plane:


FVector XFrictionForce = FVector::VectorPlaneProject(TempFrictionForce, WheelForward) * MuX; //Full friction force in X axis of the wheel
FVector YFrictionForce = FVector::VectorPlaneProject(TempFrictionForce, WheelRight) * MuY; //Full friction force in Y axis of the wheel
//should be
FVector XFrictionForce = FVector::**VectorVectorProject**(TempFrictionForce, WheelForward) * MuX; //Full friction force in X axis of the wheel
FVector YFrictionForce = FVector::**VectorVectorProject**(TempFrictionForce, WheelRight) * MuY; //Full friction force in Y axis of the wheel


I apologize if in C++ it’s called something else than VectorVectorProject, I didn’t tried to code this part yet. In any case, it needs to be projection so your forces will point in actual WheelForward and WheelRight direction. If you project them on plane they will be perpendicular to WheelForward and WheelRight.

Another thing. Here it’s better to use forward vector of the wheel, instead of the chassis, because friction circle is defined in wheel space:



float velF = FVector::DotProduct(BodyForwardVector, TempVel.GetSafeNormal());
//should be 
float velF = FVector::DotProduct(**WheelForward**, TempVel.GetSafeNormal());


Thank you!
The ski have much simpler, wiring wise, setup than a tank. They use friction circle too. Major difference is that ski are rigid bodies connected by constraint, instead of just animated static mesh without collision as on tank. For friction I still had to use raycheck, which start at the root of the ski and goes under the surface just 2 cm. This gives me a perfect point for applying friction and makes sure that it applied only when bottom of the ski is on the ground. Otherwise, if you try to use actual collision point of the ski with the ground, then this collision point is very often at the end of the ski on one of it’s side, so the whole thing start to turn in one direction without steering.

I really want to keep whole thing modular in a form of separate components. So that one would have a kind of a lego box of different parts and be able to combine them into different setups. Which means that either I have to call AddForce on main chassis from each component during their sub-stepping. Which might be a problem because I don’t know the order of their update. Or add extra component which should accumulate forces and provide FBodyInstance to other components, to guaranty that for example all suspensions get exactly the same chassis transform for their calculations.
Have you tried doing sub-stepping on separate components?

I’ve used C++ extensively many years ago. Nowadays I’m going through some C++ tutorials for UE4 to familiarize with the framework.

EDIT: I should check out your code and see how you do it.
According to this: https://github.com/EpicGames/UnrealEngine/pull/585
if I understand code correctly. They do use component based approach and code in 4 is executed separately for each wheel component.

I do all my calculations on specific classes on separate functions, but just call them from the parent (pawn) class. While sub-stepping happens on it’s own thread, this isn’t an issue as long you take it into account on your implementation. You could make delegates to the components directly but it would be harder to handle the execution order for certain things. For example I still measure suspension compression for each wheel first and after that start calculating needed forces using up-to-date values.

On the substepping example I put here earlier, I only assigned the custom physics delegate for one rigidbody. You can assign additional delegates for whatever rigidbodies you have on your scene that simulate physics. When I tried this out, it seemed to execute them in the same order you set them up in tick. I never tested if you can assign those delegates on components itself. If you can, then you could make them completely modular I guess.

So for example one could make a sort of interface for the components. Where from root you call something like CalculateAndReturnResultingForce(root.transform, root.velocity) on each of the connected components. They do their calculation and return you the vector of the force and location where it needs to be applied to root. I mean just to leave as much as possible component specific math on component side and do minimal wiring in the root body.

That’s what kind of bother me. because something like a scene component doesn’t have a rigidbody. For example if it’s a suspension then it will have a pointer to two rigid bodies - wheel and main body. But delegate can be set only on one of them, for example main body, so I won’t be able to send some other calculated force to the main body and wheel at the same time. Does it make sense? Just trying to understand how it works.

At the moment, for example I have a suspension component (which is a scene component). I first do a trace call from it to get all springs compression values. When I’ve done that for each wheel I call again another function from suspension class where I feed the opposite side values. I actually apply the forces to main rigidbody inside the suspension component which isn’t that clean design, but I wanted to keep the pawn class somewhat simple place where things are just tied together. I could even omit the opposite wheel value feeding by giving a ref to the opposite wheel (I actually had it like this at some point).

What you can do is to make your c++ component inherit either static or skeletal mesh component (or whatever makes more sense to you). As mesh components are inherited from scene components, you get all the scene component functionality and the mesh with rigidbody. I do this for wheel meshes myself as it allows me to have a separate wheel component that just handles wheel specific functionality (and visualization).

Also, you can have a scene components that have their own components. Sub-components that are either scene components or inherit from them do mess up in the BP view (if you inherit your c++ class to BP) because it just stacks all their transforms inside that single scene component. If you don’t need to change/tweak those locations from blueprints, it’s not that big of a deal anymore. Anyway, to prevent that, I’ve just created all the scene and mesh components on the pawn, even when they don’t have to communicate directly with the pawn itself. When I handle component attaching from pawn, it keeps the structure clean on inherited BP.

Thank you for explanation! So sub-stepping doesn’t really require that much of the re-design by itself and doing all math and applying all forces in one place is necessary more from the perspective of being in sync with all objects?

I think the biggest thing which you need take into consideration while running your own code from sub-steps is that it’s really running from a separate thread. So similar rules apply than with stuff that you can run on your own thread with UE4. But doing your own math, adding forces to BodyInstances should work just fine.

For things to watch out, you can check this wiki page: A new, community-hosted Unreal Engine Wiki - Announcements - Epic Developer Community Forums and section “What Not to Do”. Basically you don’t modify UObjects or use debug drawing outside of the game thread.

thanks BoredEngineer, ill have another go at that while im not tired with my mind on other things.

so, to substep a component you could do something like have all your components extending from 1 custom component. then in the main class (pawn), iterate over them all every tick and call their custom physics deligate. and this would just work fine?

now im completely lost
obviously i need to rewrite my engine function to calculate wheel angular velocities so the friction circle can work. for the moment i have no idea how.
but,
assuming i eventually sort that out, im completely confused as to whats going on with my friction circle function.
ive tried to comment everything to point out where im having trouble and renamed variables to make things more clear.


FVector ATegCar::GetFullFrictionForce(float DeltaTime, FBodyInstance * BodyInstance, FVector Loc, FVector vCollisionSurfaceNormal, int32 Index)
{
	//chassis velocity at wheel/ground vector
	FVector ChassisLinearVelocity = BodyInstance->GetUnrealWorldVelocityAtPoint(Loc);
	FTransform BodyTransform = BodyInstance->GetUnrealWorldTransform();

	//local direction of chassis
	FVector BodyForwardVector = BodyTransform.GetUnitAxis(EAxis::X);
	FVector BodyRightVector = BodyTransform.GetUnitAxis(EAxis::Y);
	FVector BodyUpVector = BodyTransform.GetUnitAxis(EAxis::Z);

	//local wheel direction vectors
	FVector WheelRight = BodyRightVector.RotateAngleAxis(CurrentAngle[Index], BodyUpVector);
	FVector WheelForward = BodyForwardVector.RotateAngleAxis(CurrentAngle[Index], BodyUpVector);

	///////////////////////////////////////////////////////////////////////////////////////
	// Calculate Mu from friction from elipse defined by MuX and MuY as radius of elipse //

	float WheelLinearVelocity = FVector::DotProduct(WheelForward, ChassisLinearVelocity.GetSafeNormal()); //wheel space
	ChassisLinearVelocity = FVector::VectorPlaneProject(ChassisLinearVelocity, vCollisionSurfaceNormal); //project onto ground surface normal

	// not sure what this bit is doing //
	float tmpX = MuX * WheelLinearVelocity;
	float tmpY = FMath::Sqrt(1.0f - (WheelLinearVelocity * WheelLinearVelocity)) * MuY;

	FVector2D vec2d;
	vec2d.X = tmpX;
	vec2d.Y = tmpY;
	float Mu = vec2d.Size();

	float wheelload = 1.0f;
	float MaxAvailableFrictionForce = wheelload * Mu; //unused?
	// end not sure about this bit //

//should this be wheel or chassis velocity?
	FVector TempFrictionForce = (ChassisLinearVelocity * -1)*BodyInstance->GetBodyMass();

	FVector XFrictionForce = TempFrictionForce.ProjectOnTo(WheelForward) * MuX; //Full friction force in X axis of the wheel
	FVector YFrictionForce = TempFrictionForce.ProjectOnTo(WheelRight) * MuY; //Full friction force in Y axis of the wheel

	FVector FullFrictionForce = TempFrictionForce.ClampMaxSize(MaxFrictionForce);//clamp vector by float value
	
	return FullFrictionForce;
}

thanks

Let’s take into account wheel then. You can play with WheelAngularVelocity and see how it works:


FVector ATegCar::GetFullFrictionForce(float DeltaTime, FBodyInstance * BodyInstance, FVector Loc, FVector vCollisionSurfaceNormal, int32 Index, **float WheelAngularVel, float WheelRadius**) //angular velocity is in radians
{
	//chassis velocity at wheel/ground vector
	FVector ChassisLinearVelocity = BodyInstance->GetUnrealWorldVelocityAtPoint(Loc);
	FTransform BodyTransform = BodyInstance->GetUnrealWorldTransform();

	//local direction of chassis
	FVector BodyForwardVector = BodyTransform.GetUnitAxis(EAxis::X);
	FVector BodyRightVector = BodyTransform.GetUnitAxis(EAxis::Y);
	FVector BodyUpVector = BodyTransform.GetUnitAxis(EAxis::Z);

	//local wheel direction vectors
	FVector WheelRight = BodyRightVector.RotateAngleAxis(CurrentAngle[Index], BodyUpVector);
	FVector WheelForward = BodyForwardVector.RotateAngleAxis(CurrentAngle[Index], BodyUpVector);

		///////////////////////////////////////////////////////////////////////////////////////
	// Calculate Mu from friction from elipse defined by MuX and MuY as radius of elipse //
	float **WFdotCV** = FVector::DotProduct(WheelForward, ChassisLinearVelocity.GetSafeNormal()); //wheel space
	
	// not sure what this bit is doing //
	float tmpX = MuX * **WFdotCV**;
	float tmpY = FMath::Sqrt(1.0f - (**WFdotCV * WFdotCV**)) * MuY;

	FVector2D vec2d;
	vec2d.X = tmpX;
	vec2d.Y = tmpY;
	float Mu = vec2d.Size();

	//float wheelload = 1.0f; //**this is where your projection of your suspension force on surface normal should be, otherwise 1.0 is like 1 Newton, which is very little to do anything unless it's a car for ants!
							// If you want to keep it hard-coded then use something like this: 9.8m/s^2 * 2500kg / 4**
	**//let's write it like this
	FVector SuspensionForce = BodyUpVector * 9.8 * 2500 / 4; //mass of car is 2,5 tons and we have 4 wheels
	FVector WheelLoad = SuspensionForce.ProjectOnTo(vCollisionSurfaceNormal); //Normal force is based on normal of the surface and force pushing on that surface**
	
	**float MaxAvailableFrictionForce = WheelLoad.Size() * Mu; //unused? - it is used, we clamp FullFrictionForce with it**
	
	//Let's calculate relative velocity properly
	**FVector WheelLinearVelocity = WheelForward * WheelAngularVel * WheelRadius; //for test, you can provide WheelAngularVel as zero, like the are locked by brakes**
	**FVector WheelRelativeVelocity = ChassisLinearVelocity - WheelLinearVelocity;**
	
	//and now we should project it to surface
	**WheelRelativeVelocity = FVector::VectorPlaneProject(WheelRelativeVelocity, vCollisionSurfaceNormal); //project onto ground surface normal**
	
	//should this be wheel or chassis velocity? **- it should be relative wheel velocity, relative because we take into account wheel spin**
	FVector TempFrictionForce = (**WheelRelativeVelocity** * -1)*BodyInstance->GetBodyMass(); //this is correct only if car moves straight forward, I think I got math to handle this properly in turns

	FVector XFrictionForce = TempFrictionForce.ProjectOnTo(WheelForward) * MuX; //Full friction force in X axis of the wheel
	FVector YFrictionForce = TempFrictionForce.ProjectOnTo(WheelRight) * MuY; //Full friction force in Y axis of the wheel
	
	**FVector FullFrictionForce = XFrictionForce + YFrictionForce; //we need to sum them up
	FullFrictionForce = FullFrictionForce.ClampMaxSize(**MaxAvailableFrictionForce**);//and now we clamp sum of friction forces in two diffferent axis**
	
	return FullFrictionForce;
}

I’ve tried to highlight in bold all the changes and extra comments, I might have missed something :smiley:

regarding:
TempFrictionForce = (WheelRelativeVelocity * -1)*BodyInstance->GetBodyMass()
this is suppose to be a momentum necessary to completely stop vehicle, this would be our friction force if friction coefficient is infinity.
[All this was rubbish, need to figure out proper math. Most likely TempFrictionForce just need to be split between all friction points in contact]

Found another bug in the code. This is not right:



FVector XFrictionForce = TempFrictionForce.ProjectOnTo(WheelForward) * MuX; //Full friction force in X axis of the wheel
FVector YFrictionForce = TempFrictionForce.ProjectOnTo(WheelRight) * MuY; //Full friction force in Y axis of the wheel


We take a force which is a vector in the plane of collision and then we project it into the plane of the wheel. This is wrong, because resulting friction force should stay in the plane of the surface. When we do this projection we align it with wheel.
The correct way is this:



FVector WheelForwardProj = FVector::VectorPlaneProject(WheelForward, vCollisionSurfaceNormal); //project on collision surface
WheelForwardProj = WheelForwardProj.GetSafeNormal(); //normalize, now it's a unit vector on surface of collision
FVector WheelRightProj = FVector::VectorPlaneProject(WheelRight, vCollisionSurfaceNormal); //project on collision surface
WheelRightProj = WheelRightProj.GetSafeNormal();
FVector XFrictionForce = TempFrictionForce.ProjectOnTo(WheelForwardProj) * MuX; //Full friction force in X axis of the wheel
FVector YFrictionForce = TempFrictionForce.ProjectOnTo(WheelRightProj) * MuY; //Full friction force in Y axis of the wheel


This seams to make more sense. I’ll go and fix my code as well.

Btw, it’s so much easier to read C++ than blueprints :smiley:

im with you there :slight_smile:

well its all getting beyond me now trying to convert abstract physics theories into working code, i have absolutely no background at all in this to call on and im completely lost.
so, ive decided to try and port some code from unity c# instead, the advanced version in the old car tutorial. hopefully ill learn some more stuff as well from doing that, fingers crossed.

No worries, one of the ways is to implement it step by step, starting from simple and adding complexity. Otherwise it’s just too much stuff to keep in mind. One of the funniest challenges for me was to get whole transmission working. Because it’s like a chain and all parts have to work properly to get a full circle.
Maybe build it first without two friction coefficients and spinning wheels? For spinning wheel to work correctly you need to add a piece of code to turn them by friction. So I would skip this part at first.

ive already implemented a simple car in that plugin you can download.
once it gets past being simple though it suddenly opens out into a whole minefield of stuff to get your head round.

i learn best by back engineering/modifying something that works already. blueprints are just too, erm… arty farty? or something to follow when it gets complex.
thats why im trying this port, so i can just read in text whats going on instead of scrolling through acres of dead space along wavy coloured lines joined to excessively padded squircles.

I agree with the UE4 vehicles having some problems. Your plugin looks cool but I have a question, is it setup to be compatible with the Custom Gravity Plugin or is that something I would need to try doing myself?

Which plugin are you referring to?

This one Blueprint/C++ Advanced Templates , Tools & Plugins - Game Development - Epic Developer Community Forums. Main idea is being able to drive on an upside down section of track and stay even when idle.