Download

Trouble Getting Dynamic Screen Edge Vector

So I’m working on a game with a fixed camera that will begin to side scroll once the player pawn, rather than the mouse, begins pushing against the edge of the screen. I’ve been having a tough time with this one for a few days now - I’ve explored different options like APlayerController::DeprojectScreenPositionToWorld. I’m facing two main challenges based on what I’m seeing in my implementation vs. what I’m finding for resources online:

  1. I’m getting values out of DeprojectScreenPositionToWorld that I did not expect.
  2. My game allows the fixed camera to zoom in and out, and the pitch adjusts based on how close or far the camera is from the player.

The behavior I’m hoping to see is that when the pawn reaches the left side of the viewport, the camera will begin moving in the same direction as the player left. The challenge with #2 above is that the left edge of the screen at the bottom of the viewport has an X value lower than at the top because of the camera pitch. The challenge with #1 is that the X value of the screen position I’m returning is closer to the center of the screen than anything by the left side of the viewport. My understanding was that if I deprojected screen position to world from a X and Y of 0, it would return the position based on the top-left corner of the viewport, but instead it’s the bottom center.

Probably worth calling out that my camera is also about 50 points higher on the Z axis than the player. Here is the code I currently have written for the function - any help I could get on this would be massively appreciated:

void ASelector::DetermineScrollLock()
{
int32 ScreenSizeX;
int32 ScreenSizeY;
FVector WorldPosition;
FVector WorldDirection;

// Get current screen size in integer values for X and Y.
PlayerController->GetViewportSize(ScreenSizeX, ScreenSizeY);

// Determine check for left side of screen.
PlayerController->DeprojectScreenPositionToWorld(0, 0, WorldPosition, WorldDirection);

if (this->GetActorLocation().X <= WorldPosition.X)
{
RightScrollLock = true;
}

UE_LOG(LogTemp, Warning, TEXT("Player Location: %f"), this-&gt;GetActorLocation().X);
UE_LOG(LogTemp, Warning, TEXT("Player Location: %f"), this-&gt;GetActorLocation().Y);
UE_LOG(LogTemp, Warning, TEXT("Projected World Location (X): %f"), WorldPosition.X);
UE_LOG(LogTemp, Warning, TEXT("Projected World Location (Y): %f"), WorldPosition.Y);

}

I was able to solve it! Want to walk through the solution I came to just in case anyone else is trying to tackle a similar challenge.

For reference, this is meant to be player movement similar to what you would see in a game like XCOM: Enemy Unknown. I didn’t want the screen scroll to trigger based on mouse location because I wanted more of a dynamic perspective around the viewing area and wanted the player’s pawn (which acts like a mouse sometimes) to move in the 3D space independent of actual mouse location. I also wanted to be able to make controls that apply well to a gamepad (again, similar to XCOM).

Looking online, I found a ton of possible solutions people had tried to utilize at different points:

  • Using LastRenderTime or WasRecentlyRendered to determine when the pawn had moved off-screen. This solution was interesting, but doesn’t work well because you don’t want to trigger the activity based on the event that the player can no longer see the pawn’s character model for multiple frames.

  • Similar to what I was doing, using DeprojectScreenPositionToWorld in order to establish artificial barriers in the 3D world that the player pawn would cross. This solution worked for a lot of other people I found online struggling, but didn’t work for me because of the perspective view and the option to allow the player to zoom in or out of the game world.

  • Establishing a line trace from the camera actor that would send projections out on the top, left, right, and bottom areas of the screen to catch the player’s pawn and trigger based on the hit event. This one seemed to be the most dynamic choice I found and worked for a good number of people. I was in the process of implementing a solution like this for my game, but then found at least in my case there was an easier solution.

Since I wanted to move the camera in the direction of the player when they run against the edge of the screen, regardless of whatever pitch the camera is taking, I found the easiest way to implement this was to use ProjectWorldToScreen from the pawn’s location, then use the GetViewportSize to determine player position against overall viewport size so I could dynamically determine how close the player was to the edge of the screen. This was nice too, because it allowed me to create a separate float variable that worked like a buffer range that let me set how close to the edge of the screen the player actually had to be to initiate the screen scroll.

Might seem simple to some people looking into it, but as a newby to UE4 and C++ this was really racking my brain.