Rotating player character to point

Hi there!

Recently I’ve been doing some testing regarding faking centrifugal gravity in UE4 and have run into a couple of hiccups. While it was relatively easy to get basic gravity multi-directional by overriding the world’s gravity to 0 and applying impulses to objects based on the position of a point, I’ve managed to hit a wall when it comes to the character.

Getting “gravity” applied to the character works perfectly fine so far, but getting them to orient to the point correctly is not. Here is the function I’m using to do some simple gravity simulation. This is also called from Blueprint each tick and is passed a vector (the location where objects repel from).

Here are some vines to show it in action Vine and Vine

I’ve omitted some of the gravity code and pasted the orientation code here. As you can see the orientation code also prevents me from looking around vertically, so any help with that would be greatly appreciated too.

FVector DirectionalVector;

FRotator CharacterDirection;

ASpaceGameCharacter* SpaceGameCharacter = Cast<ASpaceGameCharacter>(GetWorld()->GetFirstPlayerController()->GetPawn());

if (SpaceGameCharacter != NULL)
{

	DirectionalVector = PointLocation - SpaceGameCharacter->GetActorLocation();
	DirectionalVector = DirectionalVector / FMath::Sqrt(DirectionalVector.X * DirectionalVector.X + DirectionalVector.Y * DirectionalVector.Y + DirectionalVector.Z * DirectionalVector.Z);
	DirectionalVector.X = 0;

	CharacterDirection = DirectionalVector.Rotation();
	CharacterDirection.Pitch = CharacterDirection.Pitch - 90;
	CharacterDirection.Roll = SpaceGameCharacter->GetController()->GetControlRotation().Roll;
	CharacterDirection.Yaw = SpaceGameCharacter->GetController()->GetControlRotation().Yaw;

	SpaceGameCharacter->GetController()->SetControlRotation(CharacterDirection);
	SpaceGameCharacter->GetCapsuleComponent()->SetWorldRotation(CharacterDirection);
	
}

So far the character orients fairly well, but only if I’m facing the right direction as shown here. The orientation only seems to be correct if I’m facing a certain way. This has also been prototype’d out with Blueprint by traces and using the hit location normal to the same (albeit less smooth) results, it only acts as though I’m oriented correctly if I’m facing a certain way.

So basically any help, ideas, or pointing in the right direction for this problem would be greatly appreciated. In the long run I intend to try and have the character able to walk around and orient themselves correctly in a cylinder type environment

I’m not clear on exactly what you want to accomplish, but I think the central issue here is that in your game, there is perhaps no fixed ‘Up’ direction?

DirectionalVector.Rotation(), for example, will create a rotator making the assumption that Z is up. I think it’s probable that there are many instances in the ACharacter code which do the same, so you may not even be able to use ACharacter, or anyway it’s standard movement component, in this scenario.

Unrelated to your issue, but look at FVector::Normalize(), you can use that instead of dividing by the sqrt as you have done.

Thanks for the FVector::Normalize() advice, I put that in and it worked fine.

I’ll see if I can make what I’m after any clearer. I’m trying to orient the player character based on its position and the point’s position. Since I’ve already got multi-directional gravity pushing the player toward the ceiling/walls/floors/slopes etc I want to be able to orient them too so I can run around the cylinder.

Here’s another diagram that will hopefully help explain this. I’ll gladly give any further explanation if that’s necessary.

30943-orientation.png

Another example of an extremely similar issue is this Rotating a mesh in world space from the impact normal of a ray trace - Programming & Scripting - Epic Developer Community Forums

Yep, I figured that was what you were after, and I strongly suspect it’s going to be a lot more hassle than you were hoping. To be able to go fully around the circle, you obviously have to dispense with the idea of world space Z being ‘special’ in the sense that it is always the up direction. I believe ACharacter and UCharacterMovementComponent are designed with that assumption in mind. I don’t even think it would work properly using a tilted capsule component to be honest. So I think you’d at the least have to write your own movement component. In doing so, you might well have to use quaternions rather than rotators as well.

I thought as much. Though I had already made the assumption a lot of the character movement code would have to be done away with, which was the next step of getting the character to orient correctly to the angle, regardless of movement code. I mostly made this thread to get the correct angles before moving onto that stage.

Thanks for the help though.

So through a very large amount of google-fu, piecing together bits of information, copying and pasting, etc, I have managed to figure out a solution. A disclaimer I’ll add here though is that I’m not 100% certain how it works, but it works. Looking into quarternions and rotational stuff is something I’m going to do down the line to figure out how/why this works but for now I want to post a solution. Also unfortunately this doesn’t account for any other camera control, but simply will orient the player the way they want them.

Far too often I find the exact question I need answered, go unanswered, or see that someone has found a solution, but didn’t post it up themselves, so I’m going to give 2 blocks of code that anyone may find useful in the future.

The following will do a cast, and use the impact normals to orient the character accordingly, regardless of which way they are facing.

    FTransform PawnTransform = Character->GetTransform();
    FQuat PawnQuat = PawnTransform.GetRotation();
    
    FVector PawnUpVector = PawnQuat.RotateVector(FVector(0, 0, 1));
    FVector PawnRightVector = PawnQuat.RotateVector(FVector(0, 1, 0));
    FVector PawnForwardVector = PawnQuat.RotateVector(FVector(1, 0, 0));
    
    FVector PawnLocation = PawnTransform.GetLocation();
    FVector Start = PawnLocation + PawnUpVector;
    FVector End = PawnLocation - PawnUpVector * 100;
    
    FCollisionQueryParams Params(true);
    Params.AddIgnoredActor(Character);
    
    FHitResult OutHit;
    
    GetWorld()->LineTraceSingle(OutHit, Start, End, ECC_Pawn, Params);
    
    DrawDebugDirectionalArrow(GetWorld(), Start, End, 40, FColor::Green, true, 5);
    
    if (OutHit.bBlockingHit)
    {
    	FVector ImpactNormal = OutHit.ImpactNormal;
    	FVector ImpactPoint = OutHit.ImpactPoint;
    
    	DrawDebugDirectionalArrow(GetWorld(), Start, ImpactPoint, 40, FColor::Green, true, 5);
    
    	FVector c = FVector::CrossProduct(PawnUpVector, ImpactNormal);
    	float d = FVector::DotProduct(PawnUpVector, ImpactNormal);
    	float s = FMath::Sqrt((1.0 + d) * 2.0);
    	float rs = 1.0 / s;
    
    	FQuat q(c.X * rs, c.Y * rs, c.Z * rs, s * 0.5);
    
    	q = q * PawnQuat;
    	q.Normalize();
    
    	PawnTransform.SetRotation(q);
    
    	Character->GetCapsuleComponent()->SetWorldTransform(PawnTransform);
    	Character->GetController()->SetControlRotation(PawnTransform.Rotator());
    
    }

And the following will orient the character around a predefined vector point.

FVector PointLocation;
FVector DirectionalVector;

DirectionalVector = PointLocation - Character->GetActorLocation();
DirectionalVector.Normalize();
DirectionalVector.X = 0;

FTransform PawnTransform = Character->GetTransform();
FQuat PawnQuat = PawnTransform.GetRotation();

FVector PawnUpVector = PawnQuat.RotateVector(FVector(0, 0, 1));

FVector c = FVector::CrossProduct(PawnUpVector, DirectionalVector);
float d = FVector::DotProduct(PawnUpVector, DirectionalVector);
float s = FMath::Sqrt((1.0 + d) * 2.0);
float rs = 1.0 / s;

FQuat q(c.X * rs, c.Y * rs, c.Z * rs, s * 0.5);

q = q * PawnQuat;
q.Normalize();

PawnTransform.SetRotation(q);

Character->GetCapsuleComponent()->SetWorldTransform(PawnTransform);

Character->GetController()->SetControlRotation(PawnTransform.Rotator());

I’ll also provide a link to where I originally found the code to do this, someone else’s, so I can’t take any credit for it. You can find the thread here.

I very much so hope someone else finds this as helpful as I have.