How can I get the angle relative to another?

I’ve changed the way velocities effect my character class because my world isn’t flat, it has walls and such that can be walked along. So I’ve changed the velocities to be oriented to the character capsules upvector because the capsule itself no longer obeys the z is always up rule that I see throughout the character Movement Component. Currently the uVector of my Character always points toward the world center 0,0,0.

Now I’m trying to rebuild the IsWalkable evaluation so that the character doesn’t hit a 90 degree floor angle limit. What I want to do is base this angle evaluation relative to the character capsule’s up vector. Throughout the Character Movement Component I see many evaluations that go off of FVector.Z - be it a velocity or a Hit.Normal. I need to root these out and swap them for an evaluation that is compared to the characters Current orientation. So the current angle limits that are established by default and still valid but only in respect to the capsules rotation.

So how does one have a vector and compare it’s angle to another vectors?

The Dot Product of two unit vectors is the cosine of the smallest angle between them. In UE4, that’s the | operator:

FVector A, B;
float angle = FMath::Acos(A | B); // In radians.

This angle will always be between 0 and PI.

Assuming both vectors are normalized, it returns the cosine of the angle, so 0 actually means 90 degrees. 1 means they are the same vector, and -1 means one is the opposite of the other.

Hmm, maybe I found the answer? I came across Dot product. Am I understanding this correctly, if I dot product two vectors it will return between -1 and 1 depending on if the angle is between -90 and 90. Meaning 0 would be paralell?

The dot product suggestion is a good one, just remember that you need to be working with normalized / unit vectors to use the cos(A|B) approach, as the others have said.

Another possibility here is to transform the vectors in question to your local space and keep the current code as-is (working in Z). This is probably a simpler way of changing the code, because you can rotate the normals/vectors and then use the current Z-is-up logic.

For example something like this code:

// Can't walk on this surface if it is too steep.
if (WorldToComponent(Hit.ImpactNormal).Z < TestWalkableZ)
{
	return false;
}

Where I assume I have a WorldToComponent function that rotates a vector from world space to component space (you can get this from the CapsuleComponent). Then you are working in the frame of reference of the component, where Z is up.

This sounds like a good way of doing it thanks. Currently I have already gone through and toyed around with the velocities - overall it’s working as using the characters up as the vector to apply the jump/ physFalling velocities. However I get some problems - maybe this new way will help me avoid those. The large issue that I run into is during <,> evaluations. I had decided to instead Get the vector.size() and evaluating lengths. I didn’t know I could convert the vector like that. I was wondering how I was ever going to isolate different axis’s if they never technically zero out (because my character is always not aligned and his vectors are always in a decimal state)

I tried looking in CharacterOwner->CapsuleComponent-> for a method “WorldToComponent”, I could only find ComponentToWorld. Is there somewhere else I can rip the method for this?

Assuming Capsule is your CapsuleComponent:

FTransform T = Capsule->GetComponentTransform();
FVector Normal = T.InverseTransformVectorNoScale(Hit.ImpactNormal);

if (Normal.Z < TestWalkableZ) ...

Zak when you say “Where I assume I have a WorldToComponent function that rotates a vector from world space to component space (you can get this from the CapsuleComponent)” - Does that mean there is a WorldToComponent method on the capsule component? If not do you know if there is one in UE4 somewhere - or do I need to make this?

I’ve been trying to use this to change the velocity of the DoJump() in the character movement Component. It’s setup originally to Velocity = JumpZVelocity; So if I’m able to rotate a vector using the method above I would imagine it looks some like: Velocity = WorldToComponent( FVector( 0,0, JumpZVelocity)); When I do this it doesn’t behave correctly.

Have you debugged or printed out the value you get in your function? How is it behaving incorrectly?

I’m using the DoJump Function as my debugging. If I use

Velocity += CharacterOwner->GetActorUpVector() * JumpZVelocity;

then the character will jump along it’s up Vector. (Which is the desired behavior). If I use

Velocity += hkvWorldToComponent(FVector( 0, 0, JumpZVelocity));

then the character sort of jumps forwardish, but if I rotate the camera it starts to change slightly. My function is this:

FVector UCharacterMovementComponent::hkvWorldToComponent(FVector VectorToRotate)
{
	FTransform compToWorldTrans = CharacterOwner->CapsuleComponent->GetComponentTransform();
	FVector rotatedVector = compToWorldTrans.InverseTransformVectorNoScale(VectorToRotate);
	return rotatedVector;
}

I was under the impression that Storing the Capsule Transform in a FTransform, then using the InverseTransformVectorNoScale(VectorToRotate) would take the vector in question (in this case it’s (0,0,JumpZVelocity) and rotate it to what my character capsule is. Making it a new vector that point’s the strong value of JumpZVelocity along my character capsules Z Axis. Is this not how it works? I’m new to this Vector Transformation stuff so I’m learning as I go. Do I need to normalize any values first, or am I missing another step?

Thanks!

So in this case, the final result should be in world space (Velocity), so you actually want ComponentToWorld here instead – take the component’s version of jumping up with JumpZVelocity, make it work in world space.

The previous problem was taking a world space hit normal, and then needing to compare it in component space logic.

Ok so I print the Velocity to the screen to check. It appears to be following a pattern I would expect. When I start the level, the character falls to the ground at X-0, Y-0. When I jump I see the velocity print to the screen and the Velocity.Z is close to 1000, while X and Y are close to zero which makes sense. Running forward down the X axis I can see as I jump the Velocity.X value increase/ decrease, along with the Z value. Running down the Y axis makes Y and Z increase/ decrease. This is how it should work, but still my character jumps forward. Now I’m really confused because it seems like the Transform Vector thing is working, but isn’t translating.

I just switched the InverseTranformVectorNoScale to TransformVectorNoScale and it worked. Does this make sense? Either way the character is now jumping along his Up Vector using the Vector Trasform Function - So thanks ! very cool

Just so we’re on the same page:

  1. what capsule orientation are you testing this with?
  2. what does DoJump() look like now?
  3. are you saying it behaves correctly here with the GetActorUpVector() version instead? Because if you use ComponentToWorld.TransformVectorNoScale with an up vector it should be the same.

I’m using CharacterOwner->CapsuleComponent->GetComponentTransform(). DoJump looks like this now:

Velocity += hkvWorldToComponent(FVector( 0, 0, JumpZVelocity));

It’s behaving without using GetActorUpVector. Now I’m using my my Capsules Tranform.TranformVectorNoScale( 0,0, JumpZVelocity) - which causes the character to jump along it’s UpVector which is the desired effect. Is this not technically correct?

I am needing it to compare logic like Normal.Z < 0.f Because my character’s upVector is always pointed at the world 0,0,0, his upvector is always a decimal of X,Y,Z so using that logic didn’t work for me. Throughout the CharacterMovementComponent everything is based on Z is up. So I’m trying to use my character capsules UpVector as the new “Z” axis, so all logic using world Z is up needs to be transformed to be evaluated relative to my characters capsule UpVector.

So far I’ve had small success with getting the character to jump along his own UpVector, and also to fall back along it (for the most part). But I was using the capsules UpVEctor for all of this. Now I’m trying to keep all the World Z is up logic already in the CharacterMovementComponent and just transform it for evaluations. My big task right now is to root out the Walkable Floor angle limit. Currently my character can only walk up the walls to about 90 degrees. I need the 90 degree limit to be relative to my character capsules UpVector and not the world Z.

Velocity += hkvWorldToComponent(FVector( 0, 0, JumpZVelocity));

This is incorrect, it should be a Component → World transformation in this case. That’s what I meant when I wrote:

So in this case, the final result should be in world space (Velocity), so you actually want ComponentToWorld here instead – take the component’s version of jumping up with JumpZVelocity, make it work in world space.

So you probably need 2 functions, one hkvComponentToWorld() which does:

CapsuleComponent.ComponentToWorld.TransformVectorNoScale(...)

and one hkvWorldToComponent() which does:

CapsuleComponent.ComponentToWorld.InverseTransformVectorNoScale(...)

The “Inverse” part is the key, that makes it do the opposite transformation (so component-to-world becomes world-to-component).

Converting all the code to arbitrary gravity direction is going to be complex, but a good learning experience :slight_smile: There is a task on the roadmap to eventually add official support for this, but it might be a couple months away.

Yeah, it is fun and challenging. The hard part is getting the max walkable angle. There seem to be many functions throughout the characterMovementComponent that deal the Hit normal. I’ve been trying to isolate the functions that pertain to prohibiting the character from walking past world 90 degrees and converting it to be relative to the capsule. So far no luck. Jumping/ falling are easy so far. At least I can see things happening and figure out what needs to change. The walking stuff, has so many functions throughout it. I was trying to comment out functions and turn aspects off so I could isolate them but they are so interwoven it’s difficult.

As a final thought on what you’ve shown me, let me see if I can understand this. The TransformVectorNoScale I can use for when I’m telling it it which direction (so largely used with the Velocities because my character moves spherically and so all three axis will be used - so convert component to world space and use because this is the world space direction it’s needing. Then the InverseTransformVectorNoScale I use when I’m trying to keep an evaluation’s Z is up logic by converting it to the component space of the Capsule - thereby keeping the logic of Z is up relative to the Component’s.

Thank BTW, Very helpful - I’ve been plugging away at the CharacterMovementComponent for days now.

Yep, sounds like you are on the right track!