[REQUEST] Improve Python interface for controlling Actions

While working on animation systems in various projects, I’ve run into many issues with the was action playback is currently handled by the Python API. I’ve also seen numerous threads on the forum with users encountering the same or similar problems. As such, I’d like to propose a changes changes that I think should be made.

Support zero playback speed.Many users would like to be able to play actions and manually control their current frame using the setActionFrame() method without the action being automatically updated every frame. The problem is that the playAction() method does nothing if the speed argument is zero. Yes, I know you can achieve the same thing using an Action Actuator in Property mode, but there’s no reason you shouldn’t be able to do the same thing with a pure Python implementation.

Add setActionSpeed() and setActionWeight() methods.It’s currently fairly difficult to change the play speed and layer weight of a currently playing action. This is mainly due to the fact additional calls to playAction() are ignored if the name, start_frame, and end_frame arguments are the same as the action already playing on that layer. Adding these methods would make this process much easier and would also avoid some of the additional overhead that repeatedly calling playAction() incurs.

Any other suggestions for improvement are welcome as well.

just use property mode, for now until your ideas are excepted.
and logic

Animation=true--------and------------play action in property mode
________________________----------add property Speed to property frame

(however the action armature in property mode has a bug where it wont shut down, moguri is patching)

with this method, just turn it on, and set the speed value

Why you do not simply play and action with startFrame == endframe?

If you want full control then there is no reason to give control to the animation system.

Zero playback speed constantly wastes processing time as there is no effect after the first frame.

I think when you want to get setActionSpeed() and setActionWeight() it makes more sense to introduce an action player object. Such object can act as interface between a single action and the action framework.

You can even write your own (with the methods you mentioned above).

Because each call to playAction() takes roughly 15 times as long as a call to setActionFrame(). There’s absolutely no reason you should have to completely reinitialize an action just to set its frame.

Of course for many people, the logic usage is insignificant compared to other things like render time. But when you have potentially hundreds of armatures that all need to update their current action frame, speed, or weight every single frame, the time spent on each call to playAction() takes a significant toll on the frame rate.

EDIT:

It uses far less processing time than having to call playAction() every frame. If users want to leave an action on a given frame without wasting processing time, they can simply call stopAction() after calling playAction().

I already have. They of course have to call playAction(), which as I’ve already stated, is cumbersome, slow, and not at all friendly to users inexperienced with the API. Users shouldn’t have to stumble through all the quirks of the playAction() method in addition to suffering a performance hit just to implement simple functionality that the API should provide in the first place.

EDIT 2:
Just to hammer home my point, here’s a file that demonstrates how slow playAction() is compared to just calling setActionFrame(). It has 400 cubes that all play the same action. When using playAction() to update the frame, I get about 8 fps. When using setActionFrame(), I get about 100 fps.

playAction_performance.blend (405 KB)

EDIT 3:
Just did a test using Action actuators in Property mode. When connected to Always sensors with True level triggering (which is necessary if you want to update the frame every logic tick), they incur the exact same performance drop as calling playAction() every frame, so they aren’t a very viable option either.

I agree that you should not need to reinitialize the animation. Zero playback speed still feels somehow wrong design.

I think what is missing is a proper “pose” operation that establishes the pose of the given action at the given frame. I guess this could be difficult to implement.

What about a new play mode e.g. KX_ACTION_MODE_POSE.

This would be the Python equivalent to bge.logic.KX_ACTIONACT_PROPERTY, just not relying on property but on Python code without the need to reestablish the animation.

The parameters start, end and speed would be ignored when using this mode as the animation system would wait on the next call to setActionFrame().

It would do that same as with your idea of the zero speed, but feels more as a concept rather than a workaround.

What do you think?

Having a pose mode would be an acceptable option. Though the main reason I suggested just supporting zero playback speed instead is that it should be very simple to implement and seems fairly intuitive to me. All it should require is disabling the conditional that checks if the speed argument is zero. Creating a separate playback mode, on the other hand, would require a bit more work as you noted.

This still leaves the need for setActionSpeed() and setActionWeight() methods which I imagine shouldn’t be very difficult to implement.