|
|
|
|
|
|
| #include "SideScrollingCharacter.h"
|
| #include "GameFramework/CharacterMovementComponent.h"
|
| #include "Components/CapsuleComponent.h"
|
| #include "Camera/CameraComponent.h"
|
| #include "Components/InputComponent.h"
|
| #include "InputActionValue.h"
|
| #include "EnhancedInputComponent.h"
|
| #include "InputAction.h"
|
| #include "Engine/World.h"
|
| #include "SideScrollingInteractable.h"
|
| #include "Kismet/KismetMathLibrary.h"
|
| #include "TimerManager.h"
|
|
|
| ASideScrollingCharacter::ASideScrollingCharacter()
|
| {
|
| PrimaryActorTick.bCanEverTick = true;
|
|
|
|
|
| Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
|
| Camera->SetupAttachment(RootComponent);
|
|
|
| Camera->SetRelativeLocationAndRotation(FVector(0.0f, 300.0f, 0.0f), FRotator(0.0f, -90.0f, 0.0f));
|
|
|
|
|
| GetCapsuleComponent()->SetCapsuleSize(35.0f, 90.0f);
|
|
|
|
|
| bUseControllerRotationYaw = false;
|
|
|
|
|
| GetCharacterMovement()->GravityScale = 1.75f;
|
| GetCharacterMovement()->MaxAcceleration = 1500.0f;
|
| GetCharacterMovement()->BrakingFrictionFactor = 1.0f;
|
| GetCharacterMovement()->bUseSeparateBrakingFriction = true;
|
| GetCharacterMovement()->Mass = 500.0f;
|
|
|
| GetCharacterMovement()->SetWalkableFloorAngle(75.0f);
|
| GetCharacterMovement()->MaxWalkSpeed = 500.0f;
|
| GetCharacterMovement()->MinAnalogWalkSpeed = 20.0f;
|
| GetCharacterMovement()->BrakingDecelerationWalking = 2000.0f;
|
| GetCharacterMovement()->bIgnoreBaseRotation = true;
|
|
|
| GetCharacterMovement()->PerchRadiusThreshold = 15.0f;
|
| GetCharacterMovement()->LedgeCheckThreshold = 6.0f;
|
|
|
| GetCharacterMovement()->JumpZVelocity = 750.0f;
|
| GetCharacterMovement()->AirControl = 1.0f;
|
|
|
| GetCharacterMovement()->RotationRate = FRotator(0.0f, 750.0f, 0.0f);
|
| GetCharacterMovement()->bOrientRotationToMovement = true;
|
|
|
| GetCharacterMovement()->SetPlaneConstraintNormal(FVector(0.0f, 1.0f, 0.0f));
|
| GetCharacterMovement()->bConstrainToPlane = true;
|
|
|
|
|
| JumpMaxCount = 3;
|
| }
|
|
|
| void ASideScrollingCharacter::EndPlay(EEndPlayReason::Type EndPlayReason)
|
| {
|
| Super::EndPlay(EndPlayReason);
|
|
|
|
|
| GetWorld()->GetTimerManager().ClearTimer(WallJumpTimer);
|
| }
|
|
|
| void ASideScrollingCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
|
| {
|
| Super::SetupPlayerInputComponent(PlayerInputComponent);
|
|
|
|
|
| if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent))
|
| {
|
|
|
| EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &ASideScrollingCharacter::DoJumpStart);
|
| EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ASideScrollingCharacter::DoJumpEnd);
|
|
|
|
|
| EnhancedInputComponent->BindAction(InteractAction, ETriggerEvent::Triggered, this, &ASideScrollingCharacter::DoInteract);
|
|
|
|
|
| EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ASideScrollingCharacter::Move);
|
|
|
|
|
| EnhancedInputComponent->BindAction(DropAction, ETriggerEvent::Triggered, this, &ASideScrollingCharacter::Drop);
|
| EnhancedInputComponent->BindAction(DropAction, ETriggerEvent::Completed, this, &ASideScrollingCharacter::DropReleased);
|
|
|
| }
|
| }
|
|
|
| void ASideScrollingCharacter::NotifyHit(class UPrimitiveComponent* MyComp, AActor* Other, class UPrimitiveComponent* OtherComp, bool bSelfMoved, FVector HitLocation, FVector HitNormal, FVector NormalImpulse, const FHitResult& Hit)
|
| {
|
| Super::NotifyHit(MyComp, Other, OtherComp, bSelfMoved, HitLocation, HitNormal, NormalImpulse, Hit);
|
|
|
|
|
| if (!GetCharacterMovement()->IsFalling())
|
| {
|
| return;
|
| }
|
|
|
|
|
| if (OtherComp)
|
| {
|
|
|
| if (OtherComp->Mobility == EComponentMobility::Movable && OtherComp->IsSimulatingPhysics())
|
| {
|
| const FVector PushDir = FVector(ActionValueY > 0.0f ? 1.0f : -1.0f, 0.0f, 0.0f);
|
|
|
|
|
| OtherComp->AddImpulse(PushDir * JumpPushImpulse, NAME_None, true);
|
| }
|
| }
|
| }
|
|
|
| void ASideScrollingCharacter::Landed(const FHitResult& Hit)
|
| {
|
|
|
| bHasDoubleJumped = false;
|
| }
|
|
|
| void ASideScrollingCharacter::OnMovementModeChanged(EMovementMode PrevMovementMode, uint8 PreviousCustomMode )
|
| {
|
| Super::OnMovementModeChanged(PrevMovementMode, PreviousCustomMode);
|
|
|
|
|
| if (GetCharacterMovement()->MovementMode == EMovementMode::MOVE_Falling)
|
| {
|
|
|
| LastFallTime = GetWorld()->GetTimeSeconds();
|
| }
|
| }
|
|
|
| void ASideScrollingCharacter::Move(const FInputActionValue& Value)
|
| {
|
| FVector2D MoveVector = Value.Get<FVector2D>();
|
|
|
|
|
| DoMove(MoveVector.Y);
|
| }
|
|
|
| void ASideScrollingCharacter::Drop(const FInputActionValue& Value)
|
| {
|
|
|
| DoDrop(Value.Get<float>());
|
| }
|
|
|
| void ASideScrollingCharacter::DropReleased(const FInputActionValue& Value)
|
| {
|
|
|
| DoDrop(0.0f);
|
| }
|
|
|
| void ASideScrollingCharacter::DoMove(float Forward)
|
| {
|
|
|
| if (!bHasWallJumped)
|
| {
|
|
|
| ActionValueY = Forward;
|
|
|
|
|
| const FVector MoveDir = FVector(1.0f, Forward > 0.0f ? 0.1f : -0.1f, 0.0f);
|
|
|
|
|
| AddMovementInput(MoveDir, Forward);
|
| }
|
| }
|
|
|
| void ASideScrollingCharacter::DoDrop(float Value)
|
| {
|
|
|
| DropValue = Value;
|
| }
|
|
|
| void ASideScrollingCharacter::DoJumpStart()
|
| {
|
|
|
| MultiJump();
|
| }
|
|
|
| void ASideScrollingCharacter::DoJumpEnd()
|
| {
|
| StopJumping();
|
| }
|
|
|
| void ASideScrollingCharacter::DoInteract()
|
| {
|
|
|
| FHitResult OutHit;
|
|
|
| const FVector Start = GetActorLocation();
|
| const FVector End = Start + FVector(100.0f, 0.0f, 0.0f);
|
|
|
| FCollisionShape ColSphere;
|
| ColSphere.SetSphere(InteractionRadius);
|
|
|
| FCollisionObjectQueryParams ObjectParams;
|
| ObjectParams.AddObjectTypesToQuery(ECC_Pawn);
|
| ObjectParams.AddObjectTypesToQuery(ECC_WorldDynamic);
|
|
|
| FCollisionQueryParams QueryParams;
|
| QueryParams.AddIgnoredActor(this);
|
|
|
| if (GetWorld()->SweepSingleByObjectType(OutHit, Start, End, FQuat::Identity, ObjectParams, ColSphere, QueryParams))
|
| {
|
|
|
| if (ISideScrollingInteractable* Interactable = Cast<ISideScrollingInteractable>(OutHit.GetActor()))
|
| {
|
|
|
| Interactable->Interaction(this);
|
| }
|
| }
|
| }
|
|
|
| void ASideScrollingCharacter::MultiJump()
|
| {
|
|
|
| if (DropValue > 0.0f)
|
| {
|
| CheckForSoftCollision();
|
| return;
|
| }
|
|
|
|
|
| DropValue = 0.0f;
|
|
|
|
|
| if (!GetCharacterMovement()->IsFalling())
|
| {
|
| Jump();
|
| return;
|
| }
|
|
|
|
|
| if (!bHasWallJumped && !FMath::IsNearlyZero(ActionValueY))
|
| {
|
|
|
| FHitResult OutHit;
|
|
|
| const FVector Start = GetActorLocation();
|
| const FVector End = Start + (FVector(ActionValueY > 0.0f ? 1.0f : -1.0f, 0.0f, 0.0f) * WallJumpTraceDistance);
|
|
|
| FCollisionQueryParams QueryParams;
|
| QueryParams.AddIgnoredActor(this);
|
|
|
| GetWorld()->LineTraceSingleByChannel(OutHit, Start, End, ECC_Visibility, QueryParams);
|
|
|
| if (OutHit.bBlockingHit)
|
| {
|
|
|
| const FRotator BounceRot = UKismetMathLibrary::MakeRotFromX(OutHit.ImpactNormal);
|
| SetActorRotation(FRotator(0.0f, BounceRot.Yaw, 0.0f));
|
|
|
|
|
| FVector WallJumpImpulse = OutHit.ImpactNormal * WallJumpHorizontalImpulse;
|
| WallJumpImpulse.Z = GetCharacterMovement()->JumpZVelocity * WallJumpVerticalMultiplier;
|
|
|
|
|
| LaunchCharacter(WallJumpImpulse, true, true);
|
|
|
|
|
| bHasWallJumped = true;
|
|
|
|
|
| GetWorld()->GetTimerManager().SetTimer(WallJumpTimer, this, &ASideScrollingCharacter::ResetWallJump, DelayBetweenWallJumps, false);
|
|
|
| return;
|
| }
|
| }
|
|
|
|
|
|
|
|
|
| if (!bHasWallJumped)
|
| {
|
|
|
| if (GetWorld()->GetTimeSeconds() - LastFallTime < MaxCoyoteTime)
|
| {
|
| UE_LOG(LogTemp, Warning, TEXT("Coyote Jump"));
|
|
|
|
|
| Jump();
|
|
|
|
|
| } else {
|
|
|
|
|
| if (!bHasDoubleJumped)
|
| {
|
|
|
| bHasDoubleJumped = true;
|
|
|
|
|
| Jump();
|
| }
|
| }
|
| }
|
| }
|
|
|
| void ASideScrollingCharacter::CheckForSoftCollision()
|
| {
|
|
|
| DropValue = 0.0f;
|
|
|
|
|
| FHitResult OutHit;
|
|
|
| const FVector Start = GetActorLocation();
|
| const FVector End = Start + (FVector::DownVector * SoftCollisionTraceDistance);
|
|
|
| FCollisionObjectQueryParams ObjectParams;
|
| ObjectParams.AddObjectTypesToQuery(SoftCollisionObjectType);
|
|
|
| FCollisionQueryParams QueryParams;
|
| QueryParams.AddIgnoredActor(this);
|
|
|
| GetWorld()->LineTraceSingleByObjectType(OutHit, Start, End, ObjectParams, QueryParams);
|
|
|
|
|
| if (OutHit.GetActor())
|
| {
|
|
|
| SetSoftCollision(true);
|
| }
|
| }
|
|
|
| void ASideScrollingCharacter::ResetWallJump()
|
| {
|
|
|
| bHasWallJumped = false;
|
| }
|
|
|
| void ASideScrollingCharacter::SetSoftCollision(bool bEnabled)
|
| {
|
|
|
| GetCapsuleComponent()->SetCollisionResponseToChannel(SoftCollisionObjectType, bEnabled ? ECR_Ignore : ECR_Block);
|
| }
|
|
|
| bool ASideScrollingCharacter::HasDoubleJumped() const
|
| {
|
| return bHasDoubleJumped;
|
| }
|
|
|
| bool ASideScrollingCharacter::HasWallJumped() const
|
| {
|
| return bHasWallJumped;
|
| }
|
|
|