Getting a relative Left or Right Offset for Tracer Impact (for checking hits on the immediate sides of an impact)

I am working on a ledge grab component and stuck on a specific little pain point.

  1. If I detect something about where a ledge would be, I do a sphere trace at the point of impact to see if there is room for the player capsule (crouched).
  2. I’ve found after some testing I’d like to give a little leeway and allow the player to grab if the initial sphere trace (directly in the middle of the impact) doesn’t find room for a player, and they are actually missing a viable location with room by one capsule radius width to the right or the left.
  3. To this end, if the main center trace fails to find room for the player, I try to send out one to the left of it, and if that fails, one to the right of it. Order on the latter two is currently arbitrary.
  4. I’m taking the normal from the original impact in step 2, multiplying by the capsule radius, or by negative capsule radius, for right and left respectively. This creates my Offset vectors that get added later.
  5. I’m seeing it kind of work, only perpendicular to how I want.


(Any of this only happens when the player is airborne, these are just persistent debug draws after landing)

The player is in the orange box (it is a first person project), and the center sphere trace is in the purple box. The second example (right side of image) is with the player turned ~90 degrees to their right, facing a different side of the same wall actor.

Basically the two extra traces I’m trying to do are coming out parallel to the player and perpendicular to the wall. I am trying to accomplish the reverse and am not totally sure how to manipulate the vectors correctly.

I still have verifying that the object actually has an up facing side on my todo (which would execute before this check for player room and stop if there is no up facing side), but I am trying to tackle this problem for now.

Hey @nascardad19781!

Can you show your code of the traces? I’m thinking this might be solved with examination of the nodes and just moving a wire here or there!

Get back to us! :slight_smile:

This is the function that creates the trace, and it takes Right, Left, or Center for the DirectionToCheck argument.

The function that calls this function starts with Center for that argument, and if it fails to find room for a player, tries left, and if that fails, tries right.

bool ULedgeGrabber::PlayerStandingRoomTracer(FHitResult& HitResult, EPlayerRoomCheckLocation DirectionToCheck) const
{
	
	float CapsuleQuarterHeight = CharCapsule->GetScaledCapsuleHalfHeight() / 2;

	FVector Start = GetPlayerStandingRoomTracerStart(DirectionToCheck);
	FVector End = FVector(
		Start.X,
		Start.Y,
		Start.Z + CapsuleQuarterHeight
	);

	return UKismetSystemLibrary::SphereTraceSingle(
		GetWorld(),
		Start,
		End,
		CharCapsule->GetScaledCapsuleRadius()	,
		UEngineTypes::ConvertToTraceType(ECC_WorldStatic),
		false,
		TArray<AActor*>(),
		bDoDebugTraces ? EDrawDebugTrace::ForDuration : EDrawDebugTrace::None,
		HitResult,
		true
	);
	
}

The line FVector Start = GetPlayerStandingRoomTracerStart(DirectionToCheck); is calling this function, which is where the offsets are created:

FVector ULedgeGrabber::GetPlayerStandingRoomTracerStart(EPlayerRoomCheckLocation DirectionToCheck) const
{
	FVector InvertedWallTraceImpactNormal = WallTraceImpactNormal * -1;
	float PlayerCapsuleRadius = CharCapsule->GetScaledCapsuleRadius();
	float LeftRightMulitplier;

	FVector StartOffset = FVector(0, 0, 0);

	if (DirectionToCheck != EPlayerRoomCheckLocation::Center)
	{
		float LeftRightOffset = 0;
		if (DirectionToCheck == EPlayerRoomCheckLocation::Left)
		{
			LeftRightMulitplier = -1;
		}
		else if (DirectionToCheck == EPlayerRoomCheckLocation::Right)
		{
			LeftRightMulitplier = 1;
		}
		StartOffset = FVector(
			WallTraceImpactNormal.X * (PlayerCapsuleRadius * LeftRightMulitplier),
			WallTraceImpactNormal.Y * (PlayerCapsuleRadius * LeftRightMulitplier),
			WallTraceImpactNormal.Z
		);
	}
	
    
    float CapsuleQuarterHeight = CharCapsule->GetScaledCapsuleHalfHeight() / 2;
    float CapsuleThreeEighthHeight = CapsuleQuarterHeight + (CapsuleQuarterHeight / 2);
    FVector Start = FVector(
        LedgeTraceImpactPoint.X,
        LedgeTraceImpactPoint.Y,
        LedgeTraceImpactPoint.Z + CapsuleThreeEighthHeight
    );

    return (Start + StartOffset);
}

edit:
There are some variables here that are getting defined elsewhere in the class, WallTraceImpactNormal and LedgeTraceImpactPoint. I can briefly go over those.


These sphere traces are unrelated to the ones checking for player room. I have those disabled here. Probably should have used a different color come to think of it. But anyway, these are the ones that actually look for ledges. They trigger directly in front of the player if the player is airborne and an actor is nearby. What you’re seeing here is a bunch of overlapping ones from the player falling.

The horizontal sphere trace’s hit result normal is saved as WallTraceImpactNormal for use later in the class. That’s the main thing I’m using to align with the wall in general.

If the horizontal sphere trace hits something, the vertical sphere trace descends from its upper point and looks for a ledge. The point of impact is saved as LedgeTraceImpactPoint for use later in the class.

Let me know if there’s more that would help. I don’t know what the etiquette on pasting your untidy 500 line classes are here but I’m trying to avoid it.

So question:

If you just comment out the code for the offsets, can you just run it with a single trace and it go the right direction? If the offsets are occurring based off of the original, we need to examine the original.

You’ll have to forgive me, I’m not fluent in C++, I use blueprint and python- but as much as I know about python I should still be able to help with your logic, just not your syntax, and this seems like a logic problem so we should be fine. :slight_smile:

Here is an example of the sphere trace looking for player room without the offsets (just the one in the center). Includes an example at the end of it actually finding room for the player and initiating a ledge grab for context on what I’m doing in general with this.

It’s where I’d like it, just struggling to understand how to put one on either side of it if it fails to find room.

I made a quick mockup in blueprints to help work through the logic:

So what you need to do is ADD (actor’s RIGHT vector * (SIZE_FLOAT) to your initial impact point, and SUBTRACT it for the left point.

I’m thinking here where you’re multiplying it it needs to be + instead!

(Normally I would do this with a function with input of direction but I usually start with events and eventually convert. I find it easier to lay the logic out flat.)

Edit: Since you’re using normals instead of impact point, left would be + and right would be - in relation to the hit but either way it’ll work the same.

If I understand correctly, you mean doing it like so:

		StartOffset = FVector(
			WallTraceImpactNormal.X + (PlayerCapsuleRadius * LeftRightMulitplier),
			WallTraceImpactNormal.Y + (PlayerCapsuleRadius * LeftRightMulitplier),
			WallTraceImpactNormal.Z
		);

That’s a little closer, now it’s diagonal from the impact. I have to admit, I don’t understand what’s going on at this point at all.

image

I’m following the player’s right vector example you laid out though. I’m going to try to get the player’s vector and use that instead. I initially thought I couldn’t go that route because I wanted to make sure if the player was facing slightly off center from the wall:

… that the checks would not then be off a little from the wall and still occur totally parallel with the wall impact. But in your example, it looks like the right/left is occurring totally parallel with the impact point even if the trace is a little off center.

I’ll report back after trying to use the player’s right vector.

I tried using the Player Right Vector:

		FVector CharacterRightVector = Character->GetActorRightVector();
		StartOffset = FVector(
			CharacterRightVector.X * (PlayerCapsuleRadius * LeftRightMulitplier),
			CharacterRightVector.Y * (PlayerCapsuleRadius * LeftRightMulitplier),
			WallTraceImpactNormal.Z
		);

It is closer to what I had in mind, but as I guessed it places the traces right or left relative to the player when for the system to really work it’s gotta be right or left relative to the wall. I have a grappling hook, and I’m doing a lot of verticality in my level design (basically I’m making a ripoff of Dishonored), so I anticipate the player will be jumping and falling at things with their camera in all kinds of unpredictable angles, and I really want to avoid frustrating situations for the player where it seems like your character could have gotten on a ledge and the system just doesn’t see it because your camera is off-center.

An example of a real use case here:


This is me landing where’s definitely room for me towards the middle of the window. But if you land a little off center from where there’s actually room for the player in this window, I’m trying to give the player a little leeway right or left and then I’ll shift them over. So I think for it to really work the trace needs to be relative to the wall and not the player.

I am thinking now that maybe I could rewrite this to add extra Wall sphere traces to the right or left relative to the player in the initial stage (outlined at the bottom of my second post), save their results, and use that as the basis for where to put the player room thing I’m doing if the center fails. That would probably work. But if at all possible I’d like to avoid having those extra traces and somehow shift this one to be parallel to the impact I already have.

I understand if that’s just not really feasible, and apologize for the double-post, not sure on the etiquette on that here.