FQuats Don't Rotate. Sometimes Cause a Crash

Hey Everyone,

I’m trying to draw a debug capsule. Here is the Code:

		DrawDebugCapsule(WorldContextObject->GetWorld(), (End - Start) / 2 + Start, (End - Start).Length() / 2, Radius, FQuat::FindBetweenVectors(Start, End), TraceColor.ToFColorSRGB(), bPersistentDebug, DebugDuration, 0, 1);

The FQuat doesn’t rotate the capsule at all. It is always up and down. If I try anything else, I either get the same result (nothing), or it crashes the engine at runtime.

Try this

.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyActor.generated.h"

UCLASS()
class YOUR_API AMyActor : public AActor
{
	GENERATED_BODY()
	
public:	
	AMyActor();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;
	
	UPROPERTY(EditAnywhere,BlueprintReadWrite, meta=(MakeEditWidget))
	FVector Start;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (MakeEditWidget))
	FVector End;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
		float Radius;
	
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	FColor TraceColor;
	bool  bPersistentDebug;
	
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float DebugDuration;
};

.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "MyActor.h"

// Sets default values
AMyActor::AMyActor()
{
	PrimaryActorTick.bCanEverTick = true;
}

// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
	Super::BeginPlay();
}

// Called every frame
void AMyActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
    
    FVector E = GetActorLocation() + End;
    FVector S = GetActorLocation() + Start;

    DrawDebugCapsule(GetWorld(), (E - S) / 2 + S, (E- S).Length() / 2, Radius,  (S - E).ToOrientationQuat(), TraceColor, bPersistentDebug, DebugDuration, 0, 1);

}

Thank you for answering so fast.

I’ve tried ToOrientationQuat(). I’ve also tried .Rotation().Quaternion(). I’ve tried FindLookAtRotation() and a few other ones that I can’t remember right now, all of which yielded the same result - nothing.

I also tried setting the FQuat directly with FQuat(x, x, x, x) just to see if it rotated at all, but that just crashes the engine. Also, any addition to the FQuat (i.e. (Start - End).ToOrientationQuat() + FQuat(0, 0, 90, 0)) has crashed the engine.

Here is a screenshot of what’s happening. The blue spheres and red line are rotated the right way, so the capsule should run along that line, but it’s always straight up and down. The blue spheres are the start and end vectors.

in header



	void Update();
	

	FVector Offset = FVector(90, 0, 0);


	UPROPERTY(EditAnywhere,BlueprintReadWrite, meta=(MakeEditWidget))
	FVector Start;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (MakeEditWidget))
	FVector End;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
		float Radius;
	
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	FColor TraceColor;
	bool  bPersistentDebug;
	
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float DebugDuration;

in cpp

#include "Kismet/KismetMathLibrary.h" 
void AMyActor::Update() 
{
    FVector E = GetActorLocation() + End;
    FVector S = GetActorLocation() + Start;

    FRotator rotation = UKismetMathLibrary::FindLookAtRotation(S, E).Add(Offset.X, Offset.Y, Offset.Z);
        
    DrawDebugCapsule(GetWorld(), (E - S) / 2 + S, (E - S).Length() / 2, Radius, rotation.Quaternion(), TraceColor, bPersistentDebug, DebugDuration, 0, 1);

    DrawDebugSphere(GetWorld(), E, 20, 12, FColor::Blue);
    DrawDebugLine(GetWorld(),S, E, FColor::Red, 0,0);

}

I just passed you the needed properties and the function implementation, seeing as you already have the rest of the class running

Same thing. The capsule is straight up and down. It’s like it’s ignoring any rotation instructions I give it.

Please post your revised code. This is what I’m getting on my end



This is what my update function is calculating. It adds the offset to the rotation making it go along the line between start and end.

It is called from Blueprints on Tick (for testing purposes)

Header

// © 2017 Tyler Barrett. All Rights Reserved.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "TraceFromPlayerLocation.generated.h"

UENUM()
enum ETraceDirection {Up, Down, Front, Back, Left, Right};

UENUM()
enum EDrawDebugLine {None, Persistent, OneFrame, Duration};

UCLASS()
class PLAYERTRACE_API UTraceFromPlayerLocation : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()

public:
	// Draws the debug line during a line trace.
	static void DrawLineTraceDebugger(TEnumAsByte<EDrawDebugLine> DrawDebugLineSetting, double DebugDuration,
	const bool bHit, const AActor* WorldContextObject, FVector Start, FVector End,
	FHitResult OutHit, FLinearColor TraceColor, FLinearColor HitColor);

	// Draws the debug line during a sphere trace.
	static void DrawSphereTraceDebugger(TEnumAsByte<EDrawDebugLine> DrawDebugSphereSetting, double DebugDuration,
	const double Radius, const bool bHit, const AActor* WorldContextObject, FVector Start, FVector End,
	FHitResult OutHit, FLinearColor TraceColor, FLinearColor HitColor);
	
	// Performs a single line trace by channel that starts from the player location.
	UFUNCTION(BlueprintPure, Category = "Player Trace", meta = (AutoCreateRefTerm = "ActorsToIgnore", DefaultToSelf = "WorldContextObject", HidePin="WorldContextObject", AdvancedDisplay = 9))
	static void SingleLineTraceFromPlayer(bool& bHit, FHitResult& OutHit, const double Distance, const TEnumAsByte<ETraceDirection> Direction,
		const TEnumAsByte<ETraceTypeQuery> CollisionChannel, const bool bTraceComplex, TArray<AActor*> ActorsToIgnore, bool IgnoreSelf = true,
		const TEnumAsByte<EDrawDebugLine> DrawDebugLineSettings = None, double DebugDuration = 0.5, FLinearColor TraceColor = FLinearColor::Red, FLinearColor HitColor = FLinearColor::Green,
		const AActor* WorldContextObject = nullptr);

	// Performs a single line trace by channel that starts from the cameras location and goes in the direction the camera is facing.
	UFUNCTION(BlueprintPure, Category = "Player Trace", meta = (AutoCreateRefTerm = "ActorsToIgnore", DefaultToSelf = "WorldContextObject", HidePin="WorldContextObject", AdvancedDisplay = 9))
	static void SingleLineTraceFromCamera(bool& bHit, FHitResult& OutHit, const double Distance,
		const TEnumAsByte<ETraceTypeQuery> CollisionChannel, const bool bTraceComplex, TArray<AActor*> ActorsToIgnore, bool IgnoreSelf = true,
		const TEnumAsByte<EDrawDebugLine> DrawDebugLineSettings = None, double DebugDuration = 0.5, FLinearColor TraceColor = FLinearColor::Red, FLinearColor HitColor = FLinearColor::Green,
		const AActor* WorldContextObject = nullptr);

	// Performs a single sphere trace by channel that starts from the players location.
	UFUNCTION(BlueprintPure, Category = "Player Trace", meta = (AutoCreateRefTerm = "ActorsToIgnore", DefaultToSelf = "WorldContextObject", HidePin="WorldContextObject", AdvancedDisplay = 9))
	static void SingleSphereTraceFromPlayer(bool& bHit, FHitResult& OutHit, const double Distance, const TEnumAsByte<ETraceDirection> Direction, const double Radius,
		const TEnumAsByte<ETraceTypeQuery> CollisionChannel, const bool bTraceComplex, TArray<AActor*> ActorsToIgnore, bool IgnoreSelf = true,
		const TEnumAsByte<EDrawDebugLine> DrawDebugSphereSettings = None, double DebugDuration = 0.5, FLinearColor TraceColor = FLinearColor::Red, FLinearColor HitColor = FLinearColor::Green,
		const AActor* WorldContextObject = nullptr);
};

CPP

// Performs a single sphere trace by channel that starts from the player location.
void UTraceFromPlayerLocation::SingleSphereTraceFromPlayer(bool& bHit, FHitResult& OutHit, const double Distance,
	const TEnumAsByte<ETraceDirection> Direction, const double Radius,
	const TEnumAsByte<ETraceTypeQuery> CollisionChannel, const bool bTraceComplex, TArray<AActor*> ActorsToIgnore,
	bool IgnoreSelf, const TEnumAsByte<EDrawDebugLine> DrawDebugSphereSettings, double DebugDuration,
	FLinearColor TraceColor, FLinearColor HitColor, const AActor* WorldContextObject)
{
	// Creates a reference to the player and creates the start/stop vectors for the line trace.
	const ACharacter* PlayerRef = UGameplayStatics::GetPlayerCharacter(WorldContextObject->GetWorld(), 0);
	const FVector Start = PlayerRef->GetActorLocation();
	FVector End = FVector(0.0, 0.0, 0.0);

	// Sets the end of the line trace to be the correct distance and direction away from the player.
	switch (Direction)
	{
	case Up:
		End = Start + (PlayerRef->GetActorUpVector() * Distance);
		break;
	case Down:
		End = Start + -(PlayerRef->GetActorUpVector() * Distance);
		break;
	case Front:
		End = Start + (PlayerRef->GetActorForwardVector() * Distance);
		break;
	case Back:
		End = Start + -(PlayerRef->GetActorForwardVector() * Distance);
		break;
	case Right:
		End = Start + (PlayerRef->GetActorRightVector() * Distance);
		break;
	case Left:
		End = Start + -(PlayerRef->GetActorRightVector() * Distance);
		break;
	default: //This should never run!
		GEngine->AddOnScreenDebugMessage(1, 10.0, FColor::Orange, TEXT("WARNING: SingleLineTraceFromPlayer did not find a direction to trace in!"));
	}

	// Creates the collision channel, query parameters, and hit result for the line trace.
	ECollisionChannel TraceChannel = UEngineTypes::ConvertToCollisionChannel(CollisionChannel);
	FCollisionQueryParams TraceParams;
	FHitResult TraceHitResult;

	// Adds ignored actors to the query parameters.
	if(IgnoreSelf)
		TraceParams.AddIgnoredActor(PlayerRef);

	if(ActorsToIgnore.Num() > 0)
		TraceParams.AddIgnoredActors(ActorsToIgnore);

	// Performs the sphere trace and returns the hit result.
	bHit = UKismetSystemLibrary::SphereTraceSingle(WorldContextObject, Start, End, Radius, CollisionChannel, bTraceComplex, ActorsToIgnore, EDrawDebugTrace::None, OutHit, IgnoreSelf, TraceColor, HitColor, DebugDuration);
	OutHit = TraceHitResult;

	DrawSphereTraceDebugger(DrawDebugSphereSettings, DebugDuration, Radius, bHit, WorldContextObject, Start, End, OutHit, TraceColor, HitColor);
void UTraceFromPlayerLocation::DrawLineTraceDebugger(TEnumAsByte<EDrawDebugLine> DrawDebugLineSetting, double DebugDuration,
	const bool bHit, const AActor* WorldContextObject, FVector Start, FVector End,
	FHitResult OutHit, FLinearColor TraceColor, FLinearColor HitColor)
{
	// Initializes Debug Settings.
	bool bPersistentDebug;
	switch (DrawDebugLineSetting)
	{
	case None:
		return;
	case Persistent:
		bPersistentDebug = true;
		break;
	case OneFrame:
		bPersistentDebug = false;
		DebugDuration = 0.0;
		break;
	case Duration:
		bPersistentDebug = false;
		break;
	default: //This should never run!
		GEngine->AddOnScreenDebugMessage(1, 10.0, FColor::Orange, TEXT("WARNING: SingleLineTraceFromPlayer did not find a debug mode!"));
	}
	
	// Draws Debug Information.
	if(bHit)
	{
		DrawDebugLine(WorldContextObject->GetWorld(), Start, OutHit.ImpactPoint, TraceColor.ToFColorSRGB(), bPersistentDebug, DebugDuration, 0, 1.0);
		DrawDebugLine(WorldContextObject->GetWorld(), OutHit.ImpactPoint, End, HitColor.ToFColorSRGB(), bPersistentDebug, DebugDuration, 0, 1.0);
		DrawDebugSphere(WorldContextObject->GetWorld(), OutHit.ImpactPoint, 2.0, 12, HitColor.ToFColorSRGB(), bPersistentDebug, DebugDuration, 0, 1.0);
	}
	else
	{
		DrawDebugLine(WorldContextObject->GetWorld(), Start, End, TraceColor.ToFColorSRGB(), bPersistentDebug, DebugDuration, 0, 1.0);
	}
}```

Try this (name it as you wish)

h

	UFUNCTION(BlueprintCallable)
		static void DrawDebuggerCapsule(const AActor* WorldContextObject, FVector Start, FVector End, float Radius, FColor TraceColor, bool bPersistentDebug, float DebugDuration);

cpp

void UTraceFromPlayerLocation::DrawDebuggerCapsule(const AActor* WorldContextObject, FVector Start, FVector End, float Radius, FColor TraceColor, bool bPersistentDebug, float DebugDuration)
{
	const ACharacter* PlayerRef = UGameplayStatics::GetPlayerCharacter(WorldContextObject->GetWorld(), 0);
	FVector E = PlayerRef->GetActorLocation() + End;
	FVector S = PlayerRef->GetActorLocation() + Start;

	FRotator rotation = UKismetMathLibrary::FindLookAtRotation(S, E).Add(90, 0, 0);

	DrawDebugCapsule(WorldContextObject->GetWorld(), (E - S) / 2 + S, (E - S).Length() / 2, Radius, rotation.Quaternion(), TraceColor, bPersistentDebug, DebugDuration, 0, 1);

	DrawDebugSphere(WorldContextObject->GetWorld(), E, 20, 12, FColor::Blue);
	DrawDebugLine(WorldContextObject->GetWorld(), S, E, FColor::Red, 0, 0);

}

It turns out it was the .Add(90, 0, 0) that I was missing. I don’t know why that’s needed though. Shouldn’t FindLookAtRotation() be the correct rotation?

The capsule by default faces up, so if you want it to go along a vector you need to rotate it so it’s lying down along the vector direction. So you have to rotate it.

So what does FindLookAtRotation do? without the addition, I’m still setting a rotator to that value and passing it into the DrawDebugCapsule function, so does it not rotate it when you do that?

it does rotate the capsule, but it rotates it so it is always facing up between the two vectors (it’s basically always at a right angle to the line between the vectors)

Oh so if the two vectors formed a line going straight up and down, the capsule would be sideways? That’s incredibly bizarre.

Yeah seems a bit counter intuitive, but the use cases mostly have the capsule be up-right so I guess it was just an epic decision to reduce unneeded rotations for characters.

Makes sense. Seems counter-intuitive here because of the use-case (I’m doing my own sphere trace system), but someone else who’s doing something different is probably really glad it’s always upright. Anyway, thanks :slightly_smiling_face:

P.S. your picture of the forward vector made all of this instantly clear to me lol. I should’ve known that :rofl:

1 Like

Also maybe add a

if(WorldContextObject != nullptr){
/// rest of the code here
}

check to the code, so that if you don’t pass in a world context objet it doesn’t crash.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.