I have a crosshair image that will lock onto enemies that are visible in the camera and will stay on the edge of the screen when the enemy is offscreen. This works until you face more than 90º away from the enemy in either direction. If facing exactly 180º from the enemy, the indicator will appear as thought the enemy were directly in front of the camera but I would like it to stick to the closest edge of the screen instead. The red crosshair is drawn onto a 2d space and positioned within the camera view.

On screen: enter image description here

Slightly offscreen: enter image description here

Very offscreen: enter image description here

        var enemy_pos:Vector3 = enemy.global_transform.origin
        var screen_pos:Vector2 = camera.unproject_position(enemy_pos)
        var lock:TextureRect = $TargetLock
        var lock_size:Vector2 = lock.texture.get_size()
        var crosshair_center:Vector2 = crosshair.rect_position + crosshair.rect_size * 0.5
        var direction_to_enemy := (enemy_pos - camera.global_transform.origin).normalized()
        var direction_facing := -camera.global_transform.basis.z
        # currently unused, > 0 = target within 90º rotation
        # var facing_target = direction_facing.dot(direction_to_enemy)
        var screen_width = ProjectSettings.get_setting("display/window/size/width")
        var screen_height = ProjectSettings.get_setting("display/window/size/height")
        screen_pos -= lock_size / 2
        screen_pos.x = clamp(screen_pos.x, 0, screen_width - lock_size.x)
        screen_pos.y = clamp(screen_pos.y, 0, screen_height - lock_size.y)

I can understand the issue, but my vector math in 3d space is not good enough to solve this issue.


The dot product should tell you if the camera is looking roughly at the enemy or away from it. You have it in comments:

var facing_target = direction_facing.dot(direction_to_enemy)

Now, if that is less than 0, it means the camera is looking away from the enemy. In that case, take screen_pos and scale it, such that when you clamp it it will be on the edge of the screen.

Compute the center of the screen:

var screen_center = Vector2(screen_width * 0.5, screen_height * 0.5);

Then take the position form the center, scaled up (I'm using screen_width as scale factor), and offset by the center (to have it back from the top-left corner):

screen_pos = (screen_pos - screen_center) * screen_width + screen_center;

Clamp that as usual, and it should give you a position on the edge of the screen. Except when screen_pos and screen_center are equal. Hopefully that is acceptable.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.