UCanvas::Deproject() not returning desired results

I am trying to spawn a test cube at the mouse position. Based on the thread here,

https://rocket.unrealengine.com/questions/2162/mouse-position-picking-c.html

I decided that the best course of action (at least, until there’s a better way of doing this, as James.Golding mentioned that there was a better implementation in the works) was to call UCanvas::Deproject() inside my HUD’s draw call (which is the only time the canvas is valid), and save off the results of this call (the world space origin and direction) to be used later in positioning the cube I want to spawn. I am essentially doing it exactly the way Mike.Purvis does in that thread (GEngine->GameViewport->GetMousePosition() to get the mouse pos and then Canvas->Deproject() with that mouse pos).

I’ve found that the cube seems to consistently spawn at the camera position, even though I correctly pass in the cached world space origin into the call to UWorld::SpawnActor().

I then added some diagnostic code in my HUD’s DrawHUD() immediately after the Deproject() call (see screenshot attached) to tell me what the world space origin actually is. This text follows the mouse cursor around correctly (so I know it’s getting the right mouse position), and the X and Y seem to change somewhat as I move the cursor around, but the Z is not changing as the cursor moves over the cubes that are already in the level.

Any idea why this would be? It’s almost as if the projection is stopping right at the start of the camera view frustum, and not even getting into the scene at all.

Alternatively, if there’s a good way to do a Deproject() outside of a DrawHUD() call, I’d prefer to use that instead – but my understanding has been that that’s not currently possible.

I am using this function from the PlayerController, skipping all that stuff like from the mouse click demo.

//= trace to check if mouse pointer if over a terrain
FHitResult Hit;
GetHitResultUnderCursor(ECC_Visibility,Hit);

//Did it hit something?
if (Hit.bBlockingHit) 
{
  // Hit.ImpactPoint is your world hit location

}

Thanks! That’s actually what I really needed – I did not need to be using Deproject() at all.

Derp :stuck_out_tongue:

Hi there!

Are you passing in a value of world origin?

That is an out parameter, you are supposed to pass in a FVector to be filled with the world origin data by deproject, which you can then use for doing traces to obtain the z value

The only in parameters for deproject are the 2D coordinates, all others are out parameters.

When you are deprojecting it will be near the camera, because you are going from the 2D plane of the canvas to 3d space, so it starts close to current camera view.

You cannot expect deproject to account for a Z value when it is being given 2D coordinates, what z value would you like it to decide upon? You must determine the Z value yourself using a trace.

After you deproject are you doing a trace from the deprojected point down into the world? See my code below

if you want to trace exact hit location you can use

//Did Not Hit Anything?
    if(!TraceHit.bBlockingHit) return;
 
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    //        Get Hit Loc
    FVector HitLoc = TraceHit.ImpactPoint();

If you use all of my code below you will rotate the SMA’s in your level by clicking on them with this function :slight_smile:


I use deproject in my game’s custom HUD class to click on objects in the world all the time!

I posted my entire code for making an ingame mouse cursor a while ago

here’s the whole tutorial, including a video showing it working
http://forums.epicgames.com/threads/972861-Tutorial-Compile-C-for-UE4-Code-Samples-For-You-gt-gt-New-Get-Set-AnimBluePrint-Vars?p=31656285&viewfull=1#post31656285

here’s the function most related to your question

//if player controller mouse button clicked
//then do trace
void AYourHUD::CursorGetClickedActor()
{
	
	APlayerController* ThePC = GetOwningPlayerController();
	
	//could not get PC ?
	if(!ThePC) return;
	
	//Was LeftMouseButton Just Clicked?
	if(!ThePC->WasInputKeyJustPressed(EKeys::LeftMouseButton)) return;
	
	//~~~~~~~~~~
	
	//no canvas for some reason?
	if(!Canvas) return;
	
	//~~~~~~~~~~~~~~~~~~
	
	/** 
	 * Transforms 2D screen coordinates into a 3D world-space origin and direction.
	 * @param ScreenPos - screen coordinates in pixels
	 * @param WorldOrigin (out) - world-space origin vector
	 * @param WorldDirection (out) - world-space direction vector
	 */
	//void Deproject(FVector2D ScreenPos, /*out*/ FVector& WorldOrigin, /*out*/ FVector& WorldDirection);
	
	
	FVector Origin;
	FVector WorldDir;
	Canvas->Deproject(MouseLocation,Origin,WorldDir);
	
	
	//Trace
	
	FCollisionQueryParams TraceParams(FName(TEXT("HUDClick")), this);
	//TraceParams.bTraceComplex = true;
	//TraceParams.bTraceAsyncScene = true;
	TraceParams.bReturnPhysicalMaterial = false;

	//Re-initialize hit info
	FHitResult TraceHit = FHitResult(ForceInit);
	
	GetWorld()->LineTraceSingle(
		TraceHit,		//result
		Origin,	//start
		Origin + 70000 * WorldDir , //end
		ECC_PawnMovement, //collision channel
		TraceParams
	);
		
	
	//Did Not Hit Anything?
	if(!TraceHit.bBlockingHit) return;
	
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//			Get Hit Actor
	AActor* HitActor = TraceHit.GetActor();
	
	//Could not get actor?
	if (!HitActor) return;
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	//		Check Hit a Static Mesh Actor
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	if(HitActor->IsA(AStaticMeshActor::StaticClass()))
	{
		//attempt cast to SMA
		AStaticMeshActor* HitWall = Cast(HitActor);
		
		//could not cast to SMA some reason?
		if (!HitWall) return;
		
		//Static mesh actor has component?
		if(!HitWall->StaticMeshComponent) return;
		
		//Just a fun little test, repeatedly rotates clicked static mesh
		FRotator TheRot = HitWall->GetActorRotation();
		TheRot.Yaw += 3;	//rotators are in degrees now, yaaay!
		TheRot.Pitch += 3;
		HitWall->SetActorRotation(TheRot);
	}
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}

Thanks so much, Nathan! I will try that first thing tomorrow. I’m sure that will do the trick, so I am going to mark that as answered for now. Much obliged!