• Which the release of FS2020 we see an explosition of activity on the forun and of course we are very happy to see this. But having all questions about FS2020 in one forum becomes a bit messy. So therefore we would like to ask you all to use the following guidelines when posting your questions:

    • Tag FS2020 specific questions with the MSFS2020 tag.
    • Questions about making 3D assets can be posted in the 3D asset design forum. Either post them in the subforum of the modelling tool you use or in the general forum if they are general.
    • Questions about aircraft design can be posted in the Aircraft design forum
    • Questions about airport design can be posted in the FS2020 airport design forum. Once airport development tools have been updated for FS2020 you can post tool speciifc questions in the subforums of those tools as well of course.
    • Questions about terrain design can be posted in the FS2020 terrain design forum.
    • Questions about SimConnect can be posted in the SimConnect forum.

    Any other question that is not specific to an aspect of development or tool can be posted in the General chat forum.

    By following these guidelines we make sure that the forums remain easy to read for everybody and also that the right people can find your post to answer it.

Modeldef.xml Custom Lever Animations

n4gix

Resource contributor
Messages
11,674
Country
unitedstates
I have a set of power levers that I have animated for thrust control (0 to 100). Because I need the lever(s) to stop at "Idle" I have the animation <Code> set as: 50 (L: Power_Lever:1,enum) + (I had to add a space after L: to stop the silly emojies)

In Max I have the full animation set for 0 to 150 frames, with idle position at frame 50.

I wish to use RightClick to allow the lever(s) to move from frame 50 to frame 0, representing reverse thrust from 0% to 100%.

The forward thrust works perfectly, but nothing I've tried will allow me to activate the reverse thrust animation. Anyone with ideas?
Code:
  <PartInfo>
    <Name>PED_lever_throttle0</Name>
    <AnimLength>100</AnimLength>
    <Animation>
      <Parameter>
        <Code>
          50 (L:Power_Lever:1,enum) +
        </Code>
      </Parameter>
    </Animation>
    <MouseRect>
      <Cursor>Hand</Cursor>
      <TooltipText>L Power %((L:Power_Lever:1,enum))%!d!</TooltipText>
      <MouseFlags>LeftSingle+LeftDrag+MoveRepeat+Wheel+RightDrag+RightRelease</MouseFlags>
      <CallbackCode>
        <!-- FORWARD THRUST -->
        (M:Event) 'LeftSingle' scmp 0 !=
        if{
        (L:PowerPosLeftX, position) s0 0 !=
        if{
        (L:Power_Lever:1,enum) (M:X) l0 &lt;
        if{ 2 + 100 min } els{ l0 (M:X) &lt; if{ 2 - 0 max } }
        (&gt;L:Power_Lever:1,enum)
        } quit
        }

        (M:Event) 'RightSingle' scmp 0 !=
        <!-- REVERSER SCRIPT HERE -->

        (M:X) (&gt;L:PowerPosLeftX, position)
      </CallbackCode>
    </MouseRect>
  </PartInfo>
 

n4gix

Resource contributor
Messages
11,674
Country
unitedstates
I tried changing it to RightSingle and just didn't bother taking a new screenshot. What is truly weird though is that even though no action is scripted for RightSingle, using RightSingle in the sim will activate the LeftSingle script!

I need to find the magic script to invert the direction of movement, and have it control the L:var from 0 to -50 frames.
 
Messages
440
Country
us-wisconsin
Bill,

Shouldn't your string compares equal zero?
Funny thing, just caught myself doing this the other day.

Code:
<!-- Not good-->
(M:Event) 'RightSingle' scmp 0 !=

<!-- Good -->
(M:Event) 'RightSingle' scmp 0 ==
 

n4gix

Resource contributor
Messages
11,674
Country
unitedstates
Roman, I've actually had them work properly both ways. Logically though, not-zero makes more sense, given that custom L:vars are FALSE when they equal zero, and only TRUE when they are not-zero...

To add to the confusion, even the default modeldef.xml from both FSX and P3D have examples using both == and !0... :yikes:

EDIT: Changing them from
(M:Event) 'LeftSingle' scmp 0 !=
to
(M:Event) 'LeftSingle' scmp 0 ==
actually caused the lever(s) to stop moving at all! :banghead:
 
Last edited:
Messages
440
Country
us-wisconsin
Just working with the M:Events not the L:Vars.
To add to the confusion, even the default modeldef.xml from both FSX and P3D have examples using both == and !0..
Yeah, I guess it's how one wants to implement them.

A left click is performed -
(M:Event) 'LeftSingle' scmp 0 != the result is false
(M:Event) 'LeftSingle' scmp 0 == the result is true

Such as - do something when anything besides a left click is performed, one would use !=
 

n4gix

Resource contributor
Messages
11,674
Country
unitedstates
A left click is performed -
(M:Event) 'LeftSingle' scmp 0 != the result is false
(M:Event) 'LeftSingle' scmp 0 == the result is true

Such as - do something when anything besides a left click is performed, one would use !=
Unfortunately, reality bites back hard with the above interpretation Roman, as the results I've observed are just the opposite...

With the script below, LeftSingle on the left lever will move 0 to 100 perfectly:
Code:
      <MouseFlags>LeftSingle+LeftDrag+MoveRepeat+Wheel+RightDrag+RightRelease</MouseFlags>
      <CallbackCode>
        (M:Event) 'LeftSingle' scmp 0 !=
        if{
        (L:PowerPosLeftX, position) s0 0 !=
        if{
        (L:Avt_Power_Lever:1,enum) (M:X) l0 &lt;
        if{ 2 + 100 min } els{ l0 (M:X) &lt; if{ 2 - 0 max } }
        (&gt;L:Avt_Power_Lever:1,enum)
        } quit
        }
Altering the script to the below, the lever is dead, dead, D E A D:
Code:
      <MouseFlags>LeftSingle+LeftDrag+MoveRepeat+Wheel+RightDrag+RightRelease</MouseFlags>
      <CallbackCode>
        (M:Event) 'LeftSingle' scmp 0 ==
        if{
        (L:PowerPosLeftX, position) s0 0 !=
        if{
        (L:Avt_Power_Lever:1,enum) (M:X) l0 &lt;
        if{ 2 + 100 min } els{ l0 (M:X) &lt; if{ 2 - 0 max } }
        (&gt;L:Avt_Power_Lever:1,enum)
        } quit
        }
All of this and I still haven't come up with a method to accomplish my actual goal of adding a "reverse action" with a RightSingle click! :banghead:
 

tgibson

Resource contributor
Messages
10,942
Country
us-california
Perhaps using Left Single for a drag function instead of a click function is confusing FS? One of my levers uses tests for both click and drag:

Code:
    (M:Event) 'LeftSingle' scmp 0 ==
    if{
      (M:Y) (&gt;L:MOUSEPOS, number)
      quit
    }
    (M:Event) 'LeftDrag' scmp 0 ==
    if{
      (M:Y) (L:MOUSEPOS,number) - -0.5 * (L:Prop1 Pitch Lever, number) + 0 max 100 min (&gt;L:Prop1 Pitch Lever, number)
      (M:Y) (&gt;L:MOUSEPOS,number)
    }
 
Messages
440
Country
us-wisconsin
Hi Bill,

More ideas is all - When using 'LeftSingle' scmp 0 != it is most likely working because it's not picking up the LeftClick but rather anything other than LeftClick. IE LeftDrag will make it work. When working with multiple <MouseFlags> I make sure I have code that covers each mouse flag explicitly.
The same as -
What is truly weird though is that even though no action is scripted for RightSingle, using RightSingle in the sim will activate the LeftSingle script!

Also, not sure about the quit command. JMO, I would take that out because the only time the area is active is when the mouse is over the area and performing a "click/drag" operation. I would take it out until operation is proven, then maybe put it back in if wanted. I would also hold off, for now, the mouse wheel operation until the basics are proved. Another idea to prove operation, make it a 2D gauge and monitor everything just to get the code down pat.

Maybe will help, maybe not ???
Code:
      <MouseFlags>LeftSingle+LeftDrag+LeftRelease+RightSingle+RightDrag+RightRelease+MoveRepeat</MouseFlags>
      <CallbackCode>
        <!-- FORWARD THRUST -->
        (M:Event) 'LeftSingle' scmp 0 == (M:Event) 'LeftDrag' scmp 0 == ||
        if{
        (L:PowerPosLeftX, position) s0 0 !=
        if{
        (L:Power_Lever:1,enum) (M:X) l0 &lt;
        if{ 2 + 100 min } els{ l0 (M:X) &lt; if{ 2 - 0 max } }
        (&gt;L:Power_Lever:1,enum)
        }
        }

        (M:Event) 'RightSingle' scmp 0 == (M:Event) 'RightDrag' scmp 0 == ||
        if{
        
        <!-- REVERSER SCRIPT HERE -->
        
        }
        
        (M:X) (&gt;L:PowerPosLeftX, position)
      </CallbackCode>
 

taguilo

Resource contributor
Messages
1,585
Country
argentina
Bill,

I know Click Kind properties do not work exactly the same in 2D panels and VC. My questions are:

-Why are you using the X axis (M:X) for a lever that should travel on the Y axis, is that a requisite?

-You say you have full animation 0 to 150 frames, but I see in your code
<AnimLength>100</AnimLength>
Shouldn't that be 150?

-Which is the entire lenght in pixels, controlled by (L:powerPosLeftX, position) ?

Tom
 

n4gix

Resource contributor
Messages
11,674
Country
unitedstates
I know Click Kind properties do not work exactly the same in 2D panels and VC. My questions are:

-Why are you using the X axis (M:X) for a lever that should travel on the Y axis, is that a requisite?

-You say you have full animation 0 to 150 frames, but I see in your code
<AnimLength>100</AnimLength>
Shouldn't that be 150?

-Which is the entire lenght in pixels, controlled by (L:powerPosLeftX, position) ?

Tom
Tom, the levers are animated on the X axis and work properly with the script used. This is the same basic script used in the KA350i (I'm big on recycling scripts!). Using Y would simply change the direction of the drag from fore/aft to side-to-side, which I'm sure you'll agree would not be very intuitive!

I've already modified <AnimLength> to 150 as well as the <Animation...> entry.

As for pixel length, that's difficult to determine with any 3d object in the VC since it is darn near infinitely variable based on eye point position and zoom factor. What I aim for is movement of the mouse cursor equivalent to the stroke length of the lever.
 

n4gix

Resource contributor
Messages
11,674
Country
unitedstates
All's okay as far as I can see until you get to the RightSingle mouse event where the curly braces are somehow missing after the 0 != and the assignment to the L: var seems to be dangling (it will never get caught on a left click because of the "quit". In other words putting it like so just for testing)...
The curly braces are missing because that call is void. IOW, the only content is the <!-- COMMENT -->. The final statement is not part of the 'RightSingle' event. It is related to the 'LeftSingle' event and simply keeps track of the final mouse position.
 

n4gix

Resource contributor
Messages
11,674
Country
unitedstates
SOLVED!!!

Thanks to some very helpful suggestions in this thread, and what seemed like 623,280 attempts, I've finally found a working solution for my described task. Here are some notes on my findings, before I present the finalized scripts.

1. LeftSingle and RightSingle are useless here. Only LeftDrag and RightDrag work.
2. With LeftDrag and RightDrag, only scmp 0 == will work properly.
3. I had to move the last assignment of (M:X) (&gt;L: PowerPosLeftX, position) to encapsulate it within each (M:Event)'s scope in order to keep this command isolated from the two mouse events.

Here is a quick video taken on my camera phone showing how the "Both" script works. Each lever may also be controlled separately of course, but I should think most users will wish to control both simultaneously. I've got an invisible polygon in between the two levers for this purpose


Here then is the compete script for the power levers:
Code:
  <PartInfo>
    <Name>PED_lever_throttle_both</Name>
    <AnimLength>150</AnimLength>
    <Animation>
      <Parameter>
        <Code>
          50 (L:Avt_Power_Lever:1,enum) +
        </Code>
      </Parameter>
    </Animation>
    <MouseRect>
      <Cursor>Hand</Cursor>
      <TooltipText>Both Power %((L:Avt_Power_Lever:1,enum))%!d!</TooltipText>
      <MouseFlags>LeftSingle+LeftDrag+MoveRepeat+Wheel+RightDrag+RightRelease</MouseFlags>
      <CallbackCode>
        (M:Event) 'LeftDrag' scmp 0 ==
        if{
        (L:PowerPosLeftX, position) s0 0 !=
        if{
        (L:Avt_Power_Lever:1,enum) (M:X) l0 &lt;
        if{ 2 + 100 min } els{ l0 (M:X) &lt; if{ 2 - 0 max } }
        d (&gt;L:Avt_Power_Lever:1,enum)
        d (&gt;L:Avt_Power_Lever:2,enum)
        } (M:X) (&gt;L:PowerPosLeftX, position)
        }

        (M:Event) 'RightDrag' scmp 0 ==
        if{
        (L:PowerPosLeftX, position) s0 0 !=
        if{
        (L:Avt_Power_Lever:1,enum) (M:X) l0 &lt;
        if{ 2 + 0 min } els{ l0 (M:X) &lt; if{ 2 - -50 max } }
        d (&gt;L:Avt_Power_Lever:1,enum)
        d (&gt;L:Avt_Power_Lever:2,enum)
        } (M:X) (&gt;L:PowerPosLeftX, position)
        }
      </CallbackCode>
    </MouseRect>
  </PartInfo>

  <PartInfo>
    <Name>PED_lever_throttle1</Name>
    <AnimLength>150</AnimLength>
    <Animation>
      <Parameter>
        <Code>
          50 (L:Avt_Power_Lever:2,enum) +
        </Code>
      </Parameter>
    </Animation>
    <MouseRect>
      <Cursor>Hand</Cursor>
      <TooltipText>R Power %((L:Avt_Power_Lever:2,enum))%!d!</TooltipText>
      <MouseFlags>LeftSingle+LeftDrag+MoveRepeat+Wheel+RightDrag+RightRelease</MouseFlags>
      <CallbackCode>
        (M:Event) 'LeftDrag' scmp 0 ==
        if{
        (L:PowerPosLeftX, position) s0 0 !=
        if{
        (L:Avt_Power_Lever:2,enum) (M:X) l0 &lt;
        if{ 2 + 100 min } els{ l0 (M:X) &lt; if{ 2 - 0 max } }
        (&gt;L:Avt_Power_Lever:2,enum)
        } (M:X) (&gt;L:PowerPosLeftX, position)
        }

        (M:Event) 'RightDrag' scmp 0 ==
        if{
        (L:PowerPosLeftX, position) s0 0 !=
        if{
        (L:Avt_Power_Lever:2,enum) (M:X) l0 &lt;
        if{ 2 + 0 min } els{ l0 (M:X) &lt; if{ 2 - -50 max } }
        (&gt;L:Avt_Power_Lever:2,enum)
        } (M:X) (&gt;L:PowerPosLeftX, position)
        }
      </CallbackCode>
    </MouseRect>
  </PartInfo>

  <PartInfo>
    <Name>PED_lever_throttle0</Name>
    <AnimLength>150</AnimLength>
    <Animation>
      <Parameter>
        <Code>
          50 (L:Avt_Power_Lever:1,enum) +
        </Code>
      </Parameter>
    </Animation>
    <MouseRect>
      <Cursor>Hand</Cursor>
      <TooltipText>L Power %((L:Avt_Power_Lever:1,enum))%!d!</TooltipText>
      <MouseFlags>LeftSingle+LeftDrag+MoveRepeat+Wheel+RightDrag+RightRelease</MouseFlags>
      <CallbackCode>
        (M:Event) 'LeftDrag' scmp 0 ==
        if{
        (L:PowerPosLeftX, position) s0 0 !=
        if{
        (L:Avt_Power_Lever:1,enum) (M:X) l0 &lt;
        if{ 2 + 100 min } els{ l0 (M:X) &lt; if{ 2 - 0 max } }
        (&gt;L:Avt_Power_Lever:1,enum)
        } (M:X) (&gt;L:PowerPosLeftX, position)
        }

        (M:Event) 'RightDrag' scmp 0 ==
        if{
        (L:PowerPosLeftX, position) s0 0 !=
        if{
        (L:Avt_Power_Lever:1,enum) (M:X) l0 &lt;
        if{ 2 + 0 min } els{ l0 (M:X) &lt; if{ 2 - -50 max } }
        (&gt;L:Avt_Power_Lever:1,enum)
        } (M:X) (&gt;L:PowerPosLeftX, position)
        }
      </CallbackCode>
    </MouseRect>
  </PartInfo>
 

Lagaffe

Resource contributor
Messages
812
Country
france
Thank you very much Bill for sharing the solution.
I note this code for my next creation ;-)
 

taguilo

Resource contributor
Messages
1,585
Country
argentina
Hi Bill,

Tom, the levers are animated on the X axis and work properly with the script used. This is the same basic script used in the KA350i (I'm big on recycling scripts!). Using Y would simply change the direction of the drag from fore/aft to side-to-side, which I'm sure you'll agree would not be very intuitive!

Yes, I know is the same script of the KA350 and I forgot to tell this before.

(M:X) refers to side to side axis, like the cartesian X axis.
(M:Y) refers to top to botton axis, like the cartesian Y axis.

Fore/aft, as you define, is indeed a movement that involves X and Y changes, pretty evident as power levers are moved mostly diagonally.
That's the reason the X axis works in your example. Try dragging left to right only and you'll see the levers move up and down, always.
Try dragging up-down exclusively and you'll see that sometimes, if you really keep it straight up/down, levers won't move when X axis doesn't change.

No problem to monitor (M:X) but as you know I prefer to explain how this kind of stuff works so everyone has a proper idea.

Now, regarding the code you posted, I'd like to add some comments that hope will be helpful.

<MouseFlags>LeftDrag+RightDrag</MouseFlags> would be enough when controlling any kind of levers. "Weel" could be used as well. But LeftSingle,RightSingle,Move,Release,etc,etc, are not needed at all unless cursor gets stuck when part loses its focus -I know this can happen in VC then testing is mandatory.

Code:
  <CallbackCode>
    SCRIPT
  </CallbackCode>

SCRIPT within <CallbackCode/> will execute ALL and ONLY when ANY of <MouseFlags> is triggered, and only the name of the flag involved is saved to (M:Event) variable.

Then, writing

Code:
  <MouseFlags>LeftDrag+RightDrag</MouseFlags>
  <CallbackCode>
     (M:Event) 'LeftDrag' scmp 0 ==
     if{ script }
     (M:Event) 'RightDrag' scmp 0 ==
     if{ script }
  </CallbackCode>

is the same as writing

Code:
  <MouseFlags>LeftDrag+RightDrag</MouseFlags>
  <CallbackCode>
     (M:Event) 'LeftDrag' scmp 0 ==
     if{ script }
     els{ script }
  </CallbackCode>

because if Event is not 'LeftDrag', it must be 'RightDrag' as those are the only two flags defined.

In your code for both levers movement, it won't work smoothly if levers are split in different positions.
For example, with Lever1 at position 2 and lever2 at position 10, moving both levers (which is controlled by lever1 value) will make lever2 come down to 0 abruptly. Indeed something similar to moving a single joystick lever mapped to both levers.

I'd like to ask you if would be possible that you test the following snippet and let me know if it works.
It is for both levers movement.

Code:
      <MouseFlags>LeftDrag+RightDrag</MouseFlags>
      <CallbackCode>
        (L:PowerPosLeftY, position) sp0
        (L:Avt_Power_Lever:1,enum) sp1
        (L:Avt_Power_Lever:2,enum) sp2
        (M:Y) l0 > if{ 2 } els{ -2 } sp3       
        (M:Event) 'LeftDrag' scmp 0 == l1 0 >= and l2 0 >= and
        if{          
            l1 l3 + 100 min 0 max (>L:Avt_Power_Lever:1,enum)
            l2 l3 + 100 min 0 max (>L:Avt_Power_Lever:2,enum)
          }
        (M:Event) 'RightDrag' scmp 0 == l1 0 &lt;= and l2 0 &lt;= and
        if{
            l1 l3 + 0 min -50 max (>L:Avt_Power_Lever:1,enum)
            l2 l3 + 0 min -50 max (>L:Avt_Power_Lever:2,enum)
          }
        (M:Y) (>L:PowerPosLeftY, position)
    </CallbackCode>

Commom code for both flags is placed first.
Both levers operating with one flag should hit top/bottom values smoothly before enabling the other flag to work.

Many thanks!

Tom
 

n4gix

Resource contributor
Messages
11,674
Country
unitedstates
Thanks for the detailed explanation. Now that I've gotten it through my head that I'm dealing with mouse drag direction rather than the axis of rotation, things are perfectly clear now.

Your script revision is working just fine, Tom. I did have to reverse the direction of drag however by swapping these entries around:

(M:Y) l0 > if{ -2 } els{ 2 } sp3

 
Last edited:

taguilo

Resource contributor
Messages
1,585
Country
argentina
Your script revision is working just fine, Tom. I did have to reverse the direction of drag however by swapping these entries around:

(M:Y) l0 > if{ -2 } els{ 2 } sp3

Good to know Bill, thank you.

I thought Y=0 was at part's bottom but actually was at the top, so your need to make the reversion.

Tom
 
Top