Why does my character never land?

I’m running into an issue where my character enters a falling state and never leaves it. I’ve done a lot of work on the character movement component, adjusting it so that the Z is up logic evaluates based on the character capsules orientation.

For the most part everything appears to be working. My character is able to completely navigate this inverted sphere, from top to bottom. I can jump, and fall back to the ground relative to my character as expected. However, as depicted in the attached image, when I walk onto a surface which is perfectly 90 degree relative to the world’s Z axis (what would be considered a wall if my world were flat), the character enters a state of falling and never lands. I’m able to slide the character around on this perfect 90 surface as if I were sliding on ice. This is because I have air control enabled. Once I cross over or off the surface onto a surface that is not at this perfect 90 I gain control again and everything acts normally once again.

I’ve been searching for the culprit and have had no luck. Obviously the surface below the character is evaluating as not walkable. I’m trying to dig into this and find out which area in the Character movement component might still be allowing this evaluation to happen.

I think I’ve narrowed it down to the ComputeFloorDist Method DownwardSweepResult logic not correctly following the logic I’m trying to use for everything else.

if ((DownwardSweepResult->TraceStart.Size() < (DownwardSweepResult->TraceEnd.Size())) &&
			hkvWorldToComp(DownwardSweepResult->TraceStart - DownwardSweepResult->TraceEnd).SizeSquared2D() <= KINDA_SMALL_NUMBER)

In my world, because it’s spherical I believe I can use Size() to establish a Z plane relative to my character’s up vector. It’s backwards though because it’s always a positive number. So the bigger the number the further down in Z my character is. That should cover the first part of that logic. The second part:

hkvWorldToComp(DownwardSweepResult->TraceStart - DownwardSweepResult->TraceEnd).SizeSquared2D() <= KINDA_SMALL_NUMBER

I’m trying to use my Vector tranform, but I believe this isn’t doing what I thought it was doing. I’ve been using it to compare the orientation of my character capsule to a Z is up logic, but in this case I’m guessing it doesn’t actually rotate the vector into a new location, only apply a rotation derived from my capsules.

So any ideas on how I would evaluate the start of the trace - end trace SizeSquared2D if my capsule is often not perfectly up and down?

Okay the last bit was wrong. Or at least I think. Still in the Compute Floor Dist Method I have come across this in the // Sweep Test section

bBlockingHit = GetWorld()->SweepSingle(Hit, CapsuleLocation, CapsuleLocation + hkvCompToWorld(FVector(0.f, 0.f, -TraceDist)), FQuat::Identity, CollisionChannel, CapsuleShape, QueryParams, ResponseParam);

bBlockingHit will evaluate as true right up until I step onto the perfect 90 degree wall. Now I’m completely confused. I’m fairly certain the hkvCompToWorld is correctly rotating that trace dist vector and then being added to the capsule location should produce a ray that shoots out south of the capsule. Being that my capsule is rotated so that the south part of the capsule is facing the floor when I step out onto the 90 degree wall this should produce a true as is does on any other surface.

I have not resolved this yet. Could someone help me figure out what happens during the landing process? What checks does the CharacterMovementController go through to determine if the character can continue walking or fall?

What is the last thing that is evaluated before a character successfully transitions into Walking from falling?

Attached is an image of the Debug text showing which methods are being called in this constant state of falling. Once the character steps onto this hexagon which is at a perfect 90 degrees in the world they enter the Compute slide vector/ sliding phase, they also appear to be falling. Because I have air control enabled I can dictate which direction I slide on this surface. If I were to fudge the controls just a little so that the character no longer slides and stay perfectly still, then only the Physfalling Method is being called

PhsyFalling → IsValidLandingSpot → ProcessLanded →

if (CharacterOwner && CharacterOwner->ShouldNotifyLanded(Hit))
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.0, FColor::Green, FString("true Land"));
		CharacterOwner->Landed(Hit);
	}

I’m baffled, This appears to say it’s okay to land. When I print this to the screen and I wiggle my character to stop sliding on this ground and be completely still True Land will print to the screen continually - but the character is still falling. Perhaps he’s in another state that I’m not aware of. Basically he is un controllable in this state with exception to Air control.

In AdjustFloorHeight()

if (!CurrentFloor.bBlockingHit)
	{
		return;
	}

bBlockingHit becomes false as I step onto the surface. Where would have last turned bBlockingHit False? I’m guessing it’s one of the set from sweeps…

My character never landed because in the CharacterMovementComponent, in the Compute Floor Dist section.

bBlockingHit = GetWorld()->SweepSingle(Hit, CapsuleLocation, CapsuleLocation + hkvCompToWorld(FVector(0.f, 0.f, -TraceDist)), FQuat::Identity, CollisionChannel, CapsuleShape, QueryParams, ResponseParam);

The Sweep Single code above uses a collision shape, by default it’s a CapsuleShape that is created just above. This capsule shape never rotated with my character as he walked around the sphere’s slope. So eventually at 90 degrees the collision capsule was completely on its side in comparison the the character capsule orientation this caused the character to “Fall” but never land. My current fix is to use a sphere for this shape. The sphere is as long as the length of the capsule shape was. Eventually this will need to be a capsule shape again but only oriented to the character capsule.

I know this is old, but in case anyone comes across similar issues like I did in 5.3.2 when using Set Gravity Direction, this appears to all be fixed in the (current at the time of posting this) 5.4 branch (and I assume will be fixed in the official 5.4 release).