Ok so basically, I’m working on the FPS tutorial that exists on UE4 programming tutorials. Besides all the hardships that I’ve gone through because of the tutorial being outdated, I noticed that the character movement is neat, but something bothered me a bit. In the tutorial, the forward movement of the character is based on getting the controller rotation, and then getting its X scaled axis.
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
and then the tutorial gives AddMovementInput the Direction and that’s basically it.
AddMovementInput(Direction, Value);
Now the thing that bothers me is that the movement speed changes by which angle I look at horizontally, so for example, if I look straight down or up, I won’t be able to move forward or backwards, and the more I look straight forward, the faster I move.
I’ve managed to come up with a pretty primitive solution, and that is by getting FPSMesh
's* direction and adding it to the current Direction, all that while multiplying Direction by 2.
AddMovementInput(Direction*2+NewDirection, Value);
*FPSMesh
is the Skeleton of the character
I first tried adding only the direction of the Skeleton, and that would’ve solved my problem, since when I look down or up my movement speed doesn’t change, but for some odd reason, when I try to move forward I move backwards and vice versa.(Note : Moving forward/Backwards using this method is also slower than usual.)
Now this almost solves my problem, but it just doesn’t seem right to me, and I was hoping that maybe one of you guys could offer me a more appropriate solution to this problem. Thanks.
Inserting the entirety of CPP file and H for anyone who wants to dig deeper:
FPSCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Camera/CameraComponent.h"
#include "FPSCharacter.generated.h"
UCLASS()
class FPSPROJECT_API AFPSCharacter : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AFPSCharacter();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaSeconds) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
//Adds Camera to edit
UPROPERTY(EditAnywhere)
UCameraComponent* FPSCameraComponent;
//Adds Body component
UPROPERTY(VisibleAnywhere, Category = "Mesh")
USkeletalMeshComponent* FPSMesh;
// Handles input for moving forward and backward.
UFUNCTION()
void MoveForward(float Value);
// Handles input for moving right and left.
UFUNCTION()
void MoveRight(float Value);
// Sets jump flag when key is pressed.
UFUNCTION()
void StartJump();
// Clears jump flag when key is released.
UFUNCTION()
void StopJump();
//Shoots out FPSProjectile.
UFUNCTION()
void Fire();
// Gun muzzle's offset from the camera location.
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gameplay)
FVector MuzzleOffset;
// Projectile class to spawn.
UPROPERTY(EditDefaultsOnly, Category = Projectile)
TSubclassOf<class AFPSProjectile> ProjectileClass;
};
FPSCharacter.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "FPSCharacter.h"
#include "FPSProjectile.h"
#include "Engine.h"
// Sets default values
AFPSCharacter::AFPSCharacter()
{
//CONFIGURATION OF RELATIVITY
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// Create a first person camera component.
FPSCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
// Attach the camera component to our capsule component.
FPSCameraComponent->SetupAttachment(GetCapsuleComponent());
// Position the camera slightly above the eyes.
FPSCameraComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 50.0f + BaseEyeHeight));
// Allow the pawn to control camera rotation.
FPSCameraComponent->bUsePawnControlRotation = true;
//Create a default mesh for our body
FPSMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FirstPersonMesh"));
//Attaching the camera to the body
FPSMesh->SetupAttachment(FPSCameraComponent);
}
// Called when the game starts or when spawned
void AFPSCharacter::BeginPlay()
{
Super::BeginPlay();
//Disabling some enviromental effects to preserve the illusion of having a singular mesh; NOTE::ENVIROMENTAL CHANGES CAN ONLY BE APPLIED IN THE BEGINNING OF THE SCENE, AND NOT IN THE CONFIGURATION.
FPSMesh->SetCastShadow(false);
// The owning player doesn't see the regular (third-person) body mesh.
GetMesh()->SetOwnerNoSee(true);
if (GEngine)
{
// Put up a debug message for five seconds. The -1 "Key" value (first argument) indicates that we will never need to update or refresh this message.
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("We are using FPSCharacter."));
}
}
// Called every frame
void AFPSCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
void AFPSCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// Set up "movement" bindings.
PlayerInputComponent->BindAxis("MoveForward", this, &AFPSCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AFPSCharacter::MoveRight);
// Set up "look" bindings.
PlayerInputComponent->BindAxis("Turn", this, &AFPSCharacter::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp", this, &AFPSCharacter::AddControllerPitchInput);
//Set up jumping
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &AFPSCharacter::StartJump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &AFPSCharacter::StopJump);
//Set up Firing
PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AFPSCharacter::Fire);
}
void AFPSCharacter::MoveForward(float Value)
{
// Find out which way is "forward" and record that the player wants to move that way.
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::X);
FVector NewDirection = FRotationMatrix(FPSMesh->GetComponentRotation()).GetScaledAxis(EAxis::X);
AddMovementInput(Direction*2+NewDirection, Value); //Works better than Direction/NewDirection alone.
//AddMovementInput(CameraDirection, Value); //Same result as Direction.
}
void AFPSCharacter::MoveRight(float Value)
{
// Find out which way is "right" and record that the player wants to move that way.
FVector Direction = FRotationMatrix(Controller->GetControlRotation()).GetScaledAxis(EAxis::Y);
AddMovementInput(Direction, Value);
}
void AFPSCharacter::StartJump()
{
bPressedJump = true;
}
void AFPSCharacter::StopJump()
{
bPressedJump = false;
}
void AFPSCharacter::Fire()
{
// Attempt to fire a projectile.
if (ProjectileClass)
{
// Get the camera transform.
FVector CameraLocation;
FRotator CameraRotation;
GetActorEyesViewPoint(CameraLocation, CameraRotation);
// Transform MuzzleOffset from camera space to world space.
FVector MuzzleLocation = CameraLocation + FTransform(CameraRotation).TransformVector(MuzzleOffset);
FRotator MuzzleRotation = CameraRotation;
// Skew the aim to be slightly upwards.
MuzzleRotation.Pitch += 10.0f;
UWorld* World = GetWorld();
if (World)
{
FActorSpawnParameters SpawnParams;
SpawnParams.Owner = this;
SpawnParams.Instigator = Instigator;
// Spawn the projectile at the muzzle.
AFPSProjectile* Projectile = World->SpawnActor<AFPSProjectile>(ProjectileClass, MuzzleLocation, MuzzleRotation, SpawnParams);
if (Projectile)
{
// Set the projectile's initial trajectory.
FVector LaunchDirection = MuzzleRotation.Vector();
Projectile->FireInDirection(LaunchDirection);
}
}
}
}