Sign in to follow this  

Objects that need to know... (part 2)

This topic is 4661 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm still having some trouble trying to think of a slicker system for object communication inside of, or with, the core game componants (the "engine"). Here is my basic problem: Certain blah game objects need to know of some key subsystems to get things done. For instance, any object that creates other objects needs to know where the "object processor" is in order to be able to register newly created objects with it. A door needs to be able to signal a game-area change somehow (moving to a new room). Ceratin objects need to be able to trigger state changes (like winning the game, starting the game, switching to a cinema scene, etc). In a nutshell: some of my boring sub-sub-level game objects need to know about some of my key sub-sub-level core engine objects. Direct communication would be exposing the bowels of the engine subsystems. Indirect comunication may be messy and incur unnecessary overhead. There are enough of these "situations" that make me wish i had some kind of fancier communication system. However, there are not *that* many to really make it worthwhile. So what i'm asking is: should i come up with some way for "unimportant" objects to signal more global changes in the game, or should i just brute-force it and make these objects special cases, just giving them a pointer to the necessary object (even if that creates a lot of dependencies and breaks encapsulation)? My ways to solve this problem have boiled down to: - Create a generic messsaging system of some kind (if you have ideas, please share) - Keep what i currently have and just brute-force it, as mentioned above - Create a Mediator object that knows how to do everything high-level, but can be globally accessed by low-level game objects. Your thoughts and experiences are appreciated. I need as many different brains on this as possible. Thanks

Share this post


Link to post
Share on other sites
Well, for these things I either rethink the design - which, in most cases, resolves nothing really :).

Programming languages like C# have events, this seems like a good way to do such things, depending on the situation. I don't know how C++ handles it, but there has to be a way to do these things, too. You'd probably have to look at the 'Command' design pattern.

For my 'games', I try to make it so that
- An object can be constructed using the constructor. The class will register itself with the grahpics, probably. In this case however, one needs to have (semi-)global access to the subsystems,
- Objects don't render themselves, but they are being rendered. Every renderable object has an interface, IGraphicsObject, which contains a property that returns a class that will do the drawing. This can be default classes, but also more advanced drawers, like one with an animation.

Okay, this explanation probably sucked, but this is how I did it. If you have questions, just ask them and I'll try to anser :)

Lok Tar!
Sijmen Mulder

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
there is nothing wrong with doing the naive thing:

void Gun::Update(...)
{
....

// ooh! spawn a bullet because somebody hit the button.

Bullet* pBullet = Bullet::Create();

pBullet->SetPos(x,y,z);
pBullet->SetVel(dx,dy,dz);

BulletManager::AddBullet(pBullet);

}

void Bullet::Update(...)
{
...
// ooh! hit something. spawn an explosion.
// maybe this is generic through an effects system:

Effect* pEffect = EffectManager::Create(mpEffectDescriptor);

pEffect->SetPos(x,y,z);

EffectManager::ActivateEffect(pEffect);

}

no problems.
and then something like:

MainLoop::Update(...)
{
GenericObjects::Update();
BulletManager::Update();
EffectManager::Update();

}

not that you can't run in to problems there, but... it's clean and you can tell what's going on -- something that a message based system won't provide. and it's not more work, really.

did you try that, and have it not work for you?
if you did try it, and it doesn't work, why not? maybe i can help more...



Share this post


Link to post
Share on other sites
Guest Anonymous Poster
me again.

i re-read your post, and i guess i've suggested the "brute force" approach... this is what i would recommend for your gun / bullet / explosion example from the other thread.

this is not to say that a message passing system isn't useful, particularly for your other examples (switching to in-game-cinematices, starting the game, winning the game...). i actually don't see these as relating to "registering newly created objects with a manager" ... maybe i'm just missing something.

messaging systems are cool, and can be useful. for instance, you might have a generic "trigger zone" that sends a message when something enters it. and then you have a message listener that does something when it gets a message... set those up to use the same message, and you're good to go.

i'll re-read your other post, but if you had another example, perhaps i might have more to say. revisiting your design might be a good thing, as somebody else said, but since i have no idea what your design looks like, i can't say anything intelligent about it.

Share this post


Link to post
Share on other sites
Although object processing is only part of the puzzle, i'll try to explain it better:

I have PollingObjects. Most game objects inherit from it. It contains a basic Update() and Draw() interface. Since it's based on time intervals, PollingObjects are useless without a PollingEngine to drive them. PollingEngines manage the memory to a certain extent, so once Register()ed with an engine, the object is taken care of as far as deletion is concerned.

To facilitate "layers" in 2D, i have a bank of 12 PollingEngines wrapped up in a global monostate class called Sequencer. To create an object and have it "run", i do this:

Sequencer::Register( new Explosion(some params), layer_number );

every frame, i drive the sequencer by doing:

Sequencer::UpdateAll( time_interval );
Sequencer::DrawAll();


But now i want to make Sequencer a regular class because i realize now that i will need several of them.



As for the larger problem, basically, what it all boils down to is:

???->RegisterObjectWithEngine();
???->ChangeGameAreas();
???->CreateSubScreen();
???->CreateTitleScreen();
???->SwitchToActionMode();
???->SwitchToCutScene();

What and where is "???". So far, i'm thinking that the Mediator pattern would solve this the best, but i'm not sure yet. Still looking for input.

Share this post


Link to post
Share on other sites
From what I've read so far your ??? would be at least partly what I call ContextHandler in my game. It is the class and layer that handles all the interactions between the user and the engine. All the game state updating, object managing and most of the inter object communcation and are handled by the game engine. Game modules or objects handle all their own proccessing internally when their update method is called.

I'm still not sure why you need multiple sequencers though, why isn't one sufficent? Can you give an example of where you need two or more?

Share this post


Link to post
Share on other sites
While you're explaining that, could you explain why you have more than one PollingEngine? You said to facilitate 'layers' but I don't understand what you mean by this

I ask because I have a similar system but with only one 'PollingEngine'

Thanks

Share this post


Link to post
Share on other sites
On Sequencers:

I might need several for when the game state changes. For instance, lets say i'm in Game Area 1 and i go through a door and the game loads in Game Area 2. Internally, what would otherwise happen is that all objects pertaining to Game Area 1 are discarded, the sequencer is flushed, and then reloaded with the necessary game objects that Game Area 2 brings.

Now don't you hate it in cheap games when you go the next area, then go back to the first area and suddenly all the monsters you shot down pop right back? If you had mulitple sequencers, you could take all the game objects from the first area and tuck them away somewhere as backup in case you re-enter the first area. Then all the game objects would be in their places just as you left them

Another example would be like if you are playing the action sequence of the game and hit START and go to a sub-menu screen (like in Zelda or Metroid). In this case, i would cache the sequencer full of objects and reload a new one with all the objects and widgets it takes to create a subscreen. When the subscreen is done, i simply dump the current sequencer and reload the old one.

There are other ways to do this, but this way is pretty slick ;-)

On Layers:

My game is 2D. In order to facilitate some kind of pseudo-3D look, the objects are arranged into layers. I have background, foreground, sprite, transition, and GUI layers, totalling 12. It gives me a lot of freedom. This way i could have multiple parallax backgrounds and some other cool effects.

The Sequencer is actually a bank of 12 PollingEngines, each representing a layer. When it's time to draw, it does this:


foreach polling engine {
foreach object in engine {
object->Draw();
}
}


Then i end up with a decent looking 2D image. If you want the full sourcecode to my "polling engine", i've posted it here

Share this post


Link to post
Share on other sites
TechnoGoth:

I like your "Context" idea. The more i think about it the more i like it. It's sort of the easy way to do it too, but for this instance it seems appropriate. It should be a global (being the game state and all) and can do different things with requests depending on the internal game state.

Thanks for sharing.

Share this post


Link to post
Share on other sites
Instead of having the last level in memory, why dont you just save it to a file, and if someone goes back just laod it. That way you will save memory aswell. No need to have all the levels/rooms you have visitet in memory

Share this post


Link to post
Share on other sites
Oh no, i wouldn't load them *all*, just the one you were just at. I wouldn't save it to disk because i don't want the delay in parsing the file and recreating all the objects.

Share this post


Link to post
Share on other sites
In cases where objects need to access a higher level system, your best bet is the singleton pattern. For when objects need to be informed of state changes in other objects, you could use the observer pattern. Dependencies between objets are simply unavoidable. What you need a clean way of dealing with these objecs to avoid issues such as dangling pointers and exposing internal representations.

For the dangling pointer issue, I have found reference counting to be the easiest/cleanest technique (which is what MS's COM is all about). In fact, the objects of my engine provide the same QueryInterface, AddRef, and Release methods as do all COM interfaces.

To avoid exposing internal representations.. umm.. use interfaces(pure virtual base classes). As long as your objects have a rigidly defined set of interfaces, you will be fine. It may be very helpful (if not neccessary) to develop your own RTTI system. Again, my team's engine uses QueryInterface for this, which works very well.

Share this post


Link to post
Share on other sites

This topic is 4661 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this