XML:TRAM
TRAM : Totoro Animation | ||
---|---|---|
XML
TRAC << Other file types >> TRAS |
TRAM files - animation data for characters - are basically made of three chunks.
- Meta data or "header": animation type, state, flags, particle, sounds, etc.
- Animation data: pelvis heights, pelvis velocities, bone rotations
- Attack data: damage, self-damage, extends (danger zones for AI awareness) and throws
The term animation is often shortened to anim by modders following the naming of commands in BSL: chr_wait_animstate, chr_wait_animtype, env_anim, etc.
Decision path of animation lookup
- At first the game builds a pool of anims a character can use. They are loaded from the TRAC file registered in ONCC, including parent TRACs.
- Anims from child TRAC override those of the parent if their combination of state, type and variant is excatly the same.
- On the other hand child TRAC often miss some anims. For example elite striker don't have their own run anims, they must and will use the parent anims of normal strikers. The game was here designed to save resources. See TRAC page for details.
- A character possesses always one animation state. This term derives from state machines but you only need to know that states are the first point in the decision path determining what anims are actually allowed to played at a given moment.
- It follows the full decision path:
- anim state check
- event (user input / engine input for AI) -> context check -> anim type check
- anim variant check
- TRAC weight check
One detailed example:
Let's say we shapeshifted to an ninja ONCC, are in state Standing and hit the action key for taunt.
The engine recognizes the context (an hostile character to interact with) and looks up anims of type Taunt.
Since we are in combat mode the following anim must be of variant Combat if possible.
Those lookups all boile down to two valid anims, NINCOMtaunt1 and NINCOMtaunt2.
If there are multiple anims avaible at this point the engine has to pick one of them randomly.
However, NINCOMtaunt1 has a TRAC Weight (probability) of 100 while NINCOMtaunt2 has a TRAC Weight value is just 5.
Chances are quite high we will not see the Moon Walk taunt.
Keep in mind that if the engine cannot find an anim of a specific variant it will fall back to use the non-variant anim instead.
Confused how Weights work? Apparently, that Weight value is only one part of the equation - otherwise NINCOMtaunt2 would never execute.
If there is only one anim to chose from, it will always play no matter what Weight, even if it is zero.
As for multiple anim being a valid choice the calculation seems to be this for an individual anim: weight / sum of all weight.
For NINCOMtaunt2 it means 5 / 105 = 0,047 (4,7%).
List of tags, types, and flags
Use the search function of your web browser to quickly find a tag.
tag | type | description |
<Lookup> | parent tag | |
<Type> | flag | Look them up over HERE. Pool of valid TRAM (animations a character can chose from) is build from TRAC files registered in ONCC. Most of the anim types are associated with player inputs. |
<AimingType> | flag | Look them up over HERE. Pool of valid TRAM, and therefor TRAS, (animations a character can chose from) is build from TRSC files registered in ONCC.
|
<FromState> | flag |
Look them up over HERE.
|
<ToState> | flag | Look them up over HERE. |
<Varient> | flag | (misspelled because Oni misspells it) If unused, tag will be simply "<Varient />", such as for non-combat animations. Otherwise contains one of these values:
|
<FirstLevel> | int16 | Number of first level in which move becomes available to the player ("0" to make it available from the start). |
<Shortcuts> | parent tag |
Works as an alternative FromState collection. Shortcuts are accepted if their <Shortcut><FromState> value differs from the "primary" <Lookup><FromState> value. In other words: If you want to make a Shortcut that uses the same FromState value then you have to set primary FromState value to None. By that the hardcoded interpolation frame length of about 8 frames can be overridden. In the following example the new interpolation length is 0. This will require the animation to match perfectly with the previous one. For a smooth transition to next animation set a suitable value at <Interpolation><End>. <Lookup> ... <FromState>None</FromState> ... <Shortcuts> <Shortcut> <FromState>Standing</FromState> <Length>0</Length> <ReplaceAtomic>no</ReplaceAtomic> </Shortcut> </Shortcuts> |
<Shortcut> | parent tag | |
<FromState> | flag | Look them up over HERE. |
<Length> | int16 | Amount of interpolating frames. While interpolations with rotations are less noticeable, interpolations of different positions can cause an additional drift (i.e. sliding).
This was especially observed for transitions of idle to movements and vice versa, basically any combination of animations with different accelerations at start and end. By default, animations without specified Shortcut interpolation give about 8 frames of interpolation. For moves starting or ending in idle better don't use interpolation. Just let the start and end frames have same positions and rotation values as the idle. Interpolations should always be considered as last resort. This drift can be observed even in vanilla animations - most prominently in Konoko's comb_k. Konoko's left foot should be in a single position, as the animation starts from from Konoko's idle pose. However, the interpolation is causing her left foot to drift noticeably to the right and bit forward. The reason this happens is because the interpolation "mixes" two animations. When one animation transitions into another via interpolation, part of the animation system continues playing the initial animation, and part of it is playing the follow-up animation, with all the rotations and positions getting linearly interpolated. |
<ReplaceAtomic> | flag |
|
<Flags> | flag |
|
<Atomic> | parent tag | |
<Start> | int16 | |
<End> | int16 | |
<Invulnerable> | parent tag | Character will not take melee damge during a timeframe defined by Start and End frames. |
<Start> | int16 | First frame of character being invulnerable. |
<End> | int16 | Last frame of character being invulnerable. |
<Overlay> | parent tag | |
<UsedBones> | flag | Simply contains "<UsedBones />" if no bones are involved, otherwise:
|
<ReplacedBones> | flag | "<ReplacedBones />" if unused, otherwise:
|
<DirectAnimations> | parent tag | |
<Link> | link | First slot. "<Link />" if unused. |
<Link> | link | Second slot. "<Link />" if unused. |
<Pause> | parent tag | |
<Hard> | int16 | |
<Soft> | int16 | Player cannot make new inputs during this pause unless this animation is followed by a followup combo animation defined by <DirectAnimations><Link>.
Combos ignore <Interpolation><End>. |
<Interpolation> | parent tag | |
<End> | int16 |
While interpolations with rotations are less noticeable, interpolations of different positions can cause an additional drift. This was especially observed for transitions of idle to movements and vice versa, basically any combination of animations with different accelerationsat start and end. By default, animations without specified Shortcut interpolation give about 8 frames of interpolation. For moves starting or ending in idle better don't use interpolation. Just let the start and end frames have same positions and rotation values as the idle. Interpolations should always be considered as last resort. |
<Max> | int16 | unused |
<FinalRotation> | float | Ending rotation in degrees. (During animation the camera is detached rotation-wise. If this value matches the body's final rotation it will prevent a 'glitchy' re-attaching.) |
<Direction> |
| |
<Vocalization> | int | Id of one of the SoundConstances in ONCC |
<ActionFrame> | int | Frame number for spacial events associated with this animation: weapon theft in disarms, weapon holster, Mukade actually teleporting, items getting handed over to player. "Give me that hypo!" Yada yada. |
<Impact> | link | "<Impact />" if unused. |
<Particles> | parent tag | serves as group element |
<Particle> | parent tag | holds individual particle data |
<Start> | int | frame number for particle to start |
<End> | int | frame number for particle to end (if number exceeds frame count of animation the particle will simply die with end of animation) |
<Bone> | flag |
|
<Name> | link | particle name in ONCC-ONCP |
<MotionBlur> | parent tag | |
<MotionBlur> | parent tag | sequence element |
<Bones> | flag |
|
<Start> | int16 | |
<End> | int16 | |
<Lifetime> | int8 | |
<Alpha> | int8 | |
<Interval> | int8 | |
<Footsteps> | parent tag | |
<Footstep> | parent tag | |
<Frame> | int16 | |
<Type> | flag |
|
<Sounds> | parent tag | "<Sounds />" if unused. |
<Sound> | parent tag | |
<Name> | char[32] | OSBDfile.imp.oni (don't use resource type's prefix or suffix) |
<Start> | int16 | The frame when the sound starts to play. |
<Heights> | parent tag | |
<Height> | float | Absolute position. |
<Velocities> | parent tag | |
<Velocity> | 2 x float | Relative positions. (Delta values: the numbers represent only the change of position from one frame to another.) |
<Rotations> | parent tag | |
<Bone> | parent tag | There are 19 bone tags, one for each body part. |
<EKey> | int8 + 3 * float | For normal animations. The first value is the number of frames for which the rotation is maintained; the sum of all of these first EKey components always equals the total number of frames for the animation. |
<QKey> | int8 + 4 * float | For overlay animations used by TRAS aiming screens.
OniSplit v0.9.54.0 produces <QKey>s (quaternions) instead of <EKey>s (Euler rotations) for normal animations. |
<PositionOffset> | parent tag | <PositionOffset> and <Positions> belong together. In the binaries, they are written in place. Seems unused, changing those has no effect in game. |
<X> | int16 | |
<Z> | int16 | |
<Positions> | parent tag | |
<Position> | float | seems to be unused (also with chr_debug_sphere = 1 the spheres remain unchanged) |
<Height> | float | vertical extent |
<YOffset> | float | y offset of the vertical extent from character location |
<ThrowSource> | parent tag | "<ThrowSource />" if unused. |
<TargetAdjustment> | parent tag | Used to position targets during throws, relative to the position of the character executing the throw. |
<Position> | 3 * float | Contains XYZ values, which position the target character:
|
<Angle> | float | |
<Distance> | float | Activation distance. The throw can be triggered if it is within this range. However the value must be greater than the equivalent TRAM in parent TRAC. |
<TargetType> | flag | The flags are part of the animation type list.
(static throws)
(running throws)
(tackle throw = catching)
(pistol disarms)
(rifle disarm)
About the naming:
|
<SelfDamage> | parent tag | "<SelfDamage />" if unused. |
<Damage> | parent tag | sequence element |
<Points> | int16 | Damage taken by character. |
<Frame> | int16 | Frame of the animation when damage is dealt. |
<Attacks> | parent tag | |
<Attack> | parent tag | Only 2 attack parts per file are allowed. Normally the target gets only one hit. But if the attack frame ranges of both attack parts are overlapping, then the target can be hit by both of them. |
<Start> | int16 | First frame where damage can be inflicted on an opponent. |
<End> | int16 | Last frame where damage can be inflicted on an opponent. |
<Bones> | flag | The bones which can inflict damage. |
<Flags> | flag |
|
<Knockback> | float | Target gets "knockback"ed by this amount. |
<HitPoints> | int16 | Damage points inflicted by attack. |
<HitType> | flag | Animation type for opponent's animation when the attack isn't blocked. The flags are part of the animation type list. |
<HitLength> | int16 | Number of frames for how long the target should remain in his "hit" animation state when he gets hit. |
<StunLength> | int16 | Number in frames for how long the target should remain in his blocking animation when he blocks the attack. |
<StaggerLength> | int16 | Number of frames for how long the target should perform his "stagger" animation after a successful block. |
<Extents> | parent tag | Explained below. Automatically calculated by OniSplit if there's a DAE file referenced in XML. |
<Extent> | parent tag | One tag per frame. |
<Angle> | float | In degrees. |
<Length> | float | |
<MinY> | float | |
<MaxY> | float | |
<AttackRing> | parent tag | Always contains 36 <Length> tags, explained below. Automatically calculated by OniSplit if there's a DAE file referenced in the XML. (AttackRing was formerly known as "horizontal extents" in older versions of OniSplit.) |
<Length> | float | Horizontal extents, explained below. They create a "danger zone" around the attacker so that the AI has a chance to dodge an attack. |
Export
TRAM files can be extracted A) as pure XML files or B) as a pair of XML and DAE files. For editing the actual animation, you will want to use method B. While exporting an ONCC, you might see errors such as:
Cannot find instance 'TRAMKONCOMthrow_rev' Cannot find instance 'TRAMKONCOMthrow_rev' Cannot find instance 'TRAMKONRIFturn_right' Cannot find instance 'TRAMKONOKOlev18_ZomStand' Cannot find instance 'TRAMKONOKOcorner_hide' Cannot find instance 'TRAMKONPIScorner_hide'
Ignore them, as those files doesn't exist. (Someday we should remove them from the TRACs.)
Via command line
To export a single TRAM:
onisplit -extract:xml output_path -anim-body:path_to\TRBS_or_ONCCname.oni path_to\TRAMname.oni
To export merged TRAMs:
onisplit -extract:xml output_path -anim-merge -anim-body:path_to\TRBS_or_ONCCname.oni path_to\TRAMname1.oni path_to\TRAMnameN.oni
Via Vago
When you start Vago, the "General" tab should be open by default. If not, click on it. Then follow these five easy steps.
Step 1: Select "ONI" as input format ("From").
Step 2: Select "XML" as output format ("To").
Step 3: Check "Another TRAM" to chose a character animation file.
Step 4: Click "Add" button to choose an ONCC file.
Step 5: Click "Convert".
Via Simple OniSplit GUI
There was a long-standing problem with combined ONCC/TRAM files where the textures would be missing.
Simple OniSplit GUI post-edits the DAE to fix missing textures. The character-related .oni files must be all in one folder. Usually the level0_Final folder does the trick.
Step 1: Drag and drop TRAM and ONCC into the big field. (One by one or simultaneously; the order doesn't matter.)
Step 2: Hit "Convert".
Editing 3D data
XSI
You can load the DAE file into a compatible editor. So far, the community's favorite is (formerly "XSI") Mod Tool.
- For the correct Mod Tool settings, see HERE.
Here are some hints for creating animations when there is no rigging available:
- Use the ONCC model in your 3D editor that is also intended for the animations. This makes sure the pelvis height will match and feet will not float in the air or go through the ground.
- First frame and last frame should match the probable previous and follow-up animations. This will reduce the necessity for long interpolations.
- When setting keyframes each body part should have been moved. This rule of thumb will give a more natural looking animation.
- The first body part to be animated is always the pelvis.
- Watch out for pelvis rotations so that the xyz rotations don't overlap too much otherwise you will get into a gimbal lock.
See also our OCF thread.
Blender
Theoretical possible. Very difficult in practice and we don't have a tutorial on this one.
Import
onisplit -create output_path path_to\TRAMname.xml
Additional details
Existing TRAMS
TRAMS used in Oni follow a naming pattern. A list of all Combat TRAMS in game, and their naming can be found here.
Speeding up existing animations
s10k has created a XmlTools patch that allow the speedup of any existing TRAM (by removing frames). More information and download here.
Attacks
Extents and XML
Ever wondered how the AI can recognize incoming attacks and block or dodge them? Extents (found by geyser and fully uncovered by Neo) are the key. Imagine the character looked at from above, and visualize a circle with this character being at the center of the circle. Now divide this circle into 10° segments, and those are the 36 units of horizontal extents (that is, the lateral reach of the attack). The 0° point is directly in front of the character and 180° is behind the character. The segments run clockwise around the character. So the first Extent tag is the horizontal reach on the 0° line, the second tag is for the line 10° clockwise, and so on. Note that a frontal attack like a simple kick could hit a target not only if he's standing at 0° (directly in front), but also at 10° or 20° to the right, and also at 340° and 350° (a bit to the left). When setting these manually, you should guess the inner and outer ranges of the reach of the attacker's bones that have damage attached to them. Test these values with an AI that blocks often. If you did it right, your attack will often be blocked, but if the extent length is too high, the AIs will react when too far from you, which does not look good.
There are two types of extents:
- <Extents> stores this info:
- <Angle> at which this extent radiates from the character.
- <Length> of this extent.
- <MinY> minimal height of this extent.
- <MaxY> maximum height of this extent.
- Length, MinY and MaxY serve to create an invisible area in space which is "dangerous to be in". If an AI intersects with this area and notices it (refer to the Notice field in MELE), it will attempt to block or dodge according to its modifiers in its MELE profile (again, see the MELE page).
- The number of <Extent>s is equal to the number of attack frames: <End> minus <Start> plus one (because start frame counts too). For example, for TRAMKONCOMkick_low1: <End>30</End> minus <Start>22</Start> plus one = 9 <Extent>s.
- These extents are pretty impossible to guess, so leave them alone until Neo comes up with extent computation (probably from attack bones and bone rotations). However, always add at least one set of component tags, because those Length, MinY and MaxY components will be taken as the longest extent, the highest Y value, and the lowest Y value. Without those, a character won't react to this attack.
- <HorizontalExtents> stores this info:
- 36 fields (exactly 36, otherwise it won't compile back into a .oni file), which correspond to areas in 10° intervals around the character as described above.
Adding extents to an existing animation
Let's say that you want to convert a non-attack animation to a damage-dealing animation. Non-attack TRAMs do not have extents information used to notify the AIs about incoming attacks, so how do you generate this information from the animation?
- 1. Export the animation to XML with a body attached. If you extract using just "-extract:xml dest_folder TRAMsomething.oni", you'll get the 3D animation data inside the XML (namely, the tags Heights, Velocities, Rotations, PositionOffset, and Positions; for an attack animation, you'll also get Attacks and AttackRing, and inside Attacks' elements, each Attack element will have Extents at the end of it).
- However, if you extract this same animation using "-extract:xml dest_folder TRAMsomething.oni -anim-body ONCCtramuser.oni", the animation data will be placed in a DAE file along with the character model geometry. Be sure to pick a body with a representative size for the character classes that will actually use this TRAM, because the extents will be calculated from it. The XML file will be very short without the 3D data in it. This is how we want the non-attack TRAM to look. We do not know the extents information that should go in Extents or AttackRing, so we just want to add the part that distinguishes a DAE-extracted attack TRAM XML from a DAE-extracted non-attack TRAM XML. That part is the Attacks section, without the Extents under each Attack.
- 2. First, consider whether the TRAM should have something added to its Flags section, like Attack or ThrowTarget.
- 3. Now add to the XML of the non-attack TRAM data in the following format (after the <SelfDamage /> section, typically):
<Attacks> <Attack> <Start>1</Start> <End>10</End> <Bones>RightWrist RightFist</Bones> <Flags /> <Knockback>4</Knockback> <HitPoints>10</HitPoints> <HitType>KnockdownHead</HitType> <HitLength>5</HitLength> <StunLength>8</StunLength> <StaggerLength>0</StaggerLength> </Attack> </Attacks>
- Do not add an AttackRing section after Attacks.
- 4. Import this with "-create dest_folder TRAMsomething.xml". The Extents sections and the AttackRing will be calculated by OniSplit from the attached DAE.
- 5. If you need this information for a patch mod, run "-extract:xml" on the TRAMsomething.oni you've created, without using "-anim-body". Now you can copy the Extents and AttackRing data to your XML patch. For an example of how the patch should look, see the Domino Knockdowns mod.
Combos
The type and order of keystroke or mouse clicks for combos is hardcoded.
To assign an animation for a specific combo attack you set corresponding value in <Lookup><Type>.
Setting a value in <Link> of <DirectAnimations> will do two things:
- It will increase the time for player input, meaning the next animation can be executed more easily. (In vanilla the Crescent Moon Kick has no link in KONCOMcomb_k_k_kfw and therefore is difficult to use.)
- It disables <Interpolation><End> for the next combo anim.
- It enables <Pause><Soft> and <Pause><Hard>.
Just Frame combos
For short JF combos or JF anims. Created by Delano, inspired by the game series Tekken.
JF means you have to press certain keys (w + k or w + p) at the exact time, which is more difficult than it might sound.
Package 54000 has basically a collection of new state cycles.
Example 1:
- new forward kick = TRAMKONCOMkick_fw_JF <FromState>Standing
- key strokes: w + k
- old forward kick = TRAMKONCOMkick_fw <FromState>RunStart
- key strokes: w, k
Example 2:
- new forward punch = TRAMKONCOMpunch_fw_JF <FromState>Standing
- key strokes: w + p
- old forward punch = TRAMKONCOMpunch_fw <FromState>RunStart
- key strokes: w, p
Throws
Throw target animations are registered in the TRAC of the throw initiator (AKA the throw source). Target animations are forced onto the other character, removing the need to have that animation in the target's TRAC. Throw target (TRAM*tgt) animations can cover only up to 256 (0-255) frames.
Forward throws
Scenario: you load two characters into Mod Tool and rotate (+/-180°) the throw target character because you need them to stand face to face as you work on an animation. When you are done animating, the target animation would need to be reversed again. This means multiplying the velocities by -1; the rotation also needs correcting. So it looks like you need *(-1) for the x rotation and -/+180° (depending on your initial change) for the y rotation.
Excel macro
Needs update.
- Here's the macro (for all TRAMs except overlays). Demo vid here. Usage:
- Put your files into the "input_and_output" folder.
- Disable macro security if you don't want to have to click on the macro options button every time.
- Close other worksheets before you run the macro.
- If you are not afraid of VBA code, you can enter the dev environment by hitting Alt+F11. If you want to extend the attack library with more screenshots and settings, search for: "LibraryThrows", " LibraryAttack", "Picture", and "CBAttackHelp.AddItem".
Run animations
Here's a breakdown of the Striker's run animations, meant as an illustration for what would be needed to make new run TRAMs.
Run cancel:
- STRIKEidle1 / another idle animation
- STRIKErun1stepa
- STRIKErun1stepb
- STRIKEidle1 / another idle animation
Run – a minimal cycle:
- STRIKEidle1 / another idle animation
- STRIKErun1stepa
- STRIKErunstart
- STRIKErun_rt
- STRIKErun_lt (optional)
- STRIKErunstop
- STRIKEidle1 / another idle animation
Follow the images below from right to left.
List of unused animations
Here are all the known unused animations. These could be reintegrated/recycled/whatever....
From the original game
- KONOKOconsole_punch: what the name says (the engine can use that animation depending on the console's configuration, see CONS)
- KONOKOlev3_intro: looks like she was placing something (bomb?) and then running away
- KONOKOlev4_undress: from an aborted clothes-changing cutscene
- KONOKOlev16_bomb: planting a bomb
- KONCOMsuper_kick: now used in OTA scripts (mod) as spawn event, and by custom made character Shinatama Bot
- KONCOMsuper_punch: KONCOMpunch_heavy but without the shouted attack name, has HalfDamage flag, and does 10 less damage in its first attack part
- COMPISidle_special1: Comguy checking environment and his gun (meant for a feature that would visually depict an elevation in the AI's level of alertness after, say, hearing a noise)
- STRPISidle_special1: Striker checking his his gun and communicating with an ally (another unused alertness animation)
- THUGlev1_direct: Thug probably directing a truck driver
From modders
- http://mods.oni2.net/node/376
- StrikerKneeStepKickThrow.zip
- REDCOMjump_fw_crouch--double_flip--dae.zip
- KONprone_getup--dae.zip
- KONRIF_k_bk_throw.zip
- female_stun--dae.zip
FX - special effects
If the scene is overloaded by particle - including motion blur - these effects will stop to be rendered.
So, don't overuse effects.
Adding colorful trails
Open the XML-accompanied TRAM, search for the "Particles" tag, and insert your markup.
First example: TRAMSTRCOMcomb_p_p.xml
<Particles> <Particle> <StartFrame>0</StartFrame> <EndFrame>12</EndFrame> <Bone>LeftFist</Bone> <Name>contrail</Name> </Particle> </Particles>
"contrail" is looked up by the character class (ONCC) that emits the actual particle; here it is "h2h_strtrail_e01".
This second example is about creating two different contrails in a TRAM at the same time. The animation is "TRAMSTRCOMpunch_heavy.xml".
<Particles> <Particle> <Start>0</Start> <End>54</End> <Bone>RightWrist</Bone> <Name>contrail</Name> </Particle> <Particle> <Start>0</Start> <End>54</End> <Bone>LeftWrist</Bone> <Name>contrail_2</Name> </Particle> </Particles>
Note that you need to register the second contrail in the ONCC as well. Using your own contrail particle is also possible; just insert its name between the <Type> tags.
<ONCPParticle> <Name>contrail</Name> <Type>h2h_strtrail_e01</Type> <BodyPart>-1</BodyPart> </ONCPParticle> <ONCPParticle> <Name>contrail_2</Name> <Type>h2h_murtrail_e01</Type> <BodyPart>-1</BodyPart> </ONCPParticle>
Motion blur
...