I am doing a top-down game (asteroids) and for the objects that are off the screen, I want to put an indicator on the edge of the screen showing the direction where the object is located.

Is there a simple way to do this in Godot? I have a feeling I need to find some geometry courses...

in Engine

+1 vote

I found a very useful tutorial which explains the maths needed to do the necessary calculations without requiring one to study the theory from scratch. The explanation is very methodical and easy to follow:

https://gamedevelopment.tutsplus.com/tutorials/positioning-on-screen-indicators-to-point-to-off-screen-targets--gamedev-6644

by (113 points)
selected
+1 vote

One simple approach that comes to mind is to have each asteroid also contain an indicator sprite that is hidden until the asteroid goes off the screen. When the asteroid is off the screen, the indicator is un-hidden (shown) and its position is clamped to always be within the camera's view. Does that make sense?

by (629 points)

That's an interesting approach. How do you clamp something to be within the camera's view? Where is that setting? In the meantime, I have found a nice tutorial (which explains the maths nicely) which I wanted to add as an answer here.

No setting, you'd have to write some code. Not completely sure about the function names, I'd have to look them up. Something along the lines of this:

``````# asteroid script
func _process(delta):
var g_pos = get_global_position()
var camera_rect = # get the rectangle for what the camera can "see"

# if asteroid is outside camera bounds:
if g_pos.x < camera_rect.x || g_pos.y < camera_rect.y || g_pos.x > (camera_rect.x + camera_rect.w) || g_pos.y > (camera_rect.y + camera_rect.h):
\$indicator_sprite.show()
var new_pos = \$indicator_sprite.get_global_position()
# clamping ensures indicator stays in visible area of screen
# sort of a bonus that clamping also ensures indicator is as close to asteroid as possible while still being visible
new_pos.x = clamp(new_pos.x, camera_rect.x, camera_rect.x + camera_rect.w)
new_pos.y = clamp(new_pos.y, camera_rect.y, camera_rect.y + camera_rect.h)
\$indicator_sprite.set_global_position(new_pos)
else: # asteroid is inside camera bounds
\$indicator_sprite.hide()
``````

After writing that out, it doesn't seem as simple to me as I first thought :) I was lazy and left out how to get the camera rect2D, if you would like I can add that bit.

I guess it is simple in that it doesn't put the indicator in a line from the center of the camera to the asteroid (like your answer), but rather it puts the indicator at the closest point to the asteroid that's still onscreen. In some cases, the two approaches put the indicator in the same spot :)

Another note: this approach puts the indicator at exactly the edge of the screen, so it's half offscreen. to fix that, we can simply shrink our camera_rect: `x + margin`, `y + margin`, `w - (2* margin)`, `h - (2 * margin)`. We sort of pretend like the camera shows less than it does, and trick it into putting the indicator sprite in a spot where it's completely visible.

Not totally sure this comment is sensical, so let me know if you have questions :D