No announcement yet.

Rotating Rigid Body/Vehicle Smoothly

  • Filter
  • Time
  • Show
Clear All
new posts

    Rotating Rigid Body/Vehicle Smoothly

    I'm trying to smoothly rotate a custom vehicle to a specific direction.
    The vehicle doesn't have a SimObj.
    I'd prefer to use Mesh.AddTorque for that, as no SimObj allows for the movement I want.

    My goal is to use the ability to rotate smoothly for AI, auto pilot etc.

    If I try to apply torque on just one global rotation axis it works.
    But if I try do adjust all axis at the same the vehicle would just keep spinning.
    Any ideas how to change all angles smoothly at the same time?

    The code of my vehicle:
    class MyCicada extends UTAirVehicle;
    var Rotator DesiredOrientation;
    var private Vector lastpError;
    // correct if close to +32k/-32k border,
    // 'solves' the circle problem
    simulated function CorrectAngle(out float CurrentAngle, out float DesiredAngle)
    	if(Abs(CurrentAngle - DesiredAngle) >= 180 * DegToUnrRot)
    		if(CurrentAngle > DesiredAngle)
    			DesiredAngle += 360*DegToUnrRot;
    			CurrentAngle += 360*DegToUnrRot;
    simulated function float CalcTorqueStupid(float CurrentAngle, float DesiredAngle, float Angular_Velocity)
    	local float DiffAngle;
    	local float Torque;
    	// correct if close to +32k/-32k border,
    	// 'solves' the circle problem
    	CorrectAngle(CurrentAngle, DesiredAngle);
    	DiffAngle = DesiredAngle - CurrentAngle;
    	Torque = FClamp(DiffAngle, -100, 100); // 50 is very little
    	if(Abs(DiffAngle) < 80)
    		Torque /= 2;
    	if(Abs(DiffAngle) < 60)
    		Torque /= 2;
    	if(Abs(DiffAngle) < 70)
    		Torque /= 2;
    	if(Abs(DiffAngle) < 60)
    		Torque /= 2;
    	if(Abs(DiffAngle) < 50)
    		Torque /= 2;
    	if(Abs(DiffAngle) < 40)
    		Torque /= 2;
    	if(Abs(DiffAngle) < 30)
    		Torque /= 2;
    // PID controller approach
    simulated function float CalcTorquePID(float CurrentAngle, float DesiredAngle, float deltaTime, out float _lastpError)
    	local float Torque;
    	local float pError, iError, dError, pGain, iGain, dGain; 
    	// correct if close to +32k/-32k border,
    	// 'solves' the circle problem
    	CorrectAngle(CurrentAngle, DesiredAngle);
    	pError = (DesiredAngle - CurrentAngle)*UnrRotToDeg;
    	iError = pError * deltaTime;
    	dError = (pError - _lastpError)/deltaTime;
    	_lastpError = pError;
    	pGain = 0.5;
    	iGain = 0;
    	dGain = 0.5;
    	torque = pGain*pError + iGain*iError + dGain*dError;
    simulated function Tick(float DeltaTime)
    	local Vector Torque;
    	//Torque.X = CalcTorqueStupid(Rotation.Roll, DesiredOrientation.Roll, AngularVelocity.X);
    	//Torque.Y = CalcTorqueStupid(Rotation.Pitch, DesiredOrientation.Pitch, AngularVelocity.Y);
    	//Torque.Z = CalcTorqueStupid(Rotation.Yaw, DesiredOrientation.Yaw, AngularVelocity.Z);
    	Torque.X = CalcTorquePID(Rotation.Roll, DesiredOrientation.Roll, DeltaTime, lastpError.X);
    	Torque.Y = CalcTorquePID(Rotation.Pitch, DesiredOrientation.Pitch, DeltaTime, lastpError.Y);
    	Torque.Z = CalcTorquePID(Rotation.Yaw, DesiredOrientation.Yaw, DeltaTime, lastpError.Z);
    	DesiredOrientation=(Yaw=35000, Pitch=30000, Roll=2000)
    	Begin Object Name=CollisionCylinder
    	End Object
    	Begin Object Name=SVehicleMesh
    	End Object
    	Seats(0)={(	GunClass=class'UTVWeap_CicadaMissileLauncher',
    	Seats(1)={(	GunClass=class'UTVWeap_CicadaTurret',

    If you have code that can rotate a KActor smoothly to a specifieded direction that proably would work for me.

    A quick extract from what I use to match a rotation. This was obviously designed for ships but will work for any physx-driven body. How's the spaceship game coming along btw?

    var Rotator DesiredOrientation;
    var() Vector AngularThrustStrength;
    var() Vector AngularFriction;
    protected function ProcessAngularAutoMove(float DeltaTime)
    	local Vector WorldAngVel;
    	local RB_BodyInstance Body;
    	//get local-space ang vel
    	Body = CollisionComponent.GetRootBodyInstance();
    	WorldAngVel = Body.GetUnrealWorldAngularVelocity();
    	LocalAngularVelocity = WorldAngVel << Rotation;
    	//add vel to move towards the desired orientation
    	LocalAngularVelocity += CalcRotationMatchAngVel(DesiredOrientation, LocalAngularVelocity, DeltaTime);
    	//dampen LocalAngularVelocity by some friction factor if you need here
               LocalAngularVelocity.Y -= class'AFFMaths'.static.Sign(LocalAngularVelocity.Y) * FMin(Abs(LocalAngularVelocity.Y), DeltaTime * AngularFriction.Y); 
    	LocalAngularVelocity.X -= class'AFFMaths'.static.Sign(LocalAngularVelocity.X) * FMin(Abs(LocalAngularVelocity.X), DeltaTime * AngularFriction.X); 
    	LocalAngularVelocity.Z -= class'AFFMaths'.static.Sign(LocalAngularVelocity.Z) * FMin(Abs(LocalAngularVelocity.Z), DeltaTime * AngularFriction.Z); 
    	//apply ang vel back in the world space
    	CollisionComponent.SetRBAngularVelocity(LocalAngularVelocity >> Rotation, false);
    protected function Vector CalcRotationMatchAngVel(const out Rotator inRotation, const out Vector inCurrentLocalAngVel, float DeltaTime)
    	local Vector Result, AngThrust, AngTimeToRot, AngTimeToStop, AngFinal, DX,DY,DZ, AngDelta;
    	local Rotator LocalTargetRot;
    	//do a check since Pawns are autonomous proxy and can run non-simulated functions even on clients
    	if(Role == ROLE_Authority)
    		//get target rotation in local space
    		LocalTargetRot = OrthoRotation(DX << Rotation, DY << Rotation, DZ << Rotation);
    		AngDelta.X = NormalizeRotAxis(-LocalTargetRot.Roll);
    		AngDelta.Y = NormalizeRotAxis(-LocalTargetRot.Pitch);
    		AngDelta.Z = NormalizeRotAxis(LocalTargetRot.Yaw);
    		AngDelta = (AngDelta * pi) / 32768.0f;
    		//resolve local Y Pitch 
    		//get direction in which thrust should be applied to reach AngDelta
    		AngThrust.X = (AngDelta.X > 0) ? AngularThrustStrength.X : (AngDelta.X < 0) ? -AngularThrustStrength.X : 0.0f;
    		AngThrust.Y = (AngDelta.Y > 0) ? AngularThrustStrength.Y : (AngDelta.Y < 0) ? -AngularThrustStrength.Y : 0.0f;
    		AngThrust.Z = (AngDelta.Z > 0) ? AngularThrustStrength.Z : (AngDelta.Z < 0) ? -AngularThrustStrength.Z : 0.0f;
    		//modulate accel by distance to travel
    		AngThrust.X *= Sqrt(Abs(AngDelta.X));
    		AngThrust.Y *= Sqrt(Abs(AngDelta.Y));	
    		AngThrust.Z *= Sqrt(Abs(AngDelta.Z));	
    		//calculate time to desired rotation
    		AngTimeToRot.X = inCurrentLocalAngVel.X != 0 ? AngDelta.X / inCurrentLocalAngVel.X : 0.0f;
    		AngTimeToRot.Y = inCurrentLocalAngVel.Y != 0 ? AngDelta.Y / inCurrentLocalAngVel.Y : 0.0f;
    		AngTimeToRot.Z = inCurrentLocalAngVel.Z != 0 ? AngDelta.Z / inCurrentLocalAngVel.Z : 0.0f;
    		//calc time to stop from current velocity
    		AngTimeToStop.X = AngThrust.X != 0 ? inCurrentLocalAngVel.X / AngThrust.X : 0.0f;
    		AngTimeToStop.Y = AngThrust.Y != 0 ? inCurrentLocalAngVel.Y / AngThrust.Y : 0.0f;
    		AngTimeToStop.Z = AngThrust.Z != 0 ? inCurrentLocalAngVel.Z / AngThrust.Z : 0.0f;
    		//if we are approaching desired rotation too quickly, reverse thrust dir, otherwise keep approaching
    		AngFinal.X = (AngTimeToRot.X > 0 && AngTimeToRot.X < AngTimeToStop.X) ? -AngThrust.X : AngThrust.X;
    		AngFinal.Y = (AngTimeToRot.Y > 0 && AngTimeToRot.Y < AngTimeToStop.Y) ? -AngThrust.Y : AngThrust.Y;
    		AngFinal.Z = (AngTimeToRot.Z > 0 && AngTimeToRot.Z < AngTimeToStop.Z) ? -AngThrust.Z : AngThrust.Z;
    		Result = AngFinal * DeltaTime;
    	return Result;


      Your quick extract is awesome, it has been a plug and play basically. I only had to fixes some errors in my code to make it work.^^
      I'm amazed by your code: It allows for fluid and precise rotation, it use relative axes, deals the collision, can be used for AI and works in MP. My code would always refuse to do one of those.

      Now my stupid bots can fly the ships, somehow :-)

      The spaceship game is coming along nicely, thanks to you. ;-) After the rotation issue is nearly solved, I have to get started on the HUD (these white boxes just hurt in eye) and make the pilot seat use the vehicle's weapon inventory to have multiple exchangable weapons. These two are the only major issues left.

      I hate to ask, but how do you transform the Rotation of the PlayerController to the DesiredOrientation of the ship so that doing loopings, rolling and flying upside down works properly? I've tried the whole day to figure it out, but failed.


        Currently I have a couple methods of determining the desired rotation of the ship. The code I gave you is mostly used for autopilot and AI, player-controlled fighters don't use it at all, instead the PlayerController's input directly applies linear and angular thrust to their meshes. This is the most responsive way and gives the player the impression of actually steering the ship directly.
        However capital ships, which are slow and can have their camera rotation independent from the ship's rotation, do use this rotation matching code. I actually have two slightly different prototypes of that behaviour and can provide you with some example code later. But in general the mechanism can be described in the following way:

        The input is applied to the PlayerController directly - I just multiply the rotation quaternion by quaternions based on horizontal and vertical mouse movement, as well as another quat multiplication for roll (bound to Q and E keys, quite obviously), then convert QuatToRotator to apply it back to the camera (the PlayerController). This creates a sort of a 6DOF FPS camera.

        If you want the ship to be always offset from the camera by some vector, you'll also have take that newly calculated PlayerController.Rotation, transform it to world space (Offset >> PlayerController.Rotation) and make the ship move to that point. Let's call that point ShipPoint. Let's also call PlayerController.Location, or camera location or whatever point you consider the origin of the crosshair trace, CameraPoint.

        To get the new rotation, first do a trace from the crosshair into the world. Let's call the hit point of that trace TargetPoint. A vector from the ship's new desired location (ShipPoint) to TargetPoint provides the direction the ship should point in, that's DirectionVector. However to get the full rotation, with a sensible roll value, you have to construct the rotator from a full coordinates system, i.e. from 3 vectors (If I recall there's a function for that in uscript, something like RotatorFromOrtho?):

        The X vector is Normal(DirectionVector)
        The Y vector is Normal(DirectionVector) cross Normal(CameraPoint - ShipPoint)
        The Z vector is Y vector cross Normal(DirectionVector)

        I might've messed up some cross operation order there and it's all off the top of my head because I don't have access to source from here, but I'm sure you'll figure it out.


          Originally posted by Farseer View Post
          , something like RotatorFromOrtho?):


            Thanks a bunch guys, I'll start cracking.


              Finally had some time to code.

              Farseer, I got it working, thanks. I haven't bothered with the capital ships though. ;-)