Jump to content

SpriterPlusPlus - a C++ Spriter implementation


lucid

Recommended Posts

  • Added functions startResumePlayback(), and pausePlayback().
  • Now playback automatically pauses after a non-looping animation completes, and resumes whenever you setCurrentAnimation().
  • Completed bone debug display, and added an example.  Since bones aren't normally in the zOrder where rendering is done Settings::enableDebugBones must be true when creating a new instance for them to work.  Then (as with renderDebugPoints and renderDebugBoxes),  Settings::renderDebugBones must be true when you want to see them .
  • Added some convenience functions to check the value of a variable without having to retrieve it in a separate step. getStringValue, getIntValue, and getRealValue.  Specify either the objectName and a variableName to retrieve the value from an object, or just the variableName to retrieve it from that entity.  Attempting to retrieve a value that isn't there will just return 0 or an empty string, and output an error if you have an error callback set.
  • Completed animation blending.  To use this just setCurrentAnimation(animationName, blendTimeInMilliseconds).  This does work with sub-entities as well, so if you have the same sub-entity on both animations, with a different sub-entity animation on each, those sub-entity animations will also blend.  Also, the elapsed time ratio is also blended.  What this means is that if you have two animations, for instance a 4 second walk loop, and a 2 second run loop, and each has the same foot forward first, it will sync correctly, and the animation will appear to slow down as it blends towards walk, or speed up as it blends towards run, rather than speed up the walk, or slow down the run to keep them the same length. 


Please let me know if you run into any issues.

Link to comment
Share on other sites

Ok.  I've tested this with quite a few Spriter projects, and ended up finding and fixing an edge case that wasn't displaying correctly.

I also updated the readme with instructions for the recent additions, and added CMake as @labsin suggested, for easier cross-platform usage.

The implementation is now essentially complete, but of course please continue to report any bugs and make any suggestions.

Also, if anyone gets it working using other engines or xml or json loaders, and you don't mind me linking to your implementation(s) (preferably under the zlib license), please let me know in this thread. 

cheers!

Link to comment
Share on other sites

@lucid Hey, awesome work! That does fix some animations issues I had left. Thanks! No more problem so far.

Oh, in fact, one problem: "SpriterEngine Error: EntityInstance::getFile - file id -1 out of range",
any idea why this happens?

You should take the pugixml loader from there: https://github.com/Breush/SpriterPlusPlus
As my fork won't be useful anymore, I'll just delete it.
Well, I might make a final pull request fixing the compiler warnings (signed/unsigned comparisons + constructors reorder).

Link to comment
Share on other sites

29 minutes ago, lucid said:

@Breush Thanks!  I added the pugixml loader.  

As far as the getFile error, if you don't mind, please email me the Spriter project you're using, and I'll see if I can find out what's going on. 

I found out what the problem was, and it was not SpriterPlusPlus fault but mine.

Reason was: I use a "model" image to place the different limbs of my animation correctly in Spriter.
However, it becomes useless once in game. So I removed the line "<file id="6" ... />" by hand (back in the day) from the SCML file.
But the current loader is guessing the file id, and not reading it, so all files id afterwards where wrong for SpriterPlusPlus.
That's it. (Reloading the broken file in Spriter and saving a new version fixed it easily).)

Link to comment
Share on other sites

thanks @labsin for the recent commit

@Breush, you can use:

UniversalObjectInterface *myBox = myEntityInstance->getObjectInstance("hitbox"); 

you can then use
 

myBox->getPosition() 
myBox->getAngle(); 
myBox->getScale(); 
myBox->getPivot() * myBox->getSize(); 

as needed. I can probably add something to help get the bounds in another format if needed, but probably not until after the upcoming Spriter release.
 

Since Spriter allows you to set the zOrder for boxes and points, which has come in handy for a couple of users using either or both for spawning objects or displaying non-Spriter items within the zOrder, we've left it in there.  If needed I can also add something like the 'enableDebugBones' option in settings for enabling the boxes and points in zOrder.

Link to comment
Share on other sites

@lucid Thank you so much for your answer. Using "getObjectInstance()" seems very logical, I did not see it sooner somehow.
However, I'm always getting (0, 0) via myBox->getSize(). (Even after a myEntityInstance->reprocessCurrentTime()).
So, I suppose there's a bug.

By the way, what's the convention for size and scale: do the size is the effective one (already scaled)?

For the zOrder, that's all right, I guess it's not a big overhead. I was just wondering.
And, I suppose it would require a Spriter update to enable boxes/points with no zOrder information.

Link to comment
Share on other sites

On 11/25/2015, 11:27:34, Breush said:

@lucid .
I'm always getting (0, 0) via myBox->getSize(). (Even after a myEntityInstance->reprocessCurrentTime()).
So, I suppose there's a bug.

By the way, what's the convention for size and scale: do the size is the effective one (already scaled)?

@Breush Sorry for the late reply.  I thought I had already replied to this, and been focused on the next version of Spriter itself.  I just tested it and I'm not getting the getSize() bug.  It gives me the proper dimensions. If you're still having this issue, please email me the file and I'll see what I can find out.   The size is the unscaled size.  For the current size multiply size and scale;

On 12/1/2015, 6:50:41, Breush said:

Just one more remark: you should replace the calls to


getObjectInstance(std::string objectName)

by


getObjectInstance(const std::string& objectName)

in order to avoid useless object construction.

Thanks.  Fixed in the latest commit.

Link to comment
Share on other sites

I'm writing a Qt implementation. I also include a documentwrapper using QtDom (a xml library).
It seems this library doesn't provide access to xml attributes in a specific order (this is according to xml spec).
So I've made a pull request to not expect the attributes to be in the right order.

I've also used Valgrind to check for memory leaks and found a bunch of issues.
I've added commits for that in the same pull request. Feel free to cherry-pick what you want.

I also noticed something strange. In EntityInstanceData::setObjectInstance and other methods of the class, the id's already exist. So if you do ...map.insert(...) it basically does nothing, yet a new object is created in the method that's never deleted. I've added a check and a error message. I didn't check where the double id's came from.

Link to comment
Share on other sites

My SpriterPlusPlusQt is now in a working state. You should now be able to build and run it.

Using it from QML is very easy.
I created an example that's more or less the same as the SFML one (creating 100 random entities). Each frame is 10ms, so performance is good.

Now I need to add more features (sound, ...) and optimize a bit.

Still a question, are there some dimensions on the animation as a whole I could use? A bounding box.

Link to comment
Share on other sites

I've started an integration of SpriterPlusPlus into Cocos2d-X, a new library called Spriter2dX. It does not yet have much platform support (only tested with CMake for Linux) but I will be adding that as I finish integrating this into the game I am developing. I expect to have Android done very soon, other platforms may follow and of course I'll take pull requests for them.

I found this pretty easy to do, I think SpriterPlusPlus has a pretty good design for extension points. Cocos2d-X has a very different rendering system than for example SFML - so the ImageFile override had to work considerably differently. In Cocos2dX we don't just render a sprite to the screen, instead each object on the screen has a persistent object in a scene graph which will get rendered in a batch. So to work in the SpriterPlusPlus model I have each CCImageFile manage a pool of cocos2d::Sprite objects that it will position for a single tick. I have an example where I ported shamelessly robbed SpriterPlusPlus's example and its able to run 100 Grey Guy animations at about 50 fps on my laptop, haven't tried it with a real GPU.

Link to comment
Share on other sites

5 hours ago, jeremyjh said:

I've started an integration of SpriterPlusPlus into Cocos2d-X, a new library called Spriter2dX. It does not yet have much platform support (only tested with CMake for Linux) but I will be adding that as I finish integrating this into the game I am developing. I expect to have Android done very soon, other platforms may follow and of course I'll take pull requests for them.

I found this pretty easy to do, I think SpriterPlusPlus has a pretty good design for extension points. Cocos2d-X has a very different rendering system than for example SFML - so the ImageFile override had to work considerably differently. In Cocos2dX we don't just render a sprite to the screen, instead each object on the screen has a persistent object in a scene graph which will get rendered in a batch. So to work in the SpriterPlusPlus model I have each CCImageFile manage a pool of cocos2d::Sprite objects that it will position for a single tick. I have an example where I ported shamelessly robbed SpriterPlusPlus's example and its able to run 100 Grey Guy animations at about 50 fps on my laptop, haven't tried it with a real GPU.

Great to see the CMake changes getting used by someone.

I had the same problem with the qt 5 screne graph port. With the added problem that the nodes need to be created from a separate thread when asked to by the renderer. I solved it by relying on the zOrder returned by the sprite entity. This seems to be reliable. And the vectors aren't removed when they change so it's safe to keep the pointer to it.

Link to comment
Share on other sites

On 12/11/2015 at 7:00 PM, labsin said:

My SpriterPlusPlusQt is now in a working state. You should now be able to build and run it.

Using it from QML is very easy.
I created an example that's more or less the same as the SFML one (creating 100 random entities). Each frame is 10ms, so performance is good.

Now I need to add more features (sound, ...) and optimize a bit.

Still a question, are there some dimensions on the animation as a whole I could use? A bounding box.

Awesome work, @labsin.  The reason there isn't a bounding box function is that the bounding box for an animation as a whole would change with the framerate.  It wouldn't be an extreme change, but a few pixels of clipping on an engine relying on it would be noticeable.

6 hours ago, jeremyjh said:

I've started an integration of SpriterPlusPlus into Cocos2d-X, a new library called Spriter2dX. It does not yet have much platform support (only tested with CMake for Linux) but I will be adding that as I finish integrating this into the game I am developing. I expect to have Android done very soon, other platforms may follow and of course I'll take pull requests for them.

@jeremyjh Excellent.  

 

7 hours ago, jeremyjh said:

Cocos2d-X has a very different rendering system than for example SFML - so the ImageFile override had to work considerably differently. In Cocos2dX we don't just render a sprite to the screen, instead each object on the screen has a persistent object in a scene graph which will get rendered in a batch. So to work in the SpriterPlusPlus model I have each CCImageFile manage a pool of cocos2d::Sprite objects that it will position for a single tick. 

1 hour ago, labsin said:

I had the same problem with the qt 5 screne graph port. With the added problem that the nodes need to be created from a separate thread when asked to by the renderer. I solved it by relying on the zOrder returned by the sprite entity. This seems to be reliable. And the vectors aren't removed when they change so it's safe to keep the pointer to it.

@labsin and @jeremyjh .  Since it's already come up twice, I added sprites (SpriteObjectInfo) to the ObjectFactory.  I'm not sure if this will or would have eased the process, but the idea would be that you could create your SpriteObjectInfo to contain a reference or pointer to your engine or framework's persistent objects.  Then you can override ::render(), and get all of the information you need there.  This should hopefully reduce the steps and workarounds needed for systems with persistent objects.  Please let me know if this would help the situation, and/or if there needs to be some additional information passed to the constructor to make it useful.  

Link to comment
Share on other sites

Hi lucid,

I am working on the CF25 and I am facing again against the same problem. I solved it with my old implementation but this time with the new implementation I cannot figure out which part I missed. The windows port on Clickteam Fusion 2.5 is based on Direct3D that is why I needed to change this to have left handed convention:

void TransformProcessor::transformChildObject(UniversalObjectInterface *childObject) const
    {
        point parentScale = parentObject->getScale();
        childObject->setScale(multiply(childObject->getScale(), parentScale));
        if (parentScale.x*parentScale.y < 0)
        {
            childObject->setAngle( - childObject->getAngle());
        }
        childObject->setAngle(childObject->getAngle() + parentObject->getAngle());
        childObject->setAlpha(childObject->getAlpha()*parentObject->getAlpha());
        point childPosition = childObject->getPosition();
        point preMult = multiply(childPosition, parentScale);
        // Left Handed rotation in CF2.5 (Direct3D convention)
        // p'x = p.x * c + p.y * s;
        // p'y = -p.x * s + p.y * c;
        childPosition.x = (preMult.x * angleCos) + (preMult.y * angleSin);
        childPosition.y = -(preMult.x * angleSin) + (preMult.y * angleCos);
        childObject->setPosition(add(childPosition, parentObject->getPosition()));
    }

In settings.cpp, I have set the following:

    bool Settings::reverseYOnLoad = true;
    bool Settings::reversePivotYOnLoad = true;
    bool Settings::reverseAngleOnLoad = false;

Did I forget something obvious? At the moment it looks like this (top left it is another object):

CF25LeftHanded.png.acbefe698163684106f3b

Link to comment
Share on other sites

1 hour ago, conceptgame said:

...

In settings.cpp, I have set the following:

    bool Settings::reverseYOnLoad = true;
    bool Settings::reversePivotYOnLoad = true;
    bool Settings::reverseAngleOnLoad = false;

...

 

Instead of changing the values in the engine, you can overwrite them in your implementation with calling the following somewhere:

SpriterEngine::Settings::reverseYOnLoad = true;
Link to comment
Share on other sites

6 hours ago, conceptgame said:

Did I forget something obvious? At the moment it looks like this (top left it is another object):

 

Which animation is that?  Also it looks like the legs are in the normal position - does the animation play stably, or is there constant rotation and flipping of the whole character?

6 hours ago, conceptgame said:

   // Left Handed rotation in CF2.5 (Direct3D convention)
        // p'x = p.x * c + p.y * s;
        // p'y = -p.x * s + p.y * c;

Is this code used somewhere?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...