// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "GameFramework/Character.h" #include "SoriaCharacter.generated.h" UENUM() enum EAnimMovementMode { None = 0 UMETA(DisplayName = "None"), Walk = 1 UMETA(DisplayName = "Walk"), Run = 2 UMETA(DisplayName = "Run"), Sprint = 3 UMETA(DisplayName = "Sprint"), Skid = 4 UMETA(DisplayName = "Skid"), Wading = 5 UMETA(DisplayName = "Wading"), Balancing = 6 UMETA(DisplayName = "Balancing"), BusyIdle = 7 UMETA(DisplayName = "BusyIdle") }; UCLASS(config=Game) class ASoriaCharacter : public ACharacter { GENERATED_BODY() /** Camera boom positioning the camera behind the character */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true")) class USpringArmComponent* CameraBoom; /** Follow camera */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true")) class UCameraComponent* FollowCamera; public: ASoriaCharacter(); // Begin AActor overrides virtual void Tick(float DeltaSeconds) override; virtual void NotifyHit(class UPrimitiveComponent* MyComp, class AActor* Other, class UPrimitiveComponent* OtherComp, bool bSelfMoved, FVector HitLocation, FVector HitNormal, FVector NormalImpulse, const FHitResult& Hit) override; virtual void BeginPlay() override; // End AActor overrides /** * Base turn rate, in deg/sec. Other scaling may affect final turn rate. */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera) float BaseTurnRate; /** Base look up/down rate, in deg/sec. Other scaling may affect final rate. */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera) float BaseLookUpRate; /** Bool for whether the gryphon has mounted the player. */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Variables) bool GryphonMounted; /** Bool for whether the player is in the air. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = MovementAir) bool InAir; /** Bool for whether the player is Moving. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Movement) bool IsMoving; /////////////////////////////////////////READWRITE PARAMETERS////////////////////////////////////////////////////////// protected: /** Float for storing the Camera Correction speed that controls how quicly the camera will follow the player. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Camera) float CameraCorrectionSpeed; /** Float for storing the Spint Speed. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Movement) float SprintSpeed; /** Float for storing the Run Speed. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Movement) float RunSpeed; /** Float for storing the walk Speed. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Movement) float WalkSpeed; /** Float for storing the wade Speed. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Movement) float WadeSpeed; /** Float for storing the delay before going into a sprint. */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Movement) float SprintDelay; /** Float for storing the right axis of the analog stick. */ UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Movement) float MoveRightAxis; /** Float for storing the forard axis of the analog stick. */ UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Movement) float MoveForwardAxis; /** Bool for whether the player is sitting at the checkpoint. */ UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Movement) bool SittingAtCheckpoint; /** Bool for whether the player is Wading or Balancing. */ UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Movement) bool Wade = false; /** Bool for whether the player is Sprinting. */ UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Movement) bool Sprinting; /** Bool for whether the player can Sprint. */ UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Movement) bool CanSprint; /** Bool for whether the game is paused */ UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = UI) bool Paused; /** Bool for whether the player can trigger an event. */ UPROPERTY(VisibleAnywhere, Category = MovementAir) bool CanTriggerEvent; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Animation) TEnumAsByte<EAnimMovementMode> MovementMode; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Movement) bool CanInput; /////////////////////////////////////////PUBLIC FUNCTIONS////////////////////////////////////////////////////////// public: /** Return if the character is alive or not. */ UFUNCTION(BlueprintCallable, Category = Movement) bool IsCharacterAlive(); /** Change Characters Alive State. */ UFUNCTION(BlueprintCallable, Category = Movement) void SetCharacterAliveState(bool IsAlive); /**A Blueprint implementable event so that the animation can be changes via an interface in BP*/ UFUNCTION(BlueprintImplementableEvent, Category = "Movement") void SetAnimationMode(); /**A Blueprint implementable event so that the sprint can be triggered adter a delay*/ UFUNCTION(BlueprintImplementableEvent, Category = "Movement") void SprintCheck(); /**A Blueprint implementable event so that the sprint DoOnce can be reset*/ UFUNCTION(BlueprintImplementableEvent, Category = "Movement") void ResetSprintBP(); /**Called to reset sprinting variables*/ void ResetSprint(); /** Called for forwards/backward input */ void MoveForward(float Value); /** Called for side to side input */ void MoveRight(float Value); /**Check if a Provided Axis is in range of the gives values*/ bool AxisInRange(bool Forward, float Min, float Max, bool ABS); /**When there is no input reset variables and animations*/ void NoInput(); /**Set the movement speed and animation of the character*/ void SetMovement(); protected: /** * Called via input to turn at a given rate. * @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate */ void TurnAtRate(float Rate); /** * Called via input to turn look up/down at a given rate. * @param Rate This is a normalized rate, i.e. 1.0 means 100% of desired turn rate */ void LookUpAtRate(float Rate); // APawn interface virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; // End of APawn interface private: /** Is the character alive or not */ UPROPERTY(Category = Trait, EditAnywhere) bool CharacterAlive = true; /** How quickly forward speed changes */ UPROPERTY(Category = Plane, EditAnywhere) float Acceleration; public: /** Returns CameraBoom subobject **/ FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; } /** Returns FollowCamera subobject **/ FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; } };
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. // Copyright 2019-2020 Polargryph Ltd. All Rights Reserved. #include "MainCharacter/SoriaCharacter.h" #include "HeadMountedDisplayFunctionLibrary.h" #include "Camera/CameraComponent.h" #include "Components/CapsuleComponent.h" #include "Components/InputComponent.h" #include "Components/BoxComponent.h" #include "GameFramework/CharacterMovementComponent.h" #include "GameFramework/Controller.h" #include "GameFramework/SpringArmComponent.h" #include "Engine/World.h" #include "Engine/StaticMesh.h" #include "AI/Enemies/BaseEnemyController.h" #include "Kismet/KismetMathLibrary.h" #include "Engine.h" ////////////////////////////////////////////////////////////////////////// // ASoriaCharacter ASoriaCharacter::ASoriaCharacter() { // Set size for collision capsule GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f); // set our turn rates for input BaseTurnRate = 45.f; BaseLookUpRate = 45.f; // Don't rotate when the controller rotates. Let that just affect the camera. bUseControllerRotationPitch = false; bUseControllerRotationYaw = false; bUseControllerRotationRoll = false; // Configure character movement GetCharacterMovement()->bOrientRotationToMovement = true; // Character moves in the direction of input... //GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...at this rotation rate GetCharacterMovement()->JumpZVelocity = 600.f; GetCharacterMovement()->AirControl = 0.2f; // Create a camera boom (pulls in towards the player if there is a collision) CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom")); CameraBoom->SetupAttachment(RootComponent); CameraBoom->TargetArmLength = 300.0f; // The camera follows at this distance behind the character CameraBoom->bUsePawnControlRotation = true; // Rotate the arm based on the controller // Create a follow camera FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera")); FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // Attach the camera to the end of the boom and let the boom adjust to match the controller orientation FollowCamera->bUsePawnControlRotation = false; // Camera does not rotate relative to arm // Set handling parameters // Acceleration is high as to ensure movement is snappy Acceleration = 50000.f; WalkSpeed = 60.0f; RunSpeed = 350.0f; SprintSpeed = 500.0f; SprintDelay = 2.0f; WadeSpeed = 100.0f; // Note: The skeletal mesh and anim blueprint references on the Mesh component (inherited from Character) // are set in the derived blueprint asset named MyCharacter (to avoid direct content references in C++) } void ASoriaCharacter::BeginPlay() { Super::BeginPlay(); } ////////////////////////////////////////////////////////////////////////// // Input void ASoriaCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) { // Set up gameplay key bindings check(PlayerInputComponent); //PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump); //PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping); PlayerInputComponent->BindAxis("MoveForward", this, &ASoriaCharacter::MoveForward); PlayerInputComponent->BindAxis("MoveRight", this, &ASoriaCharacter::MoveRight); // We have 2 versions of the rotation bindings to handle different kinds of devices differently // "turn" handles devices that provide an absolute delta, such as a mouse. // "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput); PlayerInputComponent->BindAxis("TurnRate", this, &ASoriaCharacter::TurnAtRate); PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput); PlayerInputComponent->BindAxis("LookUpRate", this, &ASoriaCharacter::LookUpAtRate); } void ASoriaCharacter::MoveForward(float Value) { if (CanInput) { MoveForwardAxis = Value; if (AxisInRange(true, -0.05f, 0.05f, false) == true) { NoInput(); } else { // find out which way is forward const FRotator Rotation = Controller->GetControlRotation(); const FRotator YawRotation(0, Rotation.Yaw, 0); // get forward vector const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X); AddMovementInput(Direction, Value); SetMovement(); IsMoving = true; } } } void ASoriaCharacter::MoveRight(float Value) { if (CanInput) { MoveRightAxis = Value; if (AxisInRange(false, -0.05f, 0.05f, false) == true) { NoInput(); } else { // find out which way is right const FRotator Rotation = Controller->GetControlRotation(); const FRotator YawRotation(0, Rotation.Yaw, 0); // get right vector const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y); // add movement in that direction AddMovementInput(Direction, Value); SetMovement(); IsMoving = true; } } } /**Check to see if the provided axis in in the provided range with the choice of checking it againt the absoulute value of the axis or the true one. There is also the option to check if it should be looking at the forward or right axis*/ bool ASoriaCharacter::AxisInRange(bool Forward, float Min, float Max, bool ABS) { float Value; if (Forward) { if (ABS) { Value = UKismetMathLibrary::Abs(MoveForwardAxis); } else { Value = MoveForwardAxis; } } else { if (ABS) { Value = UKismetMathLibrary::Abs(MoveRightAxis); } else { Value = MoveRightAxis; } } if (!ABS) { if (Value < Max && Value > Min) { return true; } return false; } else { if (Value <= Max && Value >= Min) { return true; } return false; } return false; } /**If there is minimal/no input on the axis then set movement to Zero and reset the relevant variables*/ void ASoriaCharacter::NoInput() { //Though both the Forward and Right Input can Trigger the NoInput() function individually //both axis' need to be near 0 to count as No Input. if (AxisInRange(false, -0.05f, 0.05f, false) && AxisInRange(true, -0.05, 0.05, false)) { MovementMode = EAnimMovementMode::None; SetAnimationMode(); ResetSprint(); IsMoving = false; } } //As long as there is input from either the forward or right axis trigger this function. void ASoriaCharacter::SetMovement() { //If the character is in a wading volume trigger this code. if (Wade) { //Set the max walk speed and min analog input, the speeds can be overriden in the editor. GetCharacterMovement()->MaxWalkSpeed = WadeSpeed; GetCharacterMovement()->MinAnalogWalkSpeed = WadeSpeed; ResetSprint(); //This is for the camera function as to change its speed following the player dependant on the speed of the character. CameraCorrectionSpeed = 0.05f; //Set the correct animation. MovementMode = EAnimMovementMode::Wading; //The implementation of the animation change is done through calling this function that is triggered in the Blueprint. SetAnimationMode(); } else { //If the character is not in a wading volume and is detecting full input on either axis if (AxisInRange(true, 0.36f, 1.25f, true) == true || AxisInRange(false, 0.36f, 1.25f, true) == true) { if (!Sprinting) { GetCharacterMovement()->MaxWalkSpeed = RunSpeed; GetCharacterMovement()->MinAnalogWalkSpeed = RunSpeed; CanSprint = true; CameraCorrectionSpeed = 0.1f; MovementMode = EAnimMovementMode::Run; SetAnimationMode(); //Used a Blueprint implementation for a DoOnce and Delay as it's simpler to do. //The function just triggeres once and after a delay that sets the Sprinting variable to true. //The delay duration can be set/changed in the Blueprint. SprintCheck(); } else { //Once sprinting is true GetCharacterMovement()->MaxWalkSpeed = SprintSpeed; GetCharacterMovement()->MinAnalogWalkSpeed = SprintSpeed; CameraCorrectionSpeed = 0.25f; MovementMode = EAnimMovementMode::Sprint; SetAnimationMode(); } } //If there is minimal input then set the character to walk. else if (AxisInRange(true, 0.05f, 0.35f, true) == true || AxisInRange(false, 0.05f, 0.35f, true) == true) { ResetSprint(); GetCharacterMovement()->MaxWalkSpeed = WalkSpeed; GetCharacterMovement()->MinAnalogWalkSpeed = WalkSpeed; CameraCorrectionSpeed = 0.05f; MovementMode = EAnimMovementMode::Walk; SetAnimationMode(); } } } void ASoriaCharacter::ResetSprint() { CanSprint = false; Sprinting = false; ResetSprintBP(); } void ASoriaCharacter::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); } void ASoriaCharacter::NotifyHit(class UPrimitiveComponent* MyComp, class AActor* Other, class UPrimitiveComponent* OtherComp, bool bSelfMoved, FVector HitLocation, FVector HitNormal, FVector NormalImpulse, const FHitResult& Hit) { if (GetCharacterMovement()->IsFlying() == true) { Super::NotifyHit(MyComp, Other, OtherComp, bSelfMoved, HitLocation, HitNormal, NormalImpulse, Hit); // Deflect along the surface when we collide. FRotator CurrentRotation = GetActorRotation(); SetActorRotation(FQuat::Slerp(CurrentRotation.Quaternion(), HitNormal.ToOrientationQuat(), 0.025f)); } } void ASoriaCharacter::TurnAtRate(float Rate) { // calculate delta for this frame from the rate information AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds()); } void ASoriaCharacter::LookUpAtRate(float Rate) { // calculate delta for this frame from the rate information AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds()); } bool ASoriaCharacter::IsCharacterAlive() { return CharacterAlive; } void ASoriaCharacter::SetCharacterAliveState(bool IsAlive) { CharacterAlive = IsAlive; }