Hey @Metalclayman
here is an example in which you can run around with WASD and shoot with the arrow keys. The controls are inside the TopdownController.
UE51_CMovement.zip (67.0 KB)
TopdownGamemode.h:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/StaticMeshComponent.h"
#include "TopdownProjectile.generated.h"
class USphereComponent;
class UProjectileMovementComponent;
UCLASS(config = Game)
class CMOVEMENT_API ATopdownProjectile : public AActor
{
GENERATED_BODY()
/** Sphere collision component */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Projectile, meta = (AllowPrivateAccess = "true"))
USphereComponent* CollisionComp;
/** Projectile movement component */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = "true"))
UProjectileMovementComponent* ProjectileMovement;
public:
// Sets default values for this actor's properties
ATopdownProjectile();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Projectile")
UStaticMeshComponent* MeshComponent;
UFUNCTION()
void OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
/** Returns CollisionComp subobject **/
USphereComponent* GetCollisionComp() const { return CollisionComp; }
/** Returns ProjectileMovement subobject **/
UProjectileMovementComponent* GetProjectileMovement() const { return ProjectileMovement; }
};
TopdownGamemode.cpp:
#include "TopdownGamemode.h"
#include "TopdownController.h"
#include "TopdownCharacter.h"
#include "UObject/ConstructorHelpers.h"
ATopdownGamemode::ATopdownGamemode()
{
// use our custom PlayerController class
PlayerControllerClass = ATopdownController::StaticClass();
// set default pawn class to our Blueprinted character
static ConstructorHelpers::FClassFinder<APawn> PlayerPawnBPClass(TEXT("/Game/CMovement/Blueprints/BP_TopdownCharacter"));
if (PlayerPawnBPClass.Class != nullptr)
{
DefaultPawnClass = PlayerPawnBPClass.Class;
}
// set default controller to our Blueprinted controller
static ConstructorHelpers::FClassFinder<APlayerController> PlayerControllerBPClass(TEXT("/Game/CMovement/Blueprints/BP_TopdownController"));
if (PlayerControllerBPClass.Class != NULL)
{
PlayerControllerClass = PlayerControllerBPClass.Class;
}
}
TopdownController.h
#pragma once
#include "CoreMinimal.h"
#include "Templates/SubclassOf.h"
#include "GameFramework/PlayerController.h"
#include "InputActionValue.h"
#include "TopdownProjectile.h"
#include "TopdownController.generated.h"
/**
*
*/
UCLASS()
class CMOVEMENT_API ATopdownController : public APlayerController
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputMappingContext* DefaultMappingContext;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* MoveUpAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* MoveRightAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* ShootUpAction;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
class UInputAction* ShootRightAction;
UPROPERTY(EditDefaultsOnly, Category = Projectile)
TSubclassOf<class ATopdownProjectile> ProjectileClass;
protected:
virtual void SetupInputComponent() override;
virtual void BeginPlay();
void MoveUp(const FInputActionValue& ActionValue);
void MoveRight(const FInputActionValue& ActionValue);
void ShootUp(const FInputActionValue& ActionValue);
void ShootRight(const FInputActionValue& ActionValue);
};
TopdownController.cpp
#include "TopdownController.h"
#include "GameFramework/Pawn.h"
#include "Blueprint/AIBlueprintHelperLibrary.h"
#include "Engine/World.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
void ATopdownController::BeginPlay()
{
// Call the base class
Super::BeginPlay();
//Add Input Mapping Context
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
{
Subsystem->AddMappingContext(DefaultMappingContext, 0);
}
}
void ATopdownController::SetupInputComponent()
{
// set up gameplay key bindings
Super::SetupInputComponent();
// Set up action bindings
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(InputComponent))
{
// Setup keyboard input events
EnhancedInputComponent->BindAction(MoveUpAction, ETriggerEvent::Triggered, this, &ATopdownController::MoveUp);
EnhancedInputComponent->BindAction(MoveRightAction, ETriggerEvent::Triggered, this, &ATopdownController::MoveRight);
EnhancedInputComponent->BindAction(ShootUpAction, ETriggerEvent::Started, this, &ATopdownController::ShootUp);
EnhancedInputComponent->BindAction(ShootRightAction, ETriggerEvent::Started, this, &ATopdownController::ShootRight);
}
}
void ATopdownController::MoveUp(const FInputActionValue& ActionValue)
{
float Scale = ActionValue.Get<float>();
FVector WorldX = FVector(1,0,0);
APawn* ControlledPawn = GetPawn();
if (ControlledPawn != nullptr)
{
ControlledPawn->AddMovementInput(WorldX, Scale, false);
}
}
void ATopdownController::MoveRight(const FInputActionValue& ActionValue)
{
float Scale = ActionValue.Get<float>();
FVector WorldY = FVector(0, 1, 0);
APawn* ControlledPawn = GetPawn();
if (ControlledPawn != nullptr)
{
ControlledPawn->AddMovementInput(WorldY, Scale, false);
}
}
void ATopdownController::ShootUp(const FInputActionValue& ActionValue)
{
float Scale = ActionValue.Get<float>();
FVector WorldX = FVector(1*Scale,0, 0);
APawn* ControlledPawn = GetPawn();
if (ControlledPawn != nullptr)
{
if (ProjectileClass != nullptr)
{
UWorld* const World = GetWorld();
if (World != nullptr)
{
FVector SLocation = ControlledPawn->GetActorLocation();
FRotator SRotation = WorldX.Rotation();
//Set Spawn Collision Handling Override
FActorSpawnParameters ActorSpawnParams;
ActorSpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding;
ActorSpawnParams.Owner = ControlledPawn;
// Spawn the projectile at the muzzle
World->SpawnActor<ATopdownProjectile>(ProjectileClass, SLocation, SRotation, ActorSpawnParams);
}
}
}
}
void ATopdownController::ShootRight(const FInputActionValue& ActionValue)
{
float Scale = ActionValue.Get<float>();
FVector WorldY = FVector(0,1 * Scale, 0);
APawn* ControlledPawn = GetPawn();
if (ControlledPawn != nullptr)
{
if (ProjectileClass != nullptr)
{
UWorld* const World = GetWorld();
if (World != nullptr)
{
FVector SLocation = ControlledPawn->GetActorLocation();
FRotator SRotation = WorldY.Rotation();
//Set Spawn Collision Handling Override
FActorSpawnParameters ActorSpawnParams;
ActorSpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding;
ActorSpawnParams.Owner = ControlledPawn;
// Spawn the projectile at the muzzle
World->SpawnActor<ATopdownProjectile>(ProjectileClass, SLocation, SRotation, ActorSpawnParams);
}
}
}
}
TopdownCharacter.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "TopdownCharacter.generated.h"
UCLASS()
class CMOVEMENT_API ATopdownCharacter : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
ATopdownCharacter();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
};
TopdownCharacter.cpp
#include "TopdownCharacter.h"
#include "GameFramework/CharacterMovementComponent.h"
// Sets default values
ATopdownCharacter::ATopdownCharacter()
{
// 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;
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
GetCharacterMovement()->bOrientRotationToMovement = true;
GetCharacterMovement()->RotationRate = FRotator(0.f, 640.f, 0.f);
GetCharacterMovement()->bUseControllerDesiredRotation = false;
}
// Called when the game starts or when spawned
void ATopdownCharacter::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ATopdownCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
void ATopdownCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
}
TopdownProjectile.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/StaticMeshComponent.h"
#include "TopdownProjectile.generated.h"
class USphereComponent;
class UProjectileMovementComponent;
UCLASS(config = Game)
class CMOVEMENT_API ATopdownProjectile : public AActor
{
GENERATED_BODY()
/** Sphere collision component */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Projectile, meta = (AllowPrivateAccess = "true"))
USphereComponent* CollisionComp;
/** Projectile movement component */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Movement, meta = (AllowPrivateAccess = "true"))
UProjectileMovementComponent* ProjectileMovement;
public:
// Sets default values for this actor's properties
ATopdownProjectile();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Projectile")
UStaticMeshComponent* MeshComponent;
UFUNCTION()
void OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
/** Returns CollisionComp subobject **/
USphereComponent* GetCollisionComp() const { return CollisionComp; }
/** Returns ProjectileMovement subobject **/
UProjectileMovementComponent* GetProjectileMovement() const { return ProjectileMovement; }
};
TopdownProjectile.cpp
#include "TopdownProjectile.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Components/SphereComponent.h"
// Sets default values
ATopdownProjectile::ATopdownProjectile()
{
// Use a sphere as a simple collision representation
CollisionComp = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComp"));
CollisionComp->InitSphereRadius(5.0f);
CollisionComp->BodyInstance.SetCollisionProfileName("Projectile");
CollisionComp->OnComponentHit.AddDynamic(this, &ATopdownProjectile::OnHit); // set up a notification for when this component hits something blocking
// Players can't walk on it
CollisionComp->SetWalkableSlopeOverride(FWalkableSlopeOverride(WalkableSlope_Unwalkable, 0.f));
CollisionComp->CanCharacterStepUpOn = ECB_No;
// Set as root component
RootComponent = CollisionComp;
// Use a ProjectileMovementComponent to govern this projectile's movement
ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileComp"));
ProjectileMovement->UpdatedComponent = CollisionComp;
ProjectileMovement->InitialSpeed = 3000.f;
ProjectileMovement->MaxSpeed = 3000.f;
ProjectileMovement->bRotationFollowsVelocity = true;
ProjectileMovement->bShouldBounce = true;
MeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ProjectileMesh"));
MeshComponent->SetupAttachment(RootComponent);
static ConstructorHelpers::FObjectFinder<UStaticMesh> SphereMeshAsset(TEXT("StaticMesh'/Engine/BasicShapes/Sphere.Sphere'"));
if (SphereMeshAsset.Succeeded()) {
MeshComponent->SetStaticMesh(SphereMeshAsset.Object);
MeshComponent->SetCollisionProfileName(TEXT("OverlapAll"));
MeshComponent->SetWorldScale3D(FVector(0.25, 0.25, 0.25));
}
// Die after 3 seconds by default
InitialLifeSpan = 3.0f;
}
void ATopdownProjectile::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
// Only add impulse and destroy projectile if we hit a physics
if ((OtherActor != nullptr) && (OtherActor != this) && (OtherActor != GetOwner()) && (OtherComp != nullptr) && OtherComp->IsSimulatingPhysics())
{
OtherComp->AddImpulseAtLocation(GetVelocity() * 100.0f, GetActorLocation());
Destroy();
}
}