diff --git a/src/Animation.cpp b/src/Animation.cpp index a50c047..7a8ccbc 100644 --- a/src/Animation.cpp +++ b/src/Animation.cpp @@ -1,81 +1,93 @@ #include "Animation.h" +Animation::Animation(float _d, void* _t, std::function _cb, bool _l) +:duration(_d), target(_t), callback(_cb), loop(_l), elapsed(0.0f) {} + // linear interpolation constructor template -Animation::Animation(float _d, T _ev, T* _t, std::function _cb, bool _l) -:duration(_d), endvalue(_ev), target(_t), callback(_cb), loop(_l), -index(-1), startvalue(*_t), elapsed(0.0f) {} +LerpAnimation::LerpAnimation(float _d, T _ev, T* _t, std::function _cb, bool _l) +:Animation(_d, _t, _cb, _l), //duration(_d), target(_t), callback(_cb), loop(_l),elapsed(0.0f), +startvalue(*_t), endvalue(_ev) {} // discrete values constructor template -Animation::Animation(float _d, std::vector _v, T* _t, std::function _cb, bool _l) -:duration(_d), target(_t), callback(_cb), values(_v), loop(_l), -index(0), startvalue(*_t), elapsed(0.0f), nonelapsed(0.0f) { +DiscreteAnimation::DiscreteAnimation(float _d, std::vector _v, T* _t, std::function _cb, bool _l) +:Animation(_d, _t, _cb, _l), //duration(_d), target(_t), callback(_cb), loop(_l), elapsed(0.0f), +index(0), nonelapsed(0.0f), values(_v) { timestep = _v.size() / _d; } -template -Animation::~Animation() { +/* // don't call virtual functions (like cancel()) from base class destructor + * // child classes destructors' are called first anyway +Animation::~Animation() { // deconstructor sets target to desired end state (no partial values) cancel(); } +*/ template<> -void Animation::lerp() { - *target = endvalue.substr(0, endvalue.length() * (elapsed / duration)); +void LerpAnimation::lerp() { + *(std::string*)target = endvalue.substr(0, endvalue.length() * (elapsed / duration)); } template<> -void Animation::lerp() { +void LerpAnimation::lerp() { int delta = endvalue - startvalue; - *target = startvalue + (elapsed / duration * delta); + *(int*)target = startvalue + (elapsed / duration * delta); } template<> -void Animation::lerp() { +void LerpAnimation::lerp() { int delta = endvalue - startvalue; - *target = startvalue + (elapsed / duration * delta); + *(float*)target = startvalue + (elapsed / duration * delta); } template<> -void Animation::lerp() { +void LerpAnimation::lerp() { int delta_x = endvalue.x - startvalue.x; int delta_y = endvalue.y - startvalue.y; - target->x = startvalue.x + (elapsed / duration * delta_x); - target->y = startvalue.y + (elapsed / duration * delta_y); + ((sf::Vector2f*)target)->x = startvalue.x + (elapsed / duration * delta_x); + ((sf::Vector2f*)target)->y = startvalue.y + (elapsed / duration * delta_y); } template<> -void Animation::lerp() { +void LerpAnimation::lerp() { int delta_x = endvalue.x - startvalue.y; int delta_y = endvalue.y - startvalue.y; - target->x = startvalue.x + (elapsed / duration * delta_x); - target->y = startvalue.y + (elapsed / duration * delta_y); + ((sf::Vector2i*)target)->x = startvalue.x + (elapsed / duration * delta_x); + ((sf::Vector2i*)target)->y = startvalue.y + (elapsed / duration * delta_y); } template -void Animation::step(float delta) +void LerpAnimation::step(float delta) { + elapsed += delta; + lerp(); + if (isDone()) cancel(); //use the exact value, not my math +} + +template +void DiscreteAnimation::step(float delta) { - if (index == -1) { - // lerp function - elapsed += delta; - lerp(); - } - else { - nonelapsed += delta; - if (nonelapsed < timestep) return; - if (index == values.size() - 1) return; - elapsed += nonelapsed; // or should it be += timestep? - nonelapsed = 0; // or should it -= timestep? - index++; - *target = values[index]; - } + nonelapsed += delta; + if (nonelapsed < timestep) return; + if (index == values.size() - 1) return; + elapsed += nonelapsed; // or should it be += timestep? + nonelapsed = 0; // or should it -= timestep? + index++; + *target = values[index]; + if (isDone()) cancel(); //use the exact value, not my math } template -void Animation::cancel() { - if (index == -1) - *target = endvalue; - else - *target = values[values.size() - 1]; +void LerpAnimation::cancel() { + *target = endvalue; +} + +template +void DiscreteAnimation::cancel() { + *target = values[values.size() - 1]; +} + +bool Animation::isDone() { + return elapsed + Animation::EPSILON >= duration; } diff --git a/src/Animation.h b/src/Animation.h index 9509876..39aac87 100644 --- a/src/Animation.h +++ b/src/Animation.h @@ -1,23 +1,44 @@ #pragma once #include "Common.h" -template class Animation { +protected: static constexpr float EPSILON = 0.05; - T startvalue, endvalue; - int index; - float duration, elapsed, nonelapsed, timestep; - T* target; - std::vector values; + float duration, elapsed; + void* target; std::function callback; bool loop; public: - Animation(float, T, T*, std::function, bool); // lerp - Animation(float, std::vector, T*, std::function, bool); // discrete - ~Animation(); - void lerp(); - void step(float); - void cancel(); + //Animation(float, T, T*, std::function, bool); // lerp + //Animation(float, std::vector, T*, std::function, bool); // discrete + Animation(float, void*, std::function, bool); + virtual void step(float) = 0; + virtual void cancel() = 0; bool isDone(); }; + +template +class LerpAnimation: public Animation +{ + T startvalue, endvalue; + void lerp(); +public: + ~LerpAnimation() { cancel(); } + LerpAnimation(float, T, T*, std::function, bool); + void step(float) override final; + void cancel() override final; +}; + +template +class DiscreteAnimation: public Animation +{ + std::vector values; + float nonelapsed, timestep; + int index; +public: + DiscreteAnimation(float, std::vector, T*, std::function, bool); + ~DiscreteAnimation() { cancel(); } + void step(float) override final; + void cancel() override final; +}; diff --git a/src/GameEngine.cpp b/src/GameEngine.cpp index ea28547..74d4b55 100644 --- a/src/GameEngine.cpp +++ b/src/GameEngine.cpp @@ -27,6 +27,8 @@ GameEngine::GameEngine() scenes["py"] = new PythonScene(this, "TestScene"); IndexSprite::game = this; + + clock.restart(); } Scene* GameEngine::currentScene() { return scenes[scene]; } @@ -38,6 +40,7 @@ sf::RenderWindow & GameEngine::getWindow() { return window; } void GameEngine::run() { + clock.restart(); while (running) { currentScene()->update(); @@ -47,6 +50,7 @@ void GameEngine::run() } currentScene()->sRender(); currentFrame++; + frameTime = clock.restart().asSeconds(); } } diff --git a/src/GameEngine.h b/src/GameEngine.h index 9281a7e..35e479a 100644 --- a/src/GameEngine.h +++ b/src/GameEngine.h @@ -17,6 +17,8 @@ class GameEngine bool paused = false; int currentFrame = 0; sf::View visible; + sf::Clock clock; + float frameTime; public: GameEngine(); @@ -29,6 +31,7 @@ public: void run(); void sUserInput(); int getFrame() { return currentFrame; } + float getFrameTime() { return frameTime; } sf::View getView() { return visible; } // global textures for scripts to access diff --git a/src/McRFPy_API.cpp b/src/McRFPy_API.cpp index e2d9aef..49d62df 100644 --- a/src/McRFPy_API.cpp +++ b/src/McRFPy_API.cpp @@ -7,6 +7,8 @@ std::map McRFPy_API::menus; std::map McRFPy_API::grids; std::map McRFPy_API::callbacks; +std::list McRFPy_API::animations; + EntityManager McRFPy_API::entities; static PyMethodDef mcrfpyMethods[] = { @@ -46,6 +48,18 @@ static PyMethodDef mcrfpyMethods[] = { {"modGrid", McRFPy_API::_modGrid, METH_VARARGS, "call with a Grid object to update all fields"}, + {"createAnimation", McRFPy_API::_createAnimation, METH_VARARGS, + "Create a new animation:\n" + "createAnimation(duration:float, parent:string, target_type:string, target_id:string or int, field:string, callback:function, loop:bool, frames:list)\n" + "duration: total animation time in seconds\n" + "parent: the name of a UI menu or grid\n" + "target_type: 'caption', 'button', 'sprite', or 'entity'\n" + "target_id: integer index of the caption or button, or string ID of entity\n" + "field: what to animate. 'position', 'size', 'bgcolor', 'textcolor' or 'sprite'\n" + "callback: called when the animation completes\n" + "loop: if True, animation repeats; if False, animation is deleted\n" + "frames: if animating a sprite, list the frames. For other data types, the value will change in discrete steps at a rate of duration/len(frames).\n"}, + {NULL, NULL, 0, NULL} }; diff --git a/src/McRFPy_API.h b/src/McRFPy_API.h index 7372bed..977855f 100644 --- a/src/McRFPy_API.h +++ b/src/McRFPy_API.h @@ -44,6 +44,7 @@ public: static std::map menus; static EntityManager entities; // this is also kinda good, entities not on the current grid can still act (like monsters following you through doors??) static std::map grids; + static std::list animations; static std::map callbacks; @@ -68,6 +69,8 @@ public: static PyObject* _createGrid(PyObject*, PyObject*); static PyObject* _listGrids(PyObject*, PyObject*); static PyObject* _modGrid(PyObject*, PyObject*); + + static PyObject* _createAnimation(PyObject*, PyObject*); static PyObject* _registerPyAction(PyObject*, PyObject*); diff --git a/src/PythonScene.cpp b/src/PythonScene.cpp index 1c0c016..c998db5 100644 --- a/src/PythonScene.cpp +++ b/src/PythonScene.cpp @@ -1,6 +1,7 @@ #include "PythonScene.h" #include "ActionCode.h" #include "McRFPy_API.h" +#include "Animation.h" PythonScene::PythonScene(GameEngine* g, std::string pymodule) : Scene(g) { @@ -42,6 +43,58 @@ PythonScene::PythonScene(GameEngine* g, std::string pymodule) McRFPy_API::executePyString(pymodule + ".start()"); } +void PythonScene::animate() { + auto frametime = game->getFrameTime(); + auto it = McRFPy_API::animations.begin(); + while (it != animations.end()) { + (*it)->step(frametime); + if ((*it)->isDone()) { + auto prev = it; + it++; + animations.erase(prev); + } else it++; + } + /* // workin on it + for (auto p : animations) { + if (p.first == "int") { + ((Animation)p.second).step(frametime); + } else if (p.first == "string") { + ((Animation)p.second).step(frametime); + } else if (p.first == "float") { + ((Animation)p.second).step(frametime); + } else if (p.first == "vector2f") { + ((Animation)p.second).step(frametime); + } else if (p.first == "vector2i") { + ((Animation)p.second).step(frametime); + } else if (p.first == "color") { + ((Animation)p.second).step(frametime); // TODO + } else { + std::cout << "Animation has label " << p.first << "; no type found" << std::endl; + } + } + auto it = animations.begin(); + while (it != animations.end()) { + bool done = false; + if (p.first == "int") { + ((Animation)p.second).step(frametime); + } else if (p.first == "string") { + if ((Animation)p.second).isDone() + delete (Animation)p.second + } else if (p.first == "float") { + ((Animation)p.second).step(frametime); + } else if (p.first == "vector2f") { + ((Animation)p.second).step(frametime); + } else if (p.first == "vector2i") { + ((Animation)p.second).step(frametime); + } else if (p.first == "color") { + ((Animation)p.second).step(frametime); // TODO + if ((*it).second.isDone()) { + animations.erase(it++); + } else { it++; } + } + */ +} + void PythonScene::update() { McRFPy_API::entities.update(); @@ -57,6 +110,7 @@ void PythonScene::update() { mouseprev = mousepos; } + animate(); } void PythonScene::doLClick(sf::Vector2i mousepos) { diff --git a/src/PythonScene.h b/src/PythonScene.h index 7bf236d..dce0c08 100644 --- a/src/PythonScene.h +++ b/src/PythonScene.h @@ -4,6 +4,8 @@ #include "Scene.h" #include "GameEngine.h" #include "Grid.h" +//#include "Animation.h" +//#include class PythonScene: public Scene { @@ -13,6 +15,8 @@ class PythonScene: public Scene void doLClick(sf::Vector2i); void doRClick(sf::Vector2i); void doZoom(sf::Vector2i, int); + //std::list animations; + void animate(); public: PythonScene(GameEngine*, std::string); void update() override final;