text object in 3D space

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By vonflyhighace2
:warning: Old Version Published before Godot 3 was released.

I’m trying to display damage on an enemy with values in 3d space like a number popping up over the enemy head when hit. I Know its really simple in 2D but any ideas of how this would be done in 3d?

:bust_in_silhouette: Reply From: henriquelalves

You probably want a scene just for that; call it “billboard_label” or something, than copy the code available on the GUI_in_3D demo. Basically, in this scene you’ll have a Quad (with the Billboard option enabled, so the player will see the text orthogonally no matter the camera angle), a Viewport (with the Render target enabled), and a Label as a child of said Viewport. Then just add a script with the magic code:

get_node("area/quad").get_material_override().set_texture(FixedMaterial.PARAM_DIFFUSE, get_node("viewport").get_render_target_texture())

This will make your Quad render the label of your Viewport. You’ll probably want some other methods on the billboard_label scene to make it more functional, like “set_billboard_text(string)” to edit the Viewport label without having to access all the nodes ‘from the outside’. Than finally, when you want your enemy to display the damage, just create your “billboard_label” via script, set it’s text, and animate it accordingly (probably by using a simple timer and queue_freeing() by the end of it).

:bust_in_silhouette: Reply From: avencherus

Another option is to acquire the desired point in 3D, then unproject the point to a screen coordinate through the camera.unproject() method. Use that new 2D vector to align some desired 2D node on a CanvasLayer node. The CanvasLayer node will overlay as a GUI, and has layer Z ordering as well if you need things to appear in front or behind other 2D items.

I set up a simple example with a label. I used a position3D to fake the location of where you’d like the text to appear, then unprojected it, offset it by half the width of the label to center the label in the position where it would appear on the canvas layer.

extends Spatial

func _ready():
	var above_head = get_node("TestCube/Position3D")
	var label = get_node("CanvasLayer/Label")
	var camera = get_node("Camera")
	
	var offset = Vector2(label.get_size().width/2, 0)
	
	label.set_pos(camera.unproject_position(above_head.get_translation()) - offset)

https://drive.google.com/open?id=0BxM4Z1C-377ubEdQVXlzeFBGOVk

https://drive.google.com/open?id=0BxM4Z1C-377uQ3hGV3ozLWo4clE

:bust_in_silhouette: Reply From: clemens.tolboom

For Godot 3.2 I came up with adding a label as a child of Sprite3D

onready var sprite = $Sprite3D
onready var label = $Sprite3D/Label

func get_camera():
    var r = get_node('/root')
    return r.get_viewport().get_camera()

func position_label(label:Label, point3D:Vector3):
    var camera = get_camera()
    var cam_pos = camera.translation
    var offset = Vector2(label.get_size().x/2, 0)
    label.rect_position = camera.unproject_position(point3D) - offset

To prevent displaying it when behind the camera or out of range I only call it

func _process(_delta):
    var cam = get_camera()
    var test_point:Vector3 = sprite.global_transform.origin
    if not cam.is_position_behind(test_point):
        if test_point.distance_to(cam.global_transform.origin) < 8.0:
        position_label(label, test_point)