--======================================================================================================================
-- LINA = "LINear Animation"
-- This Lua snippet creates linear animation Lua objects. The method employed in this script is not the most memory
-- efficient; however, it is easy enough to comprehend and use. A more advanced and memory optimized version of this
-- script, using Lua meta-tables is also provided in this example section.
--
-- There are two primary properties of these animation objects, 1) an 'instant value' property, .instv and 2) a
-- "target" property, .target. The .instv 'animates to' the target value whenever the values don't match
-- (unless .paused = true). To initiate the animation, you simply set the target property and the .instv will
-- animate TO the target value, regardless of whether its up or down.
-- When creating animation objects, there are multiple ways to define the animation, depending on how many arguments
-- you supply to the constructor function. Sometimes you don't know the rate, but DO know how long something takes
-- TO animate between limits etc:
-- • With one argument, you specify the rate of the animation, in units/second. There is no hi/lo limit values.
-- • With two arguments, you specify the "UP" rate and the "DOWN" rate. There is no hi/lo limit values
-- • With three arguments, you specify the lo limit, the high limit, and the rate of animation.
-- The three methods above are only for convenience. You may still set any parameters affecting the animation limits
-- and rates via provided the method functions.
-- There are multiple'status properties' that you can query whether the animation is running or not.
-- * A .direction property( +1/-1) tells you if the .instv (animation) is increasing or decreasing. (0 if stopped)
-- * A .inTransit property (0/1) tells you if the animation is actively in progress.
-- * A .paused property (true/false) to tell you if the animation is paused.
-- The following methods are provided to control the animation and change its parameters. These may be changed while
-- the object is actively animating as well:
-- * .setLowLimit(l_low_limit)
-- * .setHiLimit(l_hi_limit)
-- * .setDnRate(l_dn_rate)
-- * .setUpRate(l_up_rate)
-- * .setTarget(l_target)
-- * .sync(sync_value) -- sets the .target and .instv values to the sync_value, stopping the animation.
-- * .setRate(l_rate) -- sets the animation rate for both UP and DOWN directions.
-- * .setPause(l_pause_state)
-- COMMON USE CASES
-- * Animate a cockpit switch.
-- * Animate a light extending from a wing.
-- * Ramp up some internal variable, like the RPM of a gyro and tie it to an FMOD sound.
-- * Drive a set of altitude 'scrollwheels' that are motorized at some max rate (and can't keep up in a dive)
-- * Animate anything driven by an electric motor that has a fixed rate.
-- * Animate a flap lever between detents when using commands like: "up a notch" or "down a notch"
--======================================================================================================================
-- 2) Global Var Declarations
lina_objects = {} -- global table to hold all the lina objects
------------------------------------------------------------------------------------------------------------------------
-- 4) X-Plane Datarefs
xdr_sim_paused = find_dataref("sim/time/paused") -- we don't want to animate when the sim is paused
------------------------------------------------------------------------------------------------------------------------
-- 9) Lua Object Constructors. Use to create new lina objects
function new_lina(...)
local args = {...}
local o = {}
-- user set properties
o.uni_rate = 0 -- animation rate is the same going up or down
o.up_rate = 0 -- animation rate when target is higher than the instv
o.dn_rate = 0 -- animation rate when the target is lower than the instv
o.lo_limit = 0 -- lower limit of the animation values (commonly 0)
o.hi_limit = 1 -- upper limit of the animation values (commonly 1)
o.duration = duration -- length of animation in seconds
o.range = range -- range of value. (range/duration = rate)
o.target = 0.0
-- calculated properties
o.instv = 0.0
o.d_error = 0.0 -- delta error
o.d_second = 0.0 -- delta value, per second
o.inTransit = false
o.direction = 0
o.last_target = 0
o.paused = false
------------------------------------------------------------------------------------------------------------------------
-- 3 Different ways to specify the animation rate during declarations
------------------------------------------------------------------------------------------------------------------------
if #args = 1 then -- (universal_rate, i.e. x units/second)
o.up_rate = args[1]
o.udn_rate = args[1]
elseif #args == 2 then -- (up_rate, down_rate) animates from instv to target at rates specified
o.up_rate = args[1]
o.dn_rate = args[2]
elseif #args == 3 then -- (lo_limit, hi_limit, duration) assumes uni_rate
o.lo_limit = args[1]
o.hi_limit = args[2]
o.duration = args[3]
o.uni_rate = (o.hi_limit - o.lo_limit) / o.duration
end
------------------------------------------------------------------------------------------------------------------------
-- "Object" METHODS
------------------------------------------------------------------------------------------------------------------------
o.setLowLimit = function(l_low_limit) -- Sets the Lower Limit. Anim won't go past this valuer
o.lo_limit = l_lo_limit
end
o.setHiLimit = function(l_hi_limit) -- Sets the Upper Limit. ....
o.hi_limit = l_hi_limit
end
o.setTarget = function(l_target) -- Sets the target value to animate to.
o.target = l_target
end
o.sync = function(sync_value) -- Syncs .target and .instv to same value, ceasing the animation.
o.target = sync_value
o.instv = sync_value
o.d_error = 0
end
o.setRate = function(l_rate) -- Sets the rate (units/secons) of the animation
end
end
------------------------------------------------------------------------------------------------------------------------
-- 10) Lua Object Instantiations
test_lina = new_lina(1.2)
------------------------------------------------------------------------------------------------------------------------
-- 11) Utility Function Definitions
function lina_flcb()
if xdr_sim_paused == 0 then -- sim is running
for i, v in pairs(lina_objects) do
v.d_error = v.target - v.instv -- calculate current error
if math.abs(v.d_error) <= v.d_second * SIM_PERIOD then -- reached target. set target = instv
v.instv = v.target
v.direction = 0
v.inTransit = false
v.d_error = 0
else -- not reached target, we need to animate the values
v.inTransit = true
if v.d_error < 0 then -- instv is higher than target. animate down
v.direction = -1
v.instv = v.instv - v.d_second * SIM_PERIOD
else -- instv is lower than target, animate up.
v.direction = 1
v.instv = v.instv + v.d_second * SIM_PERIOD
end
v.last_target = v.target
end
end
end
end
------------------------------------------------------------------------------------------------------------------------
-- 13) X-Plane Major Callbacks
function after_physics()
lina_flcb()
end
--======================================================================================================================
-- EXAMPLE USAGE
--======================================================================================================================
-- we are going to drive a custom dataref with our animation object. So we need to create it
cdr_animated_switch = create_dataref("example/animation/animated_switch", "number")
-- This custom dataref will read '1' whenever the animation is in transit and 0 when its not. We're just putting
-- it here to show how you can test against the .inTransit value to know if an animation is ongoing or not.
cdr_inTransit = create_dataref("example/animation/inTransit", "number")
-- create a linear animation object, clamped between the range 0 and 1 and it takes 5s to cover that range.
lina_switch_animation = new_lina(0, 1, 5)
-- set the .target to 1 during the the initial script load, it will start animating immediately because .instv = 0
lina_switch_animation.setTarget(1)
-- 'after_physics' is one of XLua's default flight loop functions that run continuously.
function after_physics()
-- when the scripts loaded, we set the .target to 1 above, so it will take five seconds before the animation
-- .instv property reaches the value of 1. The block below runs continuously and "monitors" the action. Once
-- the .instv property reaches 1 (after 5 seconds), then this block will execute and set the target back to 0 and
-- the animation will immediately reverse and go back down to zero.
if lina_switch_animation.instv == 1 then
lina_switch_animation.setTarget(0)
end
-- this block monitors the animation status and when the animation is actively in progress, sets the custom
-- dataref to a value of 1. When the animation stops (once .instv = .target) then this DR reads 0.
if lina_switch_animation.inTransit then
cdr_inTransit = 1
else
cdr_inTransit = 0
end
-- every flight loop, we update the custom datarefs with the relevant object values
cdr_animated_switch = lina_switch_animation.instv
end