İlk yürütme
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
|
||||
#include "Variant_Horror/HorrorCharacter.h"
|
||||
#include "Engine/World.h"
|
||||
#include "TimerManager.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
#include "Camera/CameraComponent.h"
|
||||
#include "Components/SpotLightComponent.h"
|
||||
#include "EnhancedInputComponent.h"
|
||||
#include "InputAction.h"
|
||||
|
||||
AHorrorCharacter::AHorrorCharacter()
|
||||
{
|
||||
// create the spotlight
|
||||
SpotLight = CreateDefaultSubobject<USpotLightComponent>(TEXT("SpotLight"));
|
||||
SpotLight->SetupAttachment(GetFirstPersonCameraComponent());
|
||||
|
||||
SpotLight->SetRelativeLocationAndRotation(FVector(30.0f, 17.5f, -5.0f), FRotator(-18.6f, -1.3f, 5.26f));
|
||||
SpotLight->Intensity = 0.5;
|
||||
SpotLight->SetIntensityUnits(ELightUnits::Lumens);
|
||||
SpotLight->AttenuationRadius = 1050.0f;
|
||||
SpotLight->InnerConeAngle = 18.7f;
|
||||
SpotLight->OuterConeAngle = 45.24f;
|
||||
}
|
||||
|
||||
void AHorrorCharacter::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// initialize sprint meter to max
|
||||
SprintMeter = SprintTime;
|
||||
|
||||
// Initialize the walk speed
|
||||
GetCharacterMovement()->MaxWalkSpeed = WalkSpeed;
|
||||
|
||||
// start the sprint tick timer
|
||||
GetWorld()->GetTimerManager().SetTimer(SprintTimer, this, &AHorrorCharacter::SprintFixedTick, SprintFixedTickTime, true);
|
||||
}
|
||||
|
||||
void AHorrorCharacter::EndPlay(EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
|
||||
// clear the sprint timer
|
||||
GetWorld()->GetTimerManager().ClearTimer(SprintTimer);
|
||||
}
|
||||
|
||||
void AHorrorCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
||||
{
|
||||
Super::SetupPlayerInputComponent(PlayerInputComponent);
|
||||
|
||||
{
|
||||
// Set up action bindings
|
||||
if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent))
|
||||
{
|
||||
// Sprinting
|
||||
EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Started, this, &AHorrorCharacter::DoStartSprint);
|
||||
EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Completed, this, &AHorrorCharacter::DoEndSprint);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AHorrorCharacter::DoStartSprint()
|
||||
{
|
||||
// set the sprinting flag
|
||||
bSprinting = true;
|
||||
|
||||
// are we out of recovery mode?
|
||||
if (!bRecovering)
|
||||
{
|
||||
// set the sprint walk speed
|
||||
GetCharacterMovement()->MaxWalkSpeed = SprintSpeed;
|
||||
|
||||
// call the sprint state changed delegate
|
||||
OnSprintStateChanged.Broadcast(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AHorrorCharacter::DoEndSprint()
|
||||
{
|
||||
// set the sprinting flag
|
||||
bSprinting = false;
|
||||
|
||||
// are we out of recovery mode?
|
||||
if (!bRecovering)
|
||||
{
|
||||
// set the default walk speed
|
||||
GetCharacterMovement()->MaxWalkSpeed = WalkSpeed;
|
||||
|
||||
// call the sprint state changed delegate
|
||||
OnSprintStateChanged.Broadcast(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AHorrorCharacter::SprintFixedTick()
|
||||
{
|
||||
// are we out of recovery, still have stamina and are moving faster than our walk speed?
|
||||
if (bSprinting && !bRecovering && GetVelocity().Length() > WalkSpeed)
|
||||
{
|
||||
|
||||
// do we still have meter to burn?
|
||||
if (SprintMeter > 0.0f)
|
||||
{
|
||||
// update the sprint meter
|
||||
SprintMeter = FMath::Max(SprintMeter - SprintFixedTickTime, 0.0f);
|
||||
|
||||
// have we run out of stamina?
|
||||
if (SprintMeter <= 0.0f)
|
||||
{
|
||||
// raise the recovering flag
|
||||
bRecovering = true;
|
||||
|
||||
// set the recovering walk speed
|
||||
GetCharacterMovement()->MaxWalkSpeed = RecoveringWalkSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// recover stamina
|
||||
SprintMeter = FMath::Min(SprintMeter + SprintFixedTickTime, SprintTime);
|
||||
|
||||
if (SprintMeter >= SprintTime)
|
||||
{
|
||||
// lower the recovering flag
|
||||
bRecovering = false;
|
||||
|
||||
// set the walk or sprint speed depending on whether the sprint button is down
|
||||
GetCharacterMovement()->MaxWalkSpeed = bSprinting ? SprintSpeed : WalkSpeed;
|
||||
|
||||
// update the sprint state depending on whether the button is down or not
|
||||
OnSprintStateChanged.Broadcast(bSprinting);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// broadcast the sprint meter updated delegate
|
||||
OnSprintMeterUpdated.Broadcast(SprintMeter / SprintTime);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "firstCharacter.h"
|
||||
#include "HorrorCharacter.generated.h"
|
||||
|
||||
class USpotLightComponent;
|
||||
class UInputAction;
|
||||
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FUpdateSprintMeterDelegate, float, Percentage);
|
||||
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FSprintStateChangedDelegate, bool, bSprinting);
|
||||
|
||||
/**
|
||||
* Simple first person horror character
|
||||
* Provides stamina-based sprinting
|
||||
*/
|
||||
UCLASS(abstract)
|
||||
class FIRST_API AHorrorCharacter : public AfirstCharacter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** Player light source */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
|
||||
USpotLightComponent* SpotLight;
|
||||
|
||||
protected:
|
||||
|
||||
/** Fire weapon input action */
|
||||
UPROPERTY(EditAnywhere, Category ="Input")
|
||||
UInputAction* SprintAction;
|
||||
|
||||
/** If true, we're sprinting */
|
||||
bool bSprinting = false;
|
||||
|
||||
/** If true, we're recovering stamina */
|
||||
bool bRecovering = false;
|
||||
|
||||
/** Default walk speed when not sprinting or recovering */
|
||||
UPROPERTY(EditAnywhere, Category="Walk")
|
||||
float WalkSpeed = 250.0f;
|
||||
|
||||
/** Time interval for sprinting stamina ticks */
|
||||
UPROPERTY(EditAnywhere, Category="Sprint", meta = (ClampMin = 0, ClampMax = 1, Units = "s"))
|
||||
float SprintFixedTickTime = 0.03333f;
|
||||
|
||||
/** Sprint stamina amount. Maxes at SprintTime */
|
||||
float SprintMeter = 0.0f;
|
||||
|
||||
/** How long we can sprint for, in seconds */
|
||||
UPROPERTY(EditAnywhere, Category="Sprint", meta = (ClampMin = 0, ClampMax = 10, Units = "s"))
|
||||
float SprintTime = 3.0f;
|
||||
|
||||
/** Walk speed while sprinting */
|
||||
UPROPERTY(EditAnywhere, Category="Sprint", meta = (ClampMin = 0, ClampMax = 10, Units = "cm/s"))
|
||||
float SprintSpeed = 600.0f;
|
||||
|
||||
/** Walk speed while recovering stamina */
|
||||
UPROPERTY(EditAnywhere, Category="Recovery", meta = (ClampMin = 0, ClampMax = 10, Units = "cm/s"))
|
||||
float RecoveringWalkSpeed = 150.0f;
|
||||
|
||||
/** Time it takes for the sprint meter to recover */
|
||||
UPROPERTY(EditAnywhere, Category="Recovery", meta = (ClampMin = 0, ClampMax = 10, Units = "s"))
|
||||
float RecoveryTime = 0.0f;
|
||||
|
||||
/** Sprint tick timer */
|
||||
FTimerHandle SprintTimer;
|
||||
|
||||
public:
|
||||
|
||||
/** Delegate called when the sprint meter should be updated */
|
||||
FUpdateSprintMeterDelegate OnSprintMeterUpdated;
|
||||
|
||||
/** Delegate called when we start and stop sprinting */
|
||||
FSprintStateChangedDelegate OnSprintStateChanged;
|
||||
|
||||
protected:
|
||||
|
||||
/** Constructor */
|
||||
AHorrorCharacter();
|
||||
|
||||
/** Gameplay initialization */
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
/** Gameplay cleanup */
|
||||
virtual void EndPlay(EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
/** Set up input action bindings */
|
||||
virtual void SetupPlayerInputComponent(UInputComponent* InputComponent) override;
|
||||
|
||||
protected:
|
||||
|
||||
/** Starts sprinting behavior */
|
||||
UFUNCTION(BlueprintCallable, Category = "Input")
|
||||
void DoStartSprint();
|
||||
|
||||
/** Stops sprinting behavior */
|
||||
UFUNCTION(BlueprintCallable, Category="Input")
|
||||
void DoEndSprint();
|
||||
|
||||
/** Called while sprinting at a fixed time interval */
|
||||
void SprintFixedTick();
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
|
||||
#include "Variant_Horror/HorrorGameMode.h"
|
||||
|
||||
AHorrorGameMode::AHorrorGameMode()
|
||||
{
|
||||
// stub
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/GameModeBase.h"
|
||||
#include "HorrorGameMode.generated.h"
|
||||
|
||||
/**
|
||||
* Simple GameMode for a first person horror game
|
||||
*/
|
||||
UCLASS(abstract)
|
||||
class FIRST_API AHorrorGameMode : public AGameModeBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
/** Constructor */
|
||||
AHorrorGameMode();
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
|
||||
#include "Variant_Horror/HorrorPlayerController.h"
|
||||
#include "EnhancedInputSubsystems.h"
|
||||
#include "Engine/LocalPlayer.h"
|
||||
#include "InputMappingContext.h"
|
||||
#include "firstCameraManager.h"
|
||||
#include "HorrorCharacter.h"
|
||||
#include "HorrorUI.h"
|
||||
#include "first.h"
|
||||
#include "Widgets/Input/SVirtualJoystick.h"
|
||||
|
||||
AHorrorPlayerController::AHorrorPlayerController()
|
||||
{
|
||||
// set the player camera manager class
|
||||
PlayerCameraManagerClass = AfirstCameraManager::StaticClass();
|
||||
}
|
||||
|
||||
void AHorrorPlayerController::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// only spawn touch controls on local player controllers
|
||||
if (SVirtualJoystick::ShouldDisplayTouchInterface() && IsLocalPlayerController())
|
||||
{
|
||||
// spawn the mobile controls widget
|
||||
MobileControlsWidget = CreateWidget<UUserWidget>(this, MobileControlsWidgetClass);
|
||||
|
||||
if (MobileControlsWidget)
|
||||
{
|
||||
// add the controls to the player screen
|
||||
MobileControlsWidget->AddToPlayerScreen(0);
|
||||
|
||||
} else {
|
||||
|
||||
UE_LOG(Logfirst, Error, TEXT("Could not spawn mobile controls widget."));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void AHorrorPlayerController::OnPossess(APawn* aPawn)
|
||||
{
|
||||
Super::OnPossess(aPawn);
|
||||
|
||||
// only spawn UI on local player controllers
|
||||
if (IsLocalPlayerController())
|
||||
{
|
||||
// set up the UI for the character
|
||||
if (AHorrorCharacter* HorrorCharacter = Cast<AHorrorCharacter>(aPawn))
|
||||
{
|
||||
// create the UI
|
||||
if (!HorrorUI)
|
||||
{
|
||||
HorrorUI = CreateWidget<UHorrorUI>(this, HorrorUIClass);
|
||||
HorrorUI->AddToViewport(0);
|
||||
}
|
||||
|
||||
HorrorUI->SetupCharacter(HorrorCharacter);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AHorrorPlayerController::SetupInputComponent()
|
||||
{
|
||||
Super::SetupInputComponent();
|
||||
|
||||
// only add IMCs for local player controllers
|
||||
if (IsLocalPlayerController())
|
||||
{
|
||||
// Add Input Mapping Contexts
|
||||
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()))
|
||||
{
|
||||
for (UInputMappingContext* CurrentContext : DefaultMappingContexts)
|
||||
{
|
||||
Subsystem->AddMappingContext(CurrentContext, 0);
|
||||
}
|
||||
|
||||
// only add these IMCs if we're not using mobile touch input
|
||||
if (!SVirtualJoystick::ShouldDisplayTouchInterface())
|
||||
{
|
||||
for (UInputMappingContext* CurrentContext : MobileExcludedMappingContexts)
|
||||
{
|
||||
Subsystem->AddMappingContext(CurrentContext, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "HorrorPlayerController.generated.h"
|
||||
|
||||
class UInputMappingContext;
|
||||
class UHorrorUI;
|
||||
|
||||
/**
|
||||
* Player Controller for a first person horror game
|
||||
* Manages input mappings
|
||||
* Manages UI
|
||||
*/
|
||||
UCLASS(abstract)
|
||||
class FIRST_API AHorrorPlayerController : public APlayerController
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
|
||||
/** Type of UI widget to spawn */
|
||||
UPROPERTY(EditAnywhere, Category="Horror|UI")
|
||||
TSubclassOf<UHorrorUI> HorrorUIClass;
|
||||
|
||||
/** Pointer to the UI widget */
|
||||
TObjectPtr<UHorrorUI> HorrorUI;
|
||||
|
||||
public:
|
||||
|
||||
/** Constructor */
|
||||
AHorrorPlayerController();
|
||||
|
||||
protected:
|
||||
|
||||
/** Input Mapping Contexts */
|
||||
UPROPERTY(EditAnywhere, Category ="Input|Input Mappings")
|
||||
TArray<UInputMappingContext*> DefaultMappingContexts;
|
||||
|
||||
/** Input Mapping Contexts */
|
||||
UPROPERTY(EditAnywhere, Category="Input|Input Mappings")
|
||||
TArray<UInputMappingContext*> MobileExcludedMappingContexts;
|
||||
|
||||
/** Mobile controls widget to spawn */
|
||||
UPROPERTY(EditAnywhere, Category="Input|Touch Controls")
|
||||
TSubclassOf<UUserWidget> MobileControlsWidgetClass;
|
||||
|
||||
/** Pointer to the mobile controls widget */
|
||||
TObjectPtr<UUserWidget> MobileControlsWidget;
|
||||
|
||||
/** Gameplay Initialization */
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
/** Possessed pawn initialization */
|
||||
virtual void OnPossess(APawn* aPawn) override;
|
||||
|
||||
/** Input mapping context setup */
|
||||
virtual void SetupInputComponent() override;
|
||||
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
|
||||
#include "HorrorUI.h"
|
||||
#include "HorrorCharacter.h"
|
||||
|
||||
void UHorrorUI::SetupCharacter(AHorrorCharacter* HorrorCharacter)
|
||||
{
|
||||
HorrorCharacter->OnSprintMeterUpdated.AddDynamic(this, &UHorrorUI::OnSprintMeterUpdated);
|
||||
HorrorCharacter->OnSprintStateChanged.AddDynamic(this, &UHorrorUI::OnSprintStateChanged);
|
||||
}
|
||||
|
||||
void UHorrorUI::OnSprintMeterUpdated(float Percent)
|
||||
{
|
||||
// call the BP handler
|
||||
BP_SprintMeterUpdated(Percent);
|
||||
}
|
||||
|
||||
void UHorrorUI::OnSprintStateChanged(bool bSprinting)
|
||||
{
|
||||
// call the BP handler
|
||||
BP_SprintStateChanged(bSprinting);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "HorrorUI.generated.h"
|
||||
|
||||
class AHorrorCharacter;
|
||||
|
||||
/**
|
||||
* Simple UI for a first person horror game
|
||||
* Manages character sprint meter display
|
||||
*/
|
||||
UCLASS(abstract)
|
||||
class FIRST_API UHorrorUI : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
/** Sets up delegate listeners for the passed character */
|
||||
void SetupCharacter(AHorrorCharacter* HorrorCharacter);
|
||||
|
||||
/** Called when the character's sprint meter is updated */
|
||||
UFUNCTION()
|
||||
void OnSprintMeterUpdated(float Percent);
|
||||
|
||||
/** Called when the character's sprint state changes */
|
||||
UFUNCTION()
|
||||
void OnSprintStateChanged(bool bSprinting);
|
||||
|
||||
protected:
|
||||
|
||||
/** Passes control to Blueprint to update the sprint meter widgets */
|
||||
UFUNCTION(BlueprintImplementableEvent, Category="Horror", meta = (DisplayName = "Sprint Meter Updated"))
|
||||
void BP_SprintMeterUpdated(float Percent);
|
||||
|
||||
/** Passes control to Blueprint to update the sprint meter status */
|
||||
UFUNCTION(BlueprintImplementableEvent, Category="Horror", meta = (DisplayName = "Sprint State Changed"))
|
||||
void BP_SprintStateChanged(bool bSprinting);
|
||||
};
|
||||
Reference in New Issue
Block a user