#include "SpaceshipPawn.h" #include "GameFramework/SpringArmComponent.h" #include "Camera/CameraComponent.h" #include "Components/StaticMeshComponent.h" #include "EnhancedInputComponent.h" #include "EnhancedInputSubsystems.h" #include "SpaceshipProjectile.h" ASpaceshipPawn::ASpaceshipPawn() { PrimaryActorTick.bCanEverTick = true; // Create ship mesh ShipMesh = CreateDefaultSubobject(TEXT("ShipMesh")); RootComponent = ShipMesh; ShipMesh->SetSimulatePhysics(false); ShipMesh->SetEnableGravity(false); // Create camera spring arm CameraSpringArm = CreateDefaultSubobject(TEXT("CameraSpringArm")); CameraSpringArm->SetupAttachment(RootComponent); CameraSpringArm->TargetArmLength = 400.0f; CameraSpringArm->bEnableCameraLag = true; CameraSpringArm->CameraLagSpeed = 3.0f; CameraSpringArm->bEnableCameraRotationLag = true; CameraSpringArm->CameraRotationLagSpeed = 10.0f; // Create camera Camera = CreateDefaultSubobject(TEXT("Camera")); Camera->SetupAttachment(CameraSpringArm, USpringArmComponent::SocketName); // Initialize movement variables CurrentThrust = 0.0f; TargetThrust = 0.0f; bThrottlePressed = false; CurrentVelocity = FVector::ZeroVector; CurrentRotation = GetActorQuat(); TargetRotation = GetActorRotation(); } void ASpaceshipPawn::BeginPlay() { Super::BeginPlay(); // Add Input Mapping Context if (APlayerController* PlayerController = Cast(Controller)) { if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(PlayerController->GetLocalPlayer())) { Subsystem->AddMappingContext(DefaultMappingContext, 0); } } } void ASpaceshipPawn::Tick(float DeltaTime) { Super::Tick(DeltaTime); // Handle thrust deceleration when not pressing throttle if (!bThrottlePressed) { CurrentThrust = FMath::FInterpTo(CurrentThrust, 0.0f, DeltaTime, ThrustDeceleration); } else { CurrentThrust = FMath::FInterpTo(CurrentThrust, TargetThrust, DeltaTime, ThrustAcceleration); } // Calculate movement FVector ThrustDirection = GetActorForwardVector(); FVector ThrustForce = ThrustDirection * CurrentThrust; // Apply drag FVector DragForce = -CurrentVelocity * DragCoefficient; // Update velocity CurrentVelocity += (ThrustForce + DragForce) * DeltaTime; // Update position FVector NewLocation = GetActorLocation() + (CurrentVelocity * DeltaTime); SetActorLocation(NewLocation, true); // Smooth rotation using quaternion interpolation FQuat TargetQuat = TargetRotation.Quaternion(); CurrentRotation = FQuat::Slerp(CurrentRotation, TargetQuat, RotationSpeed * DeltaTime); SetActorRotation(CurrentRotation); // 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())); } } void ASpaceshipPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) { Super::SetupPlayerInputComponent(PlayerInputComponent); if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked(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(MouseControlAction, ETriggerEvent::Started, this, &ASpaceshipPawn::HandleMouseControl); // Bind fire action EnhancedInputComponent->BindAction(FireAction, ETriggerEvent::Triggered, this, &ASpaceshipPawn::HandleFire); } } // Split the throttle handling into two functions void ASpaceshipPawn::HandleThrottleStarted(const FInputActionValue& Value) { const float ThrottleValue = Value.Get(); bThrottlePressed = true; TargetThrust = ThrottleValue * MaxThrust; } void ASpaceshipPawn::HandleThrottleReleased(const FInputActionValue& Value) { bThrottlePressed = false; } void ASpaceshipPawn::HandleMouseControl(const FInputActionValue& Value) { const FVector2D MouseValue = Value.Get(); // Get current rotation FRotator CurrentRot = GetActorRotation(); // Calculate new rotation values float NewPitch = CurrentRot.Pitch + (-MouseValue.Y * RotationSpeed * GetWorld()->GetDeltaSeconds()); float NewYaw = CurrentRot.Yaw + (MouseValue.X * RotationSpeed * GetWorld()->GetDeltaSeconds()); // Clamp pitch to prevent flipping NewPitch = FMath::ClampAngle(NewPitch, -85.0f, 85.0f); // Set target rotation TargetRotation = FRotator(NewPitch, NewYaw, CurrentRot.Roll); // Update camera spring arm rotation to match CameraSpringArm->SetRelativeRotation(FRotator(NewPitch * 0.3f, 0.0f, 0.0f)); } void ASpaceshipPawn::HandleFire(const FInputActionValue& Value) { UWorld* World = GetWorld(); if (World) { FVector SpawnLocation = ShipMesh->GetSocketLocation(TEXT("ProjectileSpawn")); if (SpawnLocation == FVector::ZeroVector) { SpawnLocation = GetActorLocation() + (GetActorForwardVector() * 100.0f); } FRotator SpawnRotation = GetActorRotation(); FActorSpawnParameters SpawnParams; SpawnParams.Owner = this; SpawnParams.Instigator = GetInstigator(); World->SpawnActor( ASpaceshipProjectile::StaticClass(), SpawnLocation, SpawnRotation, SpawnParams ); } }