I'm currently working on a system within which objects animate along a bezier curve. At various, user defined, points on the curve, there can be an event which the animating object has to react to. This could be a change of speed, change on animation routine, change of facing direction etc...
So far I have the following AnimationEvent class.
class AnimationEvent
{
private:
unsigned int m_eventID;
float m_InteroplationPoint;
float m_SpeedInMPS;
public:
AnimationEvent(unsigned int id, float interpolationPoint);
~AnimationEvent(void);
const unsigned int getEventID();
void setInterpolationPoint(float interpPoint);
const float getInterpPoint();
const float getSpeed();
};
Each event is assigned as an object along a curve and when its interpolation point is reached, the curve class calls the getSpeed()
function in order to change the current animation speed.
What I really want to do is to implement a system such as with the Decorator design pattern, where the event can be decorated with multiple objects and when reached, all decorations will be applied. The problem I am having is that I can't pass a pointer to the parent object to the child as this will then create a circular dependency, but I also have the problem where I don't know (at compilation) what type of actions the event will be decorated with.
Any ideas about how to overcome this would be greatly appreciated! If I've forgotten any info which might help, I'll try and edit it in.
You can apply a decorator-like pattern to this problem, if you are willing to keep track of all the events that have occurred to an object previously. All you need to do is store each event in a vector of some sort. Then you can do any sort of manipulations that you'd like.
#include <iostream>
#include <vector>
using namespace std;
class Animatee;
class AnimateEvent
{
public:
virtual Animatee & perform(Animatee & on) = 0;
};
class Animatee
{
public:
Animatee() : value(0){};
void addEvent(AnimateEvent * event)
{
if(event != NULL)
{
events.push_back(event);
}
}
Animatee & act()
{
for(vector<AnimateEvent * >::iterator it = events.begin(); it != events.end();++it)
{
AnimateEvent * event = *it;
event->perform(*this);
}
return *this;
}
double value; //don't do this, but something more encap'ed
private:
vector<AnimateEvent * > events;
};
class Add : public AnimateEvent
{
virtual Animatee & perform(Animatee & on)
{
on.value = on.value + 1;
return on;
}
};
class Subtract : public AnimateEvent
{
virtual Animatee & perform(Animatee & on)
{
on.value = on.value - 1;
return on;
}
};
class Multiply : public AnimateEvent
{
virtual Animatee & perform(Animatee & on)
{
on.value = on.value * 2;
return on;
}
};
class Div : public AnimateEvent
{
virtual Animatee & perform(Animatee & on)
{
on.value = on.value / 2.0;
return on;
}
};
int main() {
Animatee matee;
matee.addEvent(new Add());
//cout << "Before: " << matee.value << " After: " << matee.act().value << endl; <~~~ Undefined btw, acting on the object twice in one "statement"
double before = matee.value;
cout << "Before: " << before << " After: " << matee.act().value << endl;
matee.addEvent(new Subtract());
before = matee.value;
cout << "Before: " << before << " After: " << matee.act().value << endl;
matee.addEvent(new Subtract());
before = matee.value;
cout << "Before: " << before << " After: " << matee.act().value << endl;
matee.addEvent(new Add());
matee.addEvent(new Add());
matee.addEvent(new Add());
before = matee.value;
cout << "Before: " << before << " After: " << matee.act().value << endl;
matee.addEvent(new Multiply());
before = matee.value;
cout << "Before: " << before << " After: " << matee.act().value << endl;
before = matee.value;
cout << "Before: " << before << " After: " << matee.act().value << endl;
return 0;
}
As described you only support once differentiable speed functions, which will look horrible, barring a lot of points.
There are two different issues. The first is a possible change to the internal animation parameters (which is something that should be opaque to the positioning engine maybe) and the second is the change in speed (which is not opaque).
I would solve the two orthogonally.
Focusing on speed, the next question is how you want the decorators to compose with each other. Do they add, multiply, replace, form upper or lower bounds on the output speed?
What information on local state are they allowed to access?
Assuming they are fed the position along the curve, and only the last speed decorator means anything, std::function<double(double)>
would do fine as a speed decorator type.
This is on the same order of efficiency as calling a virtual
function or two. And the injector or the decorator can capture whatever it wants for internal state...