Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


Where to store container of all objects/actors/collision models/etc


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
15 replies to this topic

#1 Kovaz   Members   -  Reputation: 136

Like
0Likes
Like

Posted 02 August 2014 - 08:06 PM

I'm in the fairly early stages of coding a game, and I've come across this problem a few times now.

 

Essentially, I run into situations where some object needs to access a container of all other objects of the same type (or of some other type!). For example, a Collider object needs to be able to check if it has run into any other Collider objects. Or a Unit needs to get a list of nearby enemy Units to be able to pick a target.

My question is where to keep these containers?
 

  • Global? Globals are (almost) always bad in my experience, and I'm sure there's a better way than just having a global list of everything that anyone can access.
  • Static member variable? This lets objects of one type access all of the same type, but it doesn't cover cases where one type needs access to other types (for example a Pathfinding object needing access to Colliders, or an AI needing a list of all Units)
  • Manager classes? These would essentially be singletons then, and although I have very little experience with the SIngleton pattern I've heard nothing but bad things

I'm not sure of the best way forward. Every solution seems to have some serious holes and none of them seem particularly good.



Sponsor:

#2 Misantes   GDNet+   -  Reputation: 1192

Like
0Likes
Like

Posted 02 August 2014 - 08:19 PM

Take this with a large grain of salt as I'm rather new myself (I'm certain someone will come along to correct me if I'm leading you wrong). But, to avoid these sorts of problems, I keep most of these objects in my main run-time class(or really, whichever class is going to need them. Just make sure you're organizing things correctly), which contains most of the functions and everything. So, my main class will have some objects of different classes, vectors of different class objects, etc. They can all interact with each other without any problem in any function in this class and I can pass anything I need into any class functions of other classes. For a more complicated program, you probably don't want all the objects being created at once, but in that case, just have them created in whatever sub-class they'll be necessary in and basically follow the same procedure. I'm simplifying things to some extent, but the basic premise remains the same.

 

I find that when I run into problems of circular dependencies, or classes needing things from other classes that they don't have easy access to, I've probably designed things wrong and need to look into organizing the classes and functions better. Often, I've made unnecessary classes, or haven't made some probably necessary ones. Often, I'm trying to do something within a class that should probably be a function outside of the class.

 

As far as your collider object needing to check against another collider object, this shouldn't be a problem as long as the function that checks that isn't called from inside your collider object class but wherever those objects are existing in scope. But, it should be easy to call a function that checks that from outside the collider class, say, wherever else you may want to check that, make a function there that passes in your collider objects to check. So, for simplicity, let's say your main class has a function:

bool Main::CollisionCheck()
{
if(//whatever conditions for collision are met)
    return true;
else
    return false;
}

You shouldn't run into any problems or dependency issues as long as the collider objects exist in this scope(sorry for the hobo-code, I'm not sure what language you're using, the original post sounds like c++ maybe, but something object oriented anyhow, so the methods should be slightly similar). If you don't want to do this in your main class, you can consider an object manager class, and then pass in the objects to check collision on, so a function like:

bool ObjectManager::CollisionCheck(ColliderObject &co1, ColliderObject &co2)//pass in references to the objects/vectors to check
{
if(//whatever conditions for collision are met)
    return true;
else
    return false;
}

So, in a nutshell, if your object needs to access another instance of the same class or even some other class, you should probably consider making that function elsewhere. Likely, wherever you created the instances of the object. So, instead of a "ColliderObject.CollisionCheck()" function, consider making a function in the class that you created the instances of the collider objects to begin with and calling the function there. This may be your main class, or perhaps some other manager class. But, if you try to have all those functions as part of your collider object class, you're likely going to run in to some circular dependency issues.

 

Perhaps this isn't the optimal way to do things, though, so I'm likely going to just watch this thread with you and see if better advice comes along tongue.png


Edited by Misantes, 02 August 2014 - 08:59 PM.

Beginner here <- please take any opinions with grain of salt :P


#3 Kovaz   Members   -  Reputation: 136

Like
0Likes
Like

Posted 02 August 2014 - 08:59 PM

Yeah, I'm using c++. I was hoping to be able to check for collisions inside of my collider object, since the collision check is part of the process of moving and I wanted to be able to do things like bouncing off of the other object or sliding along it, and in both cases I need to know how much movement I have left after colliding.

My goal was to be able to have a really simple "main" object/function that just does a simple call like actor.update() and then the Actor figures out where he should be using his own internal stuff (PhysicsModel, Collider, Pathfinder, etc.). I'm also trying to make my objects as generic as possible, more as a learning exercise than anything, so I can add and remove functionality more easily. So hypothetically if I wanted an object to not collide with other guys, I just don't give it a Collider, or give it a dummy collider that always returns false.

 

Outside of the location of my containers I've got it mostly figured out and working properly, and I could probably take any approach I listed and hack it together and make it work, but I'm mostly curious what some best practices are in this case.



#4 Misantes   GDNet+   -  Reputation: 1192

Like
0Likes
Like

Posted 02 August 2014 - 09:48 PM

Well, having the collision check as a requisite to moving can still occur. Just run the function in your other class where the instance of the object resides in, then pass in the bool as a parameter to your move function.

 

So, a simple version, In main, you create your objects, check for collision to generate a bool, then to move your object use a function like:

CollideObject::Move(bool collision)
{
if(collision != true)
    //move your object
else
    //don't move your object
}

passing in the collision bool as a parameter.

 

Or, still have an Actor.Update() function internal to your Actor Class, but pass in the other objects as parameters. This seems to be what you're trying to do. If you need a reference to other objects, you'll need to pass them in as references to the function. So, in this case, for your actor.update scenario. If you have a vector of tiles, let's say, and want to check them for collisions internally in the class, you'll want to make your Actor.Collision() function take in the vector as a parameter. So:

Actor::Collision(std::vector<Tiles> &tiles)
{
for(int i = 0; i < tiles.size(); i++)
    {
    if(tiles[i].pos.x < actor.pos.x)//or whatever. check for collision)
       actor.move();
    else
       actor.collide();
    }
}

This way you can reference the tiles, other players, whatever you need, as long as you pass in a reference to it. This method raises dependency issues though, that I don't quite feel qualified to dispense advice on. On the surface, you'd need to include headers for each type of class you may want to reference, though that's typically not recommended as I understand it(there are other methods), and you can end up with various dependency issues pretty quickly.

 

But, personally, I understand how you'd like that all to be internal to the class, but that feels like it's bloating the class unnecessarily. You're treating the object like an object manager. It would just be easier to make an object manager class(even though you said you didn't want something like that), and handle the things internally there. Have the objects created in the object manager class and run collision checks, gravity, movement, etc within that class. It doesn't have to be a singleton. You could still call everything in one function, but then the object doesn't have to reference other instances of itself or of other classes. You won't have to repeat the same code for every type of object (for, say like gravity. Each different class of objects you have, won't have to include the gravity functions within those classes as well). And then just call one function in main like ObjectManager.UpdateObjects(), and within UpdateObjects, run your movement,gravity, etc functions for your character, enemies, gravity, etc.. All the other advice I've given, just swap into your object manager class rather than main.

 

But, that way, your Actor class is simple and isolated and not entangled with other classes, other dependencies, other states, etc. You can still have an Actor.Update() function, but, if it's dependent on other classes or conditions, call the Actor.Update function higher up on the class hierarchy under those conditions and pass in whatever information you need, if that makes sense.

 

In other words. If you have two objects, and one needs to know about the state of the other. Run a check function on that outside of those classes, and depending on the outcome, alter your Actor.Update() parameters accordingly. You can think of it as a web of dependencies. But, the more you make each class dependent on another class the more complicated and messy it gets. I try to make it a cleaner hierarchy where the objects are as isolated as possible, and the managers of those objects are the only ones that interact with them. The objects themselves don't really interact with each other, per se. Well, they do, they just don't know it tongue.png

 

And, again, I'm rather new myself, so I'm really hoping someone comes in to either validate or correct me here before I rot your mind with poor advice tongue.png I'll exit the thread here, because you're asking for best practices, and I have no idea if what I've explained qualifies as that(I'm guessing probably not. If it is, it's incidental and just stumbled upon out of trial and error).


Edited by Misantes, 03 August 2014 - 12:14 AM.

Beginner here <- please take any opinions with grain of salt :P


#5 Strewya   Members   -  Reputation: 1579

Like
2Likes
Like

Posted 03 August 2014 - 03:22 AM

It's much easier to operate on your game objects from the outside then it is on the inside, because it lowers dependencies. If you try putting all of your object interactions into your object class, it's going to grow too large, break the single responsibility principle and become a giant spaghetti monster of dependencies.

If you want a simple and flexible way of doing it, put any and every meaningful logic operation into it's own free function (or a class if it requires some state from frame to frame). Ideally, the operations you do on the game objects are unit operations (like moving all objects by their velocity), are done to all objects in bulk ( void moveObjects(std::vector<Object*>& objs); ), and receives only the data it is interested in ( void moveObjects(std::vector<Vec2*>& velocities, std::vector<Vec2*>& positions); ). Add caching techniques where neccessary to avoid creating temporary vectors all the time tongue.png

When you start thinking that way, you'll see that game objects are just dumb containers of data, and the real meat are the operations that define the transformations of that data. Because that's what actually happens, every frame you just apply a transformation function on game object data.

 

As far as your collision example goes, you should first find all collisions between all objects, and for each collision save a manifest of that collision (the objects/bodies in question, penetration vector etc). Then, loop over all manifests and resolve collisions, noting that resolving one collision might introduce another, so the whole process is iterative. Look at some popular physics libraries (Box2D for 2d, Bullet for 3d) how they do it.


devstropo.blogspot.com - Random stuff about my gamedev hobby


#6 Kovaz   Members   -  Reputation: 136

Like
0Likes
Like

Posted 03 August 2014 - 09:30 AM

Ok, that makes sense. How would you handle having objects that have very different properties in that case? For example, some objects might not collide with others (say a flying unit in an rts), or might move in different ways (or not at all)? If an object is just a dumb data container, does he have to keep track of all of that data in case it gets used? So my Actor class would still need to have a member for a collision box in case an Actor needs one, and would still need to have a velocity, acceleration, and mass in case an Actor needs to do Physics stuff. And then my processing logic in my main object also needs to know which operations to apply to which actors.

 

 

EDIT: After some thought, it shouldn't be too hard to keep track of which data is needed using different Actor subclasses, and then the main object can just dynamic_cast and check for NULL to figure out if it needs to be processed.

This seems to solve my problems in the simple cases but open up a whole bunch of different ones if I want to add any complex functionality. Maybe I'm not quite fulling grasping how an Actor should hold data and how the main class should process it.


Edited by Kovaz, 03 August 2014 - 09:32 AM.


#7 haegarr   Crossbones+   -  Reputation: 4580

Like
0Likes
Like

Posted 03 August 2014 - 09:47 AM

EDIT: After some thought, it shouldn't be too hard to keep track of which data is needed using different Actor subclasses, and then the main object can just dynamic_cast and check for NULL to figure out if it needs to be processed.

This is for sure a solution, but it is flawed due to 2 reasons: 1st it works by inheritance (at least it sounds so to me), possibly leading to the god class issue. And 2nd it wastes performance by using a dynamic_cast.

 

Assume instead that your objects are build using composition (notice that this does not necessarily mean a CES). If it has a Placement and a Collider component then it is tagged to participate on collision detection. If the Placement is marked as static then it is, well, static. On object instantiation, the existence of those components causes the object to be installed in the collision sub-system. The sub-system can internally manage several sets of objects, e.g. one for static and one for dynamic colliders, so it is able to avoid unneeded checks e.g. between two static colliders.

 

The important part is this: Having sub-systems to deal with the aspects of interaction (see also Strewya's answer above), and attach components to the game objects to mark and parametrize them as candidates of selected sub-systems. So the sub-systems are able to deal with the respective aspects in an more or less optimal manner, especially by a-priori restriction to meaningful subsets of objects and the use of suitable structures.


Edited by haegarr, 03 August 2014 - 09:51 AM.


#8 ExcessNeo   GDNet+   -  Reputation: 541

Like
1Likes
Like

Posted 03 August 2014 - 12:20 PM

  • Manager classes? These would essentially be singletons then, and although I have very little experience with the SIngleton pattern I've heard nothing but bad things

Just to touch on this, Singletons aren't bad simply misused/overused. People often discover design patterns read about the Singleton pattern and then decide all classes where it makes sense to have only one instance during the application lifetime should be a Singleton, when the rather plain solution is to simply only make one.



#9 Kovaz   Members   -  Reputation: 136

Like
0Likes
Like

Posted 03 August 2014 - 02:32 PM

 

EDIT: After some thought, it shouldn't be too hard to keep track of which data is needed using different Actor subclasses, and then the main object can just dynamic_cast and check for NULL to figure out if it needs to be processed.

This is for sure a solution, but it is flawed due to 2 reasons: 1st it works by inheritance (at least it sounds so to me), possibly leading to the god class issue. And 2nd it wastes performance by using a dynamic_cast.

 

Assume instead that your objects are build using composition (notice that this does not necessarily mean a CES). If it has a Placement and a Collider component then it is tagged to participate on collision detection. If the Placement is marked as static then it is, well, static. On object instantiation, the existence of those components causes the object to be installed in the collision sub-system. The sub-system can internally manage several sets of objects, e.g. one for static and one for dynamic colliders, so it is able to avoid unneeded checks e.g. between two static colliders.

 

The important part is this: Having sub-systems to deal with the aspects of interaction (see also Strewya's answer above), and attach components to the game objects to mark and parametrize them as candidates of selected sub-systems. So the sub-systems are able to deal with the respective aspects in an more or less optimal manner, especially by a-priori restriction to meaningful subsets of objects and the use of suitable structures.

 

 

So when you say I should build my objects using composition, wouldn't I end up having a fairly bulky Actor class that holds a bunch of data that's only sometimes useful? For example:

 

class Actor
{
...
private:
    Placement* _placement;
    Collider* _collider;
    PhysicsModel* _physics;
    ...
}

with a bunch of these objects that are only used by some actors? Wouldn't it be preferable to use inheritance to have, for example, a StaticActor that only has a Placement and a Collider, and DynamicActor that also has a PhysicsModel, etc. Unless it's better to just have a base class with a large amount of data, and just check if any of those are NULL when determining what to do with it.

 

 

  • Manager classes? These would essentially be singletons then, and although I have very little experience with the SIngleton pattern I've heard nothing but bad things

Just to touch on this, Singletons aren't bad simply misused/overused. People often discover design patterns read about the Singleton pattern and then decide all classes where it makes sense to have only one instance during the application lifetime should be a Singleton, when the rather plain solution is to simply only make one.

 

 

Ok, that's what I thought. I see it in code all the time, but whenever I try to research it I just get a million results saying "singleton bad." It does seem to be the best solution here.


Edited by Kovaz, 03 August 2014 - 02:32 PM.


#10 Strewya   Members   -  Reputation: 1579

Like
2Likes
Like

Posted 03 August 2014 - 03:10 PM

So when you say I should build my objects using composition, wouldn't I end up having a fairly bulky Actor class that holds a bunch of data that's only sometimes useful? For example:

class Actor
{
...
private:
    Placement* _placement;
    Collider* _collider;
    PhysicsModel* _physics;
    ...
}
with a bunch of these objects that are only used by some actors? Wouldn't it be preferable to use inheritance to have, for example, a StaticActor that only has a Placement and a Collider, and DynamicActor that also has a PhysicsModel, etc. Unless it's better to just have a base class with a large amount of data, and just check if any of those are NULL when determining what to do with it.

 

There are a lot of ways to do this without needing inheritance at all. One is to stop thinking all objects in your world are an instance of the same class. They're not. A missile is completely different from a tree. Terrain is different from a tree. Items are different from NPCs.

Move away from having an Actor class that contains everything in the entire game and can be morphed into anything. Instead, have multiple types of objects each composing only the data it needs, store it in an ObjectPool of its own, and when it is created, register its data parts to the systems that are in charge of applying transformations to that data or just need to read from the data.

A different way is to not have classes an all, but pools of data that are indexed by an id which, for all intents and purposes, acts as an object, it's just that its parts are spread all over the place (well, in pools which you get to decide where they are).


devstropo.blogspot.com - Random stuff about my gamedev hobby


#11 Kovaz   Members   -  Reputation: 136

Like
0Likes
Like

Posted 03 August 2014 - 04:04 PM

Ok, that makes sense. I actually had my objects as separate classes initially, but then I realized that they all had a position, and all had a sprite that needed to be drawn to the screen, so I made the superclass Actor to basically be "thing that exists somewhere and should be drawn." What's the disadvantage to using inheritance in this case? If all I have in Actor is position and sprite, and my other objects that need to do their own thing inherit from that and add on? Why is having Tree and Missile be completely separate classes (that both have the common functionality of being in a location and needing to be drawn to the screen) a better solution?

 

Sorry if it sounds like I'm contradicting you, I just want to make sure I fully grasp the logic behind what you're saying so I don't run into similar issues later.



#12 phantom   Moderators   -  Reputation: 7554

Like
1Likes
Like

Posted 03 August 2014 - 04:55 PM

Why is having Tree and Missile be completely separate classes (that both have the common functionality of being in a location and needing to be drawn to the screen) a better solution?


Because you start coming up with crazy inheritance structures just to add some extra functionality.

Lets say you start with something which needs to be drawn; Actor { Vector2 position; Sprite* graphic; }
But now you need one to be able to have collisions in some cases; CollidableActor : public Actor { BoundingBox box; }
But not you need one to also be moveable in some cases; MoveableCollidableActor : public CollidableActor { void Move(); }
But now you need one to also be able to fire; ShootableCollidableActor : public MoveableCollidableActor { void Fire(); }
Oh, but now you need a tank; Tank : public ShootableCollidableActor { void Fire(); void Move(); /* because tanks move differently */}
Oh, but now you need a tank which also has rockets; RocketTank : public Tank { void FireRocket(); }
And now I want a Jeep with no gun; Jeep : public MoveableCollidableActor { void Move(); /* because Jeeps move differently */}
And now I want a Jeep with a gun; GunJeep : ShootableCollidableActor : public MoveableCollidableActor { void Fire(); }
And now I want a turret which doesn't move and ooops.. all my shootables are moveables...

At which point you have a ridge hierarchy and things in the wrong place.
If you try to resolve it by moving 'shoot' down and 'move' up then you end up with anything which can move must shoot, but our Jeep doesn't so that's wrong.

This is why the advice is that you should prefer composition over inheritance by default.

In this case;
Actor { Vector2 position; Sprite * graphic }
Tank { Weapon * primaryWeapon; Actor *; CollisionObject * collision; MoveLogic * movelogic; }
RocketTank : Tank { RocketWeapon * secondaryWeapon; }
Jeep { Actor *; CollisionObject * collision; MoveLogic * movelogic; }
GunJeep : Jeep { Weapon * primaryWeapon }
Turret { Actor *; Weapon *; CollisionObject *}

There is still inheritance there but they are short chains (Jeep -> GunJeep, Tank -> RocketTank, MoveLogic -> {TrackedMove, WheelMove}) and are not bound so if you wanted to make a new object like a helicopter it is easy to plug it in and give it more weapons.

Chopper { Weapon* weaponArray[4]; Actor *; CollisionObject * collision; MoveLogic * movelogic; }

So we could build a Chopper with 2 tank guns and 2 rocket guns without having to worry about their firing logic.

(There is also an added bonus that these entities don't have to do their own processing; a container somewhere could hold all active CollisionObjects and process them at once, this means nice cache bonuses for code and data).

#13 Kovaz   Members   -  Reputation: 136

Like
0Likes
Like

Posted 03 August 2014 - 05:45 PM

 

Because you start coming up with crazy inheritance structures just to add some extra functionality.

Lets say you start with something which needs to be drawn; Actor { Vector2 position; Sprite* graphic; }
But now you need one to be able to have collisions in some cases; CollidableActor : public Actor { BoundingBox box; }
But not you need one to also be moveable in some cases; MoveableCollidableActor : public CollidableActor { void Move(); }
But now you need one to also be able to fire; ShootableCollidableActor : public MoveableCollidableActor { void Fire(); }
Oh, but now you need a tank; Tank : public ShootableCollidableActor { void Fire(); void Move(); /* because tanks move differently */}
Oh, but now you need a tank which also has rockets; RocketTank : public Tank { void FireRocket(); }
And now I want a Jeep with no gun; Jeep : public MoveableCollidableActor { void Move(); /* because Jeeps move differently */}
And now I want a Jeep with a gun; GunJeep : ShootableCollidableActor : public MoveableCollidableActor { void Fire(); }
And now I want a turret which doesn't move and ooops.. all my shootables are moveables...

At which point you have a ridge hierarchy and things in the wrong place.
If you try to resolve it by moving 'shoot' down and 'move' up then you end up with anything which can move must shoot, but our Jeep doesn't so that's wrong.

This is why the advice is that you should prefer composition over inheritance by default.

In this case;
Actor { Vector2 position; Sprite * graphic }
Tank { Weapon * primaryWeapon; Actor *; CollisionObject * collision; MoveLogic * movelogic; }
RocketTank : Tank { RocketWeapon * secondaryWeapon; }
Jeep { Actor *; CollisionObject * collision; MoveLogic * movelogic; }
GunJeep : Jeep { Weapon * primaryWeapon }
Turret { Actor *; Weapon *; CollisionObject *}

There is still inheritance there but they are short chains (Jeep -> GunJeep, Tank -> RocketTank, MoveLogic -> {TrackedMove, WheelMove}) and are not bound so if you wanted to make a new object like a helicopter it is easy to plug it in and give it more weapons.

Chopper { Weapon* weaponArray[4]; Actor *; CollisionObject * collision; MoveLogic * movelogic; }

So we could build a Chopper with 2 tank guns and 2 rocket guns without having to worry about their firing logic.

(There is also an added bonus that these entities don't have to do their own processing; a container somewhere could hold all active CollisionObjects and process them at once, this means nice cache bonuses for code and data).

 

 

Okay, yeah doing everything with inheritance is really messy. However, I'm still not sure how to solve some issues with composition:

1) Who gets what data? My actor needs a position to display on the screen, but so do my CollisionObject and MoveLogic. Do I just give them each a position member and then update all of them whenever any of them change? If I modify them through the unit class that's holding all of them, it seems easy enough to pass the data along, but what happens when my CollisionObject detects a collision and wants to move? I now have to get that information back to my Unit object or? I'm not really sure.

 

EDIT: My most important question: How does my position get from my MoveLogic to my CollisionObject? My gameloop at the moment is something like this:

  1. Process input from user. Pass commands to actors and let them delegate the responsibility.
  2. Loop through all logic objects (MoveLogic, Collision), and let them do their logic.
  3. Loop through Actor objects and let them update themselves based on their logic objects (grab new position from MoveLogic, etc.)
  4. Get render data from Actors, draw to window
  5. repeat.

 

2) How do I store them in containers? Without a common superclass, do I need a container for each type of object? With the actor superclass, it was easy:
 

for( auto actor: actorContainer )
{
    doSomethingToActor( actor );
}

Do I just never actually talk to the Tank, Jeep, Chopper, etc classes directly? So I have a container of Actors, and container of MoveLogics, a container of CollisionObjects, etc. and I just iterate through those regardless of who actually owns them? In that case, again how do I synchronize data between the Actor, MoveLogic, CollisionObjects, and any other members that hold similar data?

 

EDIT:

As a potential solution to my question about containers, is it ever a good idea to implement some pure virtual classes to function like Java interfaces? Something like:

class MovingObject
{
    public:
    virtual void move( Vec2 move ) = 0;
}

class AttackingObject
{
    public:
    virtual void attack( Actor* target ) = 0;
}


class Jeep: public MovingObject
{
    ...
}

class Tank: public MovingObject, public AttackingObject
{
    ...
}

That way I could refer to the objects themselves in containers, while allowing myself to implement the actual logic behind-the-scenes using composition, which also allows me to keep a container of the CollisionModel, MoveLogic, etc. objects to loop through.


Edited by Kovaz, 03 August 2014 - 07:19 PM.


#14 Strewya   Members   -  Reputation: 1579

Like
2Likes
Like

Posted 04 August 2014 - 02:42 AM

Okay, yeah doing everything with inheritance is really messy. However, I'm still not sure how to solve some issues with composition:

1) Who gets what data? My actor needs a position to display on the screen, but so do my CollisionObject and MoveLogic. Do I just give them each a position member and then update all of them whenever any of them change? If I modify them through the unit class that's holding all of them, it seems easy enough to pass the data along, but what happens when my CollisionObject detects a collision and wants to move? I now have to get that information back to my Unit object or? I'm not really sure.
 
EDIT: My most important question: How does my position get from my MoveLogic to my CollisionObject? My gameloop at the moment is something like this:


  • Process input from user. Pass commands to actors and let them delegate the responsibility.
  • Loop through all logic objects (MoveLogic, Collision), and let them do their logic.
  • Loop through Actor objects and let them update themselves based on their logic objects (grab new position from MoveLogic, etc.)
  • Get render data from Actors, draw to window
  • repeat.

Have a data structure called PositionInfo, which is given to any object that needs a position in the game world. The MoveLogic modifies this data, the CollisionLogic modifies this data during the collision resolve step, the RenderLogic reads this data to draw the object at the right location.

Have a data structure called CollisionInfo, which is given to any object that can collide with other stuff in the game world. The CollisionLogic reads it and determines if it's colliding with any other CollisionInfo structure.

Have a data structure called RenderInfo, which contains any data required to draw the object (mesh, sprite, etc). The RenderLogic reads from this data, and draw its contents at the location indicated by the PositionInfo data.

Similar with data structures AnimationInfo, LocomotionInfo, InputInfo, HealthInfo, WeaponInfo, etc etc for any other unit data information.

 

Create a class called Tree, which composes the CollisionInfo, RenderInfo and PositionInfo. The RenderLogic requires PositionInfo and RenderInfo in order to do its job. When a Tree is created, the creation logic will register the Trees' PositionInfo and RenderInfo to the RenderLogic, so on each frame the RenderLogic will loop over all registered data, read from them and do its job. The CollisionInfo and PositionInfo are registered to the CollisionLogic so dynamic objects don't pass through the tree.

Create a class called PlayerCharacter, which composes PositionInfo, CollisionInfo, RenderInfo, AnimationInfo, InputInfo, HealthInfo, LocomotionInfo, ......., and on creation each of these data members are registered to any Logic that you want to operate on your PlayerCharacter. The Logic operations are key here, as they can only operate on objects that have certain combinations of data structures. For instance, you can't register the Tree to the MoveLogic because it doesn't have a LocomotionInfo, even tho it has a PositionInfo.

 

When you want to create a Tree, ask the TreeFactory to create one (which should also automatically register the Tree data structs to the neccesary Logic). The Tree still owns the data, but the Logic has pointers to this data (and ofcourse, different caching techniques are possible here, but this is a KISS method which should be preferred until you prove it's not good enough from profiling). When you determine the Tree should be destroyed, ask the TreeFactory to destroy it, which will also unregister the data from the Logic.

 

Your example of finding a collision and wanting to move should be handled by the CollisionLogic entirely. The collision resolve step doesn't modify the position of the object that much, only so it's not in a collision anymore. But your MoveLogic will modify the position with regards to the LocomotionInfo, because the object might have certain acceleration, velocity, forces, friction, dampers etc etc in effect. First find ALL collisions for all pairs of CollisionInfo+PositionInfo, and when you find them all, resolve them one at a time (again, look at Box2D or Bullet).

 

Your game loop is then modified:

  1. InputLogic()
  2. MoveLogic()
  3. CollisionLogic()
  4. AnimationLogic()
  5. HealthLogic()
  6. WeaponLogic()
  7. ...
  8. RenderLogic()
  9. repeat

2) How do I store them in containers? Without a common superclass, do I need a container for each type of object? With the actor superclass, it was easy:
 

for( auto actor: actorContainer )
{
    doSomethingToActor( actor );
}
Do I just never actually talk to the Tank, Jeep, Chopper, etc classes directly? So I have a container of Actors, and container of MoveLogics, a container of CollisionObjects, etc. and I just iterate through those regardless of who actually owns them? In that case, again how do I synchronize data between the Actor, MoveLogic, CollisionObjects, and any other members that hold similar data?
 
EDIT:
As a potential solution to my question about containers, is it ever a good idea to implement some pure virtual classes to function like Java interfaces? Something like:

That way I could refer to the objects themselves in containers, while allowing myself to implement the actual logic behind-the-scenes using composition, which also allows me to keep a container of the CollisionModel, MoveLogic, etc. objects to loop through.

 


You don't need to sync data since all of the Logic operations have the same pointer the the one Info of a certain object they're all operating on.

Also, if you do a split of data vs logic (the Info vs Logic in my example above), you don't need the virtual interfaces. Logic operates on Info, to which it has a pointer to, and you don't have to cast them in any direction. Simply dereference, read/write and move on.


devstropo.blogspot.com - Random stuff about my gamedev hobby


#15 haegarr   Crossbones+   -  Reputation: 4580

Like
1Likes
Like

Posted 04 August 2014 - 03:09 AM


Do I just never actually talk to the Tank, Jeep, Chopper, etc classes directly? So I have a container of Actors, and container of MoveLogics, a container of CollisionObjects, etc. and I just iterate through those regardless of who actually owns them? In that case, again how do I synchronize data between the Actor, MoveLogic, CollisionObjects, and any other members that hold similar data?

There is no single right way. So the following is just one possibility...

 

Remember the mention of sub-systems above. That are places where some logic is implemented. It leads away from the object centric view to a task centric view, so to say.

 

For example: In an object centric view of things, when a collision between objects A and B is given, asking object A for colliders returns B, and asking object B for colliders returns A. That looks like 2 collisions, but is actually only one. In a task centric view of things, a sub-system is asked for collisions, and it returns { (A,B) }.

 

This could be done with a free function. However, wrapping the task into a class allows to associate management structures that are especially suitable for the task. In the case of collision detection it is known that only dynamic objects can collide. So, if the sub-system has two separate lists, one for dynamic objects and one for static objects, and objects that do not participate on collision (e.g. bird's of a flock) are not represented at all, then processing of collision detection can be done in a more efficient manner.

 

The fact that a game object is to be considered by a particular sub-system is encoded by components. Components define the how and why a game object participates.

 

This is the fundamental idea. It need to be fleshed for implementation, of course.

 

Components like CollisionVolume or Placement are data components. They have a value but no logic. Several sub-systems can refer to the same data component. E.g. the Placement component is used by all sub-systems that need the position and/or orientation of the game object. Other components may define logic, for example as extension to sub-systems, self altering data component values. This way means that for example Placement is needed exactly one for every game object that needs to be placed in the world. Synchronization is done by processing the sub-systems in order, i.e. choosing a suitable order of update() calls inside the game loop.

 

It is possible to duplicate components in several sub-systems, too (although I do not recommend this in general). There is no principle problem in copying component values as soon as logically previous sub-system runs are completed (which is automatically guaranteed due to the order in the game loop).



#16 crancran   Members   -  Reputation: 416

Like
0Likes
Like

Posted 04 August 2014 - 10:01 PM

2) How do I store them in containers? Without a common superclass, do I need a container for each type of object? With the actor superclass, it was easy: 

You are not likely going to be iterating game objects in the loop but rather their components which make up their full behavior.  Because of this, you will want to store your component structures in some cache efficient way.  As a first pass, a std::vector would suffice.  

 

The next question is where to place these vectors?  

 

I have seen some recommendations where the systems who transform the data own the vectors, but I tend to disagree with this approach.  I don't believe any one system owns this information.  Systems are designed to read/write data, transforming it along the way as a serial set of inputs & outputs; nothing more.  That's not to say these systems won't have their own internal data structures to aid them in their task, but the "input" data from the components feels more like public knowledge information.

 

I prefer the idea that my game object system owns a series of object pools, each of a specific type of class.  These pools are dynamically created either through code when the engine initializes (core components or those extended by user code) or via scripts.  The transformation subsystems that operate on my components have a central place they interact with to obtain not only information about a game object but any of the components which make up it's total behavior (aka the game object system).

 

The beauty is you can accomplish this without any Actor class or a base Component class.  That's not to say one shouldn't have them because there are instances where having wrapper classes can simplify an API making it less brittle and easier to alter through code iterations.  But none of what I described implies their necessity at all.


Edited by crancran, 04 August 2014 - 10:03 PM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS