Optimizing Bubbles

Once again we have been asked by our university to display a game (Yomi’s Bubble Adventure) at the annual gamex exhibition at Kista in Stockholm. This is due October the 30th so before that I decided to fix some bugs, minimize memory usage and improve stability. First up is a ninja-fix for the message system used by components which is using more memory then it should. The original system looked like this:

First we had an enum for all the different types of component messages:

enum EComponentType{
     COMPONENT_NONE = 0,
     COMPONENT_MESH_RENDER,
     COMPONENT_ANIMATION,
     COMPONENT_PLAYER_INPUT,
     COMPONENT_BUBBLE_CONTROL,
     COMPONENT_RIGIDBODY,
     ...
     COMPONENT_SIZE
};

And so on…
The component messenger, a class that works like a manager for observers, each game object has it’s unique instance of this object in order for components to communicate with each other on a mediator-like communication level. This class stores observers in a double std::vector with the size of all messages for instant access.

typedef std::vector > MessageList;

In the constructor for the component messenger class we did a resize: m_listeners.resize(COMPONENT_SIZE);
The reason for this was so we could register observers in the following way…

void ComponentMessenger::Register(int type, IComponentObserver* obj){
     m_listeners[type].push_back(obj);
}

…and notify observers like this:

void ComponentMessenger::Notify(int type, void* msg){
     if (!m_listeners[type].empty()){
          for (unsigned int i = 0; i < m_listeners[type].size(); i++){
               m_listeners[type][i]->Notify(type, msg);
          }
     }
}

The reason for designing the system like this was so we could have multiple components of the same type and send the message only to the component listening for that specific type of messages. Because all component messengers would keep an std::vector of all types of messages we would get instant access to the message type and the registered observers listening for the requested type.

We thought we could save cpu-cycles this way but ended up wasting memory. Component messengers keeps track of every type of messages increasing the size of every instance for every additional message we add to the enum EComponentType. And besides, most game objects didn’t have more than one instance listening to the same message.

So in the revamped version I simply removed the double std::vector and added a single std::vector storing all registered observers. Messages will be sent to all observers but only specific observers will handle the message. This requires a few more extra cpu-cycles but saves memory. Here are some examples from the component messenger class:

void ComponentMessenger::Register(IComponentObserver* obj){m_listeners.push_back(obj);}
void ComponentMessenger::Unregister(IComponentObserver* ob){
     auto it=std::find(m_listeners.begin(),m_listeners.end(),ob);
     if(it!=m_listeners.end()){m_listeners.erase(it);}
}

void ComponentMessenger::Notify(int type, void* msg){
     int i,iC=(int)m_listeners.size();
     for(i=0;iNotify(type,msg);
     }
}

This creates the same results as before but requires less memory.

Performing this ninja-fix was easy and did not break anything in the game code. It was a slight hassle to through all components and change the way they register to the component messenger, since they now only need to register themselves rather than themselves and a specific message type. That also saved a couple of cpu cycles for the initialization process.

More tweaks for Yomi coming soon!