Spawning an actor to face wall normal

Hey all!

I have a little issue with getting a spawned blueprint to face outward of a surface normal. It feels like it should be an easy fix but I just can’t figure this out.

So in more detail:
I’m spawning a jump-pad in my game and want the jump pad to face away from the wall that I’m spawning it onto. I thought I had it a couple of times by altering the pitch/roll/yaw manually after spawn but it seems if I spawn the jump-pad on one side of the wall, then the other side of the wall then the rotation of the jump-pad on the other side is wrong again.

This is in my CharacterPlayer class and in a function on it’s own being called by a key press.
Here’s my code:

void AQUBECharacterPlayer::OnThrow()
{
	if(BlueCubeBP)
	{
		FVector CamLoc;
		FRotator CamRot;
		
		Controller->GetPlayerViewPoint(CamLoc, CamRot); 

		FHitResult HitResult;
		FCollisionQueryParams CollisionQuery(NAME_None, false, this);
		FActorSpawnParameters SpawnInfo;
		SpawnInfo.Owner = this;

		if (GetWorld()->LineTraceSingle(HitResult, CamLoc, CamLoc + CamRot.Vector() * 1000, ECC_WorldStatic, CollisionQuery))
		{
			if(HitResult.GetActor())
			{
				FRotator NewRotation = HitResult.Normal.Rotation();
				AQUBEBaseActor* SpawnedBlueCube = GetWorld()->SpawnActor(BlueCubeBP, HitResult.Location, NewRotation, SpawnInfo);
			}
		}
	}
}

I’ve tried adding the Actor Rotation to NewRotation too and even tried HitResult.ImpactNormal with the same results.

Here’s an image of the issue:

I have a feeling this code may need to get a little more complex in order for it to work correctly, right?

Thanks,

Jon

#Getting the Data Before Making Conclusions

Dear ZeJudge,

Whenever working with traces,

I find visualizing the data that you are getting is essential!

So before trying to fix your algorithm,

you should always find out:

What data is your algorithm getting?!

I cant count to you the number of times my algorithms have failed,

and I instantly was assuming it was my logic and was trying to fix stuff in my thought process when,

the whole time

  1. the function was not even running

or

  1. the algorithm was not getting the data I was assuming it was getting to start with!

#DrawDebug Your Friend

For all your algorithm efforts, DrawDebug is essential!

#Code For You

for now, for testing, change this

 if(HitResult.GetActor())
 {
  FRotator NewRotation = HitResult.Normal.Rotation();
  AQUBEBaseActor* SpawnedBlueCube = GetWorld()->SpawnActor(BlueCubeBP, HitResult.Location, NewRotation, SpawnInfo);
 }

to this

 if(HitResult.bBlockingHit)
 {
  FRotator NewRotation = HitResult.ImpactNormal.Rotation();
  /*
  DrawDebugLine(
	const UWorld* InWorld, 
	FVector const& LineStart, 
	FVector const& LineEnd, 
	FColor const& Color, 
	bool bPersistentLines = false, 
	float LifeTime=-1.f, 
	uint8 DepthPriority = 0, 
	float Thickness = 0.f
  );
  */
  DrawDebugLine(
	GetWorld(), 
	HitResult.ImpactPoint, 
	HitResult.ImpactPoint + 1024 * HitResult.ImpactNormal, 
	FColor(255,0,0), 
	false, 
	3, 
	0, 
	7
  );
  //AQUBEBaseActor* SpawnedBlueCube = GetWorld()->SpawnActor(BlueCubeBP, HitResult.Location, NewRotation, SpawnInfo);
 }

:slight_smile:

Here’s a list of all DrawDebug functions

I find them essential to know what is going on with any 3d-world algorithms I am trying to write :slight_smile:

// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.

/**
 * DrawDebugHelpers.h
 */


#pragma once

/** Flush persistent lines */
ENGINE_API void FlushPersistentDebugLines(const UWorld* InWorld);
/** Draw a debug line */
ENGINE_API void DrawDebugLine(const UWorld* InWorld, FVector const& LineStart, FVector const& LineEnd, FColor const& Color, bool bPersistentLines = false, float LifeTime=-1.f, uint8 DepthPriority = 0, float Thickness = 0.f);
/** Draw a debug point */
ENGINE_API void DrawDebugPoint(const UWorld* InWorld, FVector const& Position, float Size, FColor const& PointColor, bool bPersistentLines = false, float LifeTime=-1.f, uint8 DepthPriority = 0);
/** Draw directional arrow **/
ENGINE_API void DrawDebugDirectionalArrow(const UWorld* InWorld, FVector const& LineStart, FVector const& LineEnd, float ArrowSize, FColor const& Color, bool bPersistentLines = false, float LifeTime=-1.f, uint8 DepthPriority = 0);
/** Draw a debug box */
ENGINE_API void DrawDebugBox(const UWorld* InWorld, FVector const& Center, FVector const& Extent, FColor const& Color, bool bPersistentLines = false, float LifeTime=-1.f, uint8 DepthPriority = 0);
/** Draw a debug box with rotation */
ENGINE_API void DrawDebugBox(const UWorld* InWorld, FVector const& Center, FVector const& Box, const FQuat & Rotation, FColor const& Color, bool bPersistentLines = false, float LifeTime=-1.f, uint8 DepthPriority = 0);
/** Draw Debug coordinate system */
ENGINE_API void DrawDebugCoordinateSystem(const UWorld* InWorld, FVector const& AxisLoc, FRotator const& AxisRot, float Scale, bool bPersistentLines = false, float LifeTime=-1.f, uint8 DepthPriority = 0);
/** Draw Debug Circle */
ENGINE_API void DrawDebugCircle(const UWorld* InWorld, const FMatrix& TransformMatrix, float Radius, int32 Segments, const FColor& Color, bool bPersistentLines = false, float LifeTime=-1.f, uint8 DepthPriority = 0);
/** Draw Debug 2D donut */
ENGINE_API void DrawDebug2DDonut(const UWorld* InWorld, const FMatrix& TransformMatrix, float InnerRadius, float OuterRadius, int32 Segments, const FColor& Color, bool bPersistentLines = false, float LifeTime=-1.f, uint8 DepthPriority = 0);
/** Draw a debug sphere */
ENGINE_API void DrawDebugSphere(const UWorld* InWorld, FVector const& Center, float Radius, int32 Segments, FColor const& Color, bool bPersistentLines = false, float LifeTime=-1.f, uint8 DepthPriority = 0);
/** Draw a debug cylinder */
ENGINE_API void DrawDebugCylinder(const UWorld* InWorld, FVector const& Start, FVector const& End, float Radius, int32 Segments, FColor const& Color, bool bPersistentLines = false, float LifeTime=-1.f, uint8 DepthPriority = 0);
ENGINE_API void DrawDebugCone(const UWorld* InWorld, FVector const& Origin, FVector const& Direction, float Length, float AngleWidth, float AngleHeight, int32 NumSides, FColor const& Color, bool bPersistentLines=false, float LifeTime=-1.f, uint8 DepthPriority = 0);
/** Used by gameplay when defining a cone by a vertical and horizontal dot products. */
ENGINE_API void DrawDebugAltCone(const UWorld* InWorld, FVector const& Origin, FRotator const& Rotation, float Length, float AngleWidth, float AngleHeight, FColor const& DrawColor, bool bPersistentLines=false, float LifeTime=-1.f, uint8 DepthPriority=0);
ENGINE_API void DrawDebugString(const UWorld* InWorld, FVector const& TextLocation, const FString& Text, class AActor* TestBaseActor = NULL, FColor const& TextColor = FColor::White, float Duration = -1.000000);
ENGINE_API void DrawDebugFrustum(const UWorld* InWorld, const FMatrix& FrustumToWorld, FColor const& Color, bool bPersistentLines = false, float LifeTime=-1.f, uint8 DepthPriority = 0);
/** Draw a capsule using the LineBatcher */
ENGINE_API void DrawDebugCapsule(const UWorld* InWorld, FVector const& Center, float HalfHeight, float Radius, const FQuat & Rotation, FColor const& Color, bool bPersistentLines=false, float LifeTime=-1.f, uint8 DepthPriority = 0);
/** Draw a debug camera shape.  FOV is full angle in degrees. */
ENGINE_API void DrawDebugCamera(const UWorld* InWorld, FVector const& Location, FRotator const& Rotation, float FOVDeg, float Scale=1.f, FColor const& Color=FColor::White, bool bPersistentLines=false, float LifeTime=-1.f, uint8 DepthPriority = 0);
ENGINE_API void FlushDebugStrings(const UWorld* InWorld);

ENGINE_API void DrawDebugSolidBox(const UWorld* InWorld, FVector const& Center, FVector const& Box, FColor const& Color, bool bPersistent=false, float LifeTime=-1.f, uint8 DepthPriority = 0);
ENGINE_API void DrawDebugSolidBox(const UWorld* InWorld, FVector const& Center, FVector const& Extent, FQuat const& Rotation, FColor const& Color, bool bPersistent=false, float LifeTime=-1.f, uint8 DepthPriority = 0);
ENGINE_API void DrawDebugMesh(const UWorld* InWorld, TArray const& Verts, TArray const& Indices, FColor const& Color, bool bPersistent=false, float LifeTime=-1.f, uint8 DepthPriority = 0);
ENGINE_API void DrawDebugSolidPlane(const UWorld* InWorld, FPlane const& P, FVector const& Loc, float Size, FColor const& Color, bool bPersistent=false, float LifeTime=-1, uint8 DepthPriority = 0);

The key code from above is this:

HitResult.ImpactPoint, 

HitResult.ImpactPoint + 1024 * HitResult.ImpactNormal,

This is saying

draw a line from the impact point for a length of 1024 in the direction of the impactnormal (which you are converting to a rotation)

then

run your alogorithm from all sides of the wall and see what kind of lines are being drawn

notice any oddities

and then you can start to make conclusion about how to modify your algorithm :slight_smile:

Rama

Hey Rama,

The debug line helped my find the issue.

So now here’s my code that works perfectly!

AQUBEBaseActor* SpawnedBlueCube = GetWorld()->SpawnActor(BlueCubeBP, HitResult.ImpactPoint, HitResult.ImpactNormal.Rotation() + FRotator(-90,0,0), SpawnInfo);

Thanks a lot! :smiley:

Jon