// Fill out your copyright notice in the Description page of Project Settings. #include "Player/ExoPlayerController.h" #include "EnhancedInputComponent.h" #include "EnhancedInputSubsystems.h" #include "Characters/ExoPlayerCharacter.h" #include "Components/CapsuleComponent.h" #include "GameFramework/Character.h" #include "GameFramework/CharacterMovementComponent.h" AExoPlayerController::AExoPlayerController() { PrimaryActorTick.bCanEverTick = true; } void AExoPlayerController::BeginPlay() { Super::BeginPlay(); check(InputContext); PlayerCharacter = Cast(GetPawn()); check(PlayerCharacter); UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(GetLocalPlayer()); if (Subsystem) { Subsystem->AddMappingContext(InputContext, 0); } InteractionComponent = PlayerCharacter->InteractionComponent; ShootingComponent = PlayerCharacter->ShootingComponent; // Ustawianie w komponencie poruszania pr�dko�ci zapisanej w characterze PlayerCharacter->GetCharacterMovement()->MaxWalkSpeed = PlayerCharacter->WalkSpeed; } void AExoPlayerController::PlayerTick(float DeltaTime) { Super::PlayerTick(DeltaTime); if (PlayerCharacter->bIsInCover) { //PlayerCharacter->SetEyePositionOffsetTarget( // FVector(0, 0, PlayerCharacter->LowEyeHeightOffset) // ); if (PlayerCharacter->bIsAimingMode) { CoverAimHeightOffset = CalculateCoverAimOffset(); PlayerCharacter->SetEyePositionOffsetTarget( FVector(0.f, 0.f, CoverAimHeightOffset)); } } //else //{ // PlayerCharacter->SetEyePositionOffsetTarget(FVector::ZeroVector); //} // On screen cover state DEBUG if (bShowCoverSystemDebug) { GEngine->AddOnScreenDebugMessage(2, -1, FColor::Red, FString::Printf(TEXT("In cover: %hs"), PlayerCharacter->bIsInCover ? "true" : "false") ); } } void AExoPlayerController::SetupInputComponent() { Super::SetupInputComponent(); UEnhancedInputComponent* EnhancedInputComponent = CastChecked(InputComponent); EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AExoPlayerController::Move); EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &AExoPlayerController::Look); EnhancedInputComponent->BindAction(InteractAction, ETriggerEvent::Triggered, this, &AExoPlayerController::Interact); EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerJump); EnhancedInputComponent->BindAction(DodgeAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerDodge); EnhancedInputComponent->BindAction(CrouchAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerStartCrouch); EnhancedInputComponent->BindAction(CrouchAction, ETriggerEvent::Completed, this, &AExoPlayerController::PlayerStopCrouch); EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerStartSprint); EnhancedInputComponent->BindAction(SprintAction, ETriggerEvent::Completed, this, &AExoPlayerController::PlayerStopSprint); EnhancedInputComponent->BindAction(ShootAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerShoot); EnhancedInputComponent->BindAction(ThrowAction, ETriggerEvent::Completed, this, &AExoPlayerController::PlayerThrow); EnhancedInputComponent->BindAction(AimAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerStartAim); EnhancedInputComponent->BindAction(AimAction, ETriggerEvent::Completed, this, &AExoPlayerController::PlayerStopAim); EnhancedInputComponent->BindAction(MeleAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerMeleAttack); EnhancedInputComponent->BindAction(ChangeWeaponAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerChangeWeapon); EnhancedInputComponent->BindAction(SelectFirstWeaponAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerSelectFirstWeapon); EnhancedInputComponent->BindAction(SelectSecondWeaponAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerSelectSecondWeapon); EnhancedInputComponent->BindAction(DropWeaponAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerDropWeapon); EnhancedInputComponent->BindAction(ReloadAction, ETriggerEvent::Started, this, &AExoPlayerController::PlayerReload); } void AExoPlayerController::Move(const FInputActionValue& InputActionValue) { const FVector2D InputAxisVector = InputActionValue.Get(); const FRotator Rotation = GetControlRotation(); const FRotator YawRotation(0.f, Rotation.Yaw, 0.f); const FVector ForwardDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X); const FVector RightDirection = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y); if (PlayerCharacter) { PlayerCharacter->AddMovementInput(ForwardDirection, InputAxisVector.X); PlayerCharacter->AddMovementInput(RightDirection, InputAxisVector.Y); } } void AExoPlayerController::Look(const FInputActionValue& InputActionValue) { FVector2D InputAxisVector = InputActionValue.Get(); AddYawInput(InputAxisVector.X); AddPitchInput(InputAxisVector.Y); } void AExoPlayerController::Interact() { if (InteractionComponent->InteractedActor) IInteractable::Execute_Interact(InteractionComponent->InteractedActor, PlayerCharacter); } void AExoPlayerController::PlayerJump() { PlayerCharacter->Jump(); } void AExoPlayerController::PlayerDodge() { UE_LOG(LogTemp, Error, TEXT("Player Dodge")); if (PlayerCharacter->GetCharacterMovement()->IsFalling()) return; if (PlayerCharacter->GetCharacterMovement()->IsCrouching()) return; if (!CanDodge) return; CanDodge = false; FVector DodgeDirection = PlayerCharacter->GetVelocity().GetSafeNormal(); DodgeDirection.Z = 0.f; if (DodgeDirection.IsNearlyZero()) DodgeDirection = PlayerCharacter->GetActorForwardVector(); PlayerCharacter->LaunchCharacter(DodgeDirection * PlayerCharacter->DodgeForce, true, true); GetWorldTimerManager().SetTimer(DodgeCooldownTimer, this, &AExoPlayerController::ResetDodge, PlayerCharacter->DodgeCooldown, false); } void AExoPlayerController::ResetDodge() { CanDodge = true; } void AExoPlayerController::PlayerStartCrouch() { if (bIsSprinting) { PlayerCharacter->GetCharacterMovement()->MaxWalkSpeedCrouched = PlayerCharacter->SlideSpeed; PlayerCharacter->SetTargetEyeHeight(PlayerCharacter->LowEyeHeightOffset); GetWorldTimerManager().SetTimer(SlideCooldownTimer, this, &AExoPlayerController::ResetSlide, PlayerCharacter->SlideCooldown, false); } //PlayerCharacter->Crouch(); PlayerCharacter->CrouchCustom(); //PlayerCharacter->SetTargetEyeHeight(PlayerCharacter->CrouchedEyeHeight); // PlayerCharacter->CameraComponent->SetRelativeLocation(FVector( // 0.0f, // 0.0f, // PlayerCharacter->CrouchedEyeHeight) // ); // Start checking for cover PlayerCharacter->bIsInCover = CheckForCover(); GetWorld()->GetTimerManager().SetTimer( CoverCheckTimer, this, &AExoPlayerController::OnCoverTimer, CoverCheckRate, true ); } void AExoPlayerController::PlayerStopCrouch() { //PlayerCharacter->UnCrouch(); PlayerCharacter->UnCrouchCustom(); //PlayerCharacter->SetTargetEyeHeight(PlayerCharacter->GetStandingEyeHeight()); // PlayerCharacter->CameraComponent->SetRelativeLocation(FVector( // 0.0f, // 0.0f, // PlayerCharacter->BaseEyeHeight) // ); // Stop checking for cover GetWorld()->GetTimerManager().ClearTimer(CoverCheckTimer); PlayerCharacter->bIsInCover = false; TargetCoverStandAlpha = 0.0f; } void AExoPlayerController::ResetSlide() { PlayerCharacter->GetCharacterMovement()->MaxWalkSpeedCrouched = PlayerCharacter->CrouchSpeed; PlayerCharacter->SetTargetEyeHeight(PlayerCharacter->CrouchedEyeHeight); } void AExoPlayerController::PlayerStartSprint() { if (!PlayerCharacter->bIsAimingMode) { UE_LOG(LogTemp, Display, TEXT("Start sprint")); bIsSprinting = true; PlayerCharacter->GetCharacterMovement()->MaxWalkSpeed = PlayerCharacter->SprintSpeed; } } void AExoPlayerController::PlayerStopSprint() { if (!PlayerCharacter->bIsAimingMode) { UE_LOG(LogTemp, Display, TEXT("Stop sprint")); bIsSprinting = false; PlayerCharacter->GetCharacterMovement()->MaxWalkSpeed = PlayerCharacter->WalkSpeed; } } void AExoPlayerController::PlayerShoot() { ShootingComponent->Shoot(); } void AExoPlayerController::PlayerThrow() { ShootingComponent->Throw(); } void AExoPlayerController::PlayerStartAim() { ShootingComponent->StartAiming(); } void AExoPlayerController::PlayerStopAim() { ShootingComponent->StopAiming(); if (PlayerCharacter->bIsInCover) { CalculateCoverAimOffset(); } } void AExoPlayerController::PlayerMeleAttack() { ShootingComponent->MeleAttack(); } void AExoPlayerController::PlayerChangeWeapon() { ShootingComponent->SwitchGun(); } void AExoPlayerController::PlayerSelectFirstWeapon() { ShootingComponent->SelectGun(true); } void AExoPlayerController::PlayerSelectSecondWeapon() { ShootingComponent->SelectGun(false); } void AExoPlayerController::PlayerDropWeapon() { ShootingComponent->DropGun(); } void AExoPlayerController::PlayerReload() { ShootingComponent->Reload(); } bool AExoPlayerController::CheckForCover() { // Cover is recalculated every time the player crouches or stops moving while crouched // Cover targets are cleared completely when player stands up again or dies // Do a simple 8-directional line trace FVector TraceDirection = PlayerCharacter->GetActorForwardVector(); FVector TraceStart = PlayerCharacter->GetActorLocation(); TraceStart.Z += bUseEyeHeight ? PlayerCharacter->GetCrouchingEyeHeight(true) : PlayerCharacter->GetPlayerLocationAtFeet().Z + CoverObstacleMinHeight; FCollisionQueryParams QueryParams; QueryParams.AddIgnoredActor(PlayerCharacter); TArray ValidHits; for (int i = 0; i <= 8; i++) { // Trace for possible cover at set distance FHitResult Hit; FVector TraceEnd = TraceStart + (TraceDirection * MaxDistanceFromCover); GetWorld()->LineTraceSingleByChannel( Hit, TraceStart, TraceEnd, CoverTraceChannel, QueryParams ); if (bShowCoverSystemDebug) { // DEBUG DrawDebugLine(GetWorld(), TraceStart,TraceEnd, Hit.bBlockingHit ? FColor::Blue : FColor::Red, false, 5.0f, 0, 1.0f); } if (Hit.bBlockingHit == true) { // Check if this particular hit isn't against a wall (>CoverMaxHeight) FHitResult SphereHit; FVector SpherePosition = Hit.ImpactPoint; SpherePosition.Z = PlayerCharacter->GetPlayerLocationAtFeet().Z + CoverObstacleMaxHeight + CoverAimWindowRadius; GetWorld()->SweepSingleByChannel( SphereHit, SpherePosition, SpherePosition, FQuat::Identity, CoverTraceChannel, FCollisionShape::MakeSphere(CoverAimWindowRadius), QueryParams); if (bShowCoverSystemDebug) { DrawDebugSphere(GetWorld(), SphereHit.TraceStart, CoverAimWindowRadius, 8, Hit.bBlockingHit ? FColor::Blue : FColor::Red, false, 5.0f, 0, 1); } if (SphereHit.bBlockingHit == false) { ValidHits.Add(Hit); } } // Rotate trace to search in another direction next time TraceDirection = TraceDirection.RotateAngleAxis(45, FVector(0, 0, 1)); //UE_LOG(LogTemp, Display, TEXT("Check Cover %d"), i); } return ValidHits.Num() > 0; } float AExoPlayerController::CalculateCoverAimOffset() { // Nic nie rób jak nie znajdujesz się za osłoną if (!PlayerCharacter->bIsInCover) { return 0.f; } // Oblicz maksymalną wysokość na którą można wychylić się z nad osłony MaxCoverAimHeightOffset = PlayerCharacter->GetStandingEyeHeight() * CoverAimStandFactor; FVector ObstacleTraceStart = GetCharacter()->GetActorLocation(); ObstacleTraceStart.Z += bUseEyeHeight ? PlayerCharacter->GetCurrentEyeHeight(true) : PlayerCharacter->CrouchedEyeHeight - GetCharacter()->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(); FVector ObstacleTraceEnd = ObstacleTraceStart + PlayerCameraManager->GetCameraRotation().Vector() * CoverAimFreeDistance; FCollisionQueryParams QueryParams; QueryParams.AddIgnoredActor(PlayerCharacter); TEnumAsByte ObstacleTraceChannel = ECC_WorldStatic; FHitResult Hit; bool bFreeSpace = false; // SphereTrace until no hit is found (free space to aim) // for (int i = 0; i < MaxCoverAimHeight; i += CoverAimWindowRadius) // { // ObstacleTraceStart.Z += i; // ObstacleTraceEnd.Z += i; // // GetWorld()->SweepSingleByChannel( // Hit, // ObstacleTraceStart, // ObstacleTraceEnd, // FQuat::Identity, // ObstacleTraceChannel, // FCollisionShape::MakeSphere(CoverAimWindowRadius), // QueryParams // ); // // if (bShowCoverSystemDebug) // { // DrawDebugSphere(GetWorld(), Hit.Location, CoverAimWindowRadius, 8, // Hit.bBlockingHit ? FColor::Blue : FColor::Red, // false, 0.0f, 0, 0); // } // // if (Hit.bBlockingHit == false) // { // UE_LOG(LogTemp, Display, TEXT("Free space")); // bFreeSpace = true; // break; // } // } float SafeCoverAimZOffset = 0.f; while (SafeCoverAimZOffset < MaxCoverAimHeightOffset) { ObstacleTraceStart.Z += SafeCoverAimZOffset; ObstacleTraceEnd.Z += SafeCoverAimZOffset; GetWorld()->SweepSingleByChannel( Hit, ObstacleTraceStart, ObstacleTraceEnd, FQuat::Identity, ObstacleTraceChannel, FCollisionShape::MakeSphere(CoverAimWindowRadius), QueryParams ); if (Hit.bBlockingHit == true) { // Increment check height every failed iteration SafeCoverAimZOffset += 0.2f; } else { // Break the loop if free space found break; } } // If this is true that means that free space was found if (SafeCoverAimZOffset <= MaxCoverAimHeightOffset) { //// DEBUG //// if (bShowCoverSystemDebug) { DrawDebugLine(GetWorld(), Hit.TraceStart, Hit.TraceEnd, FColor::Purple); } ////////////// return SafeCoverAimZOffset;// + //PlayerCharacter->CrouchedEyeHeight - PlayerCharacter->GetCurrentEyeHeight(); } // Return 0 offset if no free space to aim was found return 0.f; } void AExoPlayerController::UpdateSmoothCoverCamera(float DeltaTime) { if (FMath::IsNearlyEqual(CoverStandAlpha, TargetCoverStandAlpha, 0.01f)) { return; } CoverStandAlpha = FMath::Lerp(CoverStandAlpha, TargetCoverStandAlpha, DeltaTime * 5.0f); // BANDAID SOLUTION const float NewZ = FMath::Lerp( PlayerCharacter->GetCrouchingEyeHeight(), PlayerCharacter->GetStandingEyeHeight(), CoverStandAlpha ); PlayerCharacter->CameraComponent->SetRelativeLocation( FVector(0.0f, 0.0f, NewZ) ); // DEBUG if (bShowCoverSystemDebug && GEngine) { GEngine->AddOnScreenDebugMessage(1, -1, FColor::Red, FString::Printf(TEXT("Current cover stand alpha: %f"), CoverStandAlpha)); } } void AExoPlayerController::OnCoverTimer() { PlayerCharacter->bIsInCover = CheckForCover(); if (PlayerCharacter->bIsInCover == false) { TargetCoverStandAlpha = 0.0f; UpdateSmoothCoverCamera(GetWorld()->GetDeltaSeconds()); } } void AExoPlayerController::DebugCoverSystem(bool show) { bShowCoverSystemDebug = show; }