I'm trying to implement a component-based entity system but am a bit confused on how I should handle the messaging. There are two problems I'd like to resolve so I can test the system. Below is the code I have so far,
The Entity class:
class Entity{
public:
Entity(unsigned int id):
id_(id)
{};
void handleMessage(BaseMessage &message){
for(auto element: components_){
element.second->handleMessage(message);
}
}
template
void attachComponent(T *component){
//Consider making safer in case someone tries to attach same component type twice
components_[typeid(T).hash_code()] = component;
}
template
void detachComponent(void){
components_.erase(typeid(T).hash_code());
}
template
T* getComponent(void)const{
return *components_.find(typeid(T).hash_code());
}
unsigned int getInstanceID(void)const{
return id_;
}
private:
unsigned int id_;
std::map components_;
};
The Base Component and Message classes:
class BaseComponent{
public:
virtual void handleMessage(BaseMessage &message){};
};
class BaseMessage{
public:
virtual int getType(void) = 0;
};
1. Message Type Handling
My first question is how I should handle the different (derived from BaseMessage) message types.
I have thought of two ways for handling the message types of the derived message types. One is to generate a hash (i.e. using FNV) from a string that names the message type and use that hash to determine the message type. So the handleMessage(BaseMessage &message)
function, would first extract this hash from the message and then do a static_cast to the appropriate type.
The second method is to use a template as follows (similar to the attachComponent
methods of the entity class),
template
handleMessage(T& message){};
and make specializations for each message type the specific component is going to make.
Are there any drawbacks using the second method? What about performance-wise, why don't I see this kind of use more often?
2. Input Handling
My second question is what would be the optimal (in terms of latency and ease of use) way to handle input?
My thought was to create an InputHandlerComponent
which registers with the keyboard class to listen to specific key presses defined possibly in some file. For example
keyboard.register( player.getComponent() , 'W')
I wish there was a more concise guide to component based systems but I guess there are many different ways to do the same things. I have more questions but I think it'd be wiser to first try implementing what I can.
Answer
In a typical entity system you leave systems to have all the logic for your game and components to store data only, and your entities are just a simple identifier. Input handling would be much easier that way and not to mention the flexibility of your game.
There are plenty of ways to handle events, such as: the observer pattern, or signals/slots. You could do message handling with templates/virtual functions or function pointers, however it'd probably be easier to use boost::signals, even known it's not that great for performance. If you want performance*, I suggest you use templates and virtual functions or function pointers.
*I noticed that your code could really be optimised. There are alternatives to the typeid
operator, which are actually faster than using typeid
. Such as using templates/macros and simple integers to define a class's ID.
You can look at my Entity System if you need some inspiration (which is inspired from the Java framework Artemis). Although I haven't implemented a way to handle events (other than entity-related events), I left that up to the user, but after sussing out the entityx library, which I found on reddit. I figured I might be able to add an event system to my library. Please do note that my entity system isn't 100% complete on what features I want, but it works and has decent performance (but I could optimize, especially with the way I'm storing entities).
Here's what you could possibly do to handle events (inspired from entityx):
BaseReceiver/BaseListener
- A base class so you may store listeners/receivers.
Receiver/Listener
- This is a template class and requires you to override the virtual function
recieve(const T&)
, where T is event information. - Classes that want to be notified by events that send specific information when an event occurs must inherit from this class.
EventHandler
- Emits/fires events
- Has a list of Receivers/Listeners objects that will be notified by fired events
I've implement this design in C++, just now, without the use of boost::signals. You can see it here.
Pros
- Statically typed
- Virtual functions (faster than boost::signals, well it should be)
- Compile-time errors if you didn't emit notifications correctly
- C++98 compatible (I believe)
Cons
- Requires you to define functors (you can't handle events in a global function)
- No event queue; just register & fire away. (which may be not what you want)
- Not direct calls (shouldn't be that bad for events)
- No multiple event handling on the same class (this could be modified so it does allow multiple events, via multiple inheritance, but may take some time to implement)
Also, I have an example in my entity system, which does deal with rendering and input handling, it's quite simple but it presents many concepts to understand my library.
No comments:
Post a Comment