Following GUI (to a GO) with a strange offset

Hey everyone, I would like to position the healthbar of the enemies (is a GUI in a GO) above the heads of the enemies. I use the function “world_to_screen” from the module “Orthographc camera” for this. However, there is still an offset here and I’m not sure if it’s due to the DPI and how to solve it. Here is a screenshot with two floating healthbars that have this offset. Any ideas?

ENEMY SCRIPT:

local function send_healthbar_update(self)
    local world_position = go.get_position() -- Hole die Welt-Position des Enemy
    local screen_position = camera.world_to_screen(CAMERA_ID, world_position) -- Konvertiere in Bildschirmkoordinaten

    -- Aktualisiere die Position der Healthbar GUI
    msg.post("#healthbar", "update_healthbar_position", { position = screen_position })
end

GUI-Script:

    gui.set_position(healthbar_node, message.position)
    gui.set_position(healthbarBg_node, message.position)

Edit:
For a better understanding: the two healthbars in the screenshot belong to two enemies enemies that are outside the field of view

Maybe if you intended to use world_position use go.get_world_position()?


On the other hand - is there any specific feature from GUI that you need in order to implement healthbars?

I’m asking because, if not, you can do so with sprites simply, just add two sprite components (you have even 9-slice now), one for background and one for actual health:

Healthbar sprites, if solid color is only used, could be really small, even 1x1 (I used 3x3 and at this point I don’t know why xD):
healtbar health

:point_up_2: yes, I uploaded them here :sweat_smile:

And here’s even my Lua module to handle healthbar like this:

local H = {}

local healthbar_width = 10

function H.new(max_health, bar_sprite, health_sprite)
	local instance = {
		max_health = max_health or 100,
		bar_sprite = bar_sprite or "#healthbar",
		health_sprite = health_sprite or "#health"
	}

	instance.hp = vmath.vector3(instance.max_health, healthbar_width, 0)

	instance.damage = function(damage)
		instance.hp.x = instance.hp.x - damage
		go.set(instance.health_sprite, "size", instance.hp)
	end

	instance.heal = function(heal)
		instance.hp.x = instance.hp.x + heal
		go.set(instance.health_sprite, "size", instance.hp)
	end

	instance.get_hp = function()
		return instance.hp.x
	end

	instance.enable = function()
		msg.post(instance.health_sprite, "enable")
		msg.post(instance.bar_sprite, "enable")
	end

	instance.disable = function()
		msg.post(instance.health_sprite, "disable")
		msg.post(instance.bar_sprite, "disable")
	end

	go.set(instance.health_sprite, "size", instance.hp)
	go.set(instance.bar_sprite, "size", vmath.vector3(instance.max_health + 2, healthbar_width + 2, 0))

	return instance
end

return H

It simply sets the size, so the red bar will shrink down to middle, because there is no pivoting added, but you can add this offset when resizing red bar from code :wink:

And here’s this in action: :beetle:

image

3 Likes

Thank you for your detailed answer. I think I will use the Sprite solution instead. That will be easier. The behaviour that the health bar is reduced in the middle gave me the idea with the GUI. Maybe I can really solve this with an offset. I would have liked to understand the behaviour in the GUI solution.

May I use your healthbar module?

I tried to add an offset to the #health sprite, but it only moves the entire game object to the left. Is it not possible to offset only the sprite within a game object?

instance.damage = function(damage)
		instance.current_health = math.max(0, instance.current_health - damage)
		local new_width = instance.initial_width * (instance.current_health / instance.max_health)

		local offset = instance.initial_width - new_width
		local actPosition = go.get_position(instance.health_sprite)
		local newPosition = vmath.vector3(actPosition.x - offset, actPosition.y, actPosition.z)
		
		go.set(instance.health_sprite, "size", vmath.vector3(new_width, initial_size.y, initial_size.z))
		go.set_position(newPosition, instance.health_sprite)
	end

you should change the GameObject.scale not the Sprite.scale

	if(action_id == hash("attack") and action.pressed) then
		self.health = math.max(self.health - 2, 0)
		go.set("/health_bar", "scale.x", self.health / self.max_health)
	end