• Bugs
  • [spine-c] crashing in animationstate_apply

Related Discussions
...

TL😃R; is there debug info I can force the spine-c runtime to output to help me figure this out? If not, read on. Crashing during animationstate_apply.

Hi there! I've been debugging this for awhile now to no avail, and I know I'm crashing during animationstate_apply


I've left the debug statements in in the below code, and the last thing printed before the crash is a "STATEUPD DONE.." line. The game has 10 or so enemies that get drawn atm, and every so often, the first time a LotusTurret is drawn, the game crashes. We haven't been able to discern what's unique about said LotusTurret that's causing our crash.

I've tried this both with a spine-c library compiled in December and one compiled today. I originally wrote this code before tracks existed, and modified it to just use the "first" track once they came into being. (I probably wrote this in the first place before October? but am no longer sure.)

In short, what I'm trying to do:

t (input param) is the current game time
lastUpdate (class member) is the game time as of last update
SPINESTATUS is an enum I use for signaling


it can be ignored here probably
GV::getAnnounceJams() returns whether or not the set of things we're drawing has been altered this frame; the crash is ALWAYS on the first time a specific unit type of ours is drawn.
the aniMap for a given spineDrawable contains the info about the current animation state, the next one, whether or not this state loops, and the string name for the state. The tracks bit of code there just asks if we should need to change the state, and, if so, does it. This code might no longer be a good idea


I think I wrote it, again, before there was anything in the spine-c runtime that did anything like it. Maybe I need to remove it?

The rest is just a series of calls to already-defined spine functions, and unless I'm calling them in the wrong order for some reason, should be straightforward. Again, our crash is happening during animationstate_apply.

Code's below


any help or suggestions about what else I should post would be greatly appreciated, as would info on a debug option for the spine-c library.

Cheers

SPINESTATUS SpineDrawable::update (float t) {
   if (lastUpdate == 0.0f) {
      //first update
      lastUpdate = t;
   }
   //if (lastUpdate - t == 0) return SS_NONE;
   if (GV::getAnnounceJams())
   {
      char str[200] = {0};
      sprintf(str, "Spine LUSET" );
      DebugLog::Log(str);
   }
   bool isComplete = false;

   if (state->tracks[0] && 
      (state->tracks[0]->time >= state->tracks[0]->endTime)
      && aniMap.at(aniState).loops == 0)
   {
      if (aniMap.at(aniState).nextState != ANISTATE_NULL) {
         setState(aniMap.at(aniState).nextState);
      }
      
if (GV::getAnnounceJams()) { char str[200] = {0}; sprintf(str, "ANIMAP %4.2f %4.2f %01d %01d", state->tracks[0]->time, state->tracks[0]->endTime, state->tracks[0]->loop, aniMap.at(aniState).loops); DebugLog::Log(str); } } else if (!state->tracks[0]) { if (GV::getAnnounceJams()) DebugLog::Log("ERROR TRACKS0 NOT FOUND"); }
if (GV::getAnnounceJams()) { char str[200] = {0}; sprintf(str, "ANIMAP %4.2f %4.2f %8.6f", t, lastUpdate, timeScale); DebugLog::Log(str); } Skeleton_update(skeleton, (t - lastUpdate) * timeScale); if (GV::getAnnounceJams()) { char str[200] = {0}; sprintf(str, "SKELUPDATE DONE %d %d", state, skeleton); DebugLog::Log(str); } AnimationState_update(state, (t - lastUpdate) * timeScale); if (GV::getAnnounceJams()) { char str[200] = {0}; sprintf(str, "STATEUPD DONE %d %d", state, skeleton); DebugLog::Log(str); } AnimationState_apply(state, skeleton); if (GV::getAnnounceJams()) { char str[200] = {0}; sprintf(str, "STATEAPPL DONE %d %d", state, skeleton); DebugLog::Log(str); }
Skeleton_updateWorldTransform(skeleton); if (GV::getAnnounceJams()) DebugLog::Log("SKELTRANS BIT DONE"); lastUpdate = t; if (aniState == ANISTATE_DESTROY) { return SS_DESTROY; } if (GV::getAnnounceJams()) DebugLog::Log("DESTROYCHECK BIT DONE"); return SS_NONE; }

I put the code in code tags to make it a little easier to read. Unfortunately the problem itself is not something I can help you with. Nate will have to take a look at this one 🙁

With C/C++ you could be stomping memory somewhere, which can cause other stuff to crash seemingly randomly. This could be a bug literally anywhere, in your game toolkit, in your code or in spine-c. Ideally your debugger would stop at the crash so you can inspect the state of the program, see where in AnimationState it is crashing and why (eg maybe state is 0?). There's nothing AnimationState can really do to help you debug if it is memory getting stomped or a bug in AnimationState. I would get your IDE to break on the crash, or try to get it to happen consistently so you can step through the crash and see exactly what went wrong.

I'm not sure what aniMap.at(aniState).nextState does, but usually you don't need multiple states. One AnimationState per skeleton should be sufficient and allows animations to be mixed (crossfaded).

Personally I probably wouldn't tie the game code so tightly with the animation code. I like to separate the game logic from any rendering stuff, including animation. Eg, in the game logic (the model in an MVC architecture) I would have a the player have a state: idle, walking, jumping, shooting, etc and track the time he has been in that state. Elsewhere, in the rendering code, I would set the animation based on the player's state. You can see this in the Super Spineboy example game.

Thanks for the reply and format


yeah, it could really be anywhere. I was mainly looking for a flag I can compile the runtime library with that'll spit out lots of handy debug info, but perhaps that's something I'll have to do on my own.

The game never, ever crashes in the debugger, frustratingly. It makes me think it's a data initialization issue that's being missed somewhere, and I'd bet that it's more likely in my own work than yours. (I was hoping for a quick way to get some debug info to figure out what is being stomped in the runtime so I can better figure out what's going on.)

The aniMap.at(aniState).nextState bit is auto-transition code


the aniMap contains a state along with its spine name, whether or not it loops, and what should come after. If a non-looping state reaches its endTime and has a valid next state, that code'll set the next state accordingly.

Thanks for the input


if there's no builtin way to get more debug output from Spine, I'll add my own and see what I can find out.

Yup, it's OSS so you can stick in some printlns as needed.

fyeahcking wrote

The aniMap.at(aniState).nextState bit is auto-transition code


the aniMap contains a state along with its spine name, whether or not it loops, and what should come after. If a non-looping state reaches its endTime and has a valid next state, that code'll set the next state accordingly.

Still seems strange. AnimationState can queue animations via AnimationState_addAnimation. The benefit is that it will crossfade animation changes.