Game Engine Discussion

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

Recommended Posts

I've been working for a number of months now on a 2D, top-down adventure game inspired by Zelda. This is actually my third attempt at such a game, and this time around I know enough about what I'm doing to have produced a maintainable codebase. That said, I think I've made some high-level architectural problems that need addressing before I continue. The gist of it is that each object is responsible for abiding by several important rules that I'm thinking should really be handled by the engine. I'll detail a couple of the more significant ones. Before I jump right in, I might offer a disclaimer -- I've done considerable research and am familiar with a number of approaches and concepts relevant to my concerns, but I'd like to benefit from the wisdom that others have collected while encountering similar issues. Firstly, each object has the responsibility to do its own collision detection. While the real work here isn't handled by each class, it is only by convention that a given object make sure it does not move into an illegal space. The main reasoning that led me down this path was that some objects may not care about colliding with other objects -- a bird would never collide with a ghost, for example. While some good modeling could have solved this one, things get a little trickier. Lets say I have two objects that, at present, do not collide. Upon a subsequent update, they both wish to move and occupy the same space. There are number of things that could happen here. Keeping in mind that I would like the game engine to house as much logic as is reasonable, I've been leaning toward a kind of two-phase update solution that would work similar to this: 1) each object has its update() method called and will basically propose what it wishes to do on this update. 2) after all objects have had a chance to update(), the engine will then validate each object's proposal to ensure that it is legal. 3) if it is not legal, the object will have another method called, given enough information to understand what was not kosher. this would likely be details about what it collided with, when it collided (perhaps it would have collided 20 milliseconds into the 50 millisecond update), and where it collided. 4) it would probably make sense to basically repeat from step 1 in such case, until no illegal states are detected. Here's where I become a little unsure of things. Clearly one problem is that, with lots of commotion going on, there could be quite a few collisions that could trigger quite a few recalculations. This could get quite expensive, and could continue into perpetuity with poorly behaved objects. Another concern is that, despite my greatest attempts, there will probably still be places where it makes sense for an object to do quite a bit of the same logic the engine would be doing, such as pathfinding. So there might be a lot of duplicated work going on. Another concern I've run into regards who is responsible for attacking who. If object A wants to attack object B, things get interesting when B doesn't want to be attacked by A. B might be ethereal, or might be otherwise immune to A's weapon. Clearly at this point you need a mediator of some form to sort things out. Also consider that A might be very interested to know if he actually struck B with his sword and made a fleshy sound, or if it made a whoosh sound as it swung right through him, or if it made a loud striking sound as it hits B's shield. B might also be endowed with Thorns and A would be the one to take damage. Currently I have a baseclass Attackable which has a single method:
 virtual AttackResponse* handleAttack( AttackEvent* event ) = 0;
This basically works where A suggests damage to B, provides some details about his attack, and B responds with how he handles the attack. This system would probably work fairly well if every object implements the Attackable class correctly, and maybe that's an acceptable assumption. However, especially with fully scripted objects, that may not be the best solution. So these are my biggest concerns that I'd really like to distill into some very well defined contracts. If anyone has some insight to provide, I'd love to hear it. Regardless, thank you for reading and sorry for the long post! Some information can be found about the game at http://www.master-sword.org, but there really isn't much there at the moment. Thanks much, Stephen

Share on other sites
Sounds like you've got a pretty good setup already. I much prefer the "turn-based" collision method (each person checks and moves to their final location on their update), rather than trying to do everyone at once, because you can guarantee that there are no intersections in the first place. The 2 phase method always has to have a fallback if it can't resolve all collisions.

IMO, "engine" code shouldn't really be doing that much logic in the first place. More just be a collection of utilities that the game code can use if it makes life easier. If you find yourself duplicating code, just pull it out into a seperate file somewhere. Make a "pathfinder" class. Maybe a "path follower" class as well, to store information about where you are on the path and handle logic to walk around local obstacles and such. Just because a piece of code is specific to your game, doesn't mean it can't be made reusable within the game.

As for the attacking conundrum, that's basically how I usually do it. Attacker says "hey you, I'm doing this", defender says "ok, I'll do this". I'm not sure exactly what you mean by "fully scripted objects", but if you want the defender's response only partially customizable, then add another layer of handleAttack that does some stuff, and then calls the customizable one. The reusable game code rule applies here too. The base object class could have a bunch of attack response functions, and then the derived classes call whichever one they want.

Share on other sites
Quote:
 Original post by StepheltonThat said, I think I've made some high-level architectural problems that need addressing before I continue.-snip-Firstly, each object has the responsibility to do its own collision detection.

It certainly seems as though you have. That responsibility requires one object to then know about every other actor in the game. One of the basic design principles of OOP is than an object only knows about itself and its subordinates. Requiring objects to know about their world or kin is going to lead to problems.

Share on other sites
Some of the problems, I wonder if are problems (if they've ever actually happened). After all, one of "THE" roots of all evil is premature optimization.

Like how your first step is to update everything THEN evaluate whether that's legal THEN find out why THEN repeat until no bad cases? It sounds over contrived to me. I don't want my engine knowing what's legal because I want my sprites to act independently and without asking permission. I let my engine check for collisions, but I let my sprites deal with it on their own, and in a way that insures legality.

But, still... I do have a little problem with collision detection and such, but it could be more easily solved by learning more about geometry than adopting your system.

But here's one:
Quote:
 Another concern is that, despite my greatest attempts, there will probably still be places where it makes sense for an object to do quite a bit of the same logic the engine would be doing, such as pathfinding. So there might be a lot of duplicated work going on.

Back to the elementaries of code reusability. Either through common inheritance (like a class that deals with pathfinding that does not know whom it's finding a path for) or functions that come out with consistent resaults (like a func that takes in position, destination, map, and maybe size_of_mover, and outputs a path).

Quote:
Original post by Telastyn
Quote:
 Original post by StepheltonThat said, I think I've made some high-level architectural problems that need addressing before I continue.-snip-Firstly, each object has the responsibility to do its own collision detection.

It certainly seems as though you have. That responsibility requires one object to then know about every other actor in the game. One of the basic design principles of OOP is than an object only knows about itself and its subordinates. Requiring objects to know about their world or kin is going to lead to problems.

What I don't like about what you say is you use OOP as a replacement for an argument. It's like saying "they say not to go in the cave" instead of saying "a monster that will eat you lives in the cave." You do say "Requiring objects to know about their world or kin is going to lead to problems," but that doesn't add to your argument because it's something we can assume from your stance. What problems? Are they complexities?

I want my objects to know as little about each other as possible, but I don't know any other ways of mapping out how objects should behave around each other. Is it not the same as overloading an operator? (Although, on a side note, having external functions that work on the classes is an OK workaround.)

Share on other sites
Quote:
 Original post by DekuTree64Sounds like you've got a pretty good setup already. I much prefer the "turn-based" collision method (each person checks and moves to their final location on their update), rather than trying to do everyone at once...

That's a much more synchronous approach, which I hadn't really considered before. As long as each update is guaranteed to be minute, that's probably not a problem, but I can think of situations where you would miss collisions and such. That would simplify things quite a bit though, I'll give it some thought.

Quote:
 Original post by DekuTree64IMO, "engine" code shouldn't really be doing that much logic in the first place. More just be a collection of utilities that the game code can use if it makes life easier. If you find yourself duplicating code, just pull it out into a seperate file somewhere.

I wasn't referring to duplicating code, per se. But if an object has already examined the space around him and determined where he can move, it doesn't necessarily make sense to check for collisions afterward... or maybe it does, but that was what I was getting at.

Quote:
 Original post by DekuTree64I'm not sure exactly what you mean by "fully scripted objects"...

I intend to expose enough API to the scripting [LUA] to allow objects to implement their own update methods.

Quote:
 Original post by TelastynIt certainly seems as though you have. That responsibility requires one object to then know about every other actor in the game. One of the basic design principles of OOP is than an object only knows about itself and its subordinates. Requiring objects to know about their world or kin is going to lead to problems.

Perhaps I've neglected some details... I don't have each object examining each other object; rather, the object queries the current level for a list of objects at a given location. So at the end of the day, he only cares about anything in his way. And this is exactly the kind of thing I'm trying to sort out -- perhaps that's too much for the object to care about.

Quote:
 Original post by Splinter of ChaosLike how your first step is to update everything THEN evaluate whether that's legal THEN find out why THEN repeat until no bad cases? It sounds over contrived to me. I don't want my engine knowing what's legal because I want my sprites to act independently and without asking permission. I let my engine check for collisions, but I let my sprites deal with it on their own, and in a way that insures legality.But, still... I do have a little problem with collision detection and such, but it could be more easily solved by learning more about geometry than adopting your system.

Perhaps it is a little much, but I think it would ensure that everything is "correct." But maybe sacrificing some accuracy for some simplicity wouldn't be such a bad thing.

My original attempt at collision detection here was basically an event-listener system whereby every object didn't care about moving somewhere, it just moved. Then collisions would be detected, and collidees would be dispatched an event. This worked well in my simple test cases, but ultimately ended up unused because I found it much simpler for objects to simply make sure they couldn't walk into things before moving.

Quote:
 Original post by Splinter of ChaosBack to the elementaries of code reusability. Either through common inheritance (like a class that deals with pathfinding that does not know whom it's finding a path for) or functions that come out with consistent resaults (like a func that takes in position, destination, map, and maybe size_of_mover, and outputs a path).

Yes, yes, see above. I wasn't complaining about code reuse; I don't have a problem there. My concern was wasting CPU cycles to recalculate things that had already been done.

Thanks a lot for your responses. Perhaps I'm over-complicating things; I suppose I could implement things in a much more turn based style where each object basically gets a lock on the entire world for the duration of his update. Has anyone run into problems with this kind of implementation?

Share on other sites
Quote:
 Original post by Splinter of ChaosWhat problems? Are they complexities?

I perhaps thought 'would need to know about every other actor in the game' was a clear enough description of a design catastrophe that I needn't elaborate...

Problem 1:

Any time you expand the game with a new actor you get to revisit the code for every other single actor in the game, and check if you need to update the collision detection code.

Problem 2:

Actor A needs to know intimate details of Actor B. That basic principle will cause the details of B to become more public, which drives toward making them hard to change (since then you'll have to change all the other actors too [akin to problem 1]). More often you'll forget (or do it in a non-breaking way) leading to bugs.

Problem 3:

Collision detection is not universal. Not all parts of code that deals with an actor will care about it. In multiplayer, the client side might not care; or might need to detect possible collisions, and know what the reaction might be (but not actually cause it). Hard coding responses is icky here.

Problem 4:

One class solves one problem. Adding collision response to a class you're making it do multiple things. More brittle. Larger, harder to work with.

Share on other sites
Quote:
 Original post by Splinter of ChaosI perhaps thought 'would need to know about every other actor in the game' was a clear enough description of a design catastrophe that I needn't elaborate...

Hopefully I clarified this above; each object need only know if there is an obstacle in its way. It doesn't necessarily care what that is (though it could if it needs to). The level controller is responsible for maintaining a collection of objects and provides a method to query a specific region for objects.

So the typical update method, when wanting to move, looks something like this:

1) Decide where I want to go
2) Ask the level if I can move there
3) Move

Now your concerns might be a bit more applicable to my attack system, and that's precisely what my concerns were about. However, the "dialog" between an attacker and victim looks something like this "I swing my sword at you with this much force, how do you react?" "Well you hit me, but I'm a tree, so my leaves shake a little."

So this could potentially lead to situations where one object needs to know intimate details about another when this exchange happens. For example, in the above scenario, a "thunk" noise should be played. Who should play that, and how would they know? Does the attacker know that his sword makes said noise when it hits a tree, or vise versa? I think that may be avoidable as long as there are some well known attack types and responses.

Share on other sites
There's quite a bit of info here so forgive me for not reading it all.

My primary focus with engine programming is controlling the responsibility. I try to design each system so that it is nearly "fire and forget." Once the game is up running, there is very little system interaction going on.

The physics system is independently manageing everything physics related, the renderer is off doing its thing, and the game is processed as another seperate entity. Without one, the other two would run fine (sort of).

The only thing making each collision object unique is 1.) its physical properties which are contained inside the physics object. And 2.) its system level callbacks which inform each game object when a collision is *about* to occur, and when one has been known to *definitely* occur.

edit: I would just like to make this clear that not "every" object is notified of "every" collision. But each physical object can register a collision callback, which is called when there is a collision with that particular object. These callbacks are game library level and then can do any number of things, including spawning particles and playing sounds.
/edit

You should design everything so that if need be, it can be replaced as a whole and no other part of the game should be affected. For example: switching to Havok from ODE, or switching to D3D from Opengl (obviously at compile time).

Reusability, Reusability, Reusability.

[Edited by - bzroom on June 21, 2008 5:16:27 PM]

Share on other sites
Quote:
 Original post by StepheltonHopefully I clarified this above; each object need only know if there is an obstacle in its way.

Clarified what you do, not the inherent problem.

Quote:
 It doesn't necessarily care what that is (though it could if it needs to). The level controller is responsible for maintaining a collection of objects and provides a method to query a specific region for objects.

Quote:
 So the typical update method, when wanting to move, looks something like this:1) Decide where I want to go2) Ask the level if I can move there3) Move

Which are 3 things that an actor doesn't do. Some controller might decide where to move the actor, but that controller knows of the level; knows the game rules about movement and does it.

Quote:
 For example, in the above scenario, a "thunk" noise should be played. Who should play that, and how would they know? Does the attacker know that his sword makes said noise when it hits a tree, or vise versa?

So now your collision detection code is dependent on your sound system? Neither of them care. Collision detection shouldn't even care that they're swords and trees. See if geometries collide. Return bool or throw an event or send a message...

Share on other sites
Quote:
 Original post by Stephelton...As long as each update is guaranteed to be minute, that's probably not a problem, but I can think of situations where you would miss collisions and such....Perhaps I'm over-complicating things; I suppose I could implement things in a much more turn based style where each object basically gets a lock on the entire world for the duration of his update. Has anyone run into problems with this kind of implementation?

Yes, that style doesn't deal with fast moving objects very well. You can do swept collisions to avoid jumping over stationary objects in a single frame, but it's still not "correct" for moving objects since in reality they would be moving simultaneously.
Note though, that the problems only come up for 2 fast moving objects against eachother. In a Zelda-type game, you might shoot an arrow and it would fly fast, but it only needs to collide against slow things, so you're fine.

For the attack collision, querying the level for a list of intersecting objects sounds fine, since you don't need to know exactly what they are, just that you hit them and that they have a handleAttack function.

For sounds, maybe have the tree rustle played by the tree, and the sword thunk played by the character. The AttackResponse object could have a surface type, used in combination with the character's weapon type to decide what kind of impact sound to play.

Telastyn: All this response stuff would be handled at the actor/controller layer, not by the collision system. The collision system only needs to deal in collision boxes, which would probably have a parent actor pointer, but that can be forward declared since it's only used for collision response, by clients who know more than the collision system.

1. 1
2. 2
3. 3
Rutin
16
4. 4
JoeJ
13
5. 5

• 9
• 9
• 14
• 10
• 25
• Forum Statistics

• Total Topics
632646
• Total Posts
3007636
• Who's Online (See full list)

There are no registered users currently online

×