Best Object Manager/Handling Practices

Post your questions, suggestions and experiences regarding game design, integration of external libraries here. For irrEdit, irrXML and irrKlang, see the
ambiera forums
Post Reply
kklouzal
Posts: 343
Joined: Sun Mar 28, 2010 8:14 pm
Location: USA - Arizona

Best Object Manager/Handling Practices

Post by kklouzal »

What are the theories out there on object handling/managers?

e.g. A class for NPC's; A class for Weapons; A class for Players and accessing single objects from one class in another?

My thought is to store all elements of a certain type in their own vector Vector *NPC; Vector *Weapons; Vector *Players
Have a seperate function for each which will iterate through each vector and do said function passing it the ISceneNode to compare to the whole vector and do said function on the specific object
e.g. Void HurtNPC(ISceneNode* theNPC, int Dmg) would iterate through the entire vector of NPC finding the specific NPC we want to hurt and then call its member function it->Hurt(Dmg);

Is there a better method to use to accomplish this? Or am I on the right track?
Dream Big Or Go Home.
Help Me Help You.
sudi
Posts: 1686
Joined: Fri Aug 26, 2005 8:38 pm

Re: Best Object Manager/Handling Practices

Post by sudi »

Well you could thing about something like this.

Code: Select all

 
/*
  Copyright (C) 2012 Daniel Sudmann
 
  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
  arising from the use of this software.
 
  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:
 
  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
 
  Daniel Sudmann suddani@googlemail.com
*/
 
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
struct ISceneNode
{
};
 
struct Message
{
    virtual int getMessageType() = 0;
};
 
template<class T>
struct Message_ : Message
{
    static T* convert(Message* msg)
    {
        return (T*)(msg);
    }
    int getMessageType()
    {
        return getType();
    }
 
    static int getType()
    {
        static int type_id = 0;
        return (int)&type_id;
    }
};
 
struct MessageDispatcher
{
    struct EventHandlerBase
    {
        virtual bool check(Message* message) = 0;
        virtual void call(Message* message) = 0;
    };
 
    template<class T, class MessageType>
    struct EventHandler : EventHandlerBase
    {
        typedef void (T::*Callback)(MessageType*);
        EventHandler(T* object, Callback call)
        {
            Object = object;
            MyCallback = call;
        }
        void call(Message* message)
        {
            (Object->*MyCallback)(Message_<MessageType>::convert(message));
        }
        bool check(Message* message)
        {
            return message->getMessageType() == Message_<MessageType>::getType();
        }
        T* Object;
        Callback MyCallback;
    };
 
    void sendMessage(Message* msg)
    {
        for (int i=0;i<handlers.size();i++)
        {
            if (handlers[i]->check(msg))
                handlers[i]->call(msg);
        }
    }
 
    template<class T, class MessageType>
    void bind(T* t, void (T::*func)(MessageType* msg))
    {
        handlers.push_back(new EventHandler<T, MessageType>(t, func));
    }
    std::vector<EventHandlerBase*> handlers;
};
 
struct DMGMessage : Message_<DMGMessage>
{
    DMGMessage(int d, ISceneNode* n)
    {
        dmg = d;
        node = n;
    }
    int dmg;
    ISceneNode* node;
};
 
struct WhateverMessage : Message_<WhateverMessage>
{
 
};
 
struct NPC
{
    NPC(ISceneNode* n)
    {
        node = n;
    }
    ISceneNode* node;
    void onDamage(DMGMessage* dmg)
    {
        if (dmg->node != node)
            return;
        printf("NPC received %i damage\n", dmg->dmg);
    }
};
 
struct Player
{
    Player(ISceneNode* n)
    {
        node = n;
    }
    ISceneNode* node;
    void takeDamage(DMGMessage* dmg)
    {
        if (dmg->node != node)
            return;
        printf("Player received %i damage\n", dmg->dmg);
    }
};
 
struct InvinciblePlayer : Player
{
    InvinciblePlayer(ISceneNode* n) : Player(n)
    {
 
    }
    void whatever(WhateverMessage* msg)
    {
        printf("InvinciblePlayer received whatever msg\n");
    }
};
 
int main(int argc, char* argv[])
{
    srand(time(0));
 
    MessageDispatcher dispatcher;
 
    ISceneNode npc_node;
    ISceneNode player_node;
    ISceneNode iplayer_node;
 
    NPC npc(&npc_node);
    Player player(&player_node);
    InvinciblePlayer iplayer(&iplayer_node);
 
    dispatcher.bind(&npc, &NPC::onDamage);
    dispatcher.bind(&player, &Player::takeDamage);
    dispatcher.bind(&iplayer, &InvinciblePlayer::whatever);
 
    printf("Send damage!!\n");
    DMGMessage dmg(5, rand()%2 == 0 ? &player_node : rand()%2 == 0 ? &npc_node : &iplayer_node); ///hitting one node randomly, when it chooses the iplayer_node no one is hit actually
    dispatcher.sendMessage(&dmg);
    printf("Send damage Done\n");
 
    printf("\n\n");
 
    printf("Send WhateverMessage!!\n");
    WhateverMessage wtf;
    dispatcher.sendMessage(&wtf);
    printf("Send WhateverMessage Done\n");
 
 
    return 0;
}
 
We're programmers. Programmers are, in their hearts, architects, and the first thing they want to do when they get to a site is to bulldoze the place flat and build something grand. We're not excited by renovation:tinkering,improving,planting flower beds.
sudi
Posts: 1686
Joined: Fri Aug 26, 2005 8:38 pm

Re: Best Object Manager/Handling Practices

Post by sudi »

Well maybe this should have gone with some kind of explanation.
When you do it like that you are basicly letting your Entities decide wether or not the dmg belongs to them. You could however make every of your entity a Dispatcher itself and create a bigger one which distributes to the correct one so you have some kind of sorting. So like one big Entity Disptacher which somehow decides wether or not a message is ment for some entity or not, maybe a little smarter than just going through a big list^^

Next bonus would be if every entity is a messageDispatcher you could just grap any entity in your game and send a message to it. Then the entity reacts or not which is quite nice.

PS: the implementation i gave you lacks a lot of things. like unregister in case a subscriber is removed before the dispatcher is or a subscriber doesn't want to subscribe anymore
We're programmers. Programmers are, in their hearts, architects, and the first thing they want to do when they get to a site is to bulldoze the place flat and build something grand. We're not excited by renovation:tinkering,improving,planting flower beds.
kklouzal
Posts: 343
Joined: Sun Mar 28, 2010 8:14 pm
Location: USA - Arizona

Re: Best Object Manager/Handling Practices

Post by kklouzal »

Thank you very much for the ideas and supplied code; It's going to take me a minute to go through it and figure out exactly what is going on then extrapolate it out and use it in my own project; I'll post back in a day or so and let you know how i'm doing! Thank you very much for your help :)
Dream Big Or Go Home.
Help Me Help You.
kklouzal
Posts: 343
Joined: Sun Mar 28, 2010 8:14 pm
Location: USA - Arizona

Re: Best Object Manager/Handling Practices

Post by kklouzal »

Thank you thank you thank you SO much; this is an amazing system! eliminates SO many performance hindering issues I usually create throughout my projects; I usually loop through a vector of all my objects to update them each frame; now I only need to send a message to the appropriate object I need updated when it needs updated, you will most definitely receive credit for this in my projects; thank you sir/madam!
Dream Big Or Go Home.
Help Me Help You.
Abraxas)
Posts: 227
Joined: Sun Oct 18, 2009 7:24 am

Re: Best Object Manager/Handling Practices

Post by Abraxas) »

Why do you need such a complicated structure to send data?

I usually make a base class called objectManager, for example, which contains an array of my object type, array <objectType*> object. In the base class it has void Damage( s32 id).

Whenever something wants to damage another thing it calls objectManager->damage(id).

For organizing IDs, each addition to the array has its own scenenode id and id to array number id.

Ezpz
sudi
Posts: 1686
Joined: Fri Aug 26, 2005 8:38 pm

Re: Best Object Manager/Handling Practices

Post by sudi »

Well my suggestion is that just more flexible.
My System allows the following:

Imagine an FPS, you through a grenade and its supposed to interact with all dynamic objects. The physics engine gets all simulated objects and applies some force. Then normaly you would have to go to your damage system and pick the correct objects and apply damage there. Now does basicly the same just with the twist you feel like you don't have to design this damage system but it just works.
We're programmers. Programmers are, in their hearts, architects, and the first thing they want to do when they get to a site is to bulldoze the place flat and build something grand. We're not excited by renovation:tinkering,improving,planting flower beds.
sudi
Posts: 1686
Joined: Fri Aug 26, 2005 8:38 pm

Re: Best Object Manager/Handling Practices

Post by sudi »

Now one could argue that designing all systems by hand differently and carefully might be the better option, bc no doubt my system is easily abused in ways it should not be used...but then again when it gets the job done it is a valid solution and the best thing on implementation serves them all^^
We're programmers. Programmers are, in their hearts, architects, and the first thing they want to do when they get to a site is to bulldoze the place flat and build something grand. We're not excited by renovation:tinkering,improving,planting flower beds.
Abraxas)
Posts: 227
Joined: Sun Oct 18, 2009 7:24 am

Re: Best Object Manager/Handling Practices

Post by Abraxas) »

Maybe it depends on how you make your base objects. I always define a class with members like hitpoints, position, and scene node. The scene node contains the id of a unique in game iteration, and the base class has one variable for scene node id, and array position. This way if I'm using scene node ray intersection I do things one way, and world position
methods iterate another way.

Anyhow it really depends on what kind of interaction you need. Also, if you want to code fully OOP or OO/procedural.

Seeing as I always code halfway between the two, my systems need to be flexible
kklouzal
Posts: 343
Joined: Sun Mar 28, 2010 8:14 pm
Location: USA - Arizona

Re: Best Object Manager/Handling Practices

Post by kklouzal »

@sudi Do you have any suggestions on how to add an 'unbind' function? I've tried a few ways without success.

basically I created a message that is sent to all object's it's bound to; If I remove one of these objects that have that specific message bound to it the message handler still tries to call the object which is no longer in memory.

I tried a simple check to see if(!Object) { return; } but that always returns true;

I tried to create an unbind function that did a .erase on the vector after finding the id but thats not working well at all; I'm not familiar with templates and how they work so i'm getting confused

I could just loop through a vector of all these objects i'm sending the message to but that would defeat the entire purpose of using the message handler

Just looking for your input on the matter :)
Dream Big Or Go Home.
Help Me Help You.
REDDemon
Developer
Posts: 1044
Joined: Tue Aug 31, 2010 8:06 pm
Location: Genova (Italy)

Re: Best Object Manager/Handling Practices

Post by REDDemon »

when binding an object a new istance is created. So only the Handlers vector has reference to that no other one can store its reference, that's mean that externally you can't search for it and delete. A possible solution is that the bind function calls a "reference" method on the binded object in order to give it the pointer to the new handler, so that you have a parameter for search and deleting it:

Code: Select all

 
 
struct EventHandlerBase
    {
        virtual bool check(Message* message) = 0;
        virtual void call(Message* message) = 0;
        virtual EventHandlerBase * getBase() { return this;}
    };
 
 
template<class T, class MessageType>
    void bind(T* t, void (T::*func)(MessageType* msg))
    {
     EventHandler<T, MessageType> handler = newEventHandler<T,MessageType>(t,func);
     t->reference(handler->getBase());     
     handlers.push_back(new EventHandler<T, MessageType>(t, func));
    }
template < class T >
    void unbind(T * item) //maybe item has EventDispatcher as friend and "dereference" is private?.
    {
     EventHandlerBase * handler = item->dereference();
     std::vector<EventHandlerBase*>::iterator  handler_it;
     handler_it = std::find(handlers.begin(), handlers.end(), handler);
     if(handler_it != handlers.end() )
     {
           delete (*handler_it); //delete handler istance
           handlers.erase(handler_it);
           return;
     }
     item->reference( (*handler_it)); //istance not in handlers vector. maybe signal error in some way?.
    }
 
T need to have both "reference" and "dereference". just inerith any event receiver like "Player" from a "ReceiverBase":

simple example with only 1 EventHandler (theorically should have internally a vector of handlers, but keep things simple this is just example)

Code: Select all

 
class ReceiverBase
{
   ReceiverBase():myBase(0){ } //ctor
   EventHandlerBase * myBase; //reference to event handler
   friend struct EventDispatcher;
 
   void reference(EventHandlerBase* base) { assert(myBase == 0); myBase = base;}
   EventHandlerBase * dereference() { EventHandlerBase* base = myBase; myBase =0; return base;}
};
 
this is just a fast draft. A better solution exist for sure. just 5 minutes. don't expect too much from this code.

If you use a more complex version and have a vector isntead of "myBase" then you have to search in Handlers every element of "myBase" and remove that from the vector
Junior Irrlicht Developer.
Real value in social networks is not about "increasing" number of followers, but about getting in touch with Amazing people.
- by Me
Post Reply