[BEGE] module_animations devlog

[BEGE][Blender 4.0] module_animations - NLA Player devlog

Greetings,

in this episode, I’m testing an animation module that switches NLA tracks and moves the Strips around the timeline. This allows you to synchronize the animation with the events of the game. NLA animation is based on switching the mute states of tracks and blending them. Tracks that are higher in the stack have a higher priority and overlap the lower ones.
When nla_play() receives a keyword, it checks all tracks that have this word in their name. Depending on the number of tracks found and the rand(om) parameter, the type of animation is selected: activation of the state (idle) / activation of a random track (attack1, attack2) / execution of a sequence of tracks (blend_in/out => loop).

NPC animation has not yet been adapted to NLA, so it is not synchronized with AI.

source: TODO

3 Likes

Mesh animation pipeline

1 Like

Step 2 - NLA animation.

  1. I add the necessary animation to the NLA.

  1. I customize the order, track names, and strip settings.

  1. After importing the asset, the entire NLA animation is ready to use.

1 Like

Step 3 - module_animations.py

source: https://github.com/oshcherbyna/blender-module-system/blob/main/module_animations.py

  1. I activate the necessary tracks for the desired animation.

Next, I use print(get_nla_state("Lizardman")) to get the mute state of the tracks:
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1].

  1. Using print(int(''.join(map(str, [0, 1, 1, 1, 1, 1, 1, 1, 1, 1])), 2))
    I convert the resulting binary sequences to ordinary decimal numbers: 511.
#npc_nla = {"NLA Track names = Anim state names": [nla_track mute-states]}
npc_nla = {
    'Lizard Death Pose':    [1, 1, 1, 1, 1, 1, 1, 1, 1, 0],# => id[9] = 1022
    'Lizard Death':         [1, 1, 1, 1, 1, 1, 0, 1, 0, 1],# => id[8] = 1013
    'Lizard Attack1':       [1, 1, 1, 1, 1, 1, 0, 0, 1, 1],# => id[7] = 1011
    'Lizard Combat Loop':   [1, 1, 1, 1, 1, 1, 0, 1, 1, 1],# => id[6] = 1015
    'Lizard Combat':        [0, 1, 1, 1, 1, 0, 1, 1, 1, 1],# => id[5] = 495
    'Lizard Run Loop':      [1, 1, 1, 1, 0, 1, 1, 1, 1, 1],# => id[4] = 991
    'Lizard Run':           [0, 1, 1, 0, 1, 1, 1, 1, 1, 1],# => id[3] = 447
    'Lizard Walk Loop':     [1, 1, 0, 1, 1, 1, 1, 1, 1, 1],# => id[2] = 895
    'Lizard Walk':          [0, 0, 1, 1, 1, 1, 1, 1, 1, 1],# => id[1] = 255
    'Lizard Idle Loop':     [0, 1, 1, 1, 1, 1, 1, 1, 1, 1] # => id[0] = 511
}
  1. There are too many entries for one actor. So I use decimal values to transform the matrix into a row:
nla = {
    'Player':       [1023, 511, 1791, 895, 959, 1951, 1967, 951, 955, 2045, 2046],
    'Lizardman':    [511, 255, 895, 447, 991, 495, 1015, 1011, 1013, 1022],
    'Lizardman*':   [511, 255, 895, 447, 991, 495, 1015, 1011, 1013, 1022],
}
  1. Technically, switching the mute states of nla tracks occurs through a binary shift (b>>i)&1.
    Next, I play the nla-animation where necessary in the code.
if __name__ == "__main__":
    pass
#    nla_play("Player", "Idle")
    nla_play("Player", "Impact", rand=True, delay = 0.5)
#    nla_play("Player", "Attack", rand=True)
#    nla_play("Lizardman", "Attack", rand=True)
#    nla_play("Player", "Run")
#    nla_play("Lizardman", "Run")
#    nla_play("Player", "Combat")
#    nla_play("Player", "Death")