Hi,
I added a Pawn Movement Component to my custom pawn to handle some physics by myself.
Unfortunately I did not get to work.
For debugging purposes I am setting the Z-component of the velocity vector, regarding to this, my mesh attached to the RootComponent of my Pawn should lift.
See below the Helicopter Class which is the Pawn to be controlled and underneath attached the described Movement Component:
Helicopter.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "GameFramework/SpringArmComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Camera/CameraComponent.h"
#include <string>
#include "Helicopter.generated.h"
class UHelicopterMovementComponent;
UCLASS()
class SAR_API AHelicopter : public APawn
{
GENERATED_BODY()
UPROPERTY(Category = Mesh, VisibleDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
UStaticMeshComponent* Fuselage;
UPROPERTY(Category = Mesh, VisibleDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
UStaticMeshComponent* Rotor;
UPROPERTY(Category = Camera, VisibleDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
class USpringArmComponent* SpringArmPilotCam;
UPROPERTY(Category = Camera, VisibleDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
class UCameraComponent* PilotCamera;
float MainRotorRpm;
float TailRotorRpm;
bool m_engineStarted;
public:
// Sets default values for this pawn's properties
AHelicopter();
UFUNCTION(BlueprintPure, Category = "Controls")
float GetCurrentCollective();
UFUNCTION(BlueprintPure, Category = "Controls")
float GetCurrentCyclicElevation();
UFUNCTION(BlueprintPure, Category = "Controls")
float GetCurrentCyclicAileron();
UFUNCTION(BlueprintPure, Category = "Controls")
float GetCurrentYaw();
UFUNCTION(BlueprintPure, Category = "Controls")
FVector GetHelicopterVelocity();
UFUNCTION(BlueprintPure, Category = "Controls")
bool EngineStarted();
virtual void Tick(float DeltaTime) override;
virtual void NotifyHit(class UPrimitiveComponent* MyComp, class AActor* Other, class UPrimitiveComponent* OtherComp, bool bSelfMoved, FVector HitLocation, FVector HitNormal, FVector NormalImpulse, const FHitResult& Hit) override;
virtual UPawnMovementComponent* GetMovementComponent() const override;
UHelicopterMovementComponent* GetHelicopterMovementComponent() const;
void StartEngine();
void StopEngine();
private:
UPROPERTY(Category = Helicopter, VisibleDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
UHelicopterMovementComponent* HelicopterMovementComponent;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
void CollectiveInput(float Value);
void CyclicElevationInput(float Value);
void CyclicAileronInput(float Value);
void YawInput(float Value);
private:
UPROPERTY(EditAnywhere, Category = "Controls")
float m_collective;
UPROPERTY(EditAnywhere, Category = "Controls")
float m_cyclicElevation;
UPROPERTY(EditAnywhere, Category = "Controls")
float m_cyclicAileron;
UPROPERTY(EditAnywhere, Category = "Controls")
float m_yaw;
};
Helicopter.cpp
#include "Helicopter.h"
#include "Kismet/GameplayStatics.h"
#include "Engine/World.h"
#include <string>
#include "Engine/Engine.h"
#include "HelicopterMovementComponent.h"
#include "ConstructorHelpers.h"
#include "DrawDebugHelpers.h"
// Sets default values
AHelicopter::AHelicopter()
{
// 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;
// Create static mesh component
Fuselage = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Fuselage"));
const ConstructorHelpers::FObjectFinder<UStaticMesh> FuselageObj(TEXT("/Game/Meshes/ec135"));
Fuselage->SetStaticMesh(FuselageObj.Object);
RootComponent = Fuselage;
FBoxSphereBounds bb = Fuselage->Bounds;
DrawDebugBox(GetWorld(), bb.GetBox().GetCenter(), bb.BoxExtent, FColor::Red, false);
Rotor = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Rotor"));
const ConstructorHelpers::FObjectFinder<UStaticMesh> RotorObj(TEXT("/Game/Meshes/rotor"));
Rotor->SetStaticMesh(RotorObj.Object);
Rotor->AttachToComponent(RootComponent, FAttachmentTransformRules::KeepRelativeTransform);
Rotor->SetRelativeLocation(FVector(305.f, 0.f, 183.f));
// Create a spring arm component
SpringArmPilotCam = CreateDefaultSubobject<USpringArmComponent>(TEXT("Spring Arm Pilot Cam"));
SpringArmPilotCam->SetupAttachment(RootComponent); // Attach SpringArmCamera to RootComponent
SpringArmPilotCam->TargetArmLength = 0.f; // The camera follows at this distance behind the character
SpringArmPilotCam->SocketOffset = FVector(-140.f, 30.f, 40.f);
//SpringArmPilotCam->AddLocalRotation(FRotator(0.f, 0.f, -180.f));
SpringArmPilotCam->SetRelativeRotation(FRotator(0.f, 0.f, -180.f));
SpringArmPilotCam->bEnableCameraLag = false; // Do not allow camera to lag
SpringArmPilotCam->CameraLagSpeed = 15.f;
//SpringArmPilotCam->bUsePawnControlRotation = true;
// Create camera component
PilotCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("Pilot Camera"));
PilotCamera->SetupAttachment(SpringArmPilotCam, USpringArmComponent::SocketName); // Attach the camera
PilotCamera->bUsePawnControlRotation = true; // Don't rotate camera with controller
AutoPossessPlayer = EAutoReceiveInput::Player0;
AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;
HelicopterMovementComponent = CreateDefaultSubobject<UHelicopterMovementComponent>(TEXT("Helicopter Movement Component"));
HelicopterMovementComponent->UpdatedComponent = RootComponent;
m_collective = 0.f;
m_cyclicElevation = 0.f;
m_cyclicAileron = 0.f;
m_yaw = 0.f;
m_engineStarted = false;
}
// Called when the game starts or when spawned
void AHelicopter::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AHelicopter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
//Make the main rotor rotating after start
float omega = (2 * PI * MainRotorRpm);
float angle = omega * DeltaTime;
float rotation = Rotor->GetComponentRotation().Yaw;
Rotor->SetRelativeRotation(FRotator(0.f, rotation + angle, 0.f));
}
void AHelicopter::NotifyHit(UPrimitiveComponent* MyComp, AActor* Other, UPrimitiveComponent* OtherComp, bool bSelfMoved,
FVector HitLocation, FVector HitNormal, FVector NormalImpulse, const FHitResult& Hit)
{
}
// Called to bind functionality to input
void AHelicopter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
check(PlayerInputComponent);
// Bind our control axis' to callback functions
PlayerInputComponent->BindAxis("LookPitch", this, &AHelicopter::LookPitch);
PlayerInputComponent->BindAxis("LookYaw", this, &AHelicopter::LookYaw);
PlayerInputComponent->BindAction("StartEngine", IE_Pressed, this, &AHelicopter::StartEngine);
PlayerInputComponent->BindAction("StopEngine", IE_Pressed, this, &AHelicopter::StopEngine);
PlayerInputComponent->BindAxis("Collective", this, &AHelicopter::CollectiveInput);
PlayerInputComponent->BindAxis("CyclicElevation", this, &AHelicopter::CyclicElevationInput);
PlayerInputComponent->BindAxis("CyclicAileron", this, &AHelicopter::CyclicAileronInput);
PlayerInputComponent->BindAxis("Yaw", this, &AHelicopter::YawInput);
}
UPawnMovementComponent* AHelicopter::GetMovementComponent() const
{
return Cast<UHelicopterMovementComponent>(HelicopterMovementComponent);
}
void AHelicopter::StartEngine()
{
HelicopterMovementComponent->Engine.EngineStarted = true;
MainRotorRpm = 395;
m_engineStarted = true;
}
void AHelicopter::StopEngine()
{
HelicopterMovementComponent->Engine.EngineStarted = false;
MainRotorRpm = 0;
m_engineStarted = false;
}
void AHelicopter::CollectiveInput(float Value)
{
m_collective = Value;
if (HelicopterMovementComponent && (HelicopterMovementComponent->UpdatedComponent == RootComponent))
{
HelicopterMovementComponent->AddInputVector(GetActorUpVector() * Value);
}
}
void AHelicopter::CyclicElevationInput(float Value)
{
m_cyclicElevation = Value;
if (HelicopterMovementComponent && (HelicopterMovementComponent->UpdatedComponent == RootComponent))
{
HelicopterMovementComponent->AddInputVector(GetActorForwardVector() * Value);
}
}
void AHelicopter::CyclicAileronInput(float Value)
{
m_cyclicAileron = Value;
if (HelicopterMovementComponent && (HelicopterMovementComponent->UpdatedComponent == RootComponent))
{
HelicopterMovementComponent->AddInputVector(GetActorRightVector() * Value);
}
}
void AHelicopter::YawInput(float Value)
{
m_yaw = Value;
}
float AHelicopter::GetCurrentCollective()
{
return m_collective;
}
float AHelicopter::GetCurrentCyclicElevation()
{
return m_cyclicElevation;
}
float AHelicopter::GetCurrentCyclicAileron()
{
return m_cyclicAileron;
}
float AHelicopter::GetCurrentYaw()
{
return m_yaw;
}
FVector AHelicopter::GetHelicopterVelocity()
{
return GetVelocity();
}
bool AHelicopter::EngineStarted()
{
return m_engineStarted;
}
HelicopterMovementComponent.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/PawnMovementComponent.h"
#include "Curves/CurveFloat.h"
#include "HelicopterMovementComponent.generated.h"
class UMainRotor;
class UTailRotor;
USTRUCT()
struct SAR_API FHelicopterEngine
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, Category=Engine)
bool EngineStarted;
};
USTRUCT()
struct SAR_API FHelicopterSetup
{
GENERATED_USTRUCT_BODY()
UPROPERTY(EditAnywhere, Category=HelicopterSetup)
float Mass;
UPROPERTY(EditAnywhere, Category=HelicopterSetup)
FVector COG;
};
USTRUCT()
struct SAR_API FRotorSetup
{
GENERATED_USTRUCT_BODY()
// Number of blades attachted to this rotor.
UPROPERTY(EditAnywhere, Category=RotorSetup)
int BladeCount;
// Rounds per minute of this rotor during operation.
UPROPERTY(EditAnywhere, Category=RotorSetup)
int RPM;
// Blade length.
UPROPERTY(EditAnywhere, Category=RotorSetup)
float Length;
// Blade width.
UPROPERTY(EditAnywhere, Category=RotorSetup)
float Width;
// Additional offset to give the rotor.
UPROPERTY(EditAnywhere, Category=RotorSetup)
FVector AdditionalOffset;
};
/**
*
*/
UCLASS()
class SAR_API UHelicopterMovementComponent : public UPawnMovementComponent
{
GENERATED_BODY()
FMatrix Tensor;
private:
float CollectiveInput(FVector input);
float CyclicElevationInput(FVector input);
float CyclicAileronInput(FVector input);
public:
UPROPERTY(Instanced, EditAnywhere, Category=RotorSetup)
UMainRotor* MainRotor;
UPROPERTY(Instanced, EditAnywhere, Category=RotorSetup)
UTailRotor* TailRotor;
UPROPERTY(EditAnywhere, Category=HelicopterSetup)
FHelicopterSetup HelicopterSetup;
UPROPERTY(EditAnywhere, Category=Engine)
FHelicopterEngine Engine;
UHelicopterMovementComponent();
void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
void InitTensor(FBoxSphereBounds Bounds);
};
HelicopterMovementComponent.cpp
#include "HelicopterMovementComponent.h"
#include "Rotor.h"
#include "Helicopter.h"
#include "Public/MainRotor.h"
#include "Public/TailRotor.h"
UHelicopterMovementComponent::UHelicopterMovementComponent()
{
MainRotor = CreateDefaultSubobject<UMainRotor>(TEXT("MainRotorInstance"));
TailRotor = CreateDefaultSubobject<UTailRotor>(TEXT("TailRotorInstance"));
HelicopterSetup.Mass = 1455.0f;
}
void UHelicopterMovementComponent::TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if(!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime))
{
return;
}
MainRotor->RPM = 395;
UE_LOG(LogTemp, Warning, TEXT("Engine %s RPM %f "), (Engine.EngineStarted ? TEXT("True") : TEXT("False")), MainRotor->RPM)
FVector input = ConsumeInputVector();
MainRotor->collective = CollectiveInput(input);
//float lift = MainRotor->GetLift();
float lift = 10000;
Velocity.Z = Velocity.Z + (lift / HelicopterSetup.Mass) * DeltaTime;
UE_LOG(LogTemp, Warning, TEXT("Velocity %f Mass %f DeltaTime %f CollectiveInput %f Lift %f "), Velocity.Z, HelicopterSetup.Mass, DeltaTime, MainRotor->collective, lift)
UpdateComponentVelocity();
}
float UHelicopterMovementComponent::CollectiveInput(FVector input)
{
float collective;
float collectiveValue = input.Z;
if (collectiveValue > 0.f)
{
collective = collectiveValue * MainRotor->maxCollective;
}
else
{
collective = collectiveValue * -MainRotor->minCollective;
}
return collective;
}
float UHelicopterMovementComponent::CyclicElevationInput(FVector input)
{
float cyclicElevation;
float cyclicElevationValue = input.X;
if (cyclicElevationValue > 0.f)
{
cyclicElevation = cyclicElevationValue * MainRotor->maxCyclicElevation;
}
else
{
cyclicElevation = cyclicElevationValue * -MainRotor->minCyclicElevation;
}
return cyclicElevation;
}
float UHelicopterMovementComponent::CyclicAileronInput(FVector input)
{
float cyclicAileron;
float cyclicAileronValue = input.Z;
if (cyclicAileronValue > 0.f)
{
cyclicAileron = cyclicAileronValue * MainRotor->maxCyclicAileron;
}
else
{
cyclicAileron = cyclicAileronValue * -MainRotor->minCyclicAileron;
}
return cyclicAileron;
}
void UHelicopterMovementComponent::InitTensor(FBoxSphereBounds Bounds)
{
//TensorInit
}