Fixed animations, jank-noted some issues & workarounds with the Animation python API
This commit is contained in:
		
							parent
							
								
									c9b97b9b35
								
							
						
					
					
						commit
						620def19f1
					
				|  | @ -14,7 +14,7 @@ template<typename T> | |||
| DiscreteAnimation<T>::DiscreteAnimation(float _d, std::vector<T> _v, std::function<void()> _cb, std::function<void(T)> _w, bool _l) | ||||
| :Animation(_d, _cb, _l), //duration(_d), target(_t), callback(_cb), loop(_l), elapsed(0.0f),
 | ||||
| index(0), nonelapsed(0.0f), values(_v), write(_w) { | ||||
|     timestep = _v.size() / _d; | ||||
|     timestep = _d / _v.size(); | ||||
| } | ||||
| 
 | ||||
| /* // don't call virtual functions (like cancel()) from base class destructor
 | ||||
|  | @ -47,11 +47,11 @@ void LerpAnimation<float>::lerp() { | |||
| 
 | ||||
| template<> | ||||
| void LerpAnimation<sf::Vector2f>::lerp() { | ||||
|     std::cout << "sf::Vector2f implementation of lerp." << std::endl; | ||||
|     //std::cout << "sf::Vector2f implementation of lerp." << std::endl;
 | ||||
|     int delta_x = endvalue.x - startvalue.x; | ||||
|     int delta_y = endvalue.y - startvalue.y; | ||||
|     std::cout << "Start: " << startvalue.x << ", " << startvalue.y << "; End: " << endvalue.x << ", " << endvalue.y << std::endl; | ||||
|     std::cout << "Delta: " << delta_x << ", " << delta_y << std::endl; | ||||
|     //std::cout << "Start: " << startvalue.x << ", " << startvalue.y << "; End: " << endvalue.x << ", " << endvalue.y << std::endl;
 | ||||
|     //std::cout << "Delta: " << delta_x << ", " << delta_y << std::endl;
 | ||||
|     //((sf::Vector2f*)target)->x = startvalue.x + (elapsed / duration * delta_x);
 | ||||
|     //((sf::Vector2f*)target)->y = startvalue.y + (elapsed / duration * delta_y);
 | ||||
|     write(sf::Vector2f(startvalue.x + (elapsed / duration * delta_x),  | ||||
|  | @ -70,24 +70,28 @@ void LerpAnimation<sf::Vector2i>::lerp() { | |||
| 
 | ||||
| template<typename T> | ||||
| void LerpAnimation<T>::step(float delta) { | ||||
| 	if (complete) return; | ||||
|     elapsed += delta; | ||||
|     std::cout << "LerpAnimation step function. Elapsed: " << elapsed <<std::endl; | ||||
|     //std::cout << "LerpAnimation step function. Elapsed: " << elapsed <<std::endl;
 | ||||
|     lerp(); | ||||
|     if (isDone()) cancel(); //use the exact value, not my math
 | ||||
|     if (isDone()) { callback(); complete = true; cancel(); }; //use the exact value, not my math
 | ||||
| } | ||||
| 
 | ||||
| template<typename T> | ||||
| void DiscreteAnimation<T>::step(float delta) | ||||
| { | ||||
| 	if (complete) return; | ||||
|     nonelapsed += delta; | ||||
|     //std::cout << "Nonelapsed: " << nonelapsed << " elapsed (pre-add): " << elapsed << " timestep: " << timestep << " duration: " << duration << " index: " << index << std::endl;
 | ||||
|     if (nonelapsed < timestep) return; | ||||
|     if (index == values.size() - 1) return; | ||||
|     //std::cout << "values size: " << values.size() << " isDone(): " << isDone() << std::endl;
 | ||||
|     if (elapsed > duration && !complete) {callback(); complete = true; return; } | ||||
|     elapsed += nonelapsed; // or should it be += timestep?
 | ||||
|     if (index == values.size() - 1) return; | ||||
|     nonelapsed = 0; // or should it -= timestep?
 | ||||
|     index++; | ||||
|     //*(T*)target = values[index];
 | ||||
|     write(values[index]); | ||||
|     if (isDone()) cancel(); //use the exact value, not my math
 | ||||
| } | ||||
| 
 | ||||
| template<typename T> | ||||
|  | @ -111,6 +115,7 @@ namespace animation_template_implementations { | |||
|     //LerpAnimation<sf::Vector2f> implement_vector2f;
 | ||||
| 
 | ||||
|     auto implement_v2f_const = LerpAnimation<sf::Vector2<float>>(4.0, sf::Vector2<float>(), sf::Vector2f(1,1), [](){}, [](sf::Vector2f v){}, false); | ||||
|     auto implement_disc_i = DiscreteAnimation<int>(3.0, std::vector<int>{0},[](){},[](int){},false); | ||||
|     //LerpAnimation<sf::Vector2i> implement_vector2i;
 | ||||
|     //LerpAnimation<int> implment_int;
 | ||||
|     //LerpAnimation<std::string> implment_string;
 | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ protected: | |||
|     float duration, elapsed; | ||||
|     std::function<void()> callback; | ||||
|     bool loop; | ||||
| 	bool complete=false; | ||||
| public: | ||||
|     //Animation(float, T, T*, std::function<void()>, bool); // lerp
 | ||||
|     //Animation(float, std::vector<T>, T*, std::function<void()>, bool); // discrete 
 | ||||
|  |  | |||
|  | @ -630,7 +630,7 @@ PyObject* McRFPy_API::_modGrid(PyObject* self, PyObject* args) { | |||
|     return Py_None; | ||||
| } | ||||
| 
 | ||||
| PyObject* McRFPy_API::_createAnimation(PyObject *self, PyObject *args) { | ||||
| PyObject* _test_createAnimation(PyObject *self, PyObject *args) { | ||||
|     //LerpAnimation<T>::LerpAnimation(float _d, T _ev, T* _t, std::function<void()> _cb, std::function<void(T)> _w, bool _l)
 | ||||
|     std::string menu_key = "demobox1"; | ||||
|     McRFPy_API::animations.push_back( | ||||
|  | @ -651,3 +651,122 @@ PyObject* McRFPy_API::_createAnimation(PyObject *self, PyObject *args) { | |||
|     Py_INCREF(Py_None); | ||||
|     return Py_None; | ||||
| } | ||||
| 
 | ||||
| #define CEQ(A, B) (std::string(A).compare(B) == 0) | ||||
| 
 | ||||
| PyObject* McRFPy_API::_createAnimation(PyObject *self, PyObject *args) { | ||||
| 	std::cout << "Creating animation called..." << std::endl; | ||||
|     float duration; | ||||
| 	const char* parent; | ||||
| 	const char* target_type; | ||||
| 	PyObject* target_id_obj; | ||||
| 	const char* field; | ||||
| 	PyObject* callback; | ||||
| 	PyObject* loop_obj; | ||||
| 	PyObject* values_obj; | ||||
| 	PyObject* evdata; // for decoding values_obj
 | ||||
| 	//std::cout << PyUnicode_AsUTF8(PyObject_Repr(args)) << std::endl;
 | ||||
|     if (!PyArg_ParseTuple(args, "fssOsOOO", &duration, &parent, &target_type, &target_id_obj, &field, &callback, &loop_obj, &values_obj)) { return NULL; } | ||||
|     bool loop = PyObject_IsTrue(loop_obj); | ||||
|     int target_id = PyLong_AsLong(target_id_obj); | ||||
|     Py_INCREF(callback); | ||||
|     std::cout << "Animation fields received:" << | ||||
| 		"\nduration: " << duration << | ||||
| 		"\nparent: " << parent << | ||||
| 		"\ntarget type: " << target_type << | ||||
| 		"\ntarget id: " << PyUnicode_AsUTF8(PyObject_Repr(target_id_obj)) << | ||||
| 		"\nfield: " << field << | ||||
| 		"\ncallback: " << PyUnicode_AsUTF8(PyObject_Repr(callback)) << | ||||
| 		"\nloop: " << loop << | ||||
| 		"\nvalues: " << PyUnicode_AsUTF8(PyObject_Repr(values_obj)) << std::endl; | ||||
| 	/* Jank alert:
 | ||||
| 	 * The following block is meant to raise an exception when index is missing from object animations that require one, | ||||
| 	 * but accept the target_id_obj error (and accept None as an index) for menus/grids. | ||||
| 	 * Instead, I get a "latent" exception, not properly raised, when the index is None. | ||||
| 	 * That not-really-raised exception causes other scripts to silently fail to execute | ||||
| 	 * until I go into the REPL and run any code, and get a bizarre, botched traceback. | ||||
| 	 * So, Grid/Menu can just take an index of 0 in the scripts until this is dejankified | ||||
| 	 */ | ||||
| 	if (!CEQ(target_type, "menu") && !CEQ(target_type, "grid") && target_id == -1) { | ||||
| 		PyErr_SetObject(PyExc_TypeError, target_id_obj); | ||||
| 		PyErr_SetString(PyExc_TypeError, "target_id (integer, index value) is required for targets other than 'menu' or 'grid'"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	// at this point, `values` needs to be decoded based on the `field` provided
 | ||||
| 	 | ||||
| //            3.0, # duration, seconds
 | ||||
| //            "demobox1", # parent: a UIMenu or Grid key
 | ||||
| //            "menu", # target type: 'menu', 'grid', 'caption', 'button', 'sprite', or 'entity'
 | ||||
| //            None, # target id: integer index for menu or grid objs; None for grid/menu
 | ||||
| //            "position", # field: 'position', 'size', 'bgcolor', 'textcolor', or 'sprite'
 | ||||
| //            lambda: self.animation_done("demobox1"), #callback: callable once animation is complete
 | ||||
| //            False, #loop: repeat indefinitely
 | ||||
| //            [100, 100] # values: iterable of frames for 'sprite', lerp target for others
 | ||||
| 
 | ||||
| //LerpAnimation<T>::LerpAnimation(float _d, T _ev, T _sv, std::function<void()> _cb, std::function<void(T)> _w, bool _l)
 | ||||
| 	if (CEQ(target_type, "menu")) { | ||||
| 		auto obj = menus[std::string(parent)]; | ||||
| 		if (CEQ(field, "position")) { | ||||
| 			if (PyList_Check(values_obj)) evdata = PyList_AsTuple(values_obj); else evdata = values_obj; | ||||
| 			auto end_value = sf::Vector2f(PyFloat_AsDouble(PyTuple_GetItem(evdata, 0)), | ||||
| 										  PyFloat_AsDouble(PyTuple_GetItem(evdata, 1))); | ||||
| 			McRFPy_API::animations.push_back(new LerpAnimation<sf::Vector2f>( | ||||
| 				duration, end_value, | ||||
| 				obj->box.getPosition(), | ||||
| 				[=](){PyObject_Call(callback, PyTuple_New(0), NULL);}, | ||||
| 				[=](sf::Vector2f v){obj->box.setPosition(v);}, | ||||
| 				loop) | ||||
| 			);	 | ||||
| 		} | ||||
| 		else if (CEQ(field, "size")) { | ||||
| 			if (PyList_Check(values_obj)) evdata = PyList_AsTuple(values_obj); else evdata = values_obj; | ||||
| 			auto end_value = sf::Vector2f(PyFloat_AsDouble(PyTuple_GetItem(evdata, 0)), | ||||
| 										  PyFloat_AsDouble(PyTuple_GetItem(evdata, 1))); | ||||
| 			McRFPy_API::animations.push_back(new LerpAnimation<sf::Vector2f>( | ||||
| 				duration, end_value, | ||||
| 				obj->box.getSize(), | ||||
| 				[=](){PyObject_Call(callback, PyTuple_New(0), NULL);}, | ||||
| 				[=](sf::Vector2f v){obj->box.setSize(v);}, | ||||
| 				loop) | ||||
| 			); | ||||
| 		} | ||||
| 		// else if (CEQ(field, "bgcolor")) { )
 | ||||
| 	} | ||||
| 	else if (CEQ(target_type, "sprite")) { | ||||
| 		auto obj = menus[std::string(parent)]->sprites[target_id]; | ||||
| 		if (CEQ(field, "position")) { | ||||
| 			PyObject* evdata; | ||||
| 			if (PyList_Check(values_obj)) evdata = PyList_AsTuple(values_obj); else evdata = values_obj; | ||||
| 			auto end_value = sf::Vector2f(PyFloat_AsDouble(PyTuple_GetItem(evdata, 0)), | ||||
| 										  PyFloat_AsDouble(PyTuple_GetItem(evdata, 1))); | ||||
| 			McRFPy_API::animations.push_back(new LerpAnimation<sf::Vector2f>(duration, end_value, | ||||
| 				sf::Vector2f(obj.x, obj.y), | ||||
| 				[=](){PyObject_Call(callback, PyTuple_New(0), NULL);}, | ||||
| 				[&](sf::Vector2f v){obj.x = v.x; obj.y = v.y;}, | ||||
| 				loop) | ||||
| 			);			 | ||||
| 		} | ||||
| 		else if (CEQ(field, "sprite")) { | ||||
| 			auto obj = menus[std::string(parent)]; | ||||
| 			PyObject* evdata; | ||||
| 			if (PyList_Check(values_obj)) evdata = PyList_AsTuple(values_obj); else evdata = values_obj; | ||||
| 			std::vector<int> frames; | ||||
| 			for (int i = 0; i < PyTuple_Size(evdata); i++) { | ||||
| 				frames.push_back(PyLong_AsLong(PyTuple_GetItem(evdata, i))); | ||||
| 			} | ||||
| //DiscreteAnimation(float _d, std::vector<T> _v, std::function<void()> _cb, std::function<void(T)> _w, bool _l)
 | ||||
| 			McRFPy_API::animations.push_back(new DiscreteAnimation<int>( | ||||
| 				duration, | ||||
| 				frames, | ||||
| 				[=](){PyObject_Call(callback, PyTuple_New(0), NULL);}, | ||||
| 				[=](int s){obj->sprites[target_id].sprite_index = s;}, | ||||
| 				loop) | ||||
| 			); | ||||
| 			std::cout << "Frame animation constructed, there are now " <<McRFPy_API::animations.size() << std::endl; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 
 | ||||
|     Py_INCREF(Py_None); | ||||
|     return Py_None;	 | ||||
| } | ||||
|  |  | |||
|  | @ -48,11 +48,11 @@ void PythonScene::animate() { | |||
|     auto frametime = game->getFrameTime(); | ||||
|     auto it = McRFPy_API::animations.begin(); | ||||
|     while (it != McRFPy_API::animations.end()) { | ||||
|         std::cout << "Iterating" << std::endl; | ||||
|         //std::cout << "Iterating" << std::endl;
 | ||||
|         (*it)->step(frametime); | ||||
|         std::cout << "Step complete" << std::endl; | ||||
|         //std::cout << "Step complete" << std::endl;
 | ||||
|         if ((*it)->isDone()) { | ||||
|             std::cout << "Cleaning up" << std::endl; | ||||
|             std::cout << "Cleaning up Animation" << std::endl; | ||||
|             auto prev = it; | ||||
|             it++; | ||||
|             McRFPy_API::animations.erase(prev); | ||||
|  |  | |||
|  | @ -19,10 +19,12 @@ class TestScene: | |||
|         mcrfpy.createMenu(ui_name, 20, 520, 500, 200) | ||||
|         mcrfpy.createCaption(ui_name, "Hello There", 18, BLACK) | ||||
|         mcrfpy.createCaption(ui_name, "", 18, BLACK) | ||||
|         mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKBLUE, (0, 0, 0), "clicky", "testaction") | ||||
|         #mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKBLUE, (0, 0, 0), "clicky", "testaction") | ||||
|         mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKRED, (0, 0, 0), "REPL", "startrepl") | ||||
|         mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKGREEN, (0, 0, 0), "map gen", "gridgen") | ||||
|         mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKBLUE, (192, 0, 0), "anim", "animtest") | ||||
|         mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKGREEN, (0, 0, 0), "mapL", "gridgen2") | ||||
|         mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKBLUE, (192, 0, 0), "a_menu", "animtest") | ||||
|         mcrfpy.createButton(ui_name, 250, 20, 100, 50, DARKRED, GREEN, "a_spr", "animtest2") | ||||
|         mcrfpy.createSprite(ui_name, 0, randint(0, 3), 300, 20, 5.0) | ||||
|         self.menus = mcrfpy.listMenus() | ||||
|         self.menus[0].visible = True | ||||
|  | @ -30,9 +32,11 @@ class TestScene: | |||
| 
 | ||||
|         # Button behavior | ||||
|         self.clicks = 0 | ||||
|         mcrfpy.registerPyAction("testaction", self.click) | ||||
|         #mcrfpy.registerPyAction("testaction", self.click) | ||||
|         mcrfpy.registerPyAction("gridgen", self.gridgen) | ||||
|         mcrfpy.registerPyAction("animtest", lambda: mcrfpy.createAnimation()) | ||||
|         mcrfpy.registerPyAction("gridgen2", lambda: self.gridgen()) | ||||
|         mcrfpy.registerPyAction("animtest", lambda: self.createAnimation()) | ||||
|         mcrfpy.registerPyAction("animtest2", lambda: self.createAnimation2()) | ||||
| 
 | ||||
|         # create grid (title, gx, gy, gs, x, y, w, h) | ||||
|         mcrfpy.createGrid(grid_name, 20, 20, 16, 20, 20, 800, 500) | ||||
|  | @ -51,6 +55,43 @@ class TestScene: | |||
|         print("[Python] Modifying:") | ||||
|         self.grids[0].visible = True | ||||
|         mcrfpy.modGrid(self.grids[0]) | ||||
|          | ||||
|     def animation_done(self, s): | ||||
|         print(f"The `{s}` animation completed.") | ||||
|         #self.menus = mcrfpy.listMenus() | ||||
|          | ||||
|     # if (!PyArg_ParseTuple(args, "fsssiOOO", &duration, &parent, &target_type, &target_id, &field, &callback, &loop_obj, &values_obj)) return NULL; | ||||
|     def createAnimation(self): | ||||
|         print(self.menus) | ||||
|         self.menus = mcrfpy.listMenus() | ||||
|         self.menus[0].w = 500 | ||||
|         self.menus[0].h = 200 | ||||
|         print(self.menus) | ||||
|         mcrfpy.modMenu(self.menus[0]) | ||||
|         print(self.menus) | ||||
|         mcrfpy.createAnimation( | ||||
|             3.0, # duration, seconds | ||||
|             "demobox1", # parent: a UIMenu or Grid key | ||||
|             "menu", # target type: 'menu', 'grid', 'caption', 'button', 'sprite', or 'entity' | ||||
|             0, # target id: integer index for menu or grid objs; None for grid/menu | ||||
|             "size", # field: 'position', 'size', 'bgcolor', 'textcolor', or 'sprite' | ||||
|             lambda: self.animation_done("demobox1"), #callback: callable once animation is complete | ||||
|             False, #loop: repeat indefinitely | ||||
|             [150, 100] # values: iterable of frames for 'sprite', lerp target for others | ||||
|         ) | ||||
|          | ||||
|     def createAnimation2(self): | ||||
|         mcrfpy.createAnimation( | ||||
|             5, | ||||
|             "demobox1", | ||||
|             "sprite", | ||||
|             0, | ||||
|             "sprite", | ||||
|             lambda: self.animation_done("sprite change"), | ||||
|             False, | ||||
|             [0, 1, 2, 1, 2, 0] | ||||
|         ) | ||||
|          | ||||
| scene = None | ||||
| def start(): | ||||
|     global scene | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue