Skip to main content
GameDev.net gamedev.net
🔒 Locked

Objects creating other objects

Started by Shuger Feb 22, 2009 at 6:52 AM 12 replies 2.9k views
Original Post
Shuger
Shuger
Hi, I have a design problem whith objects that create other objects. For example a player that shoots bullets, should create them and add to the scene, particle emmiter should add particles and so on. The way i do this now: 1. There is a State Manager which holds State object (examples of States: game, main menu etc) 2. State holds containers with with various objects in it (enemies, bullets, player) 3. I want objects - for example particle emmiter - to be able to create objects and add them to these containers, however, emmiter itself is in one of containers so to add new object to scene it should have a reference to State object it's in. That means cyclical dependency and doesn't really look like proper design. The way i have it now is that for example, if player wants too shoot it sets some flag on, and State object checks every frame if flag is up, if so it reads info from player object and basing on that creates and adds bullet object. The problem is that now everything needs to be checked by State and it would be much more intuitive if player could spawn objects and add them to State in some easier way. Any suggestion on how to solve it? Side info: game is written in C++
empirical2
empirical2
The way I would do it is to pass the state object by pointer when calling the function on the object that causes it to process.

e.g

STATE::OnFrame(int time)
{
//stuff
Player.OnFrame(this,time);
//stuff
}

PLAYER::OnFrame(STATE * pState,int Time)
{
//stuff
pState->AddObject(OT_BULLET,X,Y);
//more stuff
}

...or set the pointer to the state object each time a sub object is created.
NotAYakk
NotAYakk
A GameObjectContainer is a container that owns a collection of game objects.

It is an abstract interface, to which you can add GameObjects, and once you pass ownership the spawned object is no longer your concern.

Possibly a GameObjectContainer is type safe templated (so the GameObjectContainer is different than a GameObjectContainer), which allows you to have some limited type-safety and isolate casting to relatively narrow points.

Objects that spawn things store a reference to the GameObjectContainer of the type of things they can spawn, and can stuff things into it. This probably wants to use a weak_ptr if you are using reference counting.

...

You could also go a step higher, and say that Objects are allowed to have a reference to their Context. And that Objects that spawn other Objects by default add them to their Context, passing in both the Object that spawned it and the new Object.

Context then becomes a bare-bones interface that is sufficient to describe the services an Object needs from it's Context.

In short: an Object can indeed have a pointer to something that has a pointer to it. The Scene is an ObjectContext, and when objects are in the Scene, they know about it.

You can do this "manually" by having every Object method include a pointer to the Context in it's signature (which has the night side effect of meaning that each object must be able to switch contexts willy-nilly), or the Objects can be created in a Context and told to move Context.

This idea of having a pointer to your Context from which you derive your ability to interact with the greater engine has some neat side effects. You could, for example, have multiple Contexts going at once (say, a two-player game on one screen), or use it to allow players to 'naturally' pass between 'levels' (each level is it's own context, and the barrier between them marshals objects from one context to another on collision) or zones (same thing).

...

Yet another idea is to create an encapsulated "BulletFactory" that knows how to place objects in the scene. An object that shoots bullets has a BulletFactory, and it uses it to create the bullets. The BulletFactory knows about both the Context and the Object doing the firing, but the Object knows nothing about the Context. The downside is that the Context pointer is still there, just hidden inside the BulletFactory, which makes inter-Context marshalling of objects even harder to deal with.
snisarenko
snisarenko
Quote:
Original post by Shuger
1. There is a State Manager which holds State object (examples of States: game, main menu etc)
2. State holds containers with with various objects in it (enemies, bullets, player)
3. I want objects - for example particle emmiter - to be able to create objects and add them to these containers, however, emmiter itself is in one of containers so to add new object to scene it should have a reference to State object it's in. That means cyclical dependency and doesn't really look like proper design.

The way i have it now is that for example, if player wants too shoot it sets some flag on, and State object checks every frame if flag is up, if so it reads info from player object and basing on that creates and adds bullet object.
The problem is that now everything needs to be checked by State and it would be much more intuitive if player could spawn objects and add them to State in some easier way.


Ask yourself these simple questions. Why does the State need to know about bullets? Why is the State updating the bullets and not the player? What happens when the player needs to be able to shoot missiles or throw grenades?

If there is no reason for the State to know about bullets, then let the State update the player, who will intern update the bullets that he fired.

Ravyne
Ravyne
Quote:
Original post by snisarenko
Quote:
Original post by Shuger
1. There is a State Manager which holds State object (examples of States: game, main menu etc)
2. State holds containers with with various objects in it (enemies, bullets, player)
3. I want objects - for example particle emmiter - to be able to create objects and add them to these containers, however, emmiter itself is in one of containers so to add new object to scene it should have a reference to State object it's in. That means cyclical dependency and doesn't really look like proper design.

The way i have it now is that for example, if player wants too shoot it sets some flag on, and State object checks every frame if flag is up, if so it reads info from player object and basing on that creates and adds bullet object.
The problem is that now everything needs to be checked by State and it would be much more intuitive if player could spawn objects and add them to State in some easier way.


Ask yourself these simple questions. Why does the State need to know about bullets? Why is the State updating the bullets and not the player? What happens when the player needs to be able to shoot missiles or throw grenades?

If there is no reason for the State to know about bullets, then let the State update the player, who will intern update the bullets that he fired.


That's retarded.

beyond their creation the player has no influence over the bullet, just as in real life. To the extent possible, a bullet is in control of its own behavior, once given an initial state. Its behavior is influenced by certain externalities perhaps, such as wind or structures, which are owned/controlled by the game world, not the player. The game world would most likely be represented by the enclosing state, or its owner.
throw table_exception("(? ???)? ? ???");
JParishy
JParishy
You haven't seen Wanted, have you?
Zipster
Zipster
The context-based approach recommended by NotAYakk is a good way to handle thingss. Each object has an interface to its creating context (or additional contexts if you want) that exposes what the object can do with the context. A few basic functions might be CreateObject(...), DestroyObject(...), RegisterEventHandler(...) (so it can receive events), etc.
snisarenko
snisarenko
Quote:
Original post by Ravyne

That's retarded.


I think you misunderstood my solution.

Quote:

To the extent possible, a bullet is in control of its own behavior, once given an initial state. Its behavior is influenced by certain externalities perhaps, such as wind or structures, which are owned/controlled by the game world, not the player. The game world would most likely be represented by the enclosing state, or its owner.


There is no question that the the bullet should be its own class and update its own physical state. Thats a given. The question is about who should keep a reference to the bullet and call its update function.
As far as physical effects like collision and wind, the player can pass each bullet to a collision system (which yes was passed as some sort of context to the player) as a physical object and notify the bullet of any collisions when the system updates, so that the bullet can process them on next update.

Now that I think about it, it is probably appropriate to pass your bullets to your global entity manager to update the bullets (so that if the player dies they can be updated.) However, I still think the player should keep a reference to all the bullets it creates so that it can manage the maximum amount of bullets used, and reuse bullets that are dead.

Quote:

beyond their creation the player has no influence over the bullet, just as in real life.


That's true for a simple weapon. But what about a weapon that can only have a certain amount of bullets active at a time. Or a weapon where a player can cancel all of his current shots with a right click. Or mines that can be thrown and then detonated remotely. In all of these cases the player needs to keep a reference to the "bullets".

What should happen to long living "bullets" (like mines) after a player dies? Should he be able to place an infinite amount of mines in the world, as he keeps spawning? What I am saying is that each specific player (or a weapon) class should be making these decisions, independently of the entity manager. Your entity manager shouldn't know the specific kind of bullets being used. If your entity manager is responsible for updating the bullets, it should see them as nothing but entities, not specific bullets from specific weapons.
Shakedown
Shakedown
Quote:
That's true for a simple weapon. But what about a weapon that can only have a certain amount of bullets active at a time. Or a weapon where a player can cancel all of his current shots with a right click. Or mines that can be thrown and then detonated remotely. In all of these cases the player needs to keep a reference to the "bullets".


That's true, but most of the time weapons will be 'simple weapons.' A particular case should be made for those special weapons, like the kind you've mentioned above. Therefore it wouldn't be appropriate for the player and bullets to be coupled in such a way only to be able to support these 'special weapons.' In that case, this coupling between the player and bullets should not be enforced for all players and bullets, but only for those special weapons.
Ravyne
Ravyne
For things like remote detonation or, say, a laser-guided rocket a'la HL2 the player probably does need some reference, but these are another case entirely. For simple "fire and forget" projectiles there is no reason for the player to maintain a reference.

Your points about having a maximum amount of "live" projectiles and re-using projectiles are moot; In the case of maximum live projectiles, the player already has a reference to whatever system is responsible for maintaining and updating bullets, and may query it for whatever statistical information may be needed (or better yet, hard limits are a rule of said system, and additional calls to create projectiles will simply return until a slot is available). In the case of re-using projectiles, that is something taken care of by the memory allocation scheme of said system, such as a pooled allocation strategy.

The player must be the player and nothing more, otherwise the single-responsibility principle is violated. The number of bullets in the air is no more a part of the player than the number of birds in the sky, and your player class certainly has no need to know anything about how bullets and other projectiles are created, only that they can be created and limited statistical information.

Thinking about this a little more from the perspective of per-character limits on specific classes of projectiles I began to think that perhaps player/enemy ownership would be correct -- however, all we need to accomplish this is some relationship between them which provides ownership semantics, but that relationship need not necessarily be true ownership. For instance, to prevent a character from creating more than three homing missiles, the system only needs some identifier for the character which created the missiles, and a rule that each character may only create three (or a means of querying the number of said projectile owned by said character). This is exactly the same way that relational databases operate -- rather than one record physically encompassing all constituent data, the other data is often in another logical (or even physical) database entirely, and ownership is expressed through a means of some identifier, usually a GUID.
throw table_exception("(? ???)? ? ???");
oliii
oliii
hmmm... Isn't that usually done via factories?

1) Make up some creation data for your bullet, passing in the type, the guid of the creator (the player who fired the bullet), ect...

2) If you need to pass some info relating to the containers as well, you can also reference the container with a assigned guid.

3) Instead of passing the (STATE* State), keep a reference to the state object's (context) in in form of a guid / handle as well.

4) call the global factory create function with all that data, or fire a creation message (publisher / subscriber pattern), that will be routed to the appropriate handler.

5) if you need to notify your player of the bullet being created or failed (container full), you'll need another message fired back at the sender, telling him the bullet was been created (or not) on his behalf.

6) If a bullet hits a target, and you need to notify the score system a player hit someone with a bullet, again send a message to the score system, with the appropriate message data (containing the bullet's owner guid).

you don't have to use messages though. It's just that I like them for these types of interactions.
Everything is better with Metal.
Shuger
Shuger
Thanks for all ideas, i'll try implementing context approach NotAYakk suggested.

I don't think that storing created objects in the creator is a good idea, it makes many things harder especially in case of enemies. Every time enemy would be destroyed i would have to take objects it created and add it to general scene containers anyway.
For now i'm going with "fire and forget" weapons so reference too bullets is not neccessary. Still, maybe in future i will add some more advanced weapons which will need to give feedback, but it is a problem i can handle ;)
sfoster
sfoster
Hi

Have been having similar quanderies myself on this, here's what I ended up with. Does it sound okay?

I have a game class that handles the base of our game.

The game class owns the game objects lists (one for each type of object), the lists are actually a list of pointers to base game objects, to make virtual functions work.

one game object is 'golfer', and another is 'golfball'. i want the golfer to be able to "create" golfballs within the game but not own them.

So I made the golfer class own a pointer to the list of golfball objects. Now the golfer just calls a create() function in itself which adds new golfballs via the pointer to the golfball list. (a side note, the golfball class will probably end up being another derived class of 'projectile' objects)

It works superbly so far but I suspect it's falling foul to all sorts of oop dependency issues, but I'm a believer in using oop as a guideline and not a set of commandments.
ToohrVyk
ToohrVyk
The creator-object needs to notify its environment that it has created objects. There are three ways in which the creator-object can communicate data to the environment:

  • By returning the information from a function called by the environment.
  • By setting one of its properties that is then queried by the environment.
  • By calling a function, provided by the environment, with the information.


Which one you use is up to you. I would personally use the third: the creator-object receives a callback function (for instance, as part of an onCreateObject event handler on the creator-object) and calls it whenever it creates an object, with the created object as an argument.

The environment then provides all creator-objects with a function that adds the created object to a temporary list of objects, then updates all creator-objects, and finally splices the temporary list at the end of the active object list (to avoid modifying the list being traversed, for obvious reasons).

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.