initial flight
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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<USphereComponent>(TEXT("CollisionSphere"));
|
||||
CollisionSphere->InitSphereRadius(34.f);
|
||||
CollisionSphere->SetCollisionProfileName(TEXT("Pawn"));
|
||||
SetRootComponent(CollisionSphere);
|
||||
|
||||
VROrigin = CreateDefaultSubobject<USceneComponent>(TEXT("VROrigin"));
|
||||
VROrigin->SetupAttachment(CollisionSphere);
|
||||
|
||||
Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));
|
||||
Camera->SetupAttachment(VROrigin);
|
||||
|
||||
LeftController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("LeftController"));
|
||||
LeftController->SetupAttachment(VROrigin);
|
||||
LeftController->MotionSource = FName("Left");
|
||||
|
||||
RightController = CreateDefaultSubobject<UMotionControllerComponent>(TEXT("RightController"));
|
||||
RightController->SetupAttachment(VROrigin);
|
||||
RightController->MotionSource = FName("Right");
|
||||
|
||||
FlightMovement = CreateDefaultSubobject<UFlightMovementComponent>(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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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[] { });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user