Hello, I am currently working with the flying code template and am trying to get the aircraft to do a “barrel roll” (A single 360 degree turn on it’s roll axis). However I have run into a roadblock and can’t figure out what to do next. Basically I have a bool set up in the tick function that, when set to true, will add a rotation to the plane making it spin. I need a way to make it stop spinning after a full 360 degree turn. Right now it either keeps spinning or will stop about halfway no matter what I change. Any suggestions on how to fix this or if there is a better way of doing this (Like setting up a timer that stops the plane from spinning after a second).
Here is the header:
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "DroneProjectPawn.generated.h"
UCLASS(config=Game)
class ADroneProjectPawn : public APawn
{
public:
GENERATED_UCLASS_BODY()
/** StaticMesh component that will be the visuals for our flying pawn */
UPROPERTY(Category=Mesh, VisibleDefaultsOnly, BlueprintReadOnly)
TSubobjectPtr<class UStaticMeshComponent> PlaneMesh;
/** Spring arm that will offset the camera */
UPROPERTY(Category = Camera, VisibleDefaultsOnly, BlueprintReadOnly)
TSubobjectPtr<class USpringArmComponent> SpringArm;
/** Camera component that will be our viewpoint */
UPROPERTY(Category=Camera, VisibleDefaultsOnly, BlueprintReadOnly)
TSubobjectPtr<class UCameraComponent> Camera;
// Begin AActor overrides
virtual void Tick(float DeltaSeconds) OVERRIDE;
virtual void ReceiveHit(class UPrimitiveComponent* MyComp, class AActor* Other, class UPrimitiveComponent* OtherComp, bool bSelfMoved, FVector HitLocation, FVector HitNormal, FVector NormalImpulse, const FHitResult& Hit) OVERRIDE;
// End AActor overrides
//Max Pitch that plane can turn
UPROPERTY(Category = Movement, EditAnywhere, BlueprintReadWrite)
float MaxPitch;
//Min Pitch that plance can turn
UPROPERTY(Category = Movement, EditAnywhere, BlueprintReadWrite)
float MinPitch;
//Max Roll that plane can turn
UPROPERTY(Category = Movement, EditAnywhere, BlueprintReadWrite)
float MaxRoll;
//Min Roll that plance can turn
UPROPERTY(Category = Movement, EditAnywhere, BlueprintReadWrite)
float MinRoll;
protected:
// Begin APawn overrides
virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) OVERRIDE; // Allows binding actions/axes to functions
// End APawn overrides
/** Bound to the vertical axis */
void ThrustInput(float Val);
/** Bound to the horizontal axis */
void MoveUpInput(float Val);
/** */
void MoveRightInput(float Val);
//Function that deploys the barrel roll right mechanic
void BarrelRollRight();
//Function that deploys the barrel roll left mechanic
void BarrelRollLeft();
private:
bool bCanBarrelRollRight;
bool bCanBarrelRollLeft;
bool bIsRollingRight;
bool bIsRollingLeft;
FRotator RotationRate;
/** How quickly forward speed changes */
UPROPERTY(Category=Plane, EditAnywhere)
float Acceleration;
/** How quickly pawn can steer */
UPROPERTY(Category=Plane, EditAnywhere)
float TurnSpeed;
/** MAx forward speed */
UPROPERTY(Category = Pitch, EditAnywhere)
float MaxSpeed;
/** Min forward speed */
UPROPERTY(Category=Yaw, EditAnywhere)
float MinSpeed;
/** Min forward speed */
UPROPERTY(Category = Camera, EditAnywhere)
float MinCameraDistance;
/** Min forward speed */
UPROPERTY(Category = Camera, EditAnywhere)
float MaxCameraDistance;
/** Current forward speed */
float CurrentForwardSpeed;
/** Current yaw speed */
float CurrentYawSpeed;
/** Current pitch speed */
float CurrentPitchSpeed;
/** Current roll speed */
float CurrentRollSpeed;
};
and the source:
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
#include "DroneProject.h"
#include "DroneProjectPawn.h"
ADroneProjectPawn::ADroneProjectPawn(const class FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
{
// Structure to hold one-time initialization
struct FConstructorStatics
{
ConstructorHelpers::FObjectFinderOptional<UStaticMesh> PlaneMesh;
FConstructorStatics()
: PlaneMesh(TEXT("/Game/Meshes/Predator.Predator"))
{
}
};
static FConstructorStatics ConstructorStatics;
// Create static mesh component
PlaneMesh = PCIP.CreateDefaultSubobject<UStaticMeshComponent>(this, TEXT("PlaneMesh0"));
PlaneMesh->SetStaticMesh(ConstructorStatics.PlaneMesh.Get());
RootComponent = PlaneMesh;
// Create a spring arm component
SpringArm = PCIP.CreateDefaultSubobject<USpringArmComponent>(this, TEXT("SpringArm0"));
SpringArm->AttachTo(RootComponent);
SpringArm->TargetArmLength = MinCameraDistance; // The camera follows at this distance behind the character
SpringArm->SocketOffset = FVector(0.f,0.f,60.f);
SpringArm->bEnableCameraLag = false;
SpringArm->CameraLagSpeed = 15.f;
// Create camera component
Camera = PCIP.CreateDefaultSubobject<UCameraComponent>(this, TEXT("Camera0"));
Camera->AttachTo(SpringArm, USpringArmComponent::SocketName);
Camera->bUseControllerViewRotation = false; // Don't rotate camera with controller
// Set handling parameters
Acceleration = 500.f;
TurnSpeed = 50.f;
MaxSpeed = 8000.f;
MinSpeed = 3000.f;
CurrentForwardSpeed = 3000.f;
MinCameraDistance = 160.f;
MaxCameraDistance = 250.f;
MaxPitch = 60.f;
MinPitch = -60.f;
MaxRoll = 45.f;
MinRoll = -45.f;
bCanBarrelRollRight = true;
bCanBarrelRollLeft = true;
bIsRollingRight = false;
bIsRollingLeft = false;
}
void ADroneProjectPawn::Tick(float DeltaSeconds)
{
const FVector LocalMove = FVector(CurrentForwardSpeed * DeltaSeconds, 0.f, 0.f);
const float OldPitch = GetActorRotation().Pitch;
const float MinDeltaPitch = MinPitch - OldPitch;
const float MaxDeltaPitch = MaxPitch - OldPitch;
const float OldRoll = GetActorRotation().Roll;
const float MinDeltaRoll = MinRoll - OldRoll;
const float MaxDeltaRoll = MaxRoll - OldRoll;
// Move plane forwards (with sweep so we stop when we collide with things)
AddActorLocalOffset(LocalMove, true);
// Calculate change in rotation this frame
FRotator DeltaRotation(0,0,0);
if (!bIsRollingLeft && !bIsRollingRight)
{
DeltaRotation.Pitch = FMath::ClampAngle(CurrentPitchSpeed * DeltaSeconds, MinDeltaPitch, MaxDeltaPitch);
DeltaRotation.Yaw = CurrentYawSpeed * DeltaSeconds;
DeltaRotation.Roll = FMath::ClampAngle(CurrentRollSpeed * DeltaSeconds, MinDeltaRoll, MaxDeltaRoll);
}
// Rotate plane
AddActorLocalRotation(DeltaRotation);
//Barrel Roll Right
if (bCanBarrelRollRight && bIsRollingRight)
{
MaxRoll = 350.f;
if (DeltaRotation.Roll <= MaxDeltaRoll)
{
this->RotationRate = FRotator(0.0f, 0.0f, 180);
this->AddActorLocalRotation(this->RotationRate * DeltaSeconds, true);
}
else
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("This is an on screen message!"));
bIsRollingRight = false;
bCanBarrelRollLeft = true;
MaxRoll = 45.f;
MinRoll = -45.f;
}
}
/*
//Barrel Roll Left
if (bCanBarrelRollLeft && bIsRollingLeft)
{
MaxRoll = 359;
MinRoll = -359;
this->RotationRate = FRotator(0.0f, 0.0f, -180.0f);
this->AddActorLocalRotation(this->RotationRate * DeltaSeconds, false);
}
*/
// Call any parent class Tick implementation
Super::Tick(DeltaSeconds);
}
void ADroneProjectPawn::ReceiveHit(class UPrimitiveComponent* MyComp, class AActor* Other, class UPrimitiveComponent* OtherComp, bool bSelfMoved, FVector HitLocation, FVector HitNormal, FVector NormalImpulse, const FHitResult& Hit)
{
Super::ReceiveHit(MyComp, Other, OtherComp, bSelfMoved, HitLocation, HitNormal, NormalImpulse, Hit);
// Set velocity to zero upon collision
CurrentForwardSpeed = 0.f;
}
void ADroneProjectPawn::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
check(InputComponent);
// Fine up and right axes
InputComponent->BindAxis("Thrust", this, &ADroneProjectPawn::ThrustInput);
InputComponent->BindAxis("MoveUp", this, &ADroneProjectPawn::MoveUpInput);
InputComponent->BindAxis("MoveRight", this, &ADroneProjectPawn::MoveRightInput);
InputComponent->BindAction("BarrelRollRight", IE_Pressed, this, &ADroneProjectPawn::BarrelRollRight);
InputComponent->BindAction("BarrelRollLeft", IE_Pressed, this, &ADroneProjectPawn::BarrelRollLeft);
}
void ADroneProjectPawn::ThrustInput(float Val)
{
// Is there no input?
bool bHasInput = !FMath::IsNearlyEqual(Val, 0.f);
// If input is not held down, reduce speed
float CurrentAcc = bHasInput ? (Val * Acceleration) : (-0.5f * Acceleration);
//If input is not held down, reduce camera distance
float CurrentCamDistance = bHasInput ? (Val * 500.f) : (-.5 * 500.f);
//Calculate new Camera Distance
float NewCameraDistance = SpringArm->TargetArmLength + (GetWorld()->GetDeltaSeconds() * CurrentCamDistance * .5f);
// Calculate new speed
float NewForwardSpeed = CurrentForwardSpeed + (GetWorld()->GetDeltaSeconds() * CurrentAcc * 20.f);
// Clamp between MinSpeed and MaxSpeed
CurrentForwardSpeed = FMath::Clamp(NewForwardSpeed, MinSpeed, MaxSpeed);
//Clamp between minCameraDistance and maxCameraDistance
SpringArm->TargetArmLength = FMath::Clamp(NewCameraDistance, MinCameraDistance, MaxCameraDistance);
}
void ADroneProjectPawn::MoveUpInput(float Val)
{
// Target pitch speed is based in input
float TargetPitchSpeed = (Val * TurnSpeed * -1.f);
// When steering, we decrease pitch slightly
TargetPitchSpeed += (FMath::Abs(CurrentYawSpeed) * -0.2f);
// Smoothly inerpolate to target pitch speed
CurrentPitchSpeed = FMath::FInterpTo(CurrentPitchSpeed, TargetPitchSpeed, GetWorld()->GetDeltaSeconds(), 2.f);
}
void ADroneProjectPawn::MoveRightInput(float Val)
{
// Target yaw speed is based on input
float TargetYawSpeed = (Val * TurnSpeed * .75f);
// Smoothly interpolate to target yaw speed
CurrentYawSpeed = FMath::FInterpTo(CurrentYawSpeed, TargetYawSpeed, GetWorld()->GetDeltaSeconds(), 2.f);
// Is there any left/right input?
const bool bIsTurning = FMath::Abs(Val) > 0.2f;
// If turning, yaw value is used to influence roll
// If not turning, roll to reverse current roll value
float TargetRollSpeed = bIsTurning ? (CurrentYawSpeed * 0.5f) : (GetActorRotation().Roll * -.25f);
// Smoothly interpolate roll speed
CurrentRollSpeed = FMath::FInterpTo(CurrentRollSpeed, TargetRollSpeed, GetWorld()->GetDeltaSeconds(), 10.f);
}
void ADroneProjectPawn::BarrelRollRight()
{
if (bCanBarrelRollRight && !bIsRollingRight)
{
bIsRollingRight = true;
bCanBarrelRollLeft = false;
}
}
void ADroneProjectPawn::BarrelRollLeft()
{
if (bCanBarrelRollLeft && !bIsRollingLeft)
{
bIsRollingLeft = true;
bCanBarrelRollRight = false;
}
}