hafen.virtual: An Image in the World
hafen.virtual():sprite():add(asset, anchor) stands a PNG in the 3D world on the client-only core a ghost uses: no server id, nothing reaches the server, nothing protected.
local icon = hafen.asset():get("icon.png")
local session = hafen.session():current() -- the character on screen
local position = session:player():gob():position()
local sprite = hafen.virtual():sprite():add(icon, position):scale(3) -- ~3 tiles tall, at its feet
sprite:rotate(math.pi / 2):alpha(0.8) -- 90 degrees, slightly translucent
hafen.virtual():sprite():remove(sprite) -- or let reload or disable clear it
| Rule |
Detail |
asset |
A hafen.asset image handle, handle-only: a path string raises. |
| The anchor |
A Position to stand it at a point or a Gob to follow one (the anchor). |
| Defaults |
Scale 1, full opacity, no tint, facing "fixed", not clickable. Every setter returns the sprite. |
The sprite
The shared vocabulary plus its own.
| Method |
Returns |
Permission |
Description |
sprite:facing() / sprite:facing(mode) |
the sprite |
Unprotected |
How it meets the viewer: "fixed", "camera" or "screen" (facing). |
sprite:image() |
string |
Unprotected |
The addon-relative image path. Read-only: the texture is sampled when placed, so another image is another sprite. |
| Rule |
Detail |
| Size |
A "fixed" or "camera" sprite is about a tile tall at scale 1, width following the image aspect, and :scale is its world size. A "screen" one draws at native pixel size and :scale multiplies that. |
Facing
| Mode |
Draws |
"fixed" |
A textured quad standing upright at the sprite’s own angle (:rotate), about a tile tall, double-sided. |
"camera" |
The same world quad turned to the viewer in yaw and pitch, keeping its world size. :rotate is stored but unused. |
"screen" |
A screen-space blit facing the camera at a constant screen size. |
local prey = session:world():gob():nearest("rabbit")
local badge = hafen.virtual():sprite():add(icon, position):facing("screen"):scale(2)
local marker = hafen.virtual():sprite():add(icon, prey):facing("camera"):offset(0, 0, 14)
| Rule |
Detail |
| World geometry |
"fixed" and "camera" are true geometry: world-scale applies, and each occludes and is occluded like anything in the scene. Any other mode raises naming the three. |
| A camera-facing quad rises along the camera’s up axis |
Tilted to a top-down view that axis is horizontal. The picture lies in the plane through its anchor, which at ground level the terrain swallows. Anchor it to a game object and lift it with :offset(0, 0, z). One at a point has no lift, so give it a gob anchor or keep the camera tilted. |
"screen" |
The world-anchored version of drawing an image at session:world():worldToScreen in a HUD overlay. The image is its own size in design pixels times the scale, bottom-centred on its world point. It is drawn on top of the scene with no depth occlusion. :rotate has no visible effect. :alpha and :tint work as on "fixed". No :outline ring is drawn. |
:facing rebuilds the visual |
The one property that decides which thing is drawn: writing it re-mills the sprite in place, same position and look. Everything else applies live. |
Clickability
A sprite with world geometry ("fixed", "camera") is made clickable with sprite:clickable(true), as a ghost is. It gains a pick surface. A click is detected client-side and consumed before any server click.
local sprite = hafen.virtual():sprite():add(icon, position)
:clickable(true)
:onClick(function(clicked_sprite, button, world_x, world_y) -- 1 = left, 3 = right; then the clicked world point
hafen.log():write(("clicked my sprite (button %d)"):format(button))
end)
| Rule |
Detail |
| Both fire |
The per-sprite :onClick(fn) and the owner-scoped SpriteClicked event, which reaches only your addon. |
A "screen" sprite is click-through |
No world geometry, so it never wins a pick. :clickable and :onClick on one are no-ops. Use "fixed" or "camera" for selection. |
Following a game object
A Gob anchor makes the sprite track it every frame. sprite:offset(x, y, z) says where it sits relative to the gob in world units, z up.
-- a marker that floats above a creature and follows it around
local icon = hafen.asset():get("marker.png")
local world = hafen.session():current():world()
local prey = world:gob():nearest(function(gob) return (gob:name() or ""):find("rabbit") end)
if prey then
hafen.virtual():sprite():add(icon, prey):scale(1.5):offset(0, 0, 14)
end
| Rule |
Detail |
| Its own facing and scale |
:rotate and :scale still work on it. |
| Dies with the gob |
A felled tree takes the image on it. |
| Listed at the gob |
gob:overlay():list() shows it as a read-only entry. Address it through this collection. |
| One drag handle |
The same :position, :rotate and :scale a ghost has, so a handle written for one drives the other. |
See Also
hafen.virtual — the section: the anchor, the shared verbs, and the whole-section switch.
- Models — the same thing with a glTF mesh instead of an image.
- Widgets — the same facing modes, on a whole window standing in the world.
hafen.asset — loading the PNG a sprite takes.
- Drawing — the same image drawn on screen instead of in the world.
- Events —
SpriteClicked.