From 87b2102733a37db1ef5045ae6522cd00ae36269e Mon Sep 17 00:00:00 2001 From: Louis Hobbs Date: Tue, 13 Dec 2022 04:11:56 +0000 Subject: [PATCH] Add Player Character --- Content/Player/PlayerCharacter.uasset | 3 + Source/Nakatomi/NakatomiCharacter.cpp | 43 ++++++ Source/Nakatomi/NakatomiCharacter.h | 41 ++++++ Source/Nakatomi/PlayerCharacter.cpp | 201 ++++++++++++++++++++++++++ Source/Nakatomi/PlayerCharacter.h | 97 +++++++++++++ 5 files changed, 385 insertions(+) create mode 100644 Content/Player/PlayerCharacter.uasset create mode 100644 Source/Nakatomi/NakatomiCharacter.cpp create mode 100644 Source/Nakatomi/NakatomiCharacter.h create mode 100644 Source/Nakatomi/PlayerCharacter.cpp create mode 100644 Source/Nakatomi/PlayerCharacter.h diff --git a/Content/Player/PlayerCharacter.uasset b/Content/Player/PlayerCharacter.uasset new file mode 100644 index 0000000..4d1b64b --- /dev/null +++ b/Content/Player/PlayerCharacter.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e7c46b9017224ae2a60fad721043f7786d5a1ad40253e0ef36cf323fea3a6d59 +size 31959 diff --git a/Source/Nakatomi/NakatomiCharacter.cpp b/Source/Nakatomi/NakatomiCharacter.cpp new file mode 100644 index 0000000..86f7f2d --- /dev/null +++ b/Source/Nakatomi/NakatomiCharacter.cpp @@ -0,0 +1,43 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "NakatomiCharacter.h" + +// Sets default values +ANakatomiCharacter::ANakatomiCharacter() +{ + // 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; + + HealthComponent = CreateDefaultSubobject(TEXT("Health Component")); +} + +// Called when the game starts or when spawned +void ANakatomiCharacter::BeginPlay() +{ + Super::BeginPlay(); + +} + +// Called every frame +void ANakatomiCharacter::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); + +} + +// Called to bind functionality to input +void ANakatomiCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) +{ + Super::SetupPlayerInputComponent(PlayerInputComponent); +} + +UHealthComponent* ANakatomiCharacter::GetHealthComponent() +{ + return HealthComponent; +} + +void ANakatomiCharacter::SetHealthComponent(UHealthComponent* component) +{ + HealthComponent = component; +} diff --git a/Source/Nakatomi/NakatomiCharacter.h b/Source/Nakatomi/NakatomiCharacter.h new file mode 100644 index 0000000..b6ad6c3 --- /dev/null +++ b/Source/Nakatomi/NakatomiCharacter.h @@ -0,0 +1,41 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Character.h" +#include "HealthComponent.h" +#include "NakatomiCharacter.generated.h" + +/** + * + */ +UCLASS() +class NAKATOMI_API ANakatomiCharacter : public ACharacter +{ + GENERATED_BODY() + +private: + + UPROPERTY(VisibleDefaultsOnly) + UHealthComponent* HealthComponent = nullptr; + +public: + // Sets default values for this character's properties + ANakatomiCharacter(); + +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; + + UHealthComponent* GetHealthComponent(); + + void SetHealthComponent(UHealthComponent* healthComponent); +}; diff --git a/Source/Nakatomi/PlayerCharacter.cpp b/Source/Nakatomi/PlayerCharacter.cpp new file mode 100644 index 0000000..a1a67b1 --- /dev/null +++ b/Source/Nakatomi/PlayerCharacter.cpp @@ -0,0 +1,201 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "PlayerCharacter.h" +// You can remove these, this is just to get intellisense to work +#include "InputTriggers.h" +#include "EnhancedInputComponent.h" +#include "EnhancedInputSubsystems.h" +#include "GameFramework/CharacterMovementComponent.h" +#include "InputMappingContext.h" + +#define COLLISION_WEAPON ECC_GameTraceChannel1 + +// Sets default values +APlayerCharacter::APlayerCharacter() +{ + // 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; + PrimaryActorTick.SetTickFunctionEnable(true); + PrimaryActorTick.bStartWithTickEnabled = true; + + //bUseControllerRotationPitch = true; + //bUseControllerRotationYaw = true; + //bUseControllerRotationRoll = false; + + // Setup the camera boom + CameraBoom = CreateDefaultSubobject(TEXT("CameraBoom")); + CameraBoom->SetupAttachment(RootComponent); + CameraBoom->bDoCollisionTest = true; + CameraBoom->bUsePawnControlRotation = true; + CameraBoom->TargetArmLength = 500.0f; + CameraBoom->bEnableCameraLag = true; + CameraBoom->CameraLagSpeed = 10.0f; + CameraBoom->SocketOffset = { 0.0f, 0.0f, 220.0f }; + + // Setup the camera component + CameraComponent = CreateDefaultSubobject(TEXT("CameraComponent")); + CameraComponent->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); + CameraComponent->bUsePawnControlRotation = false; + CameraComponent->SetRelativeRotation({ -10.0f,0.0f,0.0f }); + + // Setup the character movement + UCharacterMovementComponent* CharacterMovementComponent = GetCharacterMovement(); + CharacterMovementComponent->AirControl = 1.0f; + CharacterMovementComponent->bOrientRotationToMovement = true; +} + +// Called when the game starts or when spawned +void APlayerCharacter::BeginPlay() +{ + Super::BeginPlay(); + + DefaultMovementSpeed = GetCharacterMovement()->MaxWalkSpeed; +} + +// Called every frame +void APlayerCharacter::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); +} + +// Called to bind functionality to input +void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) +{ + Super::SetupPlayerInputComponent(PlayerInputComponent); + + if (APlayerController* PC = Cast(GetController())) + { + if (UEnhancedInputLocalPlayerSubsystem* InputSystem = ULocalPlayer::GetSubsystem(PC->GetLocalPlayer())) + { + if (!InputMappingContext.IsNull()) + { + InputSystem->AddMappingContext(InputMappingContext.LoadSynchronous(), MappingPriority); + } + } + } + + if (UEnhancedInputComponent* Input = Cast(PlayerInputComponent)) + { + if (MovementAction) + { + Input->BindAction(MovementAction, ETriggerEvent::Triggered, this, &APlayerCharacter::MovementCallback); + } + + if (LookAction) + { + Input->BindAction(LookAction, ETriggerEvent::Triggered, this, &APlayerCharacter::LookCallback); + } + + if (JumpAction) + { + Input->BindAction(JumpAction, ETriggerEvent::Triggered, this, &APlayerCharacter::JumpCallback); + } + + if (FireAction) + { + Input->BindAction(FireAction, ETriggerEvent::Started, this, &APlayerCharacter::FireCallback); + } + + if (QuitAction) + { + Input->BindAction(QuitAction, ETriggerEvent::Started, this, &APlayerCharacter::QuitCallback); + } + + if (SprintAction) + { + Input->BindAction(SprintAction, ETriggerEvent::Started, this, &APlayerCharacter::SetSprintingCallback); + Input->BindAction(SprintAction, ETriggerEvent::Completed, this, &APlayerCharacter::SetWalkingCallback); + } + } +} + +void APlayerCharacter::MovementCallback(const FInputActionInstance& Instance) +{ + FVector2D vec2 = Instance.GetValue().Get(); + + if (vec2.Size() != 0.0f) + { + AddMovementInput(GetActorForwardVector(), vec2.Y); + AddMovementInput(GetActorRightVector(), vec2.X); + } +} + +void APlayerCharacter::LookCallback(const FInputActionInstance& Instance) +{ + FVector2D vec2 = Instance.GetValue().Get(); + + if (vec2.Size() != 0.0f) + { + AddControllerYawInput(vec2.X * 45.0f * GetWorld()->GetDeltaSeconds()); + AddControllerPitchInput(vec2.Y * 45.0f * GetWorld()->GetDeltaSeconds()); + } +} + +void APlayerCharacter::JumpCallback(const FInputActionInstance& Instance) +{ + Jump(); +} + +void APlayerCharacter::FireCallback(const FInputActionInstance& Instance) +{ + // TODO: Add extra gun fire logic + + // Calculate hits from hitscan + TArray Hits = TArray(); + CalculateHits(&Hits); +} + +void APlayerCharacter::QuitCallback(const FInputActionInstance& Instance) +{ + FGenericPlatformMisc::RequestExit(true); +} + +void APlayerCharacter::SetSprintingCallback(const FInputActionInstance& Instance) +{ + GetCharacterMovement()->MaxWalkSpeed = DefaultMovementSpeed * SprintSpeedMultiplier; +} + +void APlayerCharacter::SetWalkingCallback(const FInputActionInstance& Instance) +{ + GetCharacterMovement()->MaxWalkSpeed = DefaultMovementSpeed; +} + +void APlayerCharacter::CalculateHits(TArray* hits) +{ + // Set up randomness + const int32 RandomSeed = FMath::Rand(); + FRandomStream WeaponRandomStream(RandomSeed); + const float Spread = 1.0f; // TODO: Replace with a more sensible value later + const float Range = 50000.f; // TODO: Replace with a more sensible value later + + // Calculate starting position and direction + FVector TraceStart; + FRotator PlayerRot; + GetController()->GetPlayerViewPoint(TraceStart, PlayerRot); + TraceStart = GetRootComponent()->GetComponentLocation(); + FVector AimDir = PlayerRot.Vector(); + AimDir.Z = 0.0; + TraceStart = TraceStart + AimDir * ((GetInstigator()->GetActorLocation() - TraceStart) | AimDir); + + // Calculate the hit results from the trace + TArray HitResults; + + /// Set up the collision query params, use the Weapon trace settings, Ignore the actor firing this trace + FCollisionQueryParams TraceParams(SCENE_QUERY_STAT(WeaponTrace), true, GetInstigator()); + TraceParams.bReturnPhysicalMaterial = true; + + /// Calculate the maximum distance the weapon can fire + FVector ShootDir = WeaponRandomStream.VRandCone(AimDir, FMath::DegreesToRadians(Spread), FMath::DegreesToRadians(Spread)); + FVector MaxHitLoc = TraceStart + (ShootDir * Range); + + GetWorld()->LineTraceMultiByChannel(HitResults, TraceStart, MaxHitLoc, COLLISION_WEAPON, TraceParams); + + for (FHitResult Result : HitResults) + { + hits->Add(Result); + DrawDebugLine(GetWorld(), TraceStart, Result.ImpactPoint, FColor::Blue, true, 500, 0U, 0); + + // TODO: Handle hits in a meaningful way + } +} diff --git a/Source/Nakatomi/PlayerCharacter.h b/Source/Nakatomi/PlayerCharacter.h new file mode 100644 index 0000000..907c14d --- /dev/null +++ b/Source/Nakatomi/PlayerCharacter.h @@ -0,0 +1,97 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "Camera/CameraComponent.h" +#include "CoreMinimal.h" +#include "GameFramework/SpringArmComponent.h" +#include "InputActionValue.h" +#include "EnhancedInputComponent.h" +#include "NakatomiCharacter.h" +#include "PlayerCharacter.generated.h" + +class UInputAction; +class UInputMappingContext; +//class UEnhancedInputComponent; + +/** + * + */ +UCLASS() +class NAKATOMI_API APlayerCharacter : public ANakatomiCharacter +{ + GENERATED_BODY() + +public: + // Sets default values for this character's properties + APlayerCharacter(); + +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; + + +private: + + UPROPERTY() + USpringArmComponent* CameraBoom = nullptr; + + UPROPERTY() + UCameraComponent* CameraComponent = nullptr; + + float DefaultMovementSpeed; + +protected: + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + float SprintSpeedMultiplier = 2.0f; + +public: + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + UInputAction* MovementAction; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + UInputAction* LookAction; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + UInputAction* JumpAction; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + UInputAction* FireAction; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + UInputAction* QuitAction; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + UInputAction* SprintAction; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + TSoftObjectPtr InputMappingContext; + + UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) + int MappingPriority = 0; + + void MovementCallback(const FInputActionInstance& Instance); + + void LookCallback(const FInputActionInstance& Instance); + + void JumpCallback(const FInputActionInstance& Instance); + + void FireCallback(const FInputActionInstance& Instance); + + void QuitCallback(const FInputActionInstance& Instance); + + void SetSprintingCallback(const FInputActionInstance& Instance); + + void SetWalkingCallback(const FInputActionInstance& Instance); + + void CalculateHits(TArray* hits); +};