334 lines
11 KiB
C++
334 lines
11 KiB
C++
#include "SpaceshipPawn.h"
|
|
#include "GameFramework/SpringArmComponent.h"
|
|
#include "Camera/CameraComponent.h"
|
|
#include "Components/StaticMeshComponent.h"
|
|
#include "EnhancedInputComponent.h"
|
|
#include "EnhancedInputSubsystems.h"
|
|
#include "SpaceshipProjectile.h"
|
|
#include "Kismet/GameplayStatics.h"
|
|
#include "GameFramework/GameUserSettings.h"
|
|
#include "Blueprint/UserWidget.h"
|
|
|
|
|
|
ASpaceshipPawn::ASpaceshipPawn()
|
|
{
|
|
PrimaryActorTick.bCanEverTick = true;
|
|
|
|
// Create ship mesh
|
|
ShipMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ShipMesh"));
|
|
RootComponent = ShipMesh;
|
|
ShipMesh->SetSimulatePhysics(false);
|
|
ShipMesh->SetEnableGravity(false);
|
|
|
|
// Create projectile spawn point
|
|
ProjectileSpawnPoint = CreateDefaultSubobject<USceneComponent>(TEXT("ProjectileSpawnPoint"));
|
|
ProjectileSpawnPoint->SetupAttachment(ShipMesh);
|
|
|
|
// Create camera spring arm
|
|
CameraSpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraSpringArm"));
|
|
CameraSpringArm->SetupAttachment(RootComponent);
|
|
CameraSpringArm->TargetArmLength = 400.0f;
|
|
CameraSpringArm->bEnableCameraLag = true;
|
|
CameraSpringArm->CameraLagSpeed = 10.0f;
|
|
CameraSpringArm->bEnableCameraRotationLag = true;
|
|
CameraSpringArm->CameraRotationLagSpeed = 10.0f;
|
|
CameraSpringArm->CameraLagMaxDistance = 7.0f;
|
|
CameraSpringArm->bUsePawnControlRotation = false; // Add this line
|
|
CameraSpringArm->bInheritPitch = true; // Add this line
|
|
CameraSpringArm->bInheritYaw = true; // Add this line
|
|
CameraSpringArm->bInheritRoll = false; // Add this line
|
|
|
|
// Create camera
|
|
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
|
|
Camera->SetupAttachment(CameraSpringArm, USpringArmComponent::SocketName);
|
|
|
|
// Initialize movement variables
|
|
CurrentThrust = 0.0f;
|
|
TargetThrust = 0.0f;
|
|
bThrottlePressed = false;
|
|
CurrentVelocity = FVector::ZeroVector;
|
|
CurrentPitch = 0.0f;
|
|
CurrentYaw = 0.0f;
|
|
TargetRotation = FRotator::ZeroRotator;
|
|
LastMouseDelta = FVector2D::ZeroVector;
|
|
|
|
if (ShipMesh)
|
|
{
|
|
ShipMesh->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
|
|
ShipMesh->SetCollisionObjectType(ECC_GameTraceChannel1); // This is the "Player" channel
|
|
ShipMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Block);
|
|
ShipMesh->SetCollisionResponseToChannel(ECC_GameTraceChannel1, ECollisionResponse::ECR_Ignore);
|
|
ShipMesh->SetCollisionResponseToChannel(ECC_Pawn, ECollisionResponse::ECR_Ignore);
|
|
}
|
|
}
|
|
|
|
void ASpaceshipPawn::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
// Store player controller reference
|
|
PlayerControllerRef = Cast<APlayerController>(Controller);
|
|
|
|
if (PlayerControllerRef)
|
|
{
|
|
// Setup input mapping context
|
|
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerControllerRef->GetLocalPlayer()))
|
|
{
|
|
Subsystem->AddMappingContext(DefaultMappingContext, 0);
|
|
}
|
|
|
|
// Setup mouse capture and fullscreen
|
|
PlayerControllerRef->SetShowMouseCursor(false);
|
|
PlayerControllerRef->SetInputMode(FInputModeGameOnly());
|
|
|
|
// Request fullscreen mode using GameUserSettings
|
|
UGameUserSettings* GameUserSettings = UGameUserSettings::GetGameUserSettings();
|
|
if (GameUserSettings)
|
|
{
|
|
GameUserSettings->SetFullscreenMode(EWindowMode::Fullscreen);
|
|
GameUserSettings->SetScreenResolution(FIntPoint(1920, 1080)); // Adjust the resolution if needed
|
|
GameUserSettings->ApplySettings(false);
|
|
}
|
|
}
|
|
|
|
// Create and add crosshair widget to viewport
|
|
if (CrosshairWidgetClass)
|
|
{
|
|
APlayerController* PlayerController = Cast<APlayerController>(GetController());
|
|
if (PlayerController)
|
|
{
|
|
CrosshairWidget = CreateWidget<UUserWidget>(PlayerController, CrosshairWidgetClass);
|
|
if (CrosshairWidget)
|
|
{
|
|
CrosshairWidget->AddToViewport();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ASpaceshipPawn::Tick(float DeltaTime)
|
|
{
|
|
Super::Tick(DeltaTime);
|
|
|
|
// Debug info
|
|
if (GEngine)
|
|
{
|
|
GEngine->AddOnScreenDebugMessage(-1, 0.0f, FColor::Yellow,
|
|
FString::Printf(TEXT("Thrust: %.2f"), CurrentThrust));
|
|
GEngine->AddOnScreenDebugMessage(-1, 0.0f, FColor::Green,
|
|
FString::Printf(TEXT("Velocity: %.2f"), CurrentVelocity.Size()));
|
|
}
|
|
|
|
// Smooth mouse movement
|
|
MouseDeltaSmoothed = FMath::Vector2DInterpTo(
|
|
MouseDeltaSmoothed,
|
|
LastMouseDelta,
|
|
DeltaTime,
|
|
MouseSmoothingSpeed
|
|
);
|
|
|
|
// Update rotation based on smoothed mouse movement
|
|
CurrentYaw += MouseDeltaSmoothed.X * MouseSensitivity * DeltaTime * 60.0f; // Multiply by 60 to normalize for frame rate
|
|
CurrentPitch = FMath::ClampAngle(
|
|
CurrentPitch + (MouseDeltaSmoothed.Y * MouseSensitivity * DeltaTime * 60.0f),
|
|
-85.0f,
|
|
85.0f
|
|
);
|
|
|
|
// Set target rotation
|
|
TargetRotation = FRotator(CurrentPitch, CurrentYaw, 0.0f);
|
|
|
|
// Smoothly interpolate to target rotation using quaternions for better interpolation
|
|
FQuat CurrentQuat = GetActorQuat();
|
|
FQuat TargetQuat = TargetRotation.Quaternion();
|
|
FQuat NewQuat = FQuat::Slerp(CurrentQuat, TargetQuat, RotationSpeed * DeltaTime);
|
|
SetActorRotation(NewQuat);
|
|
|
|
// Get the current forward vector
|
|
FVector CurrentDirection = GetActorForwardVector();
|
|
float CurrentSpeed = CurrentVelocity.Size();
|
|
|
|
// Handle thrust and velocity direction
|
|
if (!bThrottlePressed)
|
|
{
|
|
// Decelerate when not thrusting
|
|
CurrentThrust = FMath::FInterpTo(CurrentThrust, 0.0f, DeltaTime, ThrustDeceleration);
|
|
bWasThrottlePressed = false;
|
|
|
|
// Maintain current velocity direction when not thrusting
|
|
DesiredVelocityDirection = CurrentVelocity.GetSafeNormal();
|
|
}
|
|
else
|
|
{
|
|
// Accelerate when thrusting
|
|
CurrentThrust = FMath::FInterpConstantTo(CurrentThrust, TargetThrust, DeltaTime, ThrustAcceleration);
|
|
|
|
if (!bWasThrottlePressed)
|
|
{
|
|
// Just started thrusting - blend between current and new direction
|
|
if (!CurrentVelocity.IsNearlyZero())
|
|
{
|
|
// Blend between current velocity direction and new direction
|
|
FVector CurrentDir = CurrentVelocity.GetSafeNormal();
|
|
DesiredVelocityDirection = FMath::Lerp(CurrentDir, CurrentDirection, 1.0f - DirectionalInertia);
|
|
DesiredVelocityDirection.Normalize();
|
|
}
|
|
else
|
|
{
|
|
// If nearly stationary, use new direction
|
|
DesiredVelocityDirection = CurrentDirection;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Continuously blend towards current facing direction while thrusting
|
|
DesiredVelocityDirection = FMath::VInterpNormalRotationTo(
|
|
DesiredVelocityDirection,
|
|
CurrentDirection,
|
|
DeltaTime,
|
|
VelocityAlignmentSpeed
|
|
);
|
|
}
|
|
bWasThrottlePressed = true;
|
|
}
|
|
|
|
// Calculate thrust force
|
|
FVector ThrustForce = CurrentDirection * CurrentThrust;
|
|
|
|
// Apply drag
|
|
FVector DragForce = -CurrentVelocity * DragCoefficient;
|
|
|
|
// Update velocity with forces
|
|
CurrentVelocity += (ThrustForce + DragForce) * DeltaTime;
|
|
|
|
// Update position
|
|
FVector NewLocation = GetActorLocation() + (CurrentVelocity * DeltaTime);
|
|
SetActorLocation(NewLocation, true);
|
|
|
|
// Update spring arm rotation to match ship with smooth interpolation
|
|
if (CameraSpringArm)
|
|
{
|
|
FRotator SpringArmRotation = CameraSpringArm->GetComponentRotation();
|
|
FRotator TargetSpringArmRotation = NewQuat.Rotator();
|
|
FRotator NewSpringArmRotation = FMath::RInterpTo(
|
|
SpringArmRotation,
|
|
TargetSpringArmRotation,
|
|
DeltaTime,
|
|
RotationSpeed
|
|
);
|
|
CameraSpringArm->SetWorldRotation(NewSpringArmRotation);
|
|
}
|
|
|
|
// Reset mouse delta for next frame
|
|
LastMouseDelta = FVector2D::ZeroVector;
|
|
|
|
// Debug info
|
|
if (GEngine)
|
|
{
|
|
GEngine->AddOnScreenDebugMessage(-1, 0.0f, FColor::Yellow,
|
|
FString::Printf(TEXT("Smoothed Mouse Delta: X=%.2f Y=%.2f"), MouseDeltaSmoothed.X, MouseDeltaSmoothed.Y));
|
|
GEngine->AddOnScreenDebugMessage(-1, 0.0f, FColor::Green,
|
|
FString::Printf(TEXT("Current Rotation: P=%.2f Y=%.2f"), CurrentPitch, CurrentYaw));
|
|
}
|
|
}
|
|
|
|
void ASpaceshipPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
|
{
|
|
Super::SetupPlayerInputComponent(PlayerInputComponent);
|
|
|
|
if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
|
|
{
|
|
// Bind both Hold and Released events for throttle
|
|
EnhancedInputComponent->BindAction(ThrottleAction, ETriggerEvent::Started, this, &ASpaceshipPawn::HandleThrottleStarted);
|
|
EnhancedInputComponent->BindAction(ThrottleAction, ETriggerEvent::Completed, this, &ASpaceshipPawn::HandleThrottleReleased);
|
|
|
|
// Bind mouse control
|
|
EnhancedInputComponent->BindAction(MouseLookAction, ETriggerEvent::Triggered, this, &ASpaceshipPawn::HandleMouseLook);
|
|
|
|
// Add shooting binding
|
|
EnhancedInputComponent->BindAction(ShootAction, ETriggerEvent::Triggered, this, &ASpaceshipPawn::HandleShoot);
|
|
}
|
|
}
|
|
|
|
// Split the throttle handling into two functions
|
|
void ASpaceshipPawn::HandleThrottleStarted(const FInputActionValue& Value)
|
|
{
|
|
const float ThrottleValue = Value.Get<float>();
|
|
bThrottlePressed = true;
|
|
TargetThrust = ThrottleValue * MaxThrust;
|
|
}
|
|
|
|
void ASpaceshipPawn::HandleThrottleReleased(const FInputActionValue& Value)
|
|
{
|
|
bThrottlePressed = false;
|
|
}
|
|
|
|
void ASpaceshipPawn::HandleMouseLook(const FInputActionValue& Value)
|
|
{
|
|
const FVector2D MouseDelta = Value.Get<FVector2D>();
|
|
|
|
// Smoothly interpolate mouse delta
|
|
LastMouseDelta = MouseDelta;
|
|
}
|
|
|
|
void ASpaceshipPawn::HandleShoot(const FInputActionValue& Value)
|
|
{
|
|
// Debug message
|
|
if (GEngine)
|
|
GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Yellow, TEXT("HandleShoot Called"));
|
|
|
|
if (bCanFire)
|
|
{
|
|
Fire();
|
|
}
|
|
}
|
|
|
|
void ASpaceshipPawn::Fire()
|
|
{
|
|
// Debug messages
|
|
if (!ProjectileClass)
|
|
{
|
|
if (GEngine)
|
|
GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Red, TEXT("ProjectileClass not set!"));
|
|
return;
|
|
}
|
|
|
|
UWorld* World = GetWorld();
|
|
if (World)
|
|
{
|
|
FVector SpawnLocation = ProjectileSpawnPoint->GetComponentLocation();
|
|
FRotator SpawnRotation = GetActorRotation();
|
|
|
|
FActorSpawnParameters SpawnParams;
|
|
SpawnParams.Owner = this;
|
|
SpawnParams.Instigator = GetInstigator();
|
|
|
|
// Spawn the projectile
|
|
ASpaceshipProjectile* Projectile = World->SpawnActor<ASpaceshipProjectile>(
|
|
ProjectileClass,
|
|
SpawnLocation,
|
|
SpawnRotation,
|
|
SpawnParams
|
|
);
|
|
|
|
if (Projectile)
|
|
{
|
|
if (GEngine)
|
|
GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Green, TEXT("Projectile Spawned"));
|
|
}
|
|
else
|
|
{
|
|
if (GEngine)
|
|
GEngine->AddOnScreenDebugMessage(-1, 2.f, FColor::Red, TEXT("Failed to spawn projectile"));
|
|
}
|
|
|
|
// Start fire rate timer
|
|
bCanFire = false;
|
|
GetWorldTimerManager().SetTimer(FireTimerHandle, this, &ASpaceshipPawn::ResetFire, FireRate, false);
|
|
}
|
|
}
|
|
|
|
void ASpaceshipPawn::ResetFire()
|
|
{
|
|
bCanFire = true;
|
|
} |