====== How To Animate Model ====== **Attention:** This article is a tutorial on how to animate parts of your model (e.g. wheels, rotors, etc.). Take all information of this article with a pinch of salt: everything is based on experiments and hasn't been confirmed nor documented by BIS, yet. To animate a model, you have to make use of both, the //cfgModels// and //cfgSkeletons// class. The //cfgSkeletons// class defines the bones of a vehicle. Bones are, more or less, the __animated selections__ of a model. The //cfgModels// class is an extended version of the OFP cfgModels class. It defines the selections of a model which you want to animate or use with the setObjectTexture command, but since ArmA, you have to put everything related to animate your model in here. **Model preparation:** Besides adding selections (same as in OFP but in ArmA bone selections should never overlap) you also need to add a following named property to your first resolution LOD (press **ALT-P** to open named properties window): property name: autocenter value: 0 If your model has Geometry LOD the above needs to be also done for your Geometry LOD. This property stops engine from shifting the animation axes. If you encounter a bug when your weapon bones fall apart after you drop it to the ground the reason might be lack of Geometry or View - pilot LODs. ====== model.cfg ====== According to the article about Model Config, the cfgSkeletons and cfgModels class should be part of a model.cfg file which is located in the addon pbo file. However, this does not seems to work (I assume the model.cfg files will be put into the model p3d file during binarization). For now, you can add the cfgSkeletons and cfgModels class to your config.cpp which works like a charm. ====== CfgSkeletons ====== The //cfgSkeletons// class defines, as mentioned before, the bones (animated selections) of a vehicle. Each skeleton is a subclass within the cfgSkeletons class, consisting of three parameters: ^ Parameter ^ Description ^ | isDiscrete | currently unknown, set to 1 | | skeletonInherit | inherit bones from given class | | skeletonBones[] | define your own bones here | ====== Defining a bone ====== Bones are defined in the skeletonBones[] array which is made of a list of unsorted bones. Each bone is the name of a selection you want to animate. ====== A single bone ====== A bone is defined by using two strings: :"bone","" You may define multiple bones by stringing them together. **Attention:** Do not leave out the second string as this will lead to errors! (see linked bones for more information on the second string argument) **Example** skeletonBones[]= { "bone1","", //defines bone1 "bone2","" //defines bone2 }; **Linked bones** The second argument (empty in the example above) is used for linking two bones: :"bone1","bone2" Linking is used to make the animation of "bone1" depending on the movement of "bone2". If you e.g. have a turret, you have to make use of linking here, because the up and down movement of the turrets weapon is typically influenced by left and right movement of the turret: **Example** skeletonBones[]= { "turret_x","", //defines bone turret_x "turret_y","turret_x" //defines bone turret_y and makes it linked to bone turret_x }; **Attention:** You can not link more than two bones in a row! If you do something like "bone1","bone2","bone3" This will result in an error, as Armed Assault interprets this as: * defining "bone1" which is linked to "bone2" * defining "bone3", //which misses the second argument.// However, it should be possible to use a syntax like this (not tested yet): skeletonBones[]= { "bone1","bone2", //defines bone "bone1" and makes it linked to "bone2" "bone2","bone3", //defines bone "bone2" and makes it linked to "bone3" "bone3","" //defines bone "bone3". }; In conclusion, "bone1" is linked to "bone2", which is linked to "bone3". So "bone1" should be depending on the movement of both, "bone2" and "bone3". **cfgSkeletons Example** class cfgSkeletons { class BWMod_Tiger_Skeleton { isDiscrete=1; skeletonInherit=""; skeletonBones[]= { "wheelL","", "wheelR","", "turret_RMK_x","", "turret_RMK_y","turret_RMK_x", "turret_OSIRIS_y","", "turret_OSIRIS_x","turret_OSIRIS_y" }; }; }; ====== cfgModels ====== The cfgModels class is used to declare the selections of a model you want to animate or access via the setObjectTexture command. Since ArmA, the cfgModels class has been extended an is now used to define all animations of a model. Each of your model is a subclass inside of the cfgModels class and the models filename is used as the name of your class (without .p3d). E.g. your model p3d is named "myVehicle.p3d" your class is "class myvehicle {}". We will call them "modelclasses" in this article for the sake of simplicity. Each modelclass consists of three parameters and an additional subclass which defines the animations of your models: ^ Parameter ^ Description ^ | sectionsInherits | inherit sections (= selections) from given glass | | sections[] | define your sections here | | skeletonName | class name of skeleton used by this model | | class Animations {} | subclass which defines the animations of your model | **Sections** A section is the same as a selection: a part of the model which may be animated or changed via the setObjectTexture command. To define selections in your modelclass make use of the section[]-array. This array is an unordered list of all selections you want to use in the ways described above. **Example** sections[]= { "mainRotor","mainRotor_static","mainRotor_blur","mainRotor_dive", "tailRotor","tailRotor_static","tailRotor_blur","tailRotor_dive", "turret_RMK_x","turret_RMK_y" }; **Animations** To define animations for your model, you have to make use of the class animations in your modelclass. Each animation is a subclass in the class animations with a user definable name and consists of the following parameters: ^ Parameter ^ Description ^ | type | the type of the animation, e.g. rotating or translation. Refer to Model Config for a list of all animation types | | source | The source used to animate the selection. Refer to Model Config for a list of all sources | | selection | The name of the bone (= selection) you want to animate. Has to be defined in the cfgSkeletons class | | axis | The name of the axis you want to use (only necessary for types //rotation// and //translation//) | | memory | If using an own axis (by the axis-parameter) use value 1 if axis is located in the memory lod of your model or 0 if the axis is located in the lod (or better: every lod) where your animated selection is used | | sourceAdress | Use "loop" if you want your animation to "loop" (e.g. on wheels) or "clamp" if you want your animation to stop at a specific angle (e.g. on the steering wheel of a car) | | minValue | If source returns a value <= minValue, the animation is animated with angle0 (see below) | | maxValue | If source returns a value >= maxValue, the animation is animated with angle1 (see below) | | angle0 | The angle the selection is animated when minValue is reached | | angle1 | The angle the selection is animated when maxValue is reached | | offset0 | The distance the bone is moved when using type="rotation" and minValue is returned. Distance is calculated by the distance of axis' vertices. If the have a distance of 1m and you are using offset0 = 1, the bone will be moved by 1m. If using offset0=0.5, the bone will be moved by 0.5m, etc | | offset1 | Same as offset0, but when maxValue is returned | **Attention** I am assuming that each source is returning a value between 0 and 1 (the //animationPhase//) which is used to animate your selection. Some may even return a value from -1 to 1: * If using source="speed", the source is returning 0 when the vehicle is not moving and 1 when the vehicle is moving at maximum speed * If using source="drivingWheel", the source is returning -1 when the vehicle is turning left, 0 when the vehicle is not turning and 1 when the vehicle is turning right //On turrets, it is necessary that the classnames of the animations match the according turret selection names.// So if you're mainTurret selection is named "turret_RMK_x" you have to name your class "turret_RMK_y", too. The same goes for the mainGun selection. **Example** class mainRotor { type="rotationY"; //rotation around the Y axis source="rotorH"; selection="mainRotor"; axis=""; //no own axis, use centre of selection memory=1; sourceAddress="loop"; minValue=0; maxValue=1; angle0=0; angle1="rad -360"; }; **Axes** Since the axes seem a bit messed up when using //rotationX//, //rotationY//, //rotationZ//, //translationX//, //translationY//, //translationZ//, here is a small image of how the axes are arranged. Keep in mind that this image shows only the direction of these axes and //not// their position. The position is always defined by the center of your animated selection. As a short example, the wheels would need "rotationX" as animation type. {{https://pmc.editing.wiki/images/howtoanimmodel_axes.jpg}} **cfgModels example** class cfgModels { class bwmod_tiger { sectionsInherit=""; sections[]= { "mainRotor","mainRotor_static","mainRotor_blur","mainRotor_dive", "tailRotor","tailRotor_static","tailRotor_blur","tailRotor_dive", "turret_RMK_x","turret_RMK_y" }; skeletonName="BWMod_Tiger_Skeleton"; class Animations { class mainRotor { type="rotationY"; source="rotorH"; selection="mainRotor"; axis=""; memory=1; sourceAddress="loop"; minValue=0; maxValue=1; angle0=0; angle1="rad -360"; }; class tailRotor { type="rotationX"; source="rotorV"; selection="tailRotor"; axis=""; memory=1; sourceAddress="loop"; minValue=0; maxValue=1; angle0=0; angle1="rad -360"; }; class wheelL { type="translation"; source="altRadar"; //using altRadar, since damper doesn't seem to work on //helicopters, even though [[Model_Config]] states something //else. selection="wheelL"; axis="axis_damper"; //vertical axis, vertex distance 1 m memory=0; animPeriod=0; minValue=0; maxValue=0.05; //max value 0.05m above ground offset0=0; offset1=-0.05; //animate wheels downwards for 0.05m when maxValue is reached. }; class wheelR { type="translation"; source="altRadar"; selection="wheelR"; axis="axis_damper"; memory=0; animPeriod=0; minValue=0; maxValue=0.05; offset0=0; offset1=-0.05; }; class turret_RMK_x //the horizontal moving part of the turret { type="rotationY"; source="mainTurret"; selection="turret_RMK_x"; axis="axis_turret_RMK_x"; animPeriod=0; memory=1; minValue="rad -360"; maxValue="rad +360"; angle0="rad -360"; angle1="rad +360"; }; class turret_RMK_y //the vertical moving part of the turret { type="rotationX"; source="mainGun"; selection="turret_RMK_y"; axis="axis_turret_RMK_y"; animPeriod=0; memory=1; minValue="rad -360"; maxValue="rad +360"; angle0="rad -360"; angle1="rad +360"; }; }; }; }; ====== Machine Gun Belt Rotation ====== Open your model in O2 and make a selection called "belt_rotation", redefine the entire belt of bullets you want move on your MG and save it. Inside your model.cfg file open this class: class CfgSkeletons { class NameOfYourWeapon { isDiscrete = 0; skeletonInherit = ""; skeletonBones[] = { "belt_rotation", "" }; }; }; Again in the model.cfg file, inside your CfgModels describe the new selection and animation as you see below. class CfgModels { class Default { sections[] = {}; sectionsInherit = ""; }; class Weapon: default{}; class NameOfYourWeapon { skeletonName = "NameOfYourWeapon"; class Animations { class belt_rotation { type = "rotationx"; source = "reload"; selection = "belt_rotation"; axis = "belt_rotation_axis"; minValue = 0; maxValue = "5"; angle0 = "0"; angle1 = "1"; }; }; sections[] = { "zasleh" }; sectionsInherit = ""; }; }; To play around with the sliding belt you just need to take two values in consideration. The first is the type = "", "rotationx" will move you belt from the left to the right or right to the left, the second is the definition of the starting side the angle1 = "". "0" is a static value which means it won't move at all, negative values like "-1", "-2" ... will make the belt move from the right to the left, positive values like the "1", "2" ... will make it move from the left to the right. One thing to take into consideration is the "rotationz" not presented here. Let's say you have the belt not attached to a box but instead falling from the ground. If that is the case you want the belt to move from below to above, for that you will use the "rotationz" and again you play with the angle called angle1 = "" to set direction of the belt's movement. Now something worth noticing, let's say that you want the belt to move not straight but unbalanced, for that you will play with the angle0 = "". I think most of us won't be using this angle but now you know what it does. ====== Animate Cargo Door ====== In your model.cfg file in cfgModels put this for example: class Animations { class cargodoor { type = "rotation"; source = "MoveX1"; animPeriod = 2.5; sourceAddress = "clamp"; selection = "cargodoor"; axis = "osa_cargodoor"; memory = true; minValue = 0; maxValue = 1; angle0 = 0; angle1 = -0.488690; }; In config.cpp file in cfgVehicles put this: class AnimationSources: AnimationSources { class MoveX1 // Should be the same as your selection name. { source = "user"; //The controller is defined as a user animation. animPeriod = 10; //The animation period used for this controller. initPhase = 0; //Initial phase when object is created. 0 = CLOSED }; }; And class UserActions { class CargoDoor_OpenP { displayName = "Open cargodoor"; position = "pilotcontrol"; onlyforplayer = false; radius = 0.5; condition = "this animationPhase ""cargodoor_down"" < 0.5"; statement = "this animate [""cargodoor_down"",1]; this animate [""cargodoor_up"",1]"; };