From 31f910e51d0c478289a8da23eac6ce55dc3d268d Mon Sep 17 00:00:00 2001 From: farsight-andre <44437550+Ahuna2@users.noreply.github.com> Date: Wed, 22 Apr 2026 09:35:02 +0300 Subject: [PATCH] initial flight --- Source/WhipWing/Game/WhipWingGameMode.cpp | 8 +- Source/WhipWing/Game/WhipWingGameMode.h | 9 +- .../Player/FlightMovementComponent.cpp | 164 ++++++++++++++++-- .../WhipWing/Player/FlightMovementComponent.h | 94 ++++++++-- Source/WhipWing/Player/WhipWingPawn.cpp | 45 +++-- Source/WhipWing/Player/WhipWingPawn.h | 36 ++-- Source/WhipWing/WhipWing.Build.cs | 2 +- WhipWing.uproject | 9 +- 8 files changed, 299 insertions(+), 68 deletions(-) diff --git a/Source/WhipWing/Game/WhipWingGameMode.cpp b/Source/WhipWing/Game/WhipWingGameMode.cpp index ddac4a9..3cd73be 100644 --- a/Source/WhipWing/Game/WhipWingGameMode.cpp +++ b/Source/WhipWing/Game/WhipWingGameMode.cpp @@ -1,5 +1,7 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - #include "Game/WhipWingGameMode.h" +#include "Player/WhipWingPawn.h" +AWhipWingGameMode::AWhipWingGameMode() +{ + DefaultPawnClass = AWhipWingPawn::StaticClass(); +} diff --git a/Source/WhipWing/Game/WhipWingGameMode.h b/Source/WhipWing/Game/WhipWingGameMode.h index 9851cb9..d5204b6 100644 --- a/Source/WhipWing/Game/WhipWingGameMode.h +++ b/Source/WhipWing/Game/WhipWingGameMode.h @@ -1,17 +1,14 @@ -// Fill out your copyright notice in the Description page of Project Settings. - #pragma once #include "CoreMinimal.h" #include "GameFramework/GameModeBase.h" #include "WhipWingGameMode.generated.h" -/** - * - */ UCLASS() class WHIPWING_API AWhipWingGameMode : public AGameModeBase { GENERATED_BODY() - + +public: + AWhipWingGameMode(); }; diff --git a/Source/WhipWing/Player/FlightMovementComponent.cpp b/Source/WhipWing/Player/FlightMovementComponent.cpp index 841f7e3..c657597 100644 --- a/Source/WhipWing/Player/FlightMovementComponent.cpp +++ b/Source/WhipWing/Player/FlightMovementComponent.cpp @@ -1,34 +1,168 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - #include "Player/FlightMovementComponent.h" +#include "Engine/Engine.h" -// Sets default values for this component's properties UFlightMovementComponent::UFlightMovementComponent() { - // Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features - // off to improve performance if you don't need them. PrimaryComponentTick.bCanEverTick = true; - - // ... } - -// Called when the game starts void UFlightMovementComponent::BeginPlay() { Super::BeginPlay(); - - // ... - + ForwardYaw = GetOwner()->GetActorRotation().Yaw; + CurrentSpeed = BaseSpeed; } +void UFlightMovementComponent::UpdateControllerTransforms(const FTransform& Left, const FTransform& Right) +{ + CachedLeftTransform = Left; + CachedRightTransform = Right; + bHasControllerData = true; +} -// Called every frame void UFlightMovementComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); - // ... + if (!bHasControllerData) + return; + + float TargetPitch = CalculateTargetPitch(); + float TargetRoll = CalculateTargetRoll(); + + CurrentPitch = FMath::FInterpTo(CurrentPitch, TargetPitch, DeltaTime, SteeringSmoothingSpeed); + CurrentRoll = FMath::FInterpTo(CurrentRoll, TargetRoll, DeltaTime, SteeringSmoothingSpeed); + + if (DetectFlap(DeltaTime)) + { + VerticalVelocity += FlapLiftImpulse; + + if (bShowDebug && GEngine) + { + GEngine->AddOnScreenDebugMessage(10, 0.5f, FColor::Yellow, TEXT(">>> FLAP <<<")); + } + } + + VerticalVelocity -= Gravity * DeltaTime; + VerticalVelocity = FMath::Clamp(VerticalVelocity, -MaxFallVelocity, MaxLiftVelocity); + + FlapCooldownTimer = FMath::Max(0.f, FlapCooldownTimer - DeltaTime); + + PrevLeftPos = CachedLeftTransform.GetLocation(); + PrevRightPos = CachedRightTransform.GetLocation(); + bHasPreviousPositions = true; + + float PitchRad = FMath::DegreesToRadians(CurrentPitch); + CurrentSpeed -= Gravity * FMath::Sin(PitchRad) * GlideSpeedTransfer * DeltaTime; + CurrentSpeed = FMath::Clamp(CurrentSpeed, MinSpeed, MaxSpeed); + + FVector ForwardDir = FRotationMatrix(FRotator(CurrentPitch, ForwardYaw, 0.f)).GetUnitAxis(EAxis::X); + FVector RightDir = FRotationMatrix(FRotator(0.f, ForwardYaw, 0.f)).GetUnitAxis(EAxis::Y); + + FVector Movement = ForwardDir * CurrentSpeed * DeltaTime; + Movement += RightDir * -CurrentRoll * LateralStrafeSpeed * DeltaTime; + Movement.Z += VerticalVelocity * DeltaTime; + + FHitResult HitResult; + GetOwner()->AddActorWorldOffset(Movement, true, &HitResult); + + if (HitResult.bBlockingHit) + { + if (HitResult.Normal.Z > 0.5f && VerticalVelocity < 0.f) + { + VerticalVelocity = 0.f; + } + else if (HitResult.Normal.Z < -0.5f && VerticalVelocity > 0.f) + { + VerticalVelocity = 0.f; + } + } + + if (bShowDebug) + { + DrawDebugVisualization(); + } } +float UFlightMovementComponent::CalculateTargetPitch() const +{ + FVector LeftForward = CachedLeftTransform.GetRotation().GetForwardVector(); + FVector RightForward = CachedRightTransform.GetRotation().GetForwardVector(); + + float LeftPitch = FMath::RadiansToDegrees(FMath::Asin(LeftForward.Z)); + float RightPitch = FMath::RadiansToDegrees(FMath::Asin(RightForward.Z)); + + float AveragePitch = (LeftPitch + RightPitch) * 0.5f; + return FMath::Clamp(AveragePitch * PitchSensitivity, -MaxPitchAngle, MaxPitchAngle); +} + +float UFlightMovementComponent::CalculateTargetRoll() const +{ + float HeightDiff = CachedRightTransform.GetLocation().Z - CachedLeftTransform.GetLocation().Z; + return FMath::Clamp(HeightDiff * RollSensitivity, -MaxRollAngle, MaxRollAngle); +} + +bool UFlightMovementComponent::DetectFlap(float DeltaTime) +{ + if (!bHasPreviousPositions) + return false; + + if (FlapCooldownTimer > 0.f) + return false; + + FVector LeftPos = CachedLeftTransform.GetLocation(); + FVector RightPos = CachedRightTransform.GetLocation(); + + float LeftVertVel = (LeftPos.Z - PrevLeftPos.Z) / DeltaTime; + float RightVertVel = (RightPos.Z - PrevRightPos.Z) / DeltaTime; + float AvgVertVel = (LeftVertVel + RightVertVel) * 0.5f; + + bool bIsMovingDown = AvgVertVel < -FlapDetectionThreshold; + + if (bWasMovingDown && !bIsMovingDown) + { + bWasMovingDown = false; + FlapCooldownTimer = FlapCooldown; + return true; + } + + bWasMovingDown = bIsMovingDown; + return false; +} + +void UFlightMovementComponent::SetForwardDirection(float NewYaw) +{ + ForwardYaw = NewYaw; +} + +void UFlightMovementComponent::DrawDebugVisualization() const +{ + if (!GEngine) + return; + + float HeightDiff = CachedRightTransform.GetLocation().Z - CachedLeftTransform.GetLocation().Z; + + GEngine->AddOnScreenDebugMessage(1, 0.f, FColor::Cyan, + FString::Printf(TEXT("Roll: %+.1f (height diff: %+.1f)"), CurrentRoll, HeightDiff)); + GEngine->AddOnScreenDebugMessage(2, 0.f, FColor::Cyan, + FString::Printf(TEXT("Pitch: %+.1f"), CurrentPitch)); + GEngine->AddOnScreenDebugMessage(3, 0.f, FColor::Cyan, + FString::Printf(TEXT("Forward Yaw: %.1f (fixed)"), ForwardYaw)); + GEngine->AddOnScreenDebugMessage(11, 0.f, FColor::Cyan, + FString::Printf(TEXT("Current Speed: %.1f"), CurrentSpeed)); + GEngine->AddOnScreenDebugMessage(9, 0.f, FColor::Cyan, + FString::Printf(TEXT("Lateral: %+.1f"), -CurrentRoll * LateralStrafeSpeed)); + GEngine->AddOnScreenDebugMessage(4, 0.f, VerticalVelocity >= 0.f ? FColor::Yellow : FColor::Red, + FString::Printf(TEXT("Vert Vel: %+.1f"), VerticalVelocity)); + GEngine->AddOnScreenDebugMessage(5, 0.f, FColor::Green, + FString::Printf(TEXT("Position: %s"), *GetOwner()->GetActorLocation().ToString())); + GEngine->AddOnScreenDebugMessage(6, 0.f, FColor::White, + FString::Printf(TEXT("Grounded: %s"), VerticalVelocity == 0.f && FlapCooldownTimer <= 0.f ? TEXT("YES") : TEXT("no"))); + + FVector LP = CachedLeftTransform.GetLocation(); + FVector RP = CachedRightTransform.GetLocation(); + GEngine->AddOnScreenDebugMessage(7, 0.f, FColor::Orange, + FString::Printf(TEXT("Left ctrl: X=%+.0f Y=%+.0f Z=%+.0f"), LP.X, LP.Y, LP.Z)); + GEngine->AddOnScreenDebugMessage(8, 0.f, FColor::Orange, + FString::Printf(TEXT("Right ctrl: X=%+.0f Y=%+.0f Z=%+.0f"), RP.X, RP.Y, RP.Z)); +} diff --git a/Source/WhipWing/Player/FlightMovementComponent.h b/Source/WhipWing/Player/FlightMovementComponent.h index a31fee3..2eb291e 100644 --- a/Source/WhipWing/Player/FlightMovementComponent.h +++ b/Source/WhipWing/Player/FlightMovementComponent.h @@ -1,28 +1,98 @@ -// Fill out your copyright notice in the Description page of Project Settings. - #pragma once #include "CoreMinimal.h" #include "Components/ActorComponent.h" #include "FlightMovementComponent.generated.h" - -UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) +UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent)) class WHIPWING_API UFlightMovementComponent : public UActorComponent { GENERATED_BODY() -public: - // Sets default values for this component's properties +public: UFlightMovementComponent(); -protected: - // Called when the game starts - virtual void BeginPlay() override; + void UpdateControllerTransforms(const FTransform& Left, const FTransform& Right); -public: - // Called every frame virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; - + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Speed") + float BaseSpeed = 500.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Speed") + float MinSpeed = 150.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Speed") + float MaxSpeed = 1200.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Speed") + float GlideSpeedTransfer = 1.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Gravity") + float Gravity = 980.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Flap") + float FlapLiftImpulse = 400.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Flap") + float MaxLiftVelocity = 600.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Flap") + float MaxFallVelocity = 800.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Flap") + float FlapCooldown = 0.5f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Flap") + float FlapDetectionThreshold = 100.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Steering") + float PitchSensitivity = 1.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Steering") + float RollSensitivity = 1.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Steering") + float MaxPitchAngle = 45.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Steering") + float MaxRollAngle = 45.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Steering") + float SteeringSmoothingSpeed = 5.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Steering") + float LateralStrafeSpeed = 400.f; + + void SetForwardDirection(float NewYaw); + float GetForwardYaw() const { return ForwardYaw; } + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Flight|Debug") + bool bShowDebug = false; + +protected: + virtual void BeginPlay() override; + +private: + float CurrentSpeed = 0.f; + float VerticalVelocity = 0.f; + float CurrentPitch = 0.f; + float CurrentRoll = 0.f; + float ForwardYaw = 0.f; + + FVector PrevLeftPos = FVector::ZeroVector; + FVector PrevRightPos = FVector::ZeroVector; + bool bHasPreviousPositions = false; + + float FlapCooldownTimer = 0.f; + bool bWasMovingDown = false; + + FTransform CachedLeftTransform; + FTransform CachedRightTransform; + bool bHasControllerData = false; + + float CalculateTargetPitch() const; + float CalculateTargetRoll() const; + bool DetectFlap(float DeltaTime); + void DrawDebugVisualization() const; }; diff --git a/Source/WhipWing/Player/WhipWingPawn.cpp b/Source/WhipWing/Player/WhipWingPawn.cpp index 7e1eced..f1b313c 100644 --- a/Source/WhipWing/Player/WhipWingPawn.cpp +++ b/Source/WhipWing/Player/WhipWingPawn.cpp @@ -1,34 +1,47 @@ -// Fill out your copyright notice in the Description page of Project Settings. - - #include "Player/WhipWingPawn.h" +#include "Player/FlightMovementComponent.h" +#include "Camera/CameraComponent.h" +#include "MotionControllerComponent.h" +#include "Components/SphereComponent.h" -// Sets default values AWhipWingPawn::AWhipWingPawn() { - // Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; + AutoPossessPlayer = EAutoReceiveInput::Player0; + CollisionSphere = CreateDefaultSubobject(TEXT("CollisionSphere")); + CollisionSphere->InitSphereRadius(34.f); + CollisionSphere->SetCollisionProfileName(TEXT("Pawn")); + SetRootComponent(CollisionSphere); + + VROrigin = CreateDefaultSubobject(TEXT("VROrigin")); + VROrigin->SetupAttachment(CollisionSphere); + + Camera = CreateDefaultSubobject(TEXT("Camera")); + Camera->SetupAttachment(VROrigin); + + LeftController = CreateDefaultSubobject(TEXT("LeftController")); + LeftController->SetupAttachment(VROrigin); + LeftController->MotionSource = FName("Left"); + + RightController = CreateDefaultSubobject(TEXT("RightController")); + RightController->SetupAttachment(VROrigin); + RightController->MotionSource = FName("Right"); + + FlightMovement = CreateDefaultSubobject(TEXT("FlightMovement")); } -// Called when the game starts or when spawned void AWhipWingPawn::BeginPlay() { Super::BeginPlay(); - } -// Called every frame void AWhipWingPawn::Tick(float DeltaTime) { Super::Tick(DeltaTime); + FlightMovement->UpdateControllerTransforms( + LeftController->GetRelativeTransform(), + RightController->GetRelativeTransform() + ); } - -// Called to bind functionality to input -void AWhipWingPawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) -{ - Super::SetupPlayerInputComponent(PlayerInputComponent); - -} - diff --git a/Source/WhipWing/Player/WhipWingPawn.h b/Source/WhipWing/Player/WhipWingPawn.h index f06564c..3f02062 100644 --- a/Source/WhipWing/Player/WhipWingPawn.h +++ b/Source/WhipWing/Player/WhipWingPawn.h @@ -1,29 +1,43 @@ -// Fill out your copyright notice in the Description page of Project Settings. - #pragma once #include "CoreMinimal.h" #include "GameFramework/Pawn.h" #include "WhipWingPawn.generated.h" +class USphereComponent; +class UCameraComponent; +class UMotionControllerComponent; +class UFlightMovementComponent; + UCLASS() class WHIPWING_API AWhipWingPawn : public APawn { GENERATED_BODY() public: - // Sets default values for this pawn's properties AWhipWingPawn(); -protected: - // Called when the game starts or when spawned - virtual void BeginPlay() override; - -public: - // Called every frame virtual void Tick(float DeltaTime) override; - // Called to bind functionality to input - virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components") + UFlightMovementComponent* FlightMovement; +protected: + virtual void BeginPlay() override; + +private: + UPROPERTY(VisibleAnywhere, Category = "Components") + USphereComponent* CollisionSphere; + + UPROPERTY(VisibleAnywhere, Category = "Components") + USceneComponent* VROrigin; + + UPROPERTY(VisibleAnywhere, Category = "Components") + UCameraComponent* Camera; + + UPROPERTY(VisibleAnywhere, Category = "Components") + UMotionControllerComponent* LeftController; + + UPROPERTY(VisibleAnywhere, Category = "Components") + UMotionControllerComponent* RightController; }; diff --git a/Source/WhipWing/WhipWing.Build.cs b/Source/WhipWing/WhipWing.Build.cs index c8c6301..5bf6b2c 100644 --- a/Source/WhipWing/WhipWing.Build.cs +++ b/Source/WhipWing/WhipWing.Build.cs @@ -8,7 +8,7 @@ public class WhipWing : ModuleRules { PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); + PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay" }); PrivateDependencyModuleNames.AddRange(new string[] { }); diff --git a/WhipWing.uproject b/WhipWing.uproject index ad493d5..dc8278d 100644 --- a/WhipWing.uproject +++ b/WhipWing.uproject @@ -20,8 +20,8 @@ "SupportedTargetPlatforms": [ "Win64", "Linux", - "Android", - "VisionOS" + "LinuxArm64", + "Android" ] }, { @@ -30,6 +30,7 @@ "SupportedTargetPlatforms": [ "Win64", "Linux", + "LinuxArm64", "Android" ] }, @@ -39,8 +40,8 @@ "SupportedTargetPlatforms": [ "Win64", "Linux", - "Android", - "VisionOS" + "LinuxArm64", + "Android" ] } ],