Initial Agrarian Unreal project
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
|
||||
#include "AnimNotify_EndDash.h"
|
||||
#include "PlatformingCharacter.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
|
||||
void UAnimNotify_EndDash::Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference)
|
||||
{
|
||||
// cast the owner to the attacker interface
|
||||
if (APlatformingCharacter* PlatformingCharacter = Cast<APlatformingCharacter>(MeshComp->GetOwner()))
|
||||
{
|
||||
// tell the actor to end the dash
|
||||
PlatformingCharacter->EndDash();
|
||||
}
|
||||
}
|
||||
|
||||
FString UAnimNotify_EndDash::GetNotifyName_Implementation() const
|
||||
{
|
||||
return FString("End Dash");
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Animation/AnimNotifies/AnimNotify.h"
|
||||
#include "AnimNotify_EndDash.generated.h"
|
||||
|
||||
/**
|
||||
* AnimNotify to finish the dash animation and restore player control
|
||||
*/
|
||||
UCLASS()
|
||||
class UAnimNotify_EndDash : public UAnimNotify
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
/** Perform the Anim Notify */
|
||||
virtual void Notify(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) override;
|
||||
|
||||
/** Get the notify name */
|
||||
virtual FString GetNotifyName_Implementation() const override;
|
||||
};
|
||||
@@ -0,0 +1,367 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
|
||||
#include "PlatformingCharacter.h"
|
||||
|
||||
#include "Components/CapsuleComponent.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
#include "GameFramework/SpringArmComponent.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "Camera/CameraComponent.h"
|
||||
#include "EnhancedInputSubsystems.h"
|
||||
#include "EnhancedInputComponent.h"
|
||||
#include "TimerManager.h"
|
||||
#include "Engine/LocalPlayer.h"
|
||||
|
||||
APlatformingCharacter::APlatformingCharacter()
|
||||
{
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
// initialize the flags
|
||||
bHasWallJumped = false;
|
||||
bHasDoubleJumped = false;
|
||||
bHasDashed = false;
|
||||
bIsDashing = false;
|
||||
|
||||
// bind the dash montage ended delegate
|
||||
OnDashMontageEnded.BindUObject(this, &APlatformingCharacter::DashMontageEnded);
|
||||
|
||||
// enable press and hold jump
|
||||
JumpMaxHoldTime = 0.4f;
|
||||
|
||||
// set the jump max count to 3 so we can double jump and check for coyote time jumps
|
||||
JumpMaxCount = 3;
|
||||
|
||||
// Set size for collision capsule
|
||||
GetCapsuleComponent()->InitCapsuleSize(35.0f, 90.0f);
|
||||
|
||||
// don't rotate the mesh when the controller rotates
|
||||
bUseControllerRotationYaw = false;
|
||||
|
||||
// Configure character movement
|
||||
GetCharacterMovement()->GravityScale = 2.5f;
|
||||
GetCharacterMovement()->MaxAcceleration = 1500.0f;
|
||||
GetCharacterMovement()->BrakingFrictionFactor = 1.0f;
|
||||
GetCharacterMovement()->bUseSeparateBrakingFriction = true;
|
||||
|
||||
GetCharacterMovement()->GroundFriction = 4.0f;
|
||||
GetCharacterMovement()->MaxWalkSpeed = 750.0f;
|
||||
GetCharacterMovement()->MinAnalogWalkSpeed = 20.0f;
|
||||
GetCharacterMovement()->BrakingDecelerationWalking = 2500.0f;
|
||||
GetCharacterMovement()->PerchRadiusThreshold = 15.0f;
|
||||
|
||||
GetCharacterMovement()->JumpZVelocity = 350.0f;
|
||||
GetCharacterMovement()->BrakingDecelerationFalling = 750.0f;
|
||||
GetCharacterMovement()->AirControl = 1.0f;
|
||||
|
||||
GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f);
|
||||
GetCharacterMovement()->bOrientRotationToMovement = true;
|
||||
|
||||
GetCharacterMovement()->NavAgentProps.AgentRadius = 42.0;
|
||||
GetCharacterMovement()->NavAgentProps.AgentHeight = 192.0;
|
||||
|
||||
// create the camera boom
|
||||
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
|
||||
CameraBoom->SetupAttachment(RootComponent);
|
||||
|
||||
CameraBoom->TargetArmLength = 400.0f;
|
||||
CameraBoom->bUsePawnControlRotation = true;
|
||||
CameraBoom->bEnableCameraLag = true;
|
||||
CameraBoom->CameraLagSpeed = 8.0f;
|
||||
CameraBoom->bEnableCameraRotationLag = true;
|
||||
CameraBoom->CameraRotationLagSpeed = 8.0f;
|
||||
|
||||
// create the orbiting camera
|
||||
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
|
||||
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
|
||||
FollowCamera->bUsePawnControlRotation = false;
|
||||
}
|
||||
|
||||
void APlatformingCharacter::Move(const FInputActionValue& Value)
|
||||
{
|
||||
FVector2D MovementVector = Value.Get<FVector2D>();
|
||||
|
||||
// route the input
|
||||
DoMove(MovementVector.X, MovementVector.Y);
|
||||
}
|
||||
|
||||
void APlatformingCharacter::Look(const FInputActionValue& Value)
|
||||
{
|
||||
FVector2D LookAxisVector = Value.Get<FVector2D>();
|
||||
|
||||
// route the input
|
||||
DoLook(LookAxisVector.X, LookAxisVector.Y);
|
||||
}
|
||||
|
||||
|
||||
void APlatformingCharacter::Dash()
|
||||
{
|
||||
// route the input
|
||||
DoDash();
|
||||
}
|
||||
|
||||
void APlatformingCharacter::MultiJump()
|
||||
{
|
||||
// ignore jumps while dashing
|
||||
if(bIsDashing)
|
||||
return;
|
||||
|
||||
// are we already in the air?
|
||||
if (GetCharacterMovement()->IsFalling())
|
||||
{
|
||||
|
||||
// have we already wall jumped?
|
||||
if (!bHasWallJumped)
|
||||
{
|
||||
// run a sphere sweep to check if we're in front of a wall
|
||||
FHitResult OutHit;
|
||||
|
||||
const FVector TraceStart = GetActorLocation();
|
||||
const FVector TraceEnd = TraceStart + (GetActorForwardVector() * WallJumpTraceDistance);
|
||||
const FCollisionShape TraceShape = FCollisionShape::MakeSphere(WallJumpTraceRadius);
|
||||
|
||||
FCollisionQueryParams QueryParams;
|
||||
QueryParams.AddIgnoredActor(this);
|
||||
|
||||
if (GetWorld()->SweepSingleByChannel(OutHit, TraceStart, TraceEnd, FQuat(), ECollisionChannel::ECC_Visibility, TraceShape, QueryParams))
|
||||
{
|
||||
// rotate the character to face away from the wall, so we're correctly oriented for the next wall jump
|
||||
FRotator WallOrientation = OutHit.ImpactNormal.ToOrientationRotator();
|
||||
WallOrientation.Pitch = 0.0f;
|
||||
WallOrientation.Roll = 0.0f;
|
||||
|
||||
SetActorRotation(WallOrientation);
|
||||
|
||||
// apply a launch impulse to the character to perform the actual wall jump
|
||||
const FVector WallJumpImpulse = (OutHit.ImpactNormal * WallJumpBounceImpulse) + (FVector::UpVector * WallJumpVerticalImpulse);
|
||||
|
||||
LaunchCharacter(WallJumpImpulse, true, true);
|
||||
|
||||
// enable the jump trail
|
||||
SetJumpTrailState(true);
|
||||
|
||||
// raise the wall jump flag to prevent an immediate second wall jump
|
||||
bHasWallJumped = true;
|
||||
|
||||
GetWorld()->GetTimerManager().SetTimer(WallJumpTimer, this, &APlatformingCharacter::ResetWallJump, DelayBetweenWallJumps, false);
|
||||
}
|
||||
// no wall jump, try a double jump next
|
||||
else
|
||||
{
|
||||
// are we still within coyote time frames?
|
||||
if (GetWorld()->GetTimeSeconds() - LastFallTime < MaxCoyoteTime)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Coyote Jump"));
|
||||
|
||||
// use the built-in CMC functionality to do the jump
|
||||
Jump();
|
||||
|
||||
// enable the jump trail
|
||||
SetJumpTrailState(true);
|
||||
|
||||
// no coyote time jump
|
||||
} else {
|
||||
|
||||
// only double jump once while we're in the air
|
||||
if (!bHasDoubleJumped)
|
||||
{
|
||||
bHasDoubleJumped = true;
|
||||
|
||||
// use the built-in CMC functionality to do the double jump
|
||||
Jump();
|
||||
|
||||
// enable the jump trail
|
||||
SetJumpTrailState(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// we're grounded so just do a regular jump
|
||||
Jump();
|
||||
|
||||
// activate the jump trail
|
||||
SetJumpTrailState(true);
|
||||
}
|
||||
}
|
||||
|
||||
void APlatformingCharacter::ResetWallJump()
|
||||
{
|
||||
// reset the wall jump input lock
|
||||
bHasWallJumped = false;
|
||||
}
|
||||
|
||||
void APlatformingCharacter::DoMove(float Right, float Forward)
|
||||
{
|
||||
if (GetController() != nullptr)
|
||||
{
|
||||
// momentarily disable movement inputs if we've just wall jumped
|
||||
if (!bHasWallJumped)
|
||||
{
|
||||
// find out which way is forward
|
||||
const FRotator Rotation = GetController()->GetControlRotation();
|
||||
const FRotator YawRotation(0, Rotation.Yaw, 0);
|
||||
|
||||
// get forward vector
|
||||
const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
|
||||
|
||||
// get right vector
|
||||
const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
|
||||
|
||||
// add movement
|
||||
AddMovementInput(ForwardDirection, Forward);
|
||||
AddMovementInput(RightDirection, Right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APlatformingCharacter::DoLook(float Yaw, float Pitch)
|
||||
{
|
||||
if (GetController() != nullptr)
|
||||
{
|
||||
// add yaw and pitch input to controller
|
||||
AddControllerYawInput(Yaw);
|
||||
AddControllerPitchInput(Pitch);
|
||||
}
|
||||
}
|
||||
|
||||
void APlatformingCharacter::DoDash()
|
||||
{
|
||||
// ignore the input if we've already dashed and have yet to reset
|
||||
if (bHasDashed)
|
||||
return;
|
||||
|
||||
// raise the dash flags
|
||||
bIsDashing = true;
|
||||
bHasDashed = true;
|
||||
|
||||
// disable gravity while dashing
|
||||
GetCharacterMovement()->GravityScale = 0.0f;
|
||||
|
||||
// reset the character velocity so we don't carry momentum into the dash
|
||||
GetCharacterMovement()->Velocity = FVector::ZeroVector;
|
||||
|
||||
// enable the jump trails
|
||||
SetJumpTrailState(true);
|
||||
|
||||
// play the dash montage
|
||||
if (UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance())
|
||||
{
|
||||
const float MontageLength = AnimInstance->Montage_Play(DashMontage, 1.0f, EMontagePlayReturnType::MontageLength, 0.0f, true);
|
||||
|
||||
// has the montage played successfully?
|
||||
if (MontageLength > 0.0f)
|
||||
{
|
||||
AnimInstance->Montage_SetEndDelegate(OnDashMontageEnded, DashMontage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APlatformingCharacter::DoJumpStart()
|
||||
{
|
||||
// handle special jump cases
|
||||
MultiJump();
|
||||
}
|
||||
|
||||
void APlatformingCharacter::DoJumpEnd()
|
||||
{
|
||||
// stop jumping
|
||||
StopJumping();
|
||||
}
|
||||
|
||||
void APlatformingCharacter::DashMontageEnded(UAnimMontage* Montage, bool bInterrupted)
|
||||
{
|
||||
// end the dash
|
||||
EndDash();
|
||||
}
|
||||
|
||||
void APlatformingCharacter::EndDash()
|
||||
{
|
||||
// restore gravity
|
||||
GetCharacterMovement()->GravityScale = 2.5f;
|
||||
|
||||
// reset the dashing flag
|
||||
bIsDashing = false;
|
||||
|
||||
// are we grounded after the dash?
|
||||
if (GetCharacterMovement()->IsMovingOnGround())
|
||||
{
|
||||
// reset the dash usage flag, since we won't receive a landed event
|
||||
bHasDashed = false;
|
||||
|
||||
// deactivate the jump trails
|
||||
SetJumpTrailState(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool APlatformingCharacter::HasDoubleJumped() const
|
||||
{
|
||||
return bHasDoubleJumped;
|
||||
}
|
||||
|
||||
bool APlatformingCharacter::HasWallJumped() const
|
||||
{
|
||||
return bHasWallJumped;
|
||||
}
|
||||
|
||||
void APlatformingCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
Super::EndPlay(EndPlayReason);
|
||||
|
||||
// clear the wall jump reset timer
|
||||
GetWorld()->GetTimerManager().ClearTimer(WallJumpTimer);
|
||||
}
|
||||
|
||||
void APlatformingCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
||||
{
|
||||
// Set up action bindings
|
||||
if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent))
|
||||
{
|
||||
|
||||
// Jumping
|
||||
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &APlatformingCharacter::DoJumpStart);
|
||||
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &APlatformingCharacter::DoJumpEnd);
|
||||
|
||||
// Moving
|
||||
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &APlatformingCharacter::Move);
|
||||
EnhancedInputComponent->BindAction(MouseLookAction, ETriggerEvent::Triggered, this, &APlatformingCharacter::Look);
|
||||
|
||||
// Looking
|
||||
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &APlatformingCharacter::Look);
|
||||
|
||||
// Dashing
|
||||
EnhancedInputComponent->BindAction(DashAction, ETriggerEvent::Triggered, this, &APlatformingCharacter::Dash);
|
||||
}
|
||||
}
|
||||
|
||||
void APlatformingCharacter::Landed(const FHitResult& Hit)
|
||||
{
|
||||
Super::Landed(Hit);
|
||||
|
||||
// reset the double jump and dash flags
|
||||
bHasDoubleJumped = false;
|
||||
bHasDashed = false;
|
||||
|
||||
// deactivate the jump trail
|
||||
SetJumpTrailState(false);
|
||||
}
|
||||
|
||||
void APlatformingCharacter::OnMovementModeChanged(EMovementMode PrevMovementMode, uint8 PreviousCustomMode /*= 0*/)
|
||||
{
|
||||
Super::OnMovementModeChanged(PrevMovementMode, PreviousCustomMode);
|
||||
|
||||
// are we falling?
|
||||
if (GetCharacterMovement()->MovementMode == EMovementMode::MOVE_Falling)
|
||||
{
|
||||
// save the game time when we started falling, so we can check it later for coyote time jumps
|
||||
LastFallTime = GetWorld()->GetTimeSeconds();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "Animation/AnimInstance.h"
|
||||
#include "PlatformingCharacter.generated.h"
|
||||
|
||||
|
||||
class USpringArmComponent;
|
||||
class UCameraComponent;
|
||||
class UInputAction;
|
||||
struct FInputActionValue;
|
||||
class UAnimMontage;
|
||||
|
||||
/**
|
||||
* An enhanced Third Person Character with the following functionality:
|
||||
* - Platforming game character movement physics
|
||||
* - Press and Hold Jump
|
||||
* - Double Jump
|
||||
* - Wall Jump
|
||||
* - Dash
|
||||
*/
|
||||
UCLASS(abstract)
|
||||
class APlatformingCharacter : public ACharacter
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/** Camera boom positioning the camera behind the character */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
|
||||
USpringArmComponent* CameraBoom;
|
||||
|
||||
/** Follow camera */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
|
||||
UCameraComponent* FollowCamera;
|
||||
|
||||
protected:
|
||||
|
||||
/** Jump Input Action */
|
||||
UPROPERTY(EditAnywhere, Category="Input")
|
||||
UInputAction* JumpAction;
|
||||
|
||||
/** Move Input Action */
|
||||
UPROPERTY(EditAnywhere, Category="Input")
|
||||
UInputAction* MoveAction;
|
||||
|
||||
/** Look Input Action */
|
||||
UPROPERTY(EditAnywhere, Category="Input")
|
||||
UInputAction* LookAction;
|
||||
|
||||
/** Mouse Look Input Action */
|
||||
UPROPERTY(EditAnywhere, Category="Input")
|
||||
UInputAction* MouseLookAction;
|
||||
|
||||
/** Dash Input Action */
|
||||
UPROPERTY(EditAnywhere, Category="Input")
|
||||
UInputAction* DashAction;
|
||||
|
||||
public:
|
||||
|
||||
/** Constructor */
|
||||
APlatformingCharacter();
|
||||
|
||||
protected:
|
||||
|
||||
/** Called for movement input */
|
||||
void Move(const FInputActionValue& Value);
|
||||
|
||||
/** Called for looking input */
|
||||
void Look(const FInputActionValue& Value);
|
||||
|
||||
/** Called for dash input */
|
||||
void Dash();
|
||||
|
||||
/** Called for jump pressed to check for advanced multi-jump conditions */
|
||||
void MultiJump();
|
||||
|
||||
/** Resets the wall jump input lock */
|
||||
void ResetWallJump();
|
||||
|
||||
public:
|
||||
|
||||
/** Handles move inputs from either controls or UI interfaces */
|
||||
UFUNCTION(BlueprintCallable, Category="Input")
|
||||
virtual void DoMove(float Right, float Forward);
|
||||
|
||||
/** Handles look inputs from either controls or UI interfaces */
|
||||
UFUNCTION(BlueprintCallable, Category="Input")
|
||||
virtual void DoLook(float Yaw, float Pitch);
|
||||
|
||||
/** Handles dash inputs from either controls or UI interfaces */
|
||||
UFUNCTION(BlueprintCallable, Category="Input")
|
||||
virtual void DoDash();
|
||||
|
||||
/** Handles jump pressed inputs from either controls or UI interfaces */
|
||||
UFUNCTION(BlueprintCallable, Category="Input")
|
||||
virtual void DoJumpStart();
|
||||
|
||||
/** Handles jump pressed inputs from either controls or UI interfaces */
|
||||
UFUNCTION(BlueprintCallable, Category="Input")
|
||||
virtual void DoJumpEnd();
|
||||
|
||||
protected:
|
||||
|
||||
/** Called from a delegate when the dash montage ends */
|
||||
void DashMontageEnded(UAnimMontage* Montage, bool bInterrupted);
|
||||
|
||||
/** Passes control to Blueprint to enable or disable jump trails */
|
||||
UFUNCTION(BlueprintImplementableEvent, Category="Platforming")
|
||||
void SetJumpTrailState(bool bEnabled);
|
||||
|
||||
public:
|
||||
|
||||
/** Ends the dash state */
|
||||
void EndDash();
|
||||
|
||||
public:
|
||||
|
||||
/** Returns true if the character has just double jumped */
|
||||
UFUNCTION(BlueprintPure, Category="Platforming")
|
||||
bool HasDoubleJumped() const;
|
||||
|
||||
/** Returns true if the character has just wall jumped */
|
||||
UFUNCTION(BlueprintPure, Category="Platforming")
|
||||
bool HasWallJumped() const;
|
||||
|
||||
public:
|
||||
|
||||
/** EndPlay cleanup */
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
/** Sets up input action bindings */
|
||||
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
|
||||
|
||||
/** Handle landings to reset dash and advanced jump state */
|
||||
virtual void Landed(const FHitResult& Hit) override;
|
||||
|
||||
/** Handle movement mode changes to keep track of coyote time jumps */
|
||||
virtual void OnMovementModeChanged(EMovementMode PrevMovementMode, uint8 PreviousCustomMode = 0) override;
|
||||
|
||||
protected:
|
||||
|
||||
/** movement state flag bits, packed into a uint8 for memory efficiency */
|
||||
uint8 bHasWallJumped : 1;
|
||||
uint8 bHasDoubleJumped : 1;
|
||||
uint8 bHasDashed : 1;
|
||||
uint8 bIsDashing : 1;
|
||||
|
||||
/** timer for wall jump input reset */
|
||||
FTimerHandle WallJumpTimer;
|
||||
|
||||
/** Dash montage ended delegate */
|
||||
FOnMontageEnded OnDashMontageEnded;
|
||||
|
||||
/** Distance to trace ahead of the character to look for walls to jump from */
|
||||
UPROPERTY(EditAnywhere, Category="Wall Jump", meta = (ClampMin = 0, ClampMax = 1000, Units = "cm"))
|
||||
float WallJumpTraceDistance = 50.0f;
|
||||
|
||||
/** Radius of the wall jump sphere trace check */
|
||||
UPROPERTY(EditAnywhere, Category="Wall Jump", meta = (ClampMin = 0, ClampMax = 100, Units = "cm"))
|
||||
float WallJumpTraceRadius = 25.0f;
|
||||
|
||||
/** Impulse to apply away from the wall when wall jumping */
|
||||
UPROPERTY(EditAnywhere, Category="Wall Jump", meta = (ClampMin = 0, ClampMax = 10000, Units = "cm/s"))
|
||||
float WallJumpBounceImpulse = 800.0f;
|
||||
|
||||
/** Vertical impulse to apply when wall jumping */
|
||||
UPROPERTY(EditAnywhere, Category="Wall Jump", meta = (ClampMin = 0, ClampMax = 10000, Units = "cm/s"))
|
||||
float WallJumpVerticalImpulse = 900.0f;
|
||||
|
||||
/** Time to ignore jump inputs after a wall jump */
|
||||
UPROPERTY(EditAnywhere, Category="Wall Jump", meta = (ClampMin = 0, ClampMax = 5, Units = "s"))
|
||||
float DelayBetweenWallJumps = 0.1f;
|
||||
|
||||
/** AnimMontage to use for the Dash action */
|
||||
UPROPERTY(EditAnywhere, Category="Dash")
|
||||
UAnimMontage* DashMontage;
|
||||
|
||||
/** Last recorded time when this character started falling */
|
||||
float LastFallTime = 0.0f;
|
||||
|
||||
/** Max amount of time that can pass since we started falling when we allow a regular jump */
|
||||
UPROPERTY(EditAnywhere, Category="Coyote Time", meta = (ClampMin = 0, ClampMax = 5, Units = "s"))
|
||||
float MaxCoyoteTime = 0.16f;
|
||||
|
||||
public:
|
||||
/** Returns CameraBoom subobject **/
|
||||
FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
|
||||
|
||||
/** Returns FollowCamera subobject **/
|
||||
FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
|
||||
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
|
||||
#include "Variant_Platforming/PlatformingGameMode.h"
|
||||
|
||||
APlatformingGameMode::APlatformingGameMode()
|
||||
{
|
||||
// stub
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/GameModeBase.h"
|
||||
#include "PlatformingGameMode.generated.h"
|
||||
|
||||
/**
|
||||
* Simple GameMode for a third person platforming game
|
||||
*/
|
||||
UCLASS()
|
||||
class APlatformingGameMode : public AGameModeBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
/** Constructor */
|
||||
APlatformingGameMode();
|
||||
};
|
||||
@@ -0,0 +1,98 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
|
||||
#include "Variant_Platforming/PlatformingPlayerController.h"
|
||||
#include "EnhancedInputSubsystems.h"
|
||||
#include "InputMappingContext.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "GameFramework/PlayerStart.h"
|
||||
#include "PlatformingCharacter.h"
|
||||
#include "Engine/LocalPlayer.h"
|
||||
#include "Engine/World.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "AgrarianGame.h"
|
||||
#include "Widgets/Input/SVirtualJoystick.h"
|
||||
|
||||
void APlatformingPlayerController::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
// only spawn touch controls on local player controllers
|
||||
if (ShouldUseTouchControls() && 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(LogAgrarianGame, Error, TEXT("Could not spawn mobile controls widget."));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void APlatformingPlayerController::SetupInputComponent()
|
||||
{
|
||||
Super::SetupInputComponent();
|
||||
|
||||
// only add IMCs for local player controllers
|
||||
if (IsLocalPlayerController())
|
||||
{
|
||||
// add the input mapping context
|
||||
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 (!ShouldUseTouchControls())
|
||||
{
|
||||
for (UInputMappingContext* CurrentContext : MobileExcludedMappingContexts)
|
||||
{
|
||||
Subsystem->AddMappingContext(CurrentContext, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APlatformingPlayerController::OnPossess(APawn* InPawn)
|
||||
{
|
||||
Super::OnPossess(InPawn);
|
||||
|
||||
// subscribe to the pawn's OnDestroyed delegate
|
||||
InPawn->OnDestroyed.AddDynamic(this, &APlatformingPlayerController::OnPawnDestroyed);
|
||||
}
|
||||
|
||||
void APlatformingPlayerController::OnPawnDestroyed(AActor* DestroyedActor)
|
||||
{
|
||||
// find the player start
|
||||
TArray<AActor*> ActorList;
|
||||
UGameplayStatics::GetAllActorsOfClass(GetWorld(), APlayerStart::StaticClass(), ActorList);
|
||||
|
||||
if (ActorList.Num() > 0)
|
||||
{
|
||||
// spawn a character at the player start
|
||||
const FTransform SpawnTransform = ActorList[0]->GetActorTransform();
|
||||
|
||||
if (APlatformingCharacter* RespawnedCharacter = GetWorld()->SpawnActor<APlatformingCharacter>(CharacterClass, SpawnTransform))
|
||||
{
|
||||
// possess the character
|
||||
Possess(RespawnedCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool APlatformingPlayerController::ShouldUseTouchControls() const
|
||||
{
|
||||
// are we on a mobile platform? Should we force touch?
|
||||
return SVirtualJoystick::ShouldDisplayTouchInterface() || bForceTouchControls;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "PlatformingPlayerController.generated.h"
|
||||
|
||||
class UInputMappingContext;
|
||||
class APlatformingCharacter;
|
||||
|
||||
/**
|
||||
* Simple Player Controller for a third person platforming game
|
||||
* Manages input mappings
|
||||
* Respawns the player character at the Player Start when it's destroyed
|
||||
*/
|
||||
UCLASS(abstract, Config="Game")
|
||||
class APlatformingPlayerController : public APlayerController
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
|
||||
/** Input mapping context for this player */
|
||||
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 */
|
||||
UPROPERTY()
|
||||
TObjectPtr<UUserWidget> MobileControlsWidget;
|
||||
|
||||
/** If true, the player will use UMG touch controls even if not playing on mobile platforms */
|
||||
UPROPERTY(EditAnywhere, Config, Category = "Input|Touch Controls")
|
||||
bool bForceTouchControls = false;
|
||||
|
||||
/** Character class to respawn when the possessed pawn is destroyed */
|
||||
UPROPERTY(EditAnywhere, Category="Respawn")
|
||||
TSubclassOf<APlatformingCharacter> CharacterClass;
|
||||
|
||||
protected:
|
||||
|
||||
/** Gameplay initialization */
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
/** Initialize input bindings */
|
||||
virtual void SetupInputComponent() override;
|
||||
|
||||
/** Pawn initialization */
|
||||
virtual void OnPossess(APawn* InPawn) override;
|
||||
|
||||
/** Called if the possessed pawn is destroyed */
|
||||
UFUNCTION()
|
||||
void OnPawnDestroyed(AActor* DestroyedActor);
|
||||
|
||||
/** Returns true if the player should use UMG touch controls */
|
||||
bool ShouldUseTouchControls() const;
|
||||
};
|
||||
Reference in New Issue
Block a user