I have created a pawn and movement component with help from code i found on the internet. I have tried the code on an older verson on Inreal and it seems to work perfectly. In my current project i have done everything to update the code to the version i’m working but sometimes when jumping and strafing you gain a massive boost in that direction or just get pushed through the map because the current spped is infinite.
I have a feeling it’s the applyacceleration function in the movement componet but i ultimately have no clue so here is my code. It’s just so weird because it seems to be completely random if it works like it should or not.
#include "ExpesFPSPlayerMoveComponent.h"
#include "ExpesFPSPlayer.h"
#include "ExpesFPSPlayerCollisionComponent.h"
#include "Runtime/Core/Public/GenericPlatform/GenericPlatformMath.h"
#include "DrawDebugHelpers.h"
#include "GenericPlatform/GenericPlatformMath.h"
#include "Engine.h"
// Sets default values for this component's properties
UExpesFPSPlayerMoveComponent::UExpesFPSPlayerMoveComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
}
// Called when the game starts
void UExpesFPSPlayerMoveComponent::BeginPlay()
{
Super::BeginPlay();
Origin = Player->GetTransform().GetLocation();
}
// Called every frame
void UExpesFPSPlayerMoveComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Delta = DeltaTime;
if (Player)
{
// Grab the input from the player
WishMove = Player->ConsumeMovementInput();
if (MovementType == EMovementType::Spectate)
{
FlyMove();
Player->Collider->SetWorldLocation(Origin);
return;
}
QueueJump();
CollisionComponent->TraceGround();
if (CollisionComponent->CanGroundMove)
GroundMove();
else
AirMove();
CollisionComponent->TraceGround();
// Record the ground velocity and speed of the player
GroundVelocity.X = PlayerVelocity.X;
GroundVelocity.Y = PlayerVelocity.Y;
GroundSpeed = GroundVelocity.Size();
Player->Collider->SetWorldLocation(Origin);
// Logs
GEngine->AddOnScreenDebugMessage(20, 0.01f, FColor::Red, FString::Printf(TEXT("Position [X: %.6f, Y: %.6f, Z: %.6f]"), GetOrigin().X, GetOrigin().Y, GetOrigin().Z));
GEngine->AddOnScreenDebugMessage(2, 0.01f, FColor::Green, FString::Printf(TEXT("Is Grounded [%d]"), CollisionComponent->IsGrounded));
GEngine->AddOnScreenDebugMessage(0, 0.01f, FColor::Green, FString::Printf(TEXT("Desired Velocity [X: %.2f, Y: %.2f, Z: %.2f]"), PlayerVelocity.X, PlayerVelocity.Y, PlayerVelocity.Z));
GEngine->AddOnScreenDebugMessage(1, 0.01f, FColor::Green, FString::Printf(TEXT("Desired Linear Speed [%.2fups]"), GroundSpeed));
}
else
{
GEngine->AddOnScreenDebugMessage(0, 0.01f, FColor::Red, TEXT("NO OWNER"));
}
}
void UExpesFPSPlayerMoveComponent::ApplyFriction()
{
float speed;
float newSpeed;
float control;
float drop = 0.0f;
speed = PlayerVelocity.Size();
if (CollisionComponent->IsGrounded)
{
control = speed < GroundAcceleration ? GroundDampening : speed;
drop = control * Friction * Delta;
}
if (MovementType == EMovementType::Spectate)
drop += speed * SpectatorFriction * Delta;
newSpeed = speed - drop;
if (newSpeed < 0.f)
newSpeed = 0.f;
if (speed > 0.f)
newSpeed /= speed;
PlayerVelocity.X *= newSpeed;
PlayerVelocity.Y *= newSpeed;
}
void UExpesFPSPlayerMoveComponent::GroundMove()
{
if (CheckJump())
{
AirMove();
return;
}
ApplyFriction();
FVector wishDirection;
FVector wishvel = FVector::ZeroVector;
FVector TransformForward = Player->ForwardVector;
FVector TransformRight = Player->RightVector;
wishvel.X = TransformForward.X * WishMove.X + TransformRight.X * WishMove.Y;
wishvel.Y = TransformForward.Y * WishMove.X + TransformRight.Y * WishMove.Y;
wishvel.Z = TransformForward.Z * WishMove.X + TransformRight.Z * WishMove.Y;
wishDirection = wishvel;
float wishSpeed = wishDirection.Size();
wishDirection.Normalize();
wishSpeed *= GroundMaxSpeed;
ApplyAcceleration(wishDirection, wishSpeed, GroundAcceleration);
float vel = PlayerVelocity.Size();
ClipVelocity(PlayerVelocity, CollisionComponent->GroundTrace.ImpactNormal, PlayerVelocity, CollisionComponent->Overclip);
PlayerVelocity.Normalize();
PlayerVelocity *= vel;
// Don't do anything if standing still
if (PlayerVelocity.X == 0.f && PlayerVelocity.Y == 0.f)
return;
CollisionComponent->StepSlideMove(false);
}
void UExpesFPSPlayerMoveComponent::AirMove()
{
FVector wishDirection;
FVector currentVelocity;
float dynamicAcceleration;
//ApplyFriction();
// Set target direction for the character body to its inital state.
wishDirection.X = Player->ForwardVector.X * WishMove.X + Player->RightVector.X * WishMove.Y;
wishDirection.Y = Player->ForwardVector.Y * WishMove.X + Player->RightVector.Y * WishMove.Y;
wishDirection.Z = 0.f;
float wishSpeed = wishDirection.Size();
wishDirection.Normalize();
wishSpeed *= AirMaxSpeed;
// CPM: Aircontrol
float wishSpeed2 = wishSpeed;
if (FVector::DotProduct(PlayerVelocity, wishDirection) < 0)
dynamicAcceleration = AirStopAccelerate;
if (WishMove.X == 0.f && WishMove.Y != 0.0f)
{
if (wishSpeed > AirStrafeSpeed)
wishSpeed = AirStrafeSpeed;
dynamicAcceleration = AirStrafeAcceleration;
}
ApplyAcceleration(wishDirection, wishSpeed, dynamicAcceleration);
AirControl(wishDirection, wishSpeed);
// Apply gravity
PlayerVelocity.Z -= Gravity * Delta;
CollisionComponent->StepSlideMove(true);
}
void UExpesFPSPlayerMoveComponent::AirControl(FVector WishDirection, float WishSpeed)
{
float zspeed, speed, dot, k;
// Can't control movement if not moving forward or backward
if (WishMove.X != 0.f || WishSpeed == 0.f)
return;
zspeed = PlayerVelocity.Z;
PlayerVelocity.Z = 0;
speed = PlayerVelocity.Size();
PlayerVelocity.Normalize();
dot = FVector::DotProduct(PlayerVelocity, WishDirection);
k = 32.f;
k *= CPMAirControl * dot * dot * Delta;
// We can't change direction while slowing down
if (dot > 0)
{
PlayerVelocity.X = PlayerVelocity.X * speed + WishDirection.X * k;
PlayerVelocity.Y = PlayerVelocity.Y * speed + WishDirection.Y * k;
PlayerVelocity.Normalize();
}
PlayerVelocity.X *= speed;
PlayerVelocity.Y *= speed;
PlayerVelocity.Z = zspeed;
}
void UExpesFPSPlayerMoveComponent::ApplyAcceleration(FVector WishDirection, float WishSpeed, float DynamicAcceleration)
{
float AddSpeed;
float AccelerationSpeed;
float CurrentSpeed;
CurrentSpeed = FVector::DotProduct(WishDirection, PlayerVelocity);
AddSpeed = WishSpeed - CurrentSpeed;
if (AddSpeed <= 0.f)
return;
AccelerationSpeed = DynamicAcceleration * Delta * WishSpeed;
if (AccelerationSpeed > AddSpeed)
AccelerationSpeed = AddSpeed;
PlayerVelocity.X += AccelerationSpeed * WishDirection.X;
PlayerVelocity.Y += AccelerationSpeed * WishDirection.Y;
PlayerVelocity.Z += AccelerationSpeed * WishDirection.Z;
}
void UExpesFPSPlayerMoveComponent::FlyMove()
{
float speed, drop, friction, control, newspeed;
FVector wishvel;
float fmove, smove;
FVector wishdir;
float wishspeed;
// float scale;
// Set the view height
//pm->ps->viewheight = DEFAULT_VIEWHEIGHT;
// friction
speed = PlayerVelocity.Size();
if (speed < 1)
{
PlayerVelocity = FVector::ZeroVector;
}
else
{
drop = 0;
friction = Friction * 1.5; // extra friction
control = speed < SpectatorStopSpeed ? SpectatorStopSpeed : speed;
drop += control * friction * Delta;
// scale the velocity
newspeed = speed - drop;
if (newspeed < 0)
newspeed = 0;
newspeed /= speed;
PlayerVelocity *= newspeed;
}
// accelerate
//scale = PM_CmdScale(&pm->cmd);
fmove = WishMove.X;
smove = WishMove.Y;
wishvel.X = Player->ForwardVector.X * fmove + Player->RightVector.X * smove;
wishvel.Y = Player->ForwardVector.Y * fmove + Player->RightVector.Y * smove;
wishvel.Z += 0.f;
wishdir = wishvel;
wishspeed = wishdir.Size();
wishdir.Normalize();
wishspeed *= 1000.f;
ApplyAcceleration(wishdir, wishspeed, SpectatorAccelerate);
// move
Player->CollisionComponent->VectorMA(GetOrigin(), Delta, PlayerVelocity, Origin);
Origin.Z = 0.f;
}
bool UExpesFPSPlayerMoveComponent::CheckJump()
{
if (WishJump)
{
PlayerVelocity.Z = JumpForce;
Player->PlayJumpSound();
WishJump = false;
return true;
}
return false;
}
void UExpesFPSPlayerMoveComponent::QueueJump()
{
if (Player->JumpInput && !WishJump)
WishJump = true;
if (!Player->JumpInput)
WishJump = false;
}
float UExpesFPSPlayerMoveComponent::CmdScale()
{
int max;
float total;
float scale;
max = FGenericPlatformMath::Abs(WishMove.X);
if (FGenericPlatformMath::Abs(WishMove.Y) > max) {
max = FGenericPlatformMath::Abs(WishMove.Y);
}
/*if (abs(cmd->upmove) > max) {
max = abs(cmd->upmove);
}*/
if (!max) {
return 0;
}
total = sqrt(WishMove.X * WishMove.X + WishMove.Y * WishMove.Y);
scale = (float)GroundSpeed * max / (127.0 * total);
return scale;
}
void UExpesFPSPlayerMoveComponent::ClipVelocity(FVector In, FVector Normal, FVector& Out, float Overbounce)
{
float Backoff = FVector::DotProduct(In, Normal);
if (Backoff < 0.f)
Backoff *= Overbounce;
else
Backoff /= Overbounce;
Out.X = In.X - (Normal.X * Backoff);
Out.Y = In.Y - (Normal.Y * Backoff);
Out.Z = In.Z - (Normal.Z * Backoff);
}
void UExpesFPSPlayerMoveComponent::SetOrigin(FVector Position)
{
Origin = Position;
}
FVector UExpesFPSPlayerMoveComponent::GetOrigin()
{
return Origin;
}
Collision
#include "ExpesFPSPlayerCollisionComponent.h"
#include "ExpesFPSPlayerMoveComponent.h"
#include "ExpesFPSPlayer.h"
// Sets default values for this component's properties
UExpesFPSPlayerCollisionComponent::UExpesFPSPlayerCollisionComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
}
// Called when the game starts
void UExpesFPSPlayerCollisionComponent::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void UExpesFPSPlayerCollisionComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
Delta = DeltaTime;
}
void UExpesFPSPlayerCollisionComponent::TraceGround()
{
bool Hit = Trace(GroundTrace, MovementComponent->GetOrigin(), MovementComponent->GetOrigin() - FVector(0.f, 0.f, GroundTraceDistance));
// No hits then the player cannot ground move
if (GroundTrace.Time == 1.0f)
{
IsGrounded = false;
CanGroundMove = false;
return;
}
// Check if getting thrown off the ground
if (MovementComponent->PlayerVelocity.Z > 0 && FVector::DotProduct(MovementComponent->PlayerVelocity, GroundTrace.ImpactNormal) > 10)
{
IsGrounded = false;
CanGroundMove = false;
return;
}
// Slopes that are too steep will not be considered walkable
if (GroundTrace.ImpactNormal.Z < MinWalkNormal)
{
IsGrounded = true;
CanGroundMove = false;
return;
}
IsGrounded = true;
CanGroundMove = true;
// If the player just hit the ground
}
bool UExpesFPSPlayerCollisionComponent::SlideMove(bool Gravity)
{
int32 NumPlanes;
int32 NumBumps;
FVector Planes[5];
FVector EndVelocity;
NumPlanes = 0;
NumBumps = 4;
if (Gravity)
{
EndVelocity = MovementComponent->PlayerVelocity;
EndVelocity.Z -= MovementComponent->Gravity * Delta;
MovementComponent->PlayerVelocity.Z = (MovementComponent->PlayerVelocity.Z + EndVelocity.Z) * 0.5f;
if (IsGrounded)
{
MovementComponent->ClipVelocity(MovementComponent->PlayerVelocity, GroundTrace.ImpactNormal, MovementComponent->PlayerVelocity, Overclip);
}
}
// This is used later to resolve collisions
float TimeLeft = Delta;
// Never turn against the ground plane
if (IsGrounded)
{
NumPlanes = 1;
Planes[0] = GroundTrace.ImpactNormal;
}
// Never turn against the original velocity
Planes[NumPlanes] = MovementComponent->PlayerVelocity;
Planes[NumPlanes].Normalize();
NumPlanes++;
int32 BumpCount = 0;
for (; BumpCount < NumBumps; BumpCount++)
{
// Calculate the position we are trying to move to
FVector End = FVector::ZeroVector;
VectorMA(MovementComponent->GetOrigin(), TimeLeft, MovementComponent->PlayerVelocity, End);
// See if we can get there
FHitResult PartialTrace;
Trace(PartialTrace, MovementComponent->GetOrigin(), End);
// If there was no hit then move the player the entire way and stop
// this iteration, however if there was a hit then move the player
// along the trace until the hit.
MovementComponent->SetOrigin(TraceEnd(PartialTrace));
if (!PartialTrace.bBlockingHit)
break;
// TODO: Record the entity that was collided with
TimeLeft -= TimeLeft * PartialTrace.Time;
if (NumPlanes >= MaxClipPlanes)
{
MovementComponent->PlayerVelocity = FVector::ZeroVector;
return true;
}
// If this is the same plane we hit before, nudge the velocity
// along it, which fixes some epsilon issues with non-axial planes.
int32 i;
for (i = 0; i < NumPlanes; i++)
{
float DotProd = FVector::DotProduct(PartialTrace.ImpactNormal, Planes[i]);
if (DotProd > 0.99f)
{
MovementComponent->PlayerVelocity += PartialTrace.ImpactNormal;
break;
}
}
if (i < NumPlanes)
continue;
Planes[NumPlanes] = PartialTrace.ImpactNormal;
NumPlanes++;
// We modify the velocity of the player so it parallels all the clip planes
i = 0;
for (i = 0; i < NumPlanes; i++)
{
FVector ClipVelocity;
FVector EndClipVelocity;
// Find a plane that the player enters
float Into = FVector::DotProduct(MovementComponent->PlayerVelocity, Planes[i]);
// The move doesn't interact with the plane
if (Into >= 0.1f)
continue;
// TODO: Impact magnitude
// FROM: bg_slidemove.c
//
// if ( -into > pml.impactSpeed ) {
// pml.impactSpeed = -into;
// }
// Slide along the plane
MovementComponent->ClipVelocity(MovementComponent->PlayerVelocity, Planes[i], ClipVelocity, Overclip);
MovementComponent->ClipVelocity(EndVelocity, Planes[i], EndClipVelocity, Overclip);
// Check for a second plane that the new move enters
for (int32 j = 0; j < NumPlanes; j++)
{
if (j == i)
continue;
// The move doesn't interact with the plane
if (FVector::DotProduct(ClipVelocity, Planes[j]) >= 0.1f)
continue;
// Slide along the plane
MovementComponent->ClipVelocity(ClipVelocity, Planes[j], ClipVelocity, Overclip);
MovementComponent->ClipVelocity(EndClipVelocity, Planes[j], EndClipVelocity, Overclip);
// If it goes back into the first clip plane
if (FVector::DotProduct(ClipVelocity, Planes[j]) >= 0.1f)
continue;
// Slide the original velocity along the intersection
// of the two planes
FVector Dir;
float D;
Dir = FVector::CrossProduct(Planes[i], Planes[j]);
Dir.Normalize();
D = FVector::DotProduct(Dir, MovementComponent->PlayerVelocity);
ClipVelocity = Dir * D;
Dir = FVector::CrossProduct(Planes[i], Planes[j]);
Dir.Normalize();
D = FVector::DotProduct(Dir, EndVelocity);
EndClipVelocity = Dir * D;
// Check for third plane intersection
for (int32 k = 0; k < NumPlanes; k++)
{
if (k == i || k == j)
continue;
// The move doesn't interact with the plane
if (FVector::DotProduct(ClipVelocity, Planes[k]) >= 0.1f)
continue;
// Stop dead
MovementComponent->PlayerVelocity = FVector::ZeroVector;
//GEngine->AddOnScreenDebugMessage(20, 0.01f, FColor::Red, TEXT("Player is stuck"));
return true;
}
}
// Fixed all plane interactions, continue trying another
// move.
MovementComponent->PlayerVelocity = ClipVelocity;
EndVelocity = EndClipVelocity;
break;
}
}
if (Gravity)
MovementComponent->PlayerVelocity = EndVelocity;
return (BumpCount != 0);
}
void UExpesFPSPlayerCollisionComponent::StepSlideMove(bool Gravity)
{
FVector StartO, StartV;
FVector DownO, DownV;
FVector Down, Up;
FHitResult StepTrace;
float StepSize;
StartO = MovementComponent->GetOrigin();
StartV = MovementComponent->PlayerVelocity;
// Got where we wanted on the first go
if (SlideMove(Gravity) == 0)
return;
Down = StartO;
Down.Z -= MaxStepSize;
Trace(StepTrace, StartO, Down);
Up = FVector::ZeroVector;
Up.Z = 1;
// Never step up if the player still has up velocity
if (MovementComponent->PlayerVelocity.Z > 0.f &&
(StepTrace.Time == 1.0f || FVector::DotProduct(StepTrace.ImpactNormal, Up) < 0.7f))
return;
DownO = MovementComponent->GetOrigin();
DownV = MovementComponent->PlayerVelocity;
Up = StartO;
Up.Z += MaxStepSize;
Trace(StepTrace, StartO, Up);
if (StepTrace.bStartPenetrating)
return; // Can't step up
StepSize = TraceEnd(StepTrace).Z - StartO.Z;
MovementComponent->SetOrigin(TraceEnd(StepTrace));
MovementComponent->PlayerVelocity = StartV;
SlideMove(Gravity);
// Push down the final amount
Down = MovementComponent->GetOrigin();
Down.Z -= StepSize;
Trace(StepTrace, MovementComponent->GetOrigin(), Down);
MovementComponent->SetOrigin(TraceEnd(StepTrace));
if (StepTrace.Time < 1.0f)
{
MovementComponent->ClipVelocity(MovementComponent->PlayerVelocity, StepTrace.ImpactNormal, MovementComponent->PlayerVelocity, Overclip);
}
}
bool UExpesFPSPlayerCollisionComponent::Trace(FHitResult& Result, FVector Start, FVector End)
{
// Get the player's collider extents (note that in this case
// the collider is required to be a box).
FCollisionShape CollisionShape = Player->Collider->GetCollisionShape();
FCollisionQueryParams QueryParams(TEXT(""), false);
FCollisionObjectQueryParams ObjectQueryParams(ECC_WorldStatic);
bool Hit = GetWorld()->SweepSingleByObjectType(Result, Start, End, FQuat::Identity, ObjectQueryParams, CollisionShape, QueryParams);
return Hit;
}
FVector UExpesFPSPlayerCollisionComponent::TraceEnd(FHitResult& T)
{
// If the trace started in a collider then we calculate the escaped
// vector and return it.
if (T.bStartPenetrating)
{
FVector Loc = T.TraceStart + T.ImpactNormal * (T.PenetrationDepth) + (T.Normal * Underclip);
/*GEngine->AddOnScreenDebugMessage(50, 0.01f, FColor::Yellow, FString::Printf(TEXT("TraceEnd: Loc [X: %.6f, Y: %.6f, Z: %.6f]"), T.Normal.X, T.Normal.Y, T.Normal.Z));
GEngine->AddOnScreenDebugMessage(51, 0.01f, FColor::Yellow, FString::Printf(TEXT("TraceEnd: PenDepth [X: %.8f]"), T.PenetrationDepth));*/
return Loc;
}
if (!T.bBlockingHit)
return T.TraceEnd;
else
return T.TraceStart + T.Time * (T.TraceEnd - T.TraceStart) + (T.Normal * Underclip);
}
void UExpesFPSPlayerCollisionComponent::VectorMA(FVector Start, float Scale, FVector End, FVector& Out)
{
Out.X = Start.X + Scale * End.X;
Out.Y = Start.Y + Scale * End.Y;
Out.Z = Start.Z + Scale * End.Z;
}