原理其实很简单,将3D模型的坐标转为屏幕坐标,然后实时更新UI(血条)即可,直接上关键代码:
func updateUI(delta):
#Update for UI
#人物的全局坐标
var world_pos = global_transform.origin
#这里的uiSize即为血条的UI控件大小
#camera = get_viewport().get_camera() 即为当前视图的摄像头
#通过unproject_position将3D坐标转为屏幕坐标,这里由于人物坐标位于中心,所以需要做一定的偏移
var pos = camera.unproject_position(world_pos) - 0.5 * uiSize
pos.y -= 20 + uiSize.y
#更新UI坐标
$Node2D/UnitView.position = pos
#$Node2D/UnitView.updateStatus(currentHP, currentMala)
pass
=======================================更新====================================================
上述方法有一定局限性,最大的问题在于本质上是2D形式来切合3D模型,如果模型不再屏幕之内就会比较麻烦,可以换个一个思路,通过ViewPort在3D模型上显示2D UI,直接上代码,将视图显示在mesh上(mesh与viewport同级,2D视图挂在viewport下):
#Viewport脚本
extends Viewport
# Called when the node enters the scene tree for the first time.
func _ready():
var mesh : MeshInstance = get_node_or_null("../MeshInstance")
mesh.material_override.params_billboard_mode = true
mesh.material_override.albedo_texture = get_texture()
pass # Replace with function body.
#MeshInstance脚本
extends MeshInstance
func _ready():
pass # Replace with function body.
func _physics_process(delta):
#旋转camera,测试
get_node("../CameraHub").rotation_degrees.y += delta * 60
pass
或则直接作为一个texture显示在Sprite3D中(viewport作为字节点挂在Sprite3D下,2D视图挂在viewport下):
extends Sprite3D
# Called when the node enters the scene tree for the first time.
func _ready():
billboard = SpatialMaterial.BILLBOARD_ENABLED
texture = $Viewport.get_texture()
# $Viewport.usage = Viewport.USAGE_2D
$Viewport.transparent_bg = true
# $Viewport.render_target_v_flip = true
pass # Replace with function body.