How do quats work?

Hi, I am currently changing my FRotator based flight simulator to a Quat based system due to many problems I encountered .

I don’t have much experience in working with quats and I am getting the same errors with quats with the ones I encountered with FRotators.

I want to change the Yaw and Pitch of an actor at the same time without changing the Pitch. In FRotators, when a Yaw and Roll value is added to an actor, a value of pitch is also added to it automatically. I don’t want that to happen.

But the same thing is also happening with quats.

Here is my code


FRotator NewRot = FRotator(Rotation2D.X, Rotation2D.Y, Rotation2D.Y*0.5);
		FQuat NewQuat(NewRot);
		FRotator CurrentRot = GetActorRotation();
		FQuat CurrentQuat(CurrentRot);
		CurrentQuat = CurrentQuat * NewQuat;
		//CurrentQuat.Normalize();
		SetActorRotation(CurrentQuat.Rotator());

Also I want to know if this is the best method to do Quat based calculations?

Can you state your overall goal not just the code? why did you make a rotator out of a rotator2D with 0.5 for the y for z ?

Rama

Well X and Y are the player inputs stored in a 2d vector. Then every tick after some calculations, they are used to control the direction of the aircraft. Y is for the Yaw and X is for the pitch. I want when the aircraft rotates in the yaw directions, there is a bit of roll to it. Like it tilts a bit to the sides.(without changing the pitch)

How ever by this method, the pitch of the aircraft changes if both yaw and roll are changed at the same time.

You probably don’t want to modify your quat with Yaw, Pitch, and Roll at all. I’m pretty sure those are just abstractions to make quats easier to work with, but it introduces the exact same problem by having it still act like an FRotator.

You actually want to transform the quat itself. You store your ship’s orientaiton as a quat. And then when you have roll, pitch, yaw inputs, you transform the quat around the appropriate axes.

I’ve done this before with the GLM library, but the principle is the same.

So for roll, for example, this may work in Unreal:

MyQuat *= FQuat(FRotator(0.f, 0.f, RollAmount));

I’m creating a Quat from a rotator, and transforming my current ship’s quat by the quat that gives roll. And you do the same for pitch and yaw.

Actually I just double checked your code again and it looks like you’re basically doing just that. Maybe there are ways to optimize it a little by not converting between rotators and quats so much.

The thing is though, you say your yaw and roll change at the same time, and that seems about right. FRotator(Rotation2D.X, Rotation2D.Y, Rotation2D.Y*0.5);

The yaw and roll are both derived from the same value here. I’m not entirely sure why you’re trying to get roll from rotation2D.y*.5 but that may be why it’s happening. In fact, if the game is 3D, why have a Rotation2D of some sort at all.

I decreased the amount of conversion to something like this


		FRotator NewRot = FRotator(Rotation2D.X, Rotation2D.Y, 0);
		FQuat NewQuat(NewRot);
		FQuat CurrentQuat = GetActorQuat();
		CurrentQuat = CurrentQuat * NewQuat;
		SetActorRotation(CurrentQuat.Rotator());

I am using a FVector2D to store the player input of directions. X for top or bottom and Y for left and right.

What I am trying to achieve is, when the Yaw changes, i.e. the Y axis, there is a bit of roll to the spaceship. Like the space ship slightly twists in that direction. So


Rotation2D.Y*0.5

is used as a roll value. This works but there is a side effect.

When the Roll and Yaw changes, the pitch values goes down without any reason.

The same problem also happens in the flying template. If you move towards a side, your spaceship goes towards the ground.

I’ve posted a few options below, let us know what each ones does for you :slight_smile:

If you are using Character then you can replace GetRootComponent() with Mesh



//inside actor class you want to rotate

USceneComponent* RootComp = GetRootComponent();
if(!RootComp) 
{
  //this should probably !ever happen
   return;
}


//Get!
FVector CompWorldLocation = RootComp->GetComponentLocation(); 
FQuat CompQuatRotation = RootComp->GetComponentQuat();


//non interp case
CompQuatRotation = FRotator(Rotation2D.X, Rotation2D.Y, Rotation2D.Y*0.5).Quaternion();

//Set!
RootComp->SetWorldLocationAndRotation(CompWorldLocation,CompQuatRotation);


Just a note, I’ve not had any issues with converting a Quaternion to Rotator and back again, the main issue is actually where and how the new rotation is applied to the AActor.

So I am using USceneComponent::SetWorldLocationAndRotation as a test to see if that helps you.

Let me know how it goes!


**Interpolation**

Personally I recommend interpolating instead of just setting

This assumes you are running the code every tick



```

USceneComponent* RootComp = GetRootComponent();
if(!RootComp) 
{
  //this should probably not ever happen
   return;
}


//Get!
FVector CompWorldLocation = RootComp->GetComponentLocation(); 
FQuat CompQuatRotation = RootComp->GetComponentQuat();

//Interpolate here if running every tick, using between above rotation and your new rotation
CompQuatRotation = 
   **FQuat::Slerp(**
      CompQuatRotation,
      FRotator(Rotation2D.X, Rotation2D.Y, Rotation2D.Y*0.5).Quaternion(),
      0.03 //Interp Speed
);

//Set!
RootComp->SetWorldLocationAndRotation(CompWorldLocation,CompQuatRotation);


```




Alternative

You can also try this:



FTransform Trans = GetTransform();
Trans.SetRotation(FRotator(Rotation2D.X, Rotation2D.Y, Rotation2D.Y*0.5).Quaternion());
SetActorTransform(Trans);


Rama

Thanks for the reply Rama but sadly I am getting the same error with all method except SLerp. I have not tried SLerp yet it needs me to rewrite the whole system. But I think i will still get the same problem. Because the problem sadly happens when quats are multiplied. A pitch is introduced in it automatically.

Here is my code


		GEngine->AddOnScreenDebugMessage(-1, DeltaSeconds, FColor::Blue, "Raw Values =>  Pitch = " + FString::SanitizeFloat(Rotation2D.X) + " Roll = " + FString::SanitizeFloat(Rotation2D.Y) + " Yaw = " + FString::SanitizeFloat(Rotation2D.Y));
		FRotator NewRot = FRotator(Rotation2D.X, Rotation2D.Y, Rotation2D.Y);
		FQuat NewQuat(NewRot);
		GEngine->AddOnScreenDebugMessage(-1, DeltaSeconds, FColor::Cyan, "Change in FRotator =>  Pitch = "+ FString::SanitizeFloat(NewRot.Pitch) + " Roll = " + FString::SanitizeFloat(NewRot.Roll) + " Yaw = " + FString::SanitizeFloat(NewRot.Yaw));
		FQuat CurrentQuat = GetActorQuat();
		GEngine->AddOnScreenDebugMessage(-1, DeltaSeconds, FColor::Green, "Current FRotator =>  Pitch = " + FString::SanitizeFloat(CurrentQuat.Rotator().Pitch) + " Roll = " + FString::SanitizeFloat(CurrentQuat.Rotator().Roll) + " Yaw = " + FString::SanitizeFloat(CurrentQuat.Rotator().Yaw));
		CurrentQuat = CurrentQuat * NewQuat;
		GEngine->AddOnScreenDebugMessage(-1, DeltaSeconds, FColor::Yellow, "After Adding =>  Pitch = " + FString::SanitizeFloat(CurrentQuat.Rotator().Pitch) + " Roll = " + FString::SanitizeFloat(CurrentQuat.Rotator().Roll) + " Yaw = " + FString::SanitizeFloat(CurrentQuat.Rotator().Yaw));
		SetActorRotation(CurrentQuat.Rotator());

I have attached 2 images of how the values are changing I think after adding. So the problem is not with applying the rotators. I think it is with how quats are added. I need to ignore the FRotator Pitch component of the added quats, Guess that should solve the problem.

Revision of My Earlier Statement

You know, I just checked my code base for one of my paid projects were I developed replicating 6DOF movement of a space ship.

This is a multiplayer game where the ship can move in literally any angle and can roll and everything!

And I am using just this which I posted on the UE4 wiki:



FORCEINLINE void AddToActorRotation(const FRotator& Rot)
{
	FTransform Trans = GetTransform();
	Trans.ConcatenateRotation(Rot.Quaternion());
	SetActorRotation(Trans.Rotator());
}


The user can roll and do pitch and yaw adjustments all at the same time!

You might want to check your whole system and start over because I can now verify full 6DOF including rolling using just my above code, in a multiplayer context!

The ship unit in this game I am describing is a Character.

Good luck!

Rama

I’ve been working on my own version of a multiplayer 6DOF pawn. I have been working on the assumption that I should use APawn with UFloatingPawnMovement (and INetworkPredictionInterface for networking), but I really don’t want to get this deep into the low-level networking code for multiplayer movement (why reinvent the wheel when there are far superior solutions already coded). I just stumbled on this thread in my research and wondered if you could share a bit about how to turn ACharacter into a starship? You suggest replacing GetRootComponent with Mesh, but Mesh in Acharacter is a USkeletalMeshComponent… I feel like I’m missing a piece of information.

Kind of like magnets :smiley:

Don’t worry about “how” they work, you’d need at least a Bachelros in mathematics, just worry about WHAT they can do, how they are used, and when to use them.

1 Like