Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411709 Posts in 69403 Topics- by 58457 Members - Latest Member: FezzikTheGiant

May 20, 2024, 04:52:14 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Game coding best-practices
Pages: 1 2 [3]
Print
Author Topic: Game coding best-practices  (Read 9151 times)
Rob Lach
Level 10
*****



View Profile WWW
« Reply #40 on: March 21, 2010, 03:18:49 PM »

Anyway. MVC in a game sounds like more trouble than it's worth. Albeit, that may be because I'm not intimately familiar with MVC, and have never tried implementing it myself. Perhaps I'm just old school?

I've only worked on one game that didn't follow some sort of design pattern and I ended debugging input in my rendering code.
Logged

LemonScented
Level 7
**



View Profile
« Reply #41 on: March 21, 2010, 06:30:30 PM »

I've never seriously considered this for game event handling. Interesting! In your experience, what's the break-even point on processes vs. efficiency?

I'm going to undermine all my clever wisdom and sound like an idiot now by admitting that I don't quite understand the question. Do you mean when do I use the event system vs. when do I just have objects call each other directly in order to get optimal performance? Or is the question a more general one about "making stuff nice and maintanable" vs "making stuff fast"? I suppose my answer to both of those questions is broadly the same:

1. Don't optimise
2. (for experts only) Don't optimise yet

(Attributed to M. A. Jackson, I think)

It's important not to write grossly inefficient code, of course, but in the vast majority of cases it's decidedly unwise to choose speed over readability. I only optimise code after I've identified a performance problem, and run a profiler to ascertain the cause of it. With regards to my event system, I've never stress-tested it to see how many events I can throw around before incurring a performance hit, but it's never been a problem for me, nor do I ever think it will become one.

I was wondering about this, as I just used an event broadcast system for the first time the other week for 7DRL. My broadcaster didn't handle input, just game events. So it went like:

- input: player presses key or game entity makes decision
- input mapped to command (i.e. move, fire)
- command asks broadcaster to send event with data created by command (player moving, entity firing)

I did it like this so the input didn't deal with game-specific data requirements like xy positions, when it created the data packet to pass to the event broadcaster for the event (at least for the player -- the game entity input was of course tied in with the game).

Are there advantages/disadvantages to one approach over the other?

Unless I'm mistaken, the approach you described is pretty much the one I use. I have an InputMgr which knows basically nothing about the rest of the game. It updates to check for input (from a keyboard, mouse, joypad, whatever), and maps that to an action - i.e. it knows that "g" means that it should send a "throw grenade" event, but doesn't really know what a grenade is, who throws it, or any of that stuff. So then it asks the EventMgr to send a "throw grenade" event.

- The InputMgr only knows how to get input, how to map that to a command event (although it knows nothing about the meaning of the command), and how to get the EventMgr to broadcast that event.
- The EventMgr only knows how to broadcast events to registered listeners when it's asked to.
- The Player only knows how to register itself as a listener for certain channels of the EventMgr, and react appropriately (in this case, by sending another event to spawn the grenade, if it's possible and necessary to do so).

I might have misunderstood you but I don't see how our approaches differ.
Logged

blundis
Level 0
**


View Profile WWW
« Reply #42 on: March 22, 2010, 01:52:52 AM »

- The Player only knows how to register itself as a listener for certain channels of the EventMgr, and react appropriately (in this case, by sending another event to spawn the grenade, if it's possible and necessary to do so).

I'm interested in how you manage this, specifically how to have the event manager only send certain events to certain listeners.

When I have done this i've just sent every single event to every single event listener and have the listener decide if it's worth it's time. I suspect that implementing an event listener to only handle one event, with an easy-out if statement returning if the event isn't of interest shouldn't entail more overhead then having separate queues and checking each event in the event manager to find which listeners to send it to.

Is there a reason behind doing it this way?
Logged
Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #43 on: March 22, 2010, 02:50:50 AM »

- The Player only knows how to register itself as a listener for certain channels of the EventMgr, and react appropriately (in this case, by sending another event to spawn the grenade, if it's possible and necessary to do so).

I'm interested in how you manage this, specifically how to have the event manager only send certain events to certain listeners.

Check out the Observer design pattern. If you don't have it already, get Gamma et al's book "Design Patterns."
Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
blundis
Level 0
**


View Profile WWW
« Reply #44 on: March 22, 2010, 03:48:59 AM »

Aha I'm actually thinking of two different design patterns it turns out, the one i used more resembles the Mediator pattern. Any specific reason you would choose Observer over the Mediator pattern (which seems easier to implement)?
Logged
Rob Lach
Level 10
*****



View Profile WWW
« Reply #45 on: March 22, 2010, 04:21:25 AM »

Check out the Observer design pattern. If you don't have it already, get Gamma et al's book "Design Patterns."

Or check out Wikipedia. They have design patterns from a bunch of books on there.
Logged

Mikademus
Level 10
*****


The Magical Owl


View Profile
« Reply #46 on: March 22, 2010, 07:03:52 AM »

Aha I'm actually thinking of two different design patterns it turns out, the one i used more resembles the Mediator pattern. Any specific reason you would choose Observer over the Mediator pattern (which seems easier to implement)?

In context, a Mediator (or Director, as it is sometimes called) is something different than was discussed above. An Observer explicitly subscribes to another object, that is, it will only be notified of events from the very objects is is interested in. A Mediator on the other hand is a high-level class governing the behaviour of many other related but disparate objects, a compound that together constitutes a something. In a small scale, this can be a dialogue with many controls that do not know of each other, yet works together. In a larger scale, it can be something as large as a Game class. But if you make your game a Mediator then you've basically only plugged in your global functions or other classes inside a namespace. A better use for a Mediator in a MVC context would be as the Controller class.

Basically, an Observer will only receive what it is interested in while a misapplied Mediator can create just as big a mess of your code as you had before.
Logged

\\\"There\\\'s a tendency among the press to attribute the creation of a game to a single person,\\\" says Warren Spector, creator of Thief and Deus Ex. --IGN<br />My compilation of game engines for indies
george
Level 7
**



View Profile
« Reply #47 on: March 22, 2010, 07:42:04 AM »

Also, with these event systems you're all speaking of. Would anyone care to share some pseudocode(or real code, if you want) on how you'd set such a system up? I don't understand how you would send(or handle) an arbitrary data package which accompanies the event message.

I don't have much experience with languages like C/C++/Java, but in dynamically typed languages like Python handling the data is pretty simple.

The whole thing basically works like this (keeping in mind this is just my code, so no doubt there are some things that could be improved, and obviously I'm not showing all the code):

Code:
# when the object is initialized, register with the broadcaster and give a default priority:

def __init__(self, data):
    self.priority = 1000

    self.__dict__.update(data)

    event.register_component(self)



# take the registering object and search it for handlers (i.e. events), add these
# to a hash of event:objects; if the event already exists, just add the object

def register_component(self, component):
    for item in dir(component):
        if item.startswith('on_'):
            if not str(item) in self.listeners:
                self.listeners[str(item)] = [component]
            else:
                self.listeners[str(item)].append(component)


# when input happens, send it to the command mapper

def action(self, data):
    action = data['input']

    if action in self.actions:
        func = self.actions[action]
        func(data)


# the command mapper finds the command if it exists; for example, a move command. Note
# that it modifies the data (a dictionary/hash) sent previously.

def move(self, data):
    self.directions = {
                        'up' : {'x':0, 'y':-1},
                        'down' : {'x':0, 'y':1},
                        'right' : {'x':1, 'y':0},
                        'left' : {'x':-1, 'y':0},
                        }

    data['event'] = 'on_move'
    data['outcome'] = 'continue'
    data.update(self.directions[data['input']])

    event.broadcast(data)


# broadcast the event to the objects who registered for it before. Sort the listeners
# in order of priority.

def broadcast(self, data):
    event = data['event']
    self.listeners[event].sort()

    for component in self.listeners[event]:
        data = getattr(component, event)(data)


# these objects return the data unmodified, or modified, as the case may be,
# for example, with collision:

def on_move(self, data):
    enactor = game.gids[data['enactor']]
    dx = data['x']
    dy = data['y']
    xx = enactor.XY.x + dx
    yy = enactor.XY.y + dy

    enactor_level = enactor.Model.level

    if not enactor_level == self.gid:
        return data

    level = game.gids[self.gid].Level

    if not (level.cells[yy][xx]['kind'] == 'floor' or level.cells[yy][xx]['kind'] == 'stairwell'):
        data['outcome'] = 'failure'
        return data

    return data


# continue with this until all listeners get the event; all listeners for an event ALWAYS
# get the event, though if the data is modified, they may do nothing with it.


Hope that was somewhat informative.
Logged
LemonScented
Level 7
**



View Profile
« Reply #48 on: March 22, 2010, 01:50:47 PM »

- The Player only knows how to register itself as a listener for certain channels of the EventMgr, and react appropriately (in this case, by sending another event to spawn the grenade, if it's possible and necessary to do so).

I'm interested in how you manage this, specifically how to have the event manager only send certain events to certain listeners.

When I have done this i've just sent every single event to every single event listener and have the listener decide if it's worth it's time. I suspect that implementing an event listener to only handle one event, with an easy-out if statement returning if the event isn't of interest shouldn't entail more overhead then having separate queues and checking each event in the event manager to find which listeners to send it to.

Is there a reason behind doing it this way?

As people have said, it's basically the Observer pattern. More specifically it goes something like this:

My solution is a bit of an incomprehensible templated mess in its inner workings, in order to make the interface for actually using them as clean as possible, but there are plenty of ways to deal with the fine details of this.

Essentially, my EventMgr contains a number of event "channels". A channel is a templated class which can contain a packet of data (an integer, or a vector, or a more complex struct or class), and also provides an interface for listeners to Attach or detach themselves.
So the interface looks a bit like this:

Code:
void cPlayer::Init()
{
    EventHandle mGrenadeHandle = EventMgr().GetChannel(THROW_GRENADE_KEY).Attach(this, &cPlayer::OnGrenadeKey);
    // ...
}

void cPlayer::DeInit()
{
    EventMgr().GetChannel(THROW_GRENADE_KEY).Detach(mGrenadeHandle);
    // ...
}

void cPlayer::OnGrenadeKey(const bool & bPressed)
{
    if(bPressed)
    {
        if(mGrenadeCount > 0)
        {
            ThrowGrenade();
        }
    }
}


So, the player class picks out an event channel from the event manager (identified by some enum value), and calls the Attach method, registering both the class instance ("this") and the callback function, and Attach returns a handle to that listener request, which the player keeps around to identify the request to Detach when the player class is de-initialised. The grenade key channel happens to have a data packet that's a boolean to indicate whether the key has just been pressed or just released (it could have any predefined data packet though, including void). When the state of the "g" key changes, the InputMgr calls sets up a bool to indicate the keystate and calls EventMgr().GetChannel(THROW_GRENADE_KEY).Send(bPressed);

This causes the event channel to iterate through all of the listeners registered on that channel and calls their callback functions, passing a const reference to the data so that the callbacks can check the state and respond or ignore as appropriate. I've also got some extra fiddly internal stuff to the EventMgr which deals safely with things Attaching or Detaching listeners, or sending new events during a callback.

In reality, I'm a bit "sloppier" than in this example in my own code - my InputMgr doesn't send a different event for each keypress, it just sends an event down the "key pressed" channel with a packet of data indicating which key has been pressed (and sends a different event when a key is released), which means that some of the callbacks will check the key, realise it's not one they're interested in, and will do nothing. Similarly, with something like an explosion event, I'll send it to every registered listener and let the listener do the check for it it's in the blast radius rather than burden the event system with working that stuff out. So there is a bit of redundancy, but assuming you choose the channels and the format of the data packets sensibly, it works well. Your description of "one event channel to rule them all" will certainly work, but you'd definitely start to incur performance overheads if all the callbacks for all the monsters, crates and wooden doors in the game are invoked because they're interested in explosions, but what you actually wanted to send was just a keypress which only the player cares about.
Logged

blundis
Level 0
**


View Profile WWW
« Reply #49 on: March 24, 2010, 09:15:27 AM »

Thanks for taking the time to make that great description Smiley.
Logged
drChengele
Level 2
**


if (status = UNDER_ATTACK) launch_nukes();


View Profile
« Reply #50 on: March 24, 2010, 02:16:32 PM »

I just encountered a thread on Model-View-Controller in the tutorials section which has some neat links and explains the concept really well:
http://forums.tigsource.com/index.php?topic=11029.0

I don't use MVC (I admit that before this thread I didn't know what MVC is), although of course I spontaneously ended up separating presentation from the entities and having a manager which handles entity interactions.

For example, I handle input by keeping a state of the entire input machinery (keyboard, joypads, mouse). An action mapper reads these states and converts these to GAME ACTIONS every tick. Player entities have a DIRECT_CONTROLLER extension which reads the action mapper and issues entity's command flags. AI entities have an AI_CONTROLLER extension which processes the situation and sets entity's command flags. To me this is a very compact WORKING system, but I concede I might be missing something here. Am I going to programmer hell for this?
Logged

Praetor
Currently working on : tactical battles.
starsrift
Level 10
*****


Apparently I am a ruiner of worlds. Ooops.


View Profile WWW
« Reply #51 on: March 25, 2010, 01:20:34 PM »

I've never seriously considered this for game event handling. Interesting! In your experience, what's the break-even point on processes vs. efficiency?

I'm going to undermine all my clever wisdom and sound like an idiot now by admitting that I don't quite understand the question. Do you mean when do I use the event system vs. when do I just have objects call each other directly in order to get optimal performance? Or is the question a more general one about "making stuff nice and maintanable" vs "making stuff fast"? I suppose my answer to both of those questions is broadly the same:

1. Don't optimise
2. (for experts only) Don't optimise yet

(Attributed to M. A. Jackson, I think)

It's important not to write grossly inefficient code, of course, but in the vast majority of cases it's decidedly unwise to choose speed over readability. I only optimise code after I've identified a performance problem, and run a profiler to ascertain the cause of it. With regards to my event system, I've never stress-tested it to see how many events I can throw around before incurring a performance hit, but it's never been a problem for me, nor do I ever think it will become one.

My question was the first, the performance hit on having a bunch of extra threads sitting around in a listen loop waiting to be tossed a event, rather than just directly implementing normal function calls to efficiently move from event to event. Not only that, but the amount of code being written - it seems like a lot of work for very little extra functionality, not to mention extremely error prone (esp. for catching events "out of order"), though I could see the sense of it in large realtime games with many events happening simultaneously. Furthermore, to use your g for grenade key example, it must be kind of treacherous to design context-sensitive interfaces(like mouse selection) - I imagine you have to broadcast a second flag to all the listeners to tell them who the event is for, based on a state.
Logged

"Vigorous writing is concise." - William Strunk, Jr.
As is coding.

I take life with a grain of salt.
And a slice of lime, plus a shot of tequila.
skyy
Level 2
**


[ SkyWhy ]


View Profile
« Reply #52 on: March 25, 2010, 01:38:25 PM »

Threadpools baby, threadpools. No reason to have the threads just look at the the loop hungrily and wait. Have a theadpool that manages all this. You toss a small task (event) for the threadpool and it finds the first available thread and after execution gives out result to the given task.

Writing concurrent code is always harder and more complex than writing consecutive code. Nothing changes that. But effectively learning it and actually using those extra cores you got laying around there is the key to success. Obviously if you are making your everyday "fly around and shoot things", the concurrency is just absolutely unecessary. But when starting to get a bit more complex, I see no reason not to do concurrect programming. No reason to limit yourself on one core when multiple ones are available for use.

It's all about keeping everything synchronous which can be a daunting and a difficult task. But all this is definitely for more experienced programmers and for bit more advanced stuff.
Logged

dr.crow
Level 1
*



View Profile
« Reply #53 on: March 26, 2010, 12:08:25 PM »

Check out the Observer design pattern. If you don't have it already, get Gamma et al's book "Design Patterns."

Or check out Wikipedia. They have design patterns from a bunch of books on there.
Did some googeling: http://gameprogrammingpatterns.com/component.html
Seems incredibly well-written and pedagogic. I finally understood what components are all about.
Logged
LemonScented
Level 7
**



View Profile
« Reply #54 on: March 26, 2010, 07:02:24 PM »

My question was the first, the performance hit on having a bunch of extra threads sitting around in a listen loop waiting to be tossed a event, rather than just directly implementing normal function calls to efficiently move from event to event. Not only that, but the amount of code being written - it seems like a lot of work for very little extra functionality, not to mention extremely error prone (esp. for catching events "out of order"), though I could see the sense of it in large realtime games with many events happening simultaneously.

I don't have threads sat on listen loops for events. I use multithreading in my game, but so far only for very specific purposes (in my case, fluid rendering, which there's another thread about). The event system is completely inert until a part of the code decides to put together a packet of data and send an event, at which point everything that's registered as a listener has that data packet passed to a callback. As such, the coding required to create an entirely new type of event is pretty lightweight, just:

- Defining somewhere what the event is called, and what sort of data packet it sends (one line of code for basic data types)
- Having some part of the code send an event when it needs to (one line)
- Have another part of the code register as a listener (one line), store a handle to that registration, use the handle to de-register (one line), and provide a callback.

The "out of order" issue is a slightly bigger one in that depending on what sends the event, and when, callbacks can be called at any point during the game update loop. Generally this isn't an issue with sufficiently carefuly programming, but for the more complex cases my system also has an ability to "queue" events on a channel, which only get sent out when the channel is explicitly "flushed", so you can have much finer control over when that happens.

As for when I use the event system vs. when I just have stuff talk directly to each other, it's generally a case of common sense. Given that my primary reason for having the event system is to reduce dependencies as much as possible (to increase maintainability), I'll tend to use it to bridge the gap between parts of the code which I feel should be loosely-coupled anyway - so, the physics system, the rendering system, and the core gameplay code should be kept pretty seperate. Some systems, audio for instance, are entirely driven by events they receive, whereas some (input and some of the higher-level timing stuff) pretty much don't do anything except send events. But for parts of the code which are more tightly-coupled, say the animation system and the sprite manager, I tend to just have those work directly together. Similarly, I'm much more likely to use the events system when I don't know or care how many listeners there are, but where there could be anything between zero and many varied things that are interested than I am when I know that only one specific part of the game will need that information.

Furthermore, to use your g for grenade key example, it must be kind of treacherous to design context-sensitive interfaces(like mouse selection) - I imagine you have to broadcast a second flag to all the listeners to tell them who the event is for, based on a state.

My example wasn't quite how I actually do things with input events, although it might be closer to that in future if I find it's better for me to do it that way. Currently my input system sends events for "key down", "key up", and "key is down this frame" along with an integer identifying which key the event is talking about. There are also events for "mouse button down", "mouse button up" and "mouse move" which send a struct containing the screen position of the mouse and (optionally) the button in question. With something like mouse selection, rather than have every game entity or HUD item have a callback to test seperately if they got clicked on a "mouse button down" (because running all those individual tests could get time-consuming if done that way), I'd have the Entity Manager be the listener, work out which entity got selected, and then act appropriately by storing the handle of the currently-selected entity, or by calling some method in that one entity to say "you've been clicked".
Logged

starsrift
Level 10
*****


Apparently I am a ruiner of worlds. Ooops.


View Profile WWW
« Reply #55 on: March 27, 2010, 12:33:33 AM »

I don't have threads sat on listen loops for events...The event system is completely inert until a part of the code decides to put together a packet of data and send an event, at which point everything that's registered as a listener has that data packet passed to a callback. As such, the coding required to create an entirely new type of event is pretty lightweight...

Ohhhh, I see. Nevermind, then. Smiley I thought by "listeners", you meant you were setting up a bunch of extra threads to sit around and listen.
Logged

"Vigorous writing is concise." - William Strunk, Jr.
As is coding.

I take life with a grain of salt.
And a slice of lime, plus a shot of tequila.
Pages: 1 2 [3]
Print
Jump to:  

Theme orange-lt created by panic