Hello. I’ve been trying to fix a problem with my custom movement component for my pawn.
My problem is that when the player slides off a 90 degree ledge, the slide adjustment from ComputeSlideVector and TwoWallAdjust seems to freeze and jitter before launching the pawn up in the air.
Here’s a gif I made to demonstrate: (Let it play once to play smoothly)
Keep in mind I’m not pressing jump here. I only pressed the key to move left once to start to slide off the edge.
The blue arrow/line on top of the player is set to point the direction of the impact normal every time the capsule collision calls the hit event. I will talk more about this later in the post.
My movement component right now is currently only setup to move the player based on it’s velocity. Calculating the velocity such as gravity, running, jumping, friction is done in Blueprints temporarily by setting the velocity every tick.
I have checked my blueprint code thoroughly, but nothing in the code would make my player jump suddenly. You can see the variables on the top right says CurrentWalkRun and CurrentJumpVelocity is 0 and the only thing moving the player is the gravity velocity which is at -1500 (multiplied by DeltaTime of course). The same thing also happens with no friction applied, so that’s not the problem either,
My current C++ code for sliding the player looks like this:
void UMatoranMovementComponent::SlidePlayer(const FVector& Delta, float Time, const FVector& Normal, FHitResult& Hit)
{
const FVector OldHitNormal = Normal;
FVector SlideDelta = ComputeSlideVector(Delta, Time, OldHitNormal, Hit);
const FQuat Rotation = UpdatedComponent->GetComponentQuat();
//slide along first collision
SafeMoveUpdatedComponent(SlideDelta, Rotation, true, Hit);
//if we hit another surface, loop to adjust to each new surface (up to 5 in a single tick)
for (int NumOfSurfaces = 0; NumOfSurfaces < 5; NumOfSurfaces++)
{
if (Hit.IsValidBlockingHit())
{
TwoWallAdjust(SlideDelta, Hit, OldHitNormal);
SafeMoveUpdatedComponent(SlideDelta, Rotation, true, Hit);
}
else
{
NumOfSurfaces = 5;
}
}
};
SlidePlayer is called like this:
void UMatoranMovementComponent::PreformMove(const FVector2D& MovementInput, const FVector2D& CameraInput, bool IsJumpPressed, bool IsCrouchPressed, float DeltaTime)
{
FHitResult Hit(1.f);
const FVector OldVelocity = Velocity;
const FVector OldLocation = UpdatedComponent->GetComponentLocation();
const FQuat Rotation = UpdatedComponent->GetComponentQuat();
//Calculate player move
const FVector MoveDelta = ComputeMoveDelta(OldVelocity, DeltaTime, MovementInput, CameraInput, IsJumpPressed, IsCrouchPressed);
// Move actor
SafeMoveUpdatedComponent(MoveDelta, Rotation, true, Hit);
//If actor hits an obstacle, adjust move to slide along it
if (Hit.IsValidBlockingHit())
{
SlidePlayer(MoveDelta, 1.f - Hit.Time, Hit.Normal, Hit);
}
// Update velocity
const FVector NewLocation = UpdatedComponent->GetComponentLocation();
Velocity = ((NewLocation - OldLocation) / DeltaTime);
// Finalize
UpdateComponentVelocity();
};
PreformMove is called every tick.
ComputeMoveDelta is currently a modified function I ripped from ProjectileMovementComponent.cpp, which returns the Pawn’s current velocity (which I set in Blueprints) and adds acceleration, which right now is a function returning an empty FVector.
FVector UMatoranMovementComponent::ComputeMoveDelta(const FVector& InVelocity, float DeltaTime, const FVector2D& MovementInput,const FVector2D& CameraInput, bool IsJumpPressed, bool IsCrouchPressed) const
{
// Velocity Verlet integration (http://en.wikipedia.org/wiki/Verlet_integration#Velocity_Verlet)
// The addition of p0 is done outside this method, we are just computing the delta.
// p = p0 + v0*t + 1/2*a*t^2
// We use ComputeVelocity() here to infer the acceleration, to make it easier to apply custom velocities.
// p = p0 + v0*t + 1/2*((v1-v0)/t)*t^2
// p = p0 + v0*t + 1/2*((v1-v0))*t
const FVector NewVelocity = ComputeVelocity(InVelocity, DeltaTime, MovementInput, CameraInput, IsJumpPressed, IsCrouchPressed);
const FVector Delta = (InVelocity * DeltaTime) + (NewVelocity - InVelocity) * (0.5f * DeltaTime);
return Delta;
}
I tried enabling “Trace Complex On Move” in the Capsule Collision, which at first strangely seemed to fix it:
But after testing on other cases, it seems to freeze and unfreeze the player rapidly, slowing down the slide, but does not launch the player in the air.
The blue arrow now moves smoothly from one surface to the other instead of instantly snapping 90 degrees like it would sometimes do without complex collision, which I find odd since the triangular mesh and the collision models are 1:1 on the walls.
I also don’t want to use complex collision due to the way I set up stairs which is by having them act like a ramp collision wise, but look like stairs visually:
(Player Collision)
(Visibility Collision)
Using complex collision would make the player collide with the steps of the stairs anyway
Now, do you guys have any idea what the problem here could be? Could it be the fact that I set the velocity in Blueprints instead of in C++? Personally I think the main cause for these issues is ComputeSlideVector and TwoWallAdjust. The movement works perfectly fine in all cases except 90 degree ledges. I do not know if it’s because I’m using these functions wrong or of there is something wrong with the way they calculate the sliding angle to adjust the velocity. I looked inside them and saw that they use the crossproduct for 90 degree collisions and bellow, and use dot product for any cases above that. I do not know enough about trigonometry to be able to create my own functions to handle this. But I think Epic’s functions should work correctly here since I guess they would have fixed something like this if they had the same issues and that I’m just using the functions incorrectly or something.
Any help would be much appreciated!
Thanks for your time.
-Headstub