NokiMo
Dare Looks
Dare Looks

patreon


Off-Screen Indicators, Screen Border Constraint Code Snippet

Do you remember the VIP Target indicators, that mark valuable targets you can hit for extra points? They will randomly pop up and disappear again after a while, so you really don't want to miss one of these showing up.


VIP Target Off-Screen Indicators

To help with that, there now are off-screen indicators: So even if you are not looking their way, the game lets you know where they are!

If you have played any video game, there's a chance you are familiar with this concept – as UI/UX goes, these things are pretty basic stuff!


Implementation

What I wanted to share was my implementation of constraining the indicator UI positions to the borders of the screen: If nothing else, I can use this post as a future reference for myself—there is no way I'll never need one of these things again, for future projects, hah.

A bit of a heads-up, though: I'm not much of a mathematician. I'll try my best to explain the math, but I'm definitely no expert!

So, this is my code that constrains an input direction to a point along the border of the screen:

This code is run when positioning each of the indicator UI elements, on the screen. For this to work, localDirection has to be the vector pointing at the 3D world target, in local space (relative to the camera).

To better illustrate how the code works, I have put together these two diagrams:

Vector L on the left represents localDirection: The vector pointing at the target (P3), as seen from the camera's view (technically it's three-dimensional, since it points at an object in 3D space, but for our purpose we can ignore the depth, since we will only use the 2D components, in the UI).

Point P0 is the center of the screen, so x and y are screen width and height, divided by two. The points P1 and P2 are where L intersects with the left and bottom edge of the screen: To constrain the indicator position to this corner, we have to place it on either P1 or P2, so those are the points we want to calculate!

On the right, you have the same points, but you can see that the coordinates of P1, P2 and P3 make up three different triangles:

So, to calculate P1 and P2, we need to find out the missing width and height of their respective triangles: Luckily, we have one complete triangle, for which we have both, width and height!

Note how all three triangles have the same width/height ratio: It's always the same triangle, just scaled differently. Let's call this their aspect ratio m (I know there is a proper name for this value, but I forgot, heh). Because m is identical for all three triangles, we can use it to calculate the missing width P1x and height P2y:

This is what happens in the code, above. You'll just notice that the signatures of x and y change, depending which way localDirection is pointing, since it will change which edges of the screen we're looking at (i.e. left vs right).

One issue remains: How do we know which of the two points we actually need? We are calculating intersections for two edges, but we only need one: If you look at the diagram above, you'll see that P2 intersects with the left edge (extended down) but actually ends up below/outside the screen.

Luckily, we can predict which borders our input direction is going to actually intersect with, by comparing the aspect ratios of the direction (m) and the screen (x divided by y): If the ratio of the direction is higher than that of the screen, it will only ever intersect with either the left or the right side, but never the top or bottom one!

This only works because L has its origin in the center of the screen; for arbitrary positions we would have to change to code quite a bit, but for my use case this works just fine! (Otherwise, we could check which intersection is closer to the origin point, to determine the correct one.)


And that's how it works!

I hope it made at least some sense! While putting this post together, I actually kept finding ways to optimize my code—so if nothing else, it has helped me sort my thoughts, haha!

And if you are a math genius and have some pointers, feel free to let me know!


Related Creators