Tilting character to surface. Lost in vector math. help please

Firstly:

My character uses a gravity scale of 0 to simulate space physics.
I am using sprinting, jumping, and a grapple system to get myself around.

Okay, so I was trying to get my character model to stand perpendicularly from any surface it impacts in the game. I remembered the infamous butterfly from the BP example project and thought I could nick a bit of the code for that:

This works some of the time, but certainly not all of it. On slanted surfaces, I will get this, initially:

But as I start turning, the Character’s component still seems to rotate around the true Z axis and thus swings the head around instead of rotating around the new vertical axis for the character:

Finally, a very strange phenomenon. When I try to go back to a perfectly flat surface, my model flips completely upside-down and floats on its head:

I don’t have much of a background in vector math (or programming for that matter, but BP has certainly helped me a lot). Is there anything I can do to make this work?

1 Like

Hello world?

Hi,

Well that made me review my math a bit, here is the code I used in C++ to make this work :

void ATestActor::StickOnGround()
{
	//Grab a ref to the world here
	UWorld* world = UJotunnMain::Instance->GetWorld();

	FHitResult hitResultTrace;
	FCollisionQueryParams queryParams;
	FCollisionObjectQueryParams objQueryParams;

	queryParams.AddIgnoredActor(this);

	FVector under;

	FVector newUp;
	FVector newForward;
	FVector newRight;

	//Trace to get the floor normal under your actor
	if (world->LineTraceSingle(hitResultTrace, GetActorLocation() + FVector::UpVector*400.0f, GetActorLocation() - FVector::UpVector*2000.0f, queryParams, objQueryParams))
	{
		under = hitResultTrace.ImpactPoint;
		newUp = hitResultTrace.ImpactNormal;
	}

	//Some math to get the new Axis
	FVector currentRightVect = GetActorRightVector();
	newForward = FVector::CrossProduct(currentRightVect, newUp);
	newRight = FVector::CrossProduct(newUp, newForward);

	//Build the new transform!
	FTransform newTransform(newForward, newRight, newUp, under);
	SetActorTransform(newTransform);
}

I guess you could replicate that in BP and you will be good to go. Basically, the trick I used was to recreate your actor transform. Since you know the new Up vector (The hit Normal of the floor, you only need to find out the new Forward Vector, and Rigth Vector)

To get those, you can do a Cross Product of your new Up with the current Right to get the new forward.
Once you have the new forward, you can use it to get the new Right.

//Some math to get the new Axis
     FVector currentRightVect = GetActorRightVector();
     newForward = FVector::CrossProduct(currentRightVect, newUp);
     newRight = FVector::CrossProduct(newUp, newForward);

Then, simply construct the new Transform with that info :

 //Build the new transform!
     FTransform newTransform(newForward, newRight, newUp, under);
     SetActorTransform(newTransform);

In BP, you can use the Make Rotation From Axes and Make Transform node to do that. Cross Product node to do the cross products of course.

Important : The cross product order is important.

Hope that helps!

Thank you so much for your help, but I think I made a mistake in interpreting your explanation. Could you tell me what’s wrong or take a picture of the right way to do it in BP?

The cross product section seems to be correct, I dont see any problem in your creation of the Transform. What I suspect is your Hit event. Depending on what you use for that hit event, the hit Normal will not be that of what you have exactly under your actor (the robot in your case).

I’m not in front of a computer with access to UE4 but my guess would be to use the Event Tick node, and do a line trace from the character position (maybe a bit up, like I did in my C++ code) and line trace straight down to get the real normal of the floor at that place. Don’t forget to remove your character from that line trace or otherwise you will get wrong results!

I’ll do it in Blueprint tomorrow and post it here if you did not figure it out in the meantime.

Here, I have it working in BP:

This will work if your ground is an object type “World Static”, tell me if you have any problem.

Weird. Have you tried making it work with another actor? I did my test with a simple Static Mesh Actor that had a simple cube static mesh.

Unfortunately, when I start to test, my character for some reason spawns underneath the platform and is effectively attached to it by his skull. when I play from elsewhere, it definitely doesn’t work at all… he gets sent to odd places connected to the static object that are nowhere near the impact point. It’s quite bizarre. Thank you again for taking the time to show me!

No I haven’t. Maybe it’s an issue with capsule collider then?

It’s hard to tell, but I know the BP you see there works. I’m assuming there is something else running on your side that makes it not working.

Try applying this code to another Blueprint with a simple Cube on it and make it work. Then you can figure out what is the problem with your character.

Perhaps see if there is a way to use the root bone of your skeletal mesh, rather than the actor itself for the line trace. Just a guess. It’d probably actually be best to create an invisible “box” higher in the character hierarchy that has a locked rotation. That way the line trace is not referencing a component whose local rotation is subject to the new tilt. Which, to my mind, would provide more consistent results. I’ve not tried this yet, I’m writing from my phone.

I have a feeling that it would just make me do the same rotation as stated above: around the world z axis. Plus, if my mesh and not my collider is tilted and rotating, I imagine that would also result in my mesh clipping into all sorts of things.

I think this is what you wanted. I had a similar problem except my pawn would flip the heck out all over the place and sometimes default to 0,0,0. But Michael gave me a head start which prompted me to remember the Random Foliage BP from the Blueprint Examples map. This is my adapted variant for use with character pawns.I’d like to be able to use a ramp or “Curve” to smooth out the transitions, but this certainly worked for my purposes.

2 Likes

Also, your collision mesh should be unaffected. It will always follow its parent’s movement. In the above BP, I just used the default arrow component because it is rotation locked and will always provide a consistent point to raycast from.

Wow! This works so consistently! (except on extreme angles, but that’s okay, one step at a time) This is perfect! Thank you so so so much! I had just about given up hope. XD

I suppose you wouldn’t know how to make my character able to run on any surface? My gravity is off for this game since it takes place in space, so it’d be awesome if I could get that to work. No problem if not.

THANK YOU!

ehh… perhaps constrain to plane? I’ve not tried that yet, as my project is mostly earthbound… There should be an option for that in the Character pawn defaults.

The extreme angles thing I fixed at first by reducing the raycast length to its bare minimum. But then I found the “Ease” node. Simply type it in, it should be under Math/Interp. Speaking of interpolation, you can set up multiple raycasts from many different points and have them lerp between each other if you, perhaps, had a character that was quite large and you want a smooth transition over corners. Like if a 4 legged mech stood on a corner, you wouldn’t have to pick an angle, with 4 raycasts arranged in a square, you can simply lerp between those values to get a medium value for rotation.

Oh crap, I just realized you may be able to use more of Michael’s setup and use the raycast impact point to place your character on the nearest mesh. However, it could be quite complicated trying to find the real orientation. You’d need to convert the world rotation to local rotation. The problem is that you may run into more of this problem where your character repeatedly changes rotation.
Perhaps there is a way to create “gravity objects” in your levels that your character will automatically orient towards? If constrain to plane does not work.

Note: Constrain to plane should be editable as a command (IE: Press K to deactivate constrain to plane)

I want to be able to make my character walk on the roof, walls and floor, I got it working up to 90 degrees but the trouble is as soon it gets steeper than that the character flips back the right way up so the head points to the walls and roof rather than the feet… any ideas?

Are you childing this from the Character class? The Character class contains a character movement component which limits the angling to 90 degrees. I too am having this problem. Either you will need to reparent your character’s class or edit source code. Good luck.

what I am doing is putting my character into “flying” movement mode when climbing and setting gravity to 0. I then apply my own gravity in the direction of the wall / ceiling normal. For me this makes moving around on floor walls ceiling pretty easy but to get the character to rotate the right way is proving more tricky, I have been trying to rotate the whole character because that seems the logical thing to do.

My setup for rotating the character goes like this, I make a vector using forward and right movement input and rotate that vector by the camera angle (the camera’s rotation is in world space so doesn’t rotate with the character) to get the desired forward direction and then I lock that to the plain of the wall normal. this works perfectly for surfaces of less than 90 degrees but when it gets steeper it flips the wrong way up, still pointing in the direction it is meant to move but with its head on the roof instead of its feet.