Custom Character Pawn not bound by gravity orientation

Hey again.

I’ve been trying to make a player character from a pawn and a LOT of coding ripped from ACharacter, as I wanted to make a player that wasn’t so rigidly restrained to always be upright and could walk/run on walls and the ceiling if certain condition were met like say going through a loop-de-loop.

My problem is that My “RunnerPawn” can’t seem to find the floor at all, and even though it registers payer input it won’t move at all.

Just for clarification I have yet to implement any movement besides walking on the floor, such as Falling, or Swimming.

Everything else seems to be working except for the ComputeFloorDist() in my movement component, which again, is very much ripped and modified from Unreal’s ACharacter. I need it so that it looks for a floor in the direction of bottom of the collision capsule, rather than just beneath the pawn as if gravity was the main focus.

Here’s the code I’ve made:

void URunPawnMovementComponent::ComputeFloorDistance(const FVector& CapsuleLocation, float LineDistance, float SweepDistance, FFindFloorResult& OutFloorResult, float SweepRadius, const FHitResult* DownwardSweepResult) const
{
	float PawnRadius, PawnHalfHeight;

	RunPawnOwner->GetCapsuleComponent()->GetScaledCapsuleSize(PawnRadius, PawnHalfHeight);

	bool bSkipSweep = false;
	if (DownwardSweepResult != NULL && DownwardSweepResult->IsValidBlockingHit())
	{
		FRotator pawnRotation = RunPawnOwner->GetActorRotation();
		FVector SurfaceVector = FRotator(pawnRotation.Pitch + 90.f, pawnRotation.Yaw, pawnRotation.Roll).Vector();
		FVector TraceVector = FVector(DownwardSweepResult->TraceEnd.X - DownwardSweepResult->TraceStart.X, DownwardSweepResult->TraceEnd.Y - DownwardSweepResult->TraceStart.Y, DownwardSweepResult->TraceEnd.Z - DownwardSweepResult->TraceStart.Z);
		/*
		if (SurfaceVector.Equals(TraceVector.GetSafeNormal(), KINDA_SMALL_NUMBER))
		*/
		// Only if the supplied sweep was vertical and downward.
		if ((DownwardSweepResult->TraceStart.Z > DownwardSweepResult->TraceEnd.Z) &&
			(DownwardSweepResult->TraceStart - DownwardSweepResult->TraceEnd).SizeSquared2D() <= KINDA_SMALL_NUMBER)
		{

			GEngine->AddOnScreenDebugMessage(-1, 0.0f, FColor::Yellow, TEXT("LineCheck."));
			//Function should reject hits on the edge of the collison mesh
			if (IsWithinEdgeTolerance(DownwardSweepResult->Location, DownwardSweepResult->ImpactPoint, PawnRadius))
			{
				//Don't spam sweeps, even if this one doesn't work.
				bSkipSweep = true;

				const bool bIsWalkable = IsWalkable(*DownwardSweepResult);
				const float floorDist = (TraceVector.Size());
				OutFloorResult.SetFromSweep(*DownwardSweepResult, floorDist, bIsWalkable);

				if (bIsWalkable)
				{
					return;
				}
			}
		}
	}

	// Sweep Distance must be >= Line distance or the hit result doesn't work as a sweep result
	if (SweepDistance < LineDistance)
	{
		ensure(SweepDistance >= LineDistance);
		return;
	}

	bool bBlockingHit = false;
	FCollisionQueryParams QueryParams(SCENE_QUERY_STAT(ComputeFloorDistance), false, RunPawnOwner);
	FCollisionResponseParams ResponseParam;
	InitCollisionParams(QueryParams, ResponseParam);
	const ECollisionChannel CollisionChannel = UpdatedComponent->GetCollisionObjectType();

	// Sweep test
	if (!bSkipSweep && SweepDistance > 0.f && SweepRadius > 0.f)
	{
		GEngine->AddOnScreenDebugMessage(-1, 0.0f, FColor::Yellow, TEXT("Sweep Test"));

		// Use a shorter height to avoid sweeps giving weird results if we start on a surface.
		// This also allows us to adjust out of penetrations.
		const float ShrinkScale = 0.9f;
		const float ShrinkScaleOverlap = 0.1f;
		float ShrinkHeight = (PawnHalfHeight - PawnRadius) * (1.f - ShrinkScale);
		float TraceDist = SweepDistance + ShrinkHeight;
		FCollisionShape CapsuleShape = FCollisionShape::MakeCapsule(SweepRadius, PawnHalfHeight - ShrinkHeight);

		FHitResult Hit(1.f);
		bBlockingHit = FloorSweepTest(Hit, CapsuleLocation, CapsuleLocation + FVector(0.f, 0.f, -TraceDist), CollisionChannel, CapsuleShape, QueryParams, ResponseParam);

		if (bBlockingHit)
		{
			// Reject hits adjacent to us, we only care about hits on the bottom portion of our capsule.
			// Check 2D distance to impact point, reject if within a tolerance from radius.
			if (Hit.bStartPenetrating || !IsWithinEdgeTolerance(CapsuleLocation, Hit.ImpactPoint, CapsuleShape.Capsule.Radius))
			{
				// Use a capsule with a slightly smaller radius and shorter height to avoid the adjacent object.
				// Capsule must not be nearly zero or the trace will fall back to a line trace from the start point and have the wrong length.
				CapsuleShape.Capsule.Radius = FMath::Max(0.f, CapsuleShape.Capsule.Radius - SWEEP_EDGE_REJECT_DISTANCE - KINDA_SMALL_NUMBER);
				if (!CapsuleShape.IsNearlyZero())
				{
					ShrinkHeight = (PawnHalfHeight - PawnRadius) * (1.f - ShrinkScaleOverlap);
					TraceDist = SweepDistance + ShrinkHeight;
					CapsuleShape.Capsule.HalfHeight = FMath::Max(PawnHalfHeight - ShrinkHeight, CapsuleShape.Capsule.Radius);
					Hit.Reset(1.f, false);

					bBlockingHit = FloorSweepTest(Hit, CapsuleLocation, CapsuleLocation + FVector(0.f, 0.f, -TraceDist), CollisionChannel, CapsuleShape, QueryParams, ResponseParam);
				}
			}

			// Reduce hit distance by ShrinkHeight because we shrank the capsule for the trace.
			// We allow negative distances here, because this allows us to pull out of penetrations.
			const float MaxPenetrationAdjust = FMath::Max(MAX_FLOOR_DIST, PawnRadius);
			const float SweepResult = FMath::Max(-MaxPenetrationAdjust, Hit.Time * TraceDist - ShrinkHeight);

			OutFloorResult.SetFromSweep(Hit, SweepResult, false);
			if (Hit.IsValidBlockingHit() && IsWalkable(Hit))
			{
				if (SweepResult <= SweepDistance)
				{
					// Hit within test distance.
					OutFloorResult.bWalkableFloor = true;
					GEngine->AddOnScreenDebugMessage(-1, 0.0f, FColor::Yellow, TEXT("Found Floor."));
					return;
				}
			}
		}
	}

	// Since we require a longer sweep than line trace, we don't want to run the line trace if the sweep missed everything.
	// We do however want to try a line trace if the sweep was stuck in penetration.
	if (!OutFloorResult.bBlockingHit && !OutFloorResult.HitResult.bStartPenetrating)
	{
		OutFloorResult.FloorDist = SweepDistance;
		return;
	}

	// No hits were acceptable.
	OutFloorResult.bWalkableFloor = false;
	OutFloorResult.FloorDist = SweepDistance;
}

I must admit, this is the first player character I’ve ever designed, and I’m not sure if I’m in over my head. Is there a better way for me to do this?

Thank you for your time.