I made the below function as you might find it useful to animate multiple floating arrows, as alternative to the static approach above.
Use createAnimatedArrow(...) instead of createMarker(...) for your arrow.
It uses the stream in/out events so we don't waste CPU on markers that are far away. You could further optimise it to bind/unbind the render event at the first and last stream in / out, saving 1 pairs call.
local markers = {}
local animationMethod = "InOutQuad" -- What easing function to use, see https://wiki.multitheftauto.com/wiki/Easing
local animationTime = 1000 -- The time for the animation to go from bottom to top
local animationAmplitude = 0.3 -- How far the marker will move
local function onAnimationFrame(dt)
for marker,anim in pairs(markers) do
-- Check if marker is still valid or destroyed
if isElement(marker) then
-- Find animation progress from time since last frame
local delta = dt / animationTime
anim.t = anim.t + delta * anim.direction
-- Reverse direction if we are above 1 or below 0
if anim.t < 0 then
anim.direction = anim.direction * -1
anim.t = 0
elseif anim.t > 1 then
anim.direction = anim.direction * -1
anim.t = 1
end
-- Find the easing value (usually a 0-1 value) from our animation progress (0-1)
local anim_time = getEasingValue(anim.t, animationMethod)
-- Find the z value we want to offset with
local z_anim = animationAmplitude * anim_time
setElementPosition(marker, anim.x, anim.y, anim.z + z_anim)
else
-- Dereference it
markers[marker] = nil
end
end
end
addEventHandler("onClientPreRender", root, onAnimationFrame)
local function onArrowMarkerStreamIn()
if markers[source] then return end
-- Store the position
-- 't' to keep track of the animation progress
-- 'direction' to keep track of whether we are moving up or down
local x, y, z = getElementPosition(source)
markers[source] = {t = 0, direction = 1, x = x, y = y, z = z}
end
local function onArrowMarkerStreamOut()
if not markers[source] then return end
-- If the marker element is still valid, set it to its original position
if isElement(source) then
setElementPosition(source, markers[source].x, markers[source].y, markers[source].z)
end
-- Dereference marker
markers[source] = nil
end
function createAnimatedArrow(x, y, z, markerType, ...)
if markerType ~= "arrow" then return false end
local marker = createMarker(x, y, z, markerType, ...)
if not marker then return false end
-- Use the stream in / stream out events to only animate markers that are nearby
addEventHandler("onClientElementStreamIn", marker, onArrowMarkerStreamIn)
addEventHandler("onClientElementStreamOut", marker, onArrowMarkerStreamOut)
return marker
end
If your markers are serverside, or you want to change its position after creating it, you'll have to change a few things, let me know if you need help.