lucid Posted November 3, 2015 Report Posted November 3, 2015 SpriterPlusPlus a C++ Implementation for Spriter animations About SpriterPlusPlus aims to be as quick as possible playing back animations, and iterating additional characters once loaded. Additional instances are flyweights that take up less ram than the initially the loaded model. Each flyweight instance minimizes cycles spent in random access and iteratating through containers using stored pointers and iterators wherever possible. Included example Sfml for display and sound playback. And two examples for loading: TinyXml and PugiXml Supported Features Basic animations Bone animations All curve types (Instant, Linear, Quadratic, Cubic, Quartic, Quintic, Bezier) Points Collision Rectangles SubEntities Events (Triggers) Sounds Variables Tags Character Maps Animation Blending How to use All necessary files are in the 'spriterengine' folder. The other included files are for example purposes. Extend (All of the following can be found in spriterengine/override) : FileFactory and pass to SpriterModel so it can use your own inherited versions of : SpriterFileDocumentWrapper (and Element and Attribute Wrappers) to create parsing code. You can specify a separate SpriterFileDocumentWrapper for scml (xml) and scon (json), which will be automatically selected upon loading based on the file extension ImageFile to store shared texture/image resources to be used by sprite objects SoundFile to store shared sound resources to be used by sound objects, and create your custom extended version of SoundObjectInfoReference to playback and control volume of the shared sound resources ObjectFactory and pass to SpriterModel to enable debug rendering: spriterengine/objectinfo/PointInstanceInfo spriterengine/objectinfo/BoneInstanceInfo spriterengine/objectinfo/BoxInstanceInfo Settings.cpp has static variables to control the display of points, bones, and boxes: bool Settings::renderDebugPoints; bool Settings::renderDebugBones; bool Settings::renderDebugBoxes; enableDebugBones must be true while creating a new instance in order for renderDebugBones to have an effect: * bool Settings::enableDebugBones; spriterengine/objectinfo/TriggerObjectInfo if you want to be able to perform a function upon being triggering events ###### (Alternatively you can query a specific trigger for your instance using : getTriggerObject("triggerName")->getTriggerCount() // in case more than one event was triggered in the elapsed time Creating an instance : include "spriterengine/spriterengine.h" ScmlModel scmlModel(fileName, new customFileFactory, new customObjectFactory/optional/); EntityInstance *entityInstance = scmlModel.getNewEntityInstance(entityName or entityIndex); Controlling playback : entityInstance->setCurrentAnimation(animationName); // defaults to first animation entityInstance->setCurrentAnimation(animationName, blendTimeInMilliseconds); // blends the current animation to the new one over time entityInstance->setTimeElapsed(inMilliseconds); entityInstance->pausePlayback(); // playback is automatically paused at the end of a non-looping animation entityInstance->startResumePlayback(); // playback automatically resumes on a setCurrentAnimation() command after setting the elapsed Time: entityInstance->render(); and if you're using sound and/or events: entityInstance->playSoundTriggers(); or entityInstance->playEventTriggers(); or entityInstance->playAllTriggers(); Controlling the entire character entityInstance->setPosition(SpriterEngine::point(x,y)) entityInstance->setAngle(SpriterEngine::toRadians(angle)); entityInstance->setScale(SpriterEngine::point(w,h)); Character Maps entityInstance->applyCharacterMap("charMapOne"); entityInstance->applyCharacterMap("charMapTwo"); entityInstance->removeCharacterMap("charMapOne"); entityInstance->removeAllCharacterMaps(); Metadata to retrieve a variable value from an object : entityInstance->getRealValue("objectName","varName"); // or getIntValue, or getStringValue to retrieve a variable from the entity itself : entityInstance->getRealValue("varName"); // or getIntValue, or getStringValue if you would like to store the variable to avoid repeated retrieval for performance reasons (should normally not be necessary): UniversalObjectInterface *myVariable = entityInstance->getVariable("objectName", "varName"); // to retrieve from an object or UniversalObjectInterface *myVariable = entityInstance->getVariable("varName"); // to retrieve from the entity itself and then use myVariable->getRealValue(); // or getIntValue, or getStringValue to check if a tag is active : bool myTagIsActive = entityInstance->tagIsActive("objectName", "tagName"); or if the tag is on the entity itself : bool myTagIsActive = entityInstance->tagIsActive("tagName"); Additional options Error output In Settings.cpp there is a function pointer to a callback function to display error messages ErrorFunctionPointer Settings::errFunction; Any function with the signature void myErrorFunction(const std::string &errorMessage) can be used here. Two basic error functions are provided: * nullError is the default, and takes no action * simpleError outputs the error message to std::cerr In most cases, there should be no errors, unless you are loading an invalid file, attempting to retrieve the wrong type of data from an object or variable (myStringVariable->getIntValue()), or attempting to access a missing variable or object Loading options In Settings.cpp you can set loading option, if your engine requires the y, pivotY, or angle to be reversed to display properly (you attempt to load a character in your engine and the positions, pivots, or angles seem reversed). These are all defaulted to true, which is the setting that should work correctly for most engines. Settings::reverseYOnLoad; Settings::reversePivotYOnLoad; Settings::reverseAngleOnLoad; Feedback To provide feedback, report errors, or give suggestions, please use this forum thread. Quote
bwwd Posted November 4, 2015 Report Posted November 4, 2015 Is it complied for windows cause i cant run SpriterPlusPlus.exe , it says its not win32 application. Quote
Breush Posted November 4, 2015 Report Posted November 4, 2015 Well, that's awesome! I'll try integrating it to my SFML-based project right now. Thanks to the example, I guess it won'tbe so hard Quote
DarkGOd Posted November 4, 2015 Report Posted November 4, 2015 Hi! It's finally here, I'm *happy* I'm also sad to be the bearer of bad news though, but it just won't compile with anything but MSVC, at least it doesnt with neither gcc not clang; that means no OSX, no Linux, no Android, no iOS :/ Which I assume is not intended since Spriter itself is so multiplatform friendly On my linux (thought I doubt that'd be different on osx or windows) with gcc 4.9: http://hastebin.com/uroxefobis.vhdl and with clang 3.5: http://hastebin.com/favarilaha.vhdl Also my C++ is very rusty but to me it looks like it's using the rather recent C++14 stuff, which on OSX at least will make it very hard to support "oldish" versions of the system :/ That's something to ponder at least, but obviously the biggest urgency is to actually let it work on non MSVC stuff Thanks! Quote
Mike at BrashMonkey Posted November 4, 2015 Report Posted November 4, 2015 Thanks for the feedback everyone. Keep it coming. Reading through this thread and addressing the issues will be the first thing Edgar does when he gets back (in roughly 2 days) cheers. Quote
lucid Posted November 4, 2015 Author Report Posted November 4, 2015 Hello everyone. Had a moment to drop in. As I Mike said I will be able to look into everything further when I get back. @breush, please let us know how it goes. @darkgod - I'll make the needed changes to get it to compile on clang and gcc (looks like they're just being a bit more strict about a few things). Should be no problem. I'll also look into removing the c++ 11 stuff, which is mostly 'auto's to remove the need for long ugly iterator names and additional typedefs. @bwwd - I may have inadvertently done the final compile for win 64, but it's definitely windows. Will take a look when I get back. Thanks for the feedback, everyone! Quote
Breush Posted November 5, 2015 Report Posted November 5, 2015 @lucid @darkgod As my pull request got merged, the compile-time errors should be fixed. I work with C++11 and the STL, so I'm OK with the current implementation but others might be concerned these requirements, notably on small systems like smartphones. By the way, I had to make a loader using pugixml, I can share it with you guys later, once I sure that the problems I'm experiencing are not linked to that change. Quote
DarkGOd Posted November 5, 2015 Report Posted November 5, 2015 Confirmed, works on my linux now (I've had to fix 3 auto& in the example's main.cpp though); thanks! Quote
Breush Posted November 5, 2015 Report Posted November 5, 2015 So, here's a quick sum-up from my experience.Cool: It works great! And that's a good point. It fixes problems I had using scml-pp concerning negative scaling. It's easy to interface with user's pre-existant factories. Question: I see the functions to override for debugging, but I didn't find any option to activate it. (Excepted it to be in global/global.h). Not so cool: Triggers (sounds at least) do not work properly. Either I get infinite of them or none. I spent way too much time writing a XML loader. The basic functions and types that are specific to the XML library need to be factored out from the document structure. That way one would just need to write the content of predefined basic functions (getAttribute, getFirstChild, etc.). Every time I see a "new ..." in the code I just wonder who should delete it... For instance "new SfmlSoundObjectInfoReference" in example/sfmlsoundfile.cpp Something to do: Functions in global/global.h are "static" but should be "inline" instead. I think this is due to a misundertanding of what "static" means outside a class definition. So, I'm very happy with it! But right now my biggest issue concern sounds, I hope to understand the problem soon enough. And by the way, "To provide feedback, report errors, or give suggestions, please use the relevant Spriter forum thread", this is the one, right? Quote
lucid Posted November 6, 2015 Author Report Posted November 6, 2015 @bwwd, do you have any other machines you can test on? I verified it's 32 bit. On 11/5/2015, 8:39:07, Breush said: Question: I see the functions to override for debugging, but I didn't find any option to activate it. (Excepted it to be in global/global.h). Yes, that's something I didn't get to before the deadline. I do plan to fix it to make it optional. At the moment, if your objectFactory creates drawable boxes and points, they are automatically drawn. On 11/5/2015, 8:39:07, Breush said: Triggers (sounds at least) do not work properly. Either I get infinite of them or none. The file I'm testing with is working correctly. I will get an example file suitable for everyone to test with that includes at least one instance of every feature soon. I can also check if your scml file has the same issue here (either attach it here or send it to lucid@brashmonkey.com if you'd like me to test it) On 11/5/2015, 8:39:07, Breush said: I spent way too much time writing a XML loader. The basic functions and types that are specific to the XML library need to be factored out from the document structure. That way one would just need to write the content of predefined basic functions (getAttribute, getFirstChild, etc.). Agreed. This is another thing I just didn't get a chance to get to for the deadline. On 11/5/2015, 8:39:07, Breush said: Every time I see a "new ..." in the code I just wonder who should delete it... For instance "new SfmlSoundObjectInfoReference" in example/sfmlsoundfile.cpp Anything function that has a name newSomething() or getNewSomething() should be taken as creating a new instance, so: SomeType *myPointer = newSomething(); should be treated the same as if it were: SomeType *myPointer = new Something(); Anything using that function should either take ownership of this new pointer and delete it, or it should immediately pass it to a function which expects a new pointer, which should always have a parameter name reflecting this: myFunction(SomeType *newSomething) That means this function is expecting a new instance, and will take ownership of it and delete it upon destruction. If you notice I deviated from this anywhere, please let me know. On 11/5/2015, 8:39:07, Breush said: "To provide feedback, report errors, or give suggestions, please use the relevant Spriter forum thread", this is the one, right? yes On 11/5/2015, 1:50:41, Breush said: I had to make a loader using pugixml, I can share it with you guys later, once I sure that the problems I'm experiencing are not linked to that change. thank you, please do Thanks for all the excellent and useful feedback. Quote
bwwd Posted November 6, 2015 Report Posted November 6, 2015 No i dont have other machines, current spriter works so why new one shouldnt ? I ran "SpriterPlusPlus.exe" from "SpriterPlusPlus-master\Release" folder but it doesnt have icon and it says its not win32 application.I will redownload again but if you say it runs for you i dont know, do i need some libraries to run this new version on windows ? Im on WinXP SP3 That SpriterPlusPlus.exe file is only 160kb in size -- downloaded new 161kb version and now i have this : it says encountered illegal instruction , microsoft website says its command.com mismatch or something. Quote
lucid Posted November 7, 2015 Author Report Posted November 7, 2015 Working on the general loading wrappers now. Should make adding a new parsing library very simple. @bwwd this is an implementation for Spriter (to run animations in your own game or engine), not a new version of Spriter. Could you give a more specific error message? I would try compiling it your end if possible. Quote
bwwd Posted November 7, 2015 Report Posted November 7, 2015 ah i misinterpreted it , im not using spriter files directly so its not a top priority for me until deformation is available for clients im forced to choose spine if they want to use deformation in realtime but for spriter its deformation exported to single frames , too bad cause i prefere to work with deformation in spriter , in spine they dont have auto subdivision and you have to take care of every vert which is insane with more complicated setups, having less points to move verts is better and have auto subdivided verts between points and you cant change density of the grid like in spriter , i hope that idea will stay and we will be able to set different diffent wireframe density for every bodypart, spine is very different in this aspect and every vert counts and has to be moved manually and other verts wont follow and you need a lot of them to create smooth S shape instead of Z shape.just headaches, only weights to bones are saving this feature in spine. Quote
lucid Posted November 7, 2015 Author Report Posted November 7, 2015 Committed some changes that make it so you should only have to override a 3 simple classes - wrappers for documents, elements, and attributes, and now the loader class interfaces with the wrapper so you don't have to worry about the details of the loading code. If anyone tries to overload these, please let me know how it goes whether it works smoothly, or you run into problems. @bwwd, I know it's been an extremely long time waiting for the deformation. It's also my personal most wanted feature. Trust me I know it's been slow going, but when we finally get there, it's going to be much, better than the proof of concept version that's in there now. Quote
labsin Posted November 8, 2015 Report Posted November 8, 2015 I don't like the layout of the repo. I think the spriterengine should be in it's own repository, so I can add it as a git submodule to my project. You could then add another repo with the example. Adding binary files to the repo is a bit strange. You could add a download link in the README or add them to the Release tab. And this repo is meant for developers that want to build their own implementation, so they'll need to build it anyway. Only using Visual studio as build option is a bit cumbersome for Unix/Linux devs. CMake would be a great option. It's cross platform and extendable. You could add it to the example and the engine and let them work together. SFML is also using it. Quote
Breush Posted November 8, 2015 Report Posted November 8, 2015 @lucid OK thanks, nice new wrappers. I updated my fork https://github.com/Breush/SpriterPlusPlus with the PugiXml loaders. You should check example/override/pugixml* and the pugixml at the root directory. Works all right for me. lucid 1 Quote
labsin Posted November 8, 2015 Report Posted November 8, 2015 I've added a pull request for a gcc change and a fix for header files not found. I could build the engine now with: cd spriterengine LANG=C g++ -std=c++14 -shared -fPIC -I. */*.cpp -o spriterengine.so (LANG=C only because I like the terminal output in English) lucid 1 Quote
lucid Posted November 8, 2015 Author Report Posted November 8, 2015 Thanks for the feedback and PugiXML loader examples, @Breush and thanks for the fixes and the suggestions, @labsin. I merged your pull request, and I will get to your other suggestions later. Quote
Breush Posted November 9, 2015 Report Posted November 9, 2015 I needed to set looping to false for a specific animation. I made a way to overwrite the default value via entityInstance->currentAnimation->isLooping but that did not work. Is looping a thing yet? And, I must say I did not find any option to turn looping off in Spriter itself too. Quote
Mike at BrashMonkey Posted November 9, 2015 Report Posted November 9, 2015 1 hour ago, Breush said: I needed to set looping to false for a specific animation. I made a way to overwrite the default value via entityInstance->currentAnimation->isLooping but that did not work. Is looping a thing yet? And, I must say I did not find any option to turn looping off in Spriter itself too. To set looping off for an animation once its in the game, just click the looping icon at the right side of the time-line palette in Spriter. (the button will go dark to represent "off", the button is orange when "on" by default) Playback in Spriter is always looping if you left click the play button. BUT, if you right-click the play button the animation will play just once and stop. cheers. Quote
Breush Posted November 9, 2015 Report Posted November 9, 2015 57 minutes ago, BrashAdmin said: To set looping off for an animation once its in the game, just click the looping icon at the right side of the time-line palette in Spriter. (the button will go dark to represent "off", the button is orange when "on" by default) Playback in Spriter is always looping if you left click the play button. BUT, if you right-click the play button the animation will play just once and stop. Oh, Okay. As I left-clicked the play button, I thought this button did not what I expected it to do. English is not my native language and "Repeat Playback" tooltip was not very eloquent to me. But I understand now, thanks. So now that I tested it with a file containing a non-looping animation, I can safely say it's not working in SpriterPlusPlus. -> Happens when I have a time step that does not divide the length (in time) of the animation. Quote
ALO Posted November 9, 2015 Report Posted November 9, 2015 Hi! I ported this to allegro, quite simple task & works great. However, i have the same issue that Breush have, i can't seem to find any functions that turns on / off looping, or if the current animation is complete. (Looping is "off" in the scml - does not matter). I was thinking that i could use the timing represented in the scml, but that would cause more hard-coding than i want. Also, i can't find any functions to allow / set flipping (e.g. walking right or left). I could make a "hack" for it, but yet again, more hard-coding than i want. Did i miss anything, or isn't those functions currently present? Thanks! Quote
lucid Posted November 9, 2015 Author Report Posted November 9, 2015 13 hours ago, Breush said: So now that I tested it with a file containing a non-looping animation, I can safely say it's not working in SpriterPlusPlus. 9 hours ago, ALO said: However, i have the same issue that Breush have, i can't seem to find any functions that turns on / off looping, or if the current animation is complete. (Looping is "off" in the scml - does not matter). I was thinking that i could use the timing represented in the scml, but that would cause more hard-coding than i want. Correct. Currently, all SpriterPlusPlus is doing with the looping/nonlooping information is making it so it doesn't tween from the last frame to the first. I will add in something to keep track of animation playback status, and either allow some type of 'animationFinished' callback and/or allow it to ignore setTimeElapsed() until the animation is restarted. 9 hours ago, ALO said: I ported this to allegro, quite simple task & works great. Excellent. If you decide you want to share it, please link here so we can add it to the list once we have one up. 9 hours ago, ALO said: Also, i can't find any functions to allow / set flipping (e.g. walking right or left). I could make a "hack" for it, but yet again, more hard-coding than i want. For now you can just set scale of the entityinstance to -1,1 (-x,y or for vertical flip x,-y). I can add convenience functions to setFlipX and setFlipY that will automatically do that later though, to make the flipping code more readable when using it in your projects Quote
lucid Posted November 13, 2015 Author Report Posted November 13, 2015 Just an update on the latest: I will update the README with these additions soon, but for anyone who's actively working with it: There's a settings.cpp file with some statics so you can set whether to show points and/or boxes for debugging. Since bones aren't part of the zorder where rendering takes place, I will need to add a couple of things before 'enableDebugBones' does anything. Also, in settings.cpp, you can specify a callback function to output error messages that will specify if and which function took an unexpected path. There's an included function simpleError() that just outputs to std::cerr. The default setting is a nullError() function which does nothing to save on processing error output. And of course if your authoring tool or engine has a different error output you'd like to use, you can set it here. You can either set it at the top of settings.cpp, as the initial value for errFunction, or you can set it using setErrorFunction(). Also, there were a couple of bug fixes. One bug where if you start playback on a trigger (for instance, if you start your animation at the beginning, and you have a sound on the first frame), it wasn't playing the sound or trigger immediately. I also fixed another bug where entityinstances weren't updating their transformations after angle changes, which would appear most obvious if you had sprites without bone parents, or top level bones that were offset far from 0,0. Next, I will be adding functions like startAnimation() and pauseAnimation() to automatically handle pausing after a non-looping animation finishes. Once that's functioning correctly, I will add animation blending. Lastly, in preparation for animation blending, and to work more easily with the error output, I created a new class PointInstanceInfo. If you created any child classes of PointObjectInfo to display your point objects, just change them to inherit from PointInstanceInfo instead. Quote
Recommended Posts
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.