How to make ASOBO_GT_Anim_Code <ANIM_CODE> persistent?

I have a component setup like this:

		<Component ID="myComp" Node="myComp">
			<UseTemplate Name="ASOBO_GT_Anim_Code">
				<ANIM_NAME>myComp</ANIM_NAME>
				<ANIM_CODE>
				  (L:Var1, NUMBER) 5 == if{  (L:VAR2, NUMBER)}        
				</ANIM_CODE>  
				<ANIM_LENGTH>20</ANIM_LENGTH>
			</UseTemplate>
		</Component>

Var1 is changing every frame
So every frame the condition is not met the animation goes back to 0.

How can I make the animation stay on the frame I need when the first condition is no met ?

More generally. Is there a way to make a component run its model behaviour code only once when it is created or spawned and keep the values it computed at this time?

You could use an ASOBO_GT_Update template with update_once set to True to check a L:var once when a sim object is spawned

        <UseTemplate Name="ASOBO_GT_Update">
	        <UPDATE_ONCE>True</UPDATE_ONCE>
         	<UPDATE_CODE>
	        	(L:Var1) 5 == if{ (L:Var2) (&gt;L:Var3) }   
        	</UPDATE_CODE>
        </UseTemplate>

and use L:Var3 in the animation.

<Component ID="myComp" Node="myComp">
    <UseTemplate Name="ASOBO_GT_Update">
	     <UPDATE_ONCE>True</UPDATE_ONCE>
         <UPDATE_CODE>
	        	(L:Var1) 5 == if{ (L:Var2) (&gt;L:Var3) }   
        </UPDATE_CODE>
    </UseTemplate>
	<UseTemplate Name="ASOBO_GT_Anim_Code">
		<ANIM_NAME>myComp</ANIM_NAME>
		<ANIM_CODE>  
             (L:Var3)
		</ANIM_CODE>  
		<ANIM_LENGTH>20</ANIM_LENGTH>
	</UseTemplate>
</Component>

Otherwise
you could do this, at least I think it’ll work, the animation will read its value from the 3rd var, and only update it if the first var is true

<Component ID="myComp" Node="myComp">
	<UseTemplate Name="ASOBO_GT_Anim_Code">
		<ANIM_NAME>myComp</ANIM_NAME>
		<ANIM_CODE>
			 (L:Var1) 5 == if{ 
                    (L:Var2) (&gt;L:Var3)
             }        
             (L:Var3)
		</ANIM_CODE>  
		<ANIM_LENGTH>20</ANIM_LENGTH>
	</UseTemplate>
</Component>
1 Like

@kcgb
The ASOBO_GT_Update seems to be used for input events.
It has an INTERACTION_MODEL parameter related to this.
Your code works when I execute it in the behaviour debug window, but it won’t run on its own, even when I replace the UPDATE_ONCE to false or use the FREQUENCY parameter. It says in the debug window that the code is executed but it isn’t. If I click the exec button , it works fine.

I tried setting variable in the anim code but it didn’t work either.

I would have thought should work fine if in a set of <Component/> brackets.

I assume this is for a scenery object and not an aircraft?
the other thing I’d try is using ASOBO_Update_Template instead of ASOBO_GT_Update

ASOBO_GT_Update == 2020 template
ASOBO_Update_Template == 2024 template

Yes, its for a scenery.
I have a simconnect app that spawns a bunch of instances of the same simobject. And I want each objects to use different parameters.

I’ll take a look at ASOBO_Update_Template

Hi,
any idea where I might find info on the difference between these two?
Cheers

ASOBO_Update_Template

https://docs.flightsimulator.com/msfs2024/html/5_Content_Configuration/Models/ModelBehaviors/TemplateExplorer/Asobo_EX1/Base/Component/Update.html?rhhlterm=asobo_update_template&rhsearch=asobo_update_template

ASOBO_GT_Update

https://docs.flightsimulator.com/msfs2024/html/5_Content_Configuration/Models/ModelBehaviors/TemplateExplorer/Asobo/Generic/Updates.html?rhhlterm=asobo_gt_update&rhsearch=asobo_gt_update

Thanks!
I did a bit of testing and haven’t found any difference
 They even have the same bug on camera change so most likely the same thing under the hood.

The template is fine, the code is not far wrong, just a small bug.

<Component ID="myComp" Node="myComp">
	<UseTemplate Name="ASOBO_GT_Anim_Code">
		<ANIM_NAME>myComp</ANIM_NAME>
		<ANIM_CODE>
            (L:Var1, NUMBER) 5 == if{ 
                (L:VAR2, NUMBER) (&gt;O:ANIM_FRAME)
            }
           (O:ANIM_FRAME)
		</ANIM_CODE>  
		<ANIM_LENGTH>20</ANIM_LENGTH>
	</UseTemplate>
</Component>

O: simvars are local to that component, i.e. that var name won’t appear in any other component. The code would work the same if you used L:VAR3 with the dev advantage you could see that working in the ‘Behaviours’ debug window, but for ‘production’ use you might as well make it an O: var as it’s not relevant elsewhere in your model XML.

You see the logic? O:ANIM_FRAMEwill be zero until L:Var1 is 5, at which point L:VAR2 will be COPIED to O:ANIM_FRAME. When L:Var1 changes from 5, O:ANIM_FRAME stops getting updated. But if L:Var1 becomes 5 again, the VAR2 to ANIM_FRAME update will repeat.

O:ANIM_FRAME is always used at the end of the anim code block to control the animation.

So the small change in the code is simply to NOT use L:Var2 directly for the animation, instead we copy that value to another variable which then persists.

Ah, edit
 @kcgb was way ahead of me with the same answer further up this thread. +1 for his answer which should be noted with this thread as ‘problem solved’


I’ll give it a try but for some reason, every time I tried setting a var inside the <ANIM_CODE/> it would not work. Maybe because my simobject is not an aircraft, just a scenery simobject.

What is the scope of an O: var ? Is it limited to that specific component or can it be used by other components in that simobject ?

All the var types are described in the RPN section of the SDK docs. O:, L: and Z: variables are all similar in usage in model XML. You don’t need to specify the NUMBER units, just (L:VAR) or (&gt;L:VAR). The types just vary a bit in how they fit into the sim.

O: are local to the component they’re in.

Z: are local to the SimObject they’re in.

L: are global in the sim, i.e. all currently active simobjects see the sameL: vars. Uniquely, L: vars can be listed in the systems.cfg (see SDK docs) such that their values are persisted between sim sessions which can be extremely convenient, e.g. for remembering units choices.

For many uses the fact that L:vars are easily viewable in the Behaviours tool actually trumps any consideration of the coding elegance, so using those at least during development is neat.

Thanks for the reply.
I tried your code and setting the O:var works, but it won’t work for an L:var.

What I’m trying to do is, I have n instances of the same simobject,
L:var 1 is incremented every frame and goes back to 0 when reaching n.
L: var2 is changed every frame depending on the L:var1 value. (read by simconnect from an external confiog file)
I also tried creating n vars L:var2:n

What I can’t figure out is how to tell each instance of my simobject which n
to update on.

if I increment var1 through simconnect it updates every frame (or any other interval), but each instance of my simobject uses the same var1 value.

So I tried to increment var1 inside the model behaviour. but settings an Lvar in the anim code won’t work.

As I understand it, every simobject is updated every frame, so they all use the same L:var1 value.

Hence why I tried the <UPDATE_ONCE> approach thinking it would increment var1 after each instance of my simobjects is set.

Maybe my approach is completely wrong but I’m not sure how to do this any other way.

forget UPDATE_ONCE - it’s much easier to code that in your RPN code than to try and guess what some template is doing.

You sound very unclear on what variables you want your simobject to SHARE, and what should be local to that simobject. If you’re using L: vars, all the XML in all the simobjects are going to read and write the same simvars, so you almost certainly don’t want the RPN in every simobject writing to the same L: var.

Where is L:Var1 getting written ?

Where is L:VAR2 getting written ?

If those are only being written in an external simconnect app, you should be ok using an O: var as the local animation frame number unique to each simobject. If you’re writing to those L: vars in model XML in the same simobject, then all the simobjects are going to be overwriting each other.

You can’t invent a second colon in an L var name. An underscore would be ok.

If all you’re trying to do is randomize the behaviour of different simobjects there’ll be easier ways to do that in RPN.

L:var1 is the variable shared by all instances. (and should be incremented between every instance)

L:var2 is the value each instance use for the anim (and should be different for each instance) and is set through simconnect.

i shortened my first example for clarity but it looks like:
for n instances:

originally I tried:

         <UPDATE_CODE>
	        	(L:Var2:(L:Var1)) 
        </UPDATE_CODE>

and then tried to increment L:var1 after each instance is spawned, but that didn’t work

then I tried:

         <UPDATE_CODE>
	        	(L:Var1) 0 == if{ (L:Var2:0) (&gt;L:Var3) }   
                        (L:Var1) 1 == if{ (L:Var2:1) (&gt;L:Var3) }  
                        (L:Var1) 2 == if{ (L:Var2:2) (&gt;L:Var3) }  
                        ...
                        (L:Var1) n == if{ (L:Var2:n) (&gt;L:Var3) }  
        </UPDATE_CODE>

but same problem. I don’t know how to increment var1 between each instances.

it seems all instances are updated at once on each frame so I don’t know how to differentiate them.

Where is it being updated?

i tried in simconnect first, but it changes every frame and all instances of my simobject use the same value on each frame.

So i tried incrementing it in the model behaviour instead with <update_once> but that doesn’t work either. each instance still use the same value on each frame.

I think the model baviour is read only once each frame then applied to each instance. If that is the case I don’t know how to get around that to differentiate each instance.

Does your part keep moving or is the animation going to be a single fixed number that never changes and you just want that frame number to be a different fixed number for each simobject? It’s beginning to dawn on me that it’s probably the latter.

If it’s just a fixed frame number for each simobject, can that number be random or do you really need to assign a specific number.

E.g. can the number be derived from the location of the simobject ?

It is the second case: the animation is going to be a single fixed number that never changes and you just want that frame number to be a different fixed number for each simobject.

but that number isn’t random, it comes from a config file read by simconnect.
Unfortunately the only parameters you can pass to the simobject when creating it is its position, all other parameters needed for the animations have to be derived from simvars.

hence the methods I tried.
settings n vars L:var2:n with simconnect.
or a single L;var2 + L:var1 incremented instead of the :n approach.

I also tried in simconnect: spawn one instance, increment Lvar1, repeat.
but I don’t know how to freeze the animation in the model behaviour, that’s why I tried the update_once method.

OK - you’re following a well-trodden path and the main trick (which is usually missed) is to encode the mapping of (simvars) → (animation frame) into your RPN.

An example of where this is missed is e.g. for multiple wind turbines. The obvious item is to rotate the vertical axis of the turbine according to the wind direction (from Wind X and Wind Z) that should mean the turbines are pointing into wind. But also you want to initialise each turbine simobject with a random offset for the blades rotation, otherwise all the turbines in a wind farm will rotate exactly in sync (look at the turbines in MSFS).

So
 my suggestion is you look at how you calculated your simobject frame number, and see if you can encode that into the RPN. I understand the frame number is in a config file. But you must have had some formula to choose those otherwise random would be ok.

So I got it working, sort of.
To differentiate the simobjects I modified to xml to something like this:

<ANIM_CODE> 
    0 (L:1:IS_SCOPED_VAR_SET) ==
     if{
        (L:LVAR1) (&gt;L:1:LVAR2)
        1 (&gt;L:1:IS_SCOPED_VAR_SET)
        (L:1:LVAR2) 
    } 
    els{
          (L:1:LVAR2) 
    } 
</ANIM_CODE>  

So I can now set different values for each instance of my simobject and only have the anim fetch the globally scoped LVAR1 once, this part works great.

The issue now is:
My simconnect code tries to do this:

  • set LVAR1 = someArrayToStoreMyData[n] (with SimConnect_SetDataOnSimObject())
  • spawn one instance (with SimConnect_AICreateSimulatedObject())
  • n++;
  • repeat.

The issue with this is that the simconnect loop has to run at the same speed as msfs. Otherwise I can have 5 instances spawned before before n is incremented.

I couldn’t find in the documentation a way to know that the previous simobject as actually been spawned in the sim.

SimConnect_AICreateSimulatedObject returns before the object is actually spawned.
SimConnect_SubscribeToSystemEvent(“ObjectAdded”) also triggers before the simobject is actually spawned in the sim.

Are these returning when they added their data to a queue, or are they run asynchronously? I couldn’t find anything about this in the documentation either.
So I can’t use any of these two.

Ideally in my anim code I would have a line that sets a var I can request in simconnect to know I can increment n.

<ANIM_CODE>
0 (L:1:IS_SCOPED_VAR_SET) ==
if{
(L:LVAR1) (>L:1:LVAR2)
1 (>L:1:IS_SCOPED_VAR_SET)

        1 (&gt;L:INSTANCE_IS_SET)

(L:1:LVAR2)
}
els{
(L:1:LVAR2)
}
</ANIM_CODE>

Since the simobject is not in the user aircraft scope I’m not sure how to do this.