0 votes

I'm using a cube MeshInstance as the RPG-character.

So I have 4 nodes in a 3D scene:
- CubeKinematicBody
-- Cube
MeshInstance
- Camera
- Label

The kinematic body gets its own global 3D location (which is the same as the cube's),

The camera gets this 3D location and turns it into a 2D location on the computer screen.

I set these variables under func _process(delta).
And these variables change and seem to represent the cube's 3D or 2D-screen location as I move the character around in-game.

But when I try to use the screen-location in the Label node, its value stops changing as I move around.

I thought if I used getglobaltransform() to get the cube's location,
it'd be global.

How can I get RPG dialogue to appear above a 3D character's head in a 3D game?

Here's my code:
-------KINEMATIC NODE------

extends KinematicBody

var direction = Vector3(0,0,0)
var myCube3DLoc = Vector3(0,0,0)


func _process(delta):       #Use this for things you want to work every frame. e.g. movement.
    myCube3DLoc.x = get_global_transform().origin.x
    myCube3DLoc.y = get_global_transform().origin.y
    myCube3DLoc.z = get_global_transform().origin.z
#   print("myCube3DLoc using get_global_transform().origin.x, y, or z returns: ", myCube3DLoc)

-------CAMERA NODE-------

extends Camera

onready var myCube3DLoc = get_node("../Cube_KinematicBody").myCube3DLoc
onready var myCubeScrnLoc = unproject_position(myCube3DLoc)
    #unproject_position(Vector3) returns the 2D coordinate in the Viewport rectangle that maps to the given 3D point in worldspace.


func _process(delta):
    myCube3DLoc = get_node("../Cube_KinematicBody").myCube3DLoc
    print("Camera node says... unproject_position(myCube3DLoc) returns: ", unproject_position(myCube3DLoc))

-------LABEL NODE-------

extends Label

onready var myCubeScrnLoc = get_node("../Camera").myCubeScrnLoc

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
    myCubeScrnLoc = get_node("../Camera").myCubeScrnLoc
    myCubeScrnLoc = myCubeScrnLoc   
        #^This doesnt help. The value still doesn't change.

    print("LableNode: myCubeScrnLoc equals: ", myCubeScrnLoc)
        #^This does't change as I move the cube, even though it's under func _process(delta).

    set_position(myCubeScrnLoc)

P.S. I also tried putting the label node underneath the kinematic one. It doesn't work. Maybe because the Label node isn't even in the 3D world?

Would signals help?

P.P.S. sorry if this post has weird formatting. I guess I'm not used to Godot Q&A forum posting.

asked Jan 16 in Engine by Sdhy (50 points)
edited Jan 16 by Sdhy

I would think that the value from unproject_position() would give the correct location of the cube on the Viewport. Maybe some node has to be set up or initialized in the _ready() function of the Label script?

What is setting up or initializing a node? I'm not sure what to do here.

2nd Edit: I tried using a Viewport node, but it's just like the label node.
When the Viewport node tries to get the screen position from the camera node,
it doesn't change when the cube moves around.

Would signals or global variables help?

It may be easier to write something like this in the Label node's script:

# Replace these with paths to the respective nodes in the scene tree.
onready var camera = $path/to/camera_node
onready var cube = $path/to/cube_node

func _process(delta):
     var cube_position = camera.unproject_position(cube)
     set_position(cube_position)

1 Answer

0 votes
Best answer

Thanks, Ertain! Your simple script worked!
It also means all the code can fit on a single label node,

I just had to change the variable for unproject_position to a Vector3 variable (otherwise, it woudn't work).
I also changed some variable names.

The text appears beside the cube, so I'll have to figure out how to make it apear above, instead.

For anyone else who wants to know, below is a copy of the modified script that now makes the label node follow the cube character.
Copy and paste this into a Label node script:

extends Label

#This lets you get the character-cube's 3D location:
onready var cube = get_node("../Cube_KinematicBody")

#This lets you use a function that turns the cube's 3D location into a 2D location on your screen:
onready var camera = get_node("../Camera")

#We need to set a blank 3D Vector variable:
var cubeLoc = Vector3(0,0,0)    

#Everything under this func _process(delta) is "updated" every frame:
func _process(delta):
    #We want to get the cube's 3D location at each frame.
    #We need to do this once for each coordinate.
    #We'll use the 3D Vector we defined earlier:
    cubeLoc.x = cube.get_global_transform().origin.x
    cubeLoc.y = cube.get_global_transform().origin.y
    cubeLoc.z = cube.get_global_transform().origin.z

    #We need to convert the character-cube's 3D location into a 2D coordinate on your game-window's screen and set it to a variable:
    var label_pos = camera.unproject_position(cubeLoc)

    #And finally, we set this label node (i.e. the RPG dialogue) to that spot on the game-screen:
    set_position(label_pos)

Notes:
- The label node is on the same level as the camera and cube nodes.
- You can use the kinematicBody node for the cube's 3D location. You don't have to use whatever mesh node is underneath it.
- Kinematic movement code not included. You can find a YouTube tutorial.
- I'm not sure how to make the label actually appear above the cube. But at least it now follows the cube!

answered 5 days ago by Sdhy (50 points)
Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.