ArmA: How to animate a model

## Introduction

**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 stopps engine from shifting the animation axes.

If you encounter a bug when your weapon bones fall apart after you dropp 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, if you use the model.cfg file, you always need to binarize your addon - otherwise, your animations won't work.

Another option is to add both classes (cfgSkeletons and cfgModels) to the config.cpp file of your addon.

This way, you do not need to binarize your addon, so it's a perfect way to test it.
However, the first method is prefered and should always be used when releasing your addon.

## 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:

 **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 strining them togeter.

**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:

 **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 userdefinable name and consists of the following parameters:

#### 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

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 examle, the wheels would need "rotationX" as animation type.

### Rules Of Engagement

#### cfgSkeletons

* Binarise gets confused if your cfgSkeletons classname is same as your cfgModels.
	+ Declare your skeleton class as MyModelSkeleton vs MyModel
* Similar to any other classes in any standard config.cpp of any pbo, it is possible, albeit less likely, to have same class(es) over declared in multiple folders. This is typical if you are running more than one project on the p: drive. Far better, you always use an OFPEC\_TAG name

```
class OFPEC_TAG_MyModelSkeleton

```

* Declaring non-existent bones in a skeleton is 'ok'

Typically, you will have a base class skeleton (such as a 'vehicle') from which, you derive a tank.

Typically, all vehicles have number plates (vez, spz). Some, don't.

It is 'ok' to format for the general case. The only effect is that this non-existent bone will appear in the binary p3d as part of the skeleton list. No harm done.

#### cfgModels

* sections[]= have nothing to do with this document. They are a list of NamedSelections, from which, setObjectTexture can be used in-game. They are part of hiddenselections[]= and are, confusingly, not related to skeletons and animations. If a non-existent NamedSelection is, in fact, listed in this segment, it is ignored, and not present in the binary model.

* source= is the ANIMATION CLASS name. Often called 'controller'
* selection= is the BONE NAME in cfgSkeletons
* axis= is an axis. it is rarely declared as a bone, it rarely appears in sections[]=. It often appears in the NamedSelections list of the model.

* When creating class animation{... it is ok to declare non existent bones.

This is very prevalent, very common, and very 'ok' when you have a generalised base class that deals with four standard doors, only one of which appears in a \*specific\* model.

```
 class WideDoor{...
 class FrontDoor{...
 class BackDoor{...
 class EmergencyExit{....

```

Highly complex models such as aircraft **frequently** have multiple compass, rpm, altimeter and other classes in their base classes which may, or may not be present in THE model.

If the bone is not in cfgSkeletons, the cfgModel class is entirely ignored during the binarisation process.

If the bone IS declared in the skeleton but doesn't actually exist in the model. The cfgModel class IS binarised (probably not what you want to happen).

### User Animation

```
class Rotation; //very standard, most often defined in the first model.cfg of the p:tree

cfgModels
{
  class InheritedFromSomething //most often defined in the first model.cfg of the p:tree
  {
      class Animations;
  };
  class PinkElephant:InheritedFromSomething// this is for the PinkElephant.p3d and no other
  {
    class Animations:Animations // bring in all the base class classes
    {
       class OFPEC_TAG_MyPinkElephant_SlidingDoor:Rotation
       {
         source = SomeClassName;// defined in config.cpp
         animPeriod=1;
         selection=SomeBoneName;
         axis=SomeAxis;
         angle1=rad 85;
       };
    };
  };
 };
};

```

config.cpp

```
class CfgVehicles
{
 class SomeBaseClass
 {
    class  AnimationSources
   {
    class UserActions; // maybe
   };
 };
 class  OFPEC_TAG_MyPinkElephant:SomeBaseClass
 {
   .....
   model=PinkElephant;
   class  AnimationSources:AnimationSources
   {
     class OFPEC_TAG_MyPinkElephant_SlidingDoor /// <<<<<THE name used in THE cfgModels for THIS p3d
     {
       source = "user";
       animPeriod = 1;
       initPhase = 0;
     };

  class UserActions :UserActions
  {
   class OFPEC_TAG_OpenDoor1// ensure no possibilty of a clash with base names (if any)
   {
    displayName = "Open Sliding door";
    onlyforplayer = "true";
    position = "SomeAxis";// THE axis declared in the model
    radius = 18100;
    condition = "this animationPhase ""OFPEC_TAG_MyPinkElephant_SlidingDoor"" < 0.5 ";
    statement = "this animate [""OFPEC_TAG_MyPinkElephant_SlidingDoor"", 1]; this say ""OFPEC_TAG_ElephantOpenSound""";
   };
   class OFPEC_TAG_CloseDoor1:OFPEC_TAG_OpenDoor1
   {
    displayName = "Close Sliding door";
    condition = "this animationPhase ""OFPEC_TAG_MyPinkElephant_SlidingDoor"" >= 0.5 ";
    statement = "this animate [""OFPEC_TAG_MyPinkElephant_SlidingDoor"", 0]; this say ""OFPEC_TAG_ElephantCloseSound""";
   };
  };//endof useractions
 };//endof animations
};//endof PinkElephant
};// endof cfgVehicles

```

```
class CfgSounds
{
 class OFPEC_TAG_ElephantOpenSound// <<< make sure no other class could possibly called this
 {
   name = "whatever";
   sound[] = {"\some\stupid\hard\path\somesound.ogg", 1, 1};
   titles[] = {};
 };

```

```
 class OFPEC_TAG_ElephantCloseSound:OFPEC_TAG_ElephantOpenSound
 {
   name = "think of a number";
   sound[] = {"\some\stupid\hard\path\someOthersound", 1, 1};// wss equivalent
 };
};

```

### 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";
			};
		};
	};
};
```