Getting action start and end frames with bpy.data.actions[name].frame_range

First off, I know it’s a bad idea to use the bpy in the game engine; just bear with me for a second.

This piece I wrote works perfectly inside Blender,


import bge, bpy

class DS_Anim:
    
    layer = 0
    priority = 0
    mode = 0
    layerWeight = 0
    ipoFlags = 0
    isLoop = False
    isTransition = False
    
    def __init__(self, name, speed):
        self.name = name
        self.speed = speed
        self.start = bpy.data.actions[name].frame_range[0]
        self.end = bpy.data.actions[name].frame_range[1]
        
        if self.isCycle:
            self.end -= 1
    
    def getCurrent(self, obj):
        self.currentFrame = obj.getActionFrame(self.layer)
    
    def play(self, obj):
        self.getCurrentFrame(obj)
        obj.playAction(self.name, self.start, self.end, self.layer,
        self.priority, self.blend, self.mode,
        self.layerWeight, self.ipoFlags, self.speed)
    
    def playback(self, obj):
        self.getCurrent(target)
        target.playAction(self.name, self.end, self.start, self.layer,
        self.priority, self.blend, self.mode,
        self.layerWeight, self.ipoFlags, self.speed)

    def stop(self, target):
        target.stopAction(self.layer)


Then there’s a few more class functions and bunch of subclasses where I define different kinds of animations (idles, movement, etc) and assign them to their corresponding layer, mode and priority values, but you get the main idea: it’s a simple module that helps organizing the process of playing actions… and it’s very, VERY nice to just let blender use it’s knowledge of the action in question instead of typing beggining and end frame for each animation like a lunatic, because if I want the action to stop or start at a specific point I just have to call a function.

And here comes the question: how exactly should I go about “fixing” this system so that it doesn’t depend on the bpy? I’ve been googling this stuff and no one seems to even be using a similar method so, any ideas?

bpy is the Blender API.

Importing bpy without Blender (after publishing the game) will fail.

I suggest you write a Blender script that reads the animation configuration from blender data and transfers it to BGE data. Run this script before starting the game engine (in Blender rather than the BGE). So it is more a setup script than a game script.

You could even generate bge code.

Hmm. Guess I could sort through the action data blocks being used by a file, create instances of my class out of them and then dump it all somewhere in the bge module itself… something like bge.actions[name].getWhatevs would help moar than just me, right?

Nevermind, I fixed it myself.

Tested this a few hundred times so I’m guessing it works exactly as it should.


# bpyToBge.py

import bpy
import dataTransfer


def getActionData(animationList):    
    for i in range(len(bpy.data.actions)):
        act = (bpy.data.actions[i].name,
        bpy.data.actions[i].frame_range[0],
        bpy.data.actions[i].frame_range[1])
                    
        animationList.append(act)
   
    dataTransfer.animationList = animationList

#dataTransfer.py (run this on startup)

import bpyToBge
animationList = list()
bpyToBge.getActionData(animationList)

That gives you a list of every action in your .blend file; each item is itself a list containing name and frameRange, so [0] = name, [1] = frameStart, [2] = frameEnd. I personally prefer to get it all into my own module like so ~


import bge

#ds_animations.py


class ds_animation:
    
    ipoFlags = 0
    current = 0
    speed = 1
    isPlaying = False
    layer = 0
    layerWeight = 0.25
    priority = 100
    mode = 0
    blend = 9
    isCycle = False
    isPlaying = False 
    
    def __init__(self, name, start, end):
        self.name = name
        self.start = start
        self.end = end

#define class funcs such as action.play(), action.stop(), etc. it's all standard bge code.

#ds_loader.py

import dataTransfer
from ds_animations import ds_animation

class ds_container(list):
    
    def __init__(self, info):
        
        self.info = info #fancy descriptor for my data type
    
    def addObject(self, name, object):    
        self.__dict__[name] = object
        self.append(object)

class ds_master:
    actions = ds_container('List of actions present in the current .blend file
')

myStuff = ds_master    

def loadActions(cont):
    loadFrom = dataTransfer.animationList 
    
    for i in range(len(loadFrom)):
        x = loadFrom[i]
        newAction = ds_animation(x[0], x[1], x[2])
        myStuff.actions.addObject(x[0],newAction)

That packs it all up nicely into a module I can call from the bge whenever I need to work with actions. Three main ways to look up an action,

Using keys;
·myStuff.actions.dict[actionName]

Looping through the list itself
·for action in myStuff.actions)

Using vars()
vars(myStuff.actions)

Pretty cool, huh?

^edited out the last pieces of effy code, can be marked as solved.

Side note: any type of data that the bge can read can be extracted out of the bpy with this method; so, theoretically, it’d be possible to create a similar class to list through objects – haven’t tested it, so don’t quote me on that one just yet. I’ll dive into it sometime and see what I can find.