views:

254

answers:

6

What are the reasons for and against a game object drawing and updating itself? For example, if you have a game where the player has a position on screen, why not have an all-encompassing class:

public class Player {
    private int x, y, xVelocity, yVelocity;
    private Sprite s;
    //...

    public Player() {
        // load the sprite here, somehow?
    }

    public void draw(CustomGraphicsClass g) {
        g.draw(s, x, y);
    }

    public void update(long timeElapsed) {
        x += (xVelocity * timeElapsed);
        y += (yVelocity * timeElapsed);
    }
}

What is wrong with this design? What are the downfalls or concerns? How would you better write something like this, or better architect this type of thing in a game?

Also, somewhat connected, how would you implement loading that Sprite image?

And furthermore, how would you then implement collision between two Players?

(I should probably separate these extra two questions into new questions, huh?)

+1  A: 

It's ok, but if you have many different objects, all with sprites, positions and velociites that need updating, then there will be a lot of replication. You could push the location, velocity into a base class, but this hard-wires the type of motion. Better is to separate out the motion from the object itself.

For example, consider a ball bouncing up and down. That's not easy to model with just velocity alone. Instead, create a Motion class, with ConstantMotiion (for velocity) and BounceMotion for bouncing. The motion class then takes care of updating the object's position state from frame to frame.

mdma
+3  A: 

A possible drawback might be a violation of seperation of concerns. I would think that, ideally, the objects shouldn't know how they're rendered, so that the rendering engine could be tweaked seperately from the game objects...though, in practice, it may be that these things are often too interdependent to be seperated in this way.

Beska
+1 This is called AOP - Aspect Oriented Programming.
Danny Varod
+2  A: 

Suppose that every time your object (i.e. Player) is updated it should

1) redraw himself

2) notify other units that it's updated

3) write "update-action" to the history/log/whatever else (maybe you'll want to have a possibility to reproduce the whole game after it's finished, like a movie).

....

n) any other interaction between your Player-object and its environment.

In all these cases you'll have to change your update method, like:

public void update(long timeElapsed) {
   dosmth();

   redraw();
   notifyUnits();
   updateHistory();
}

It's very annoying. That's why in such cases Observer-pattern should be used. Your object should notify all listeners that it was updated. Listeners (GraphicsContext, History, units) will react in a proper way and your program will stay easy maintainable because all its parts will be responsible only for 1 concrete task.

Roman
note that redraw() should not directly repaint, but is merely an indication that the object needs to be drawn. An external renderer takes care of iterating across objects that need redrawing and drawing them in the correct sequence, applying appropriate clipping etc.
mdma
+1  A: 

It is useful to look at the possibility that most of the objects in a system should only modify engine-specific descriptors (velocity, active animation) and allow the engine to take care of the actual effect. There are a few exceptions to this (usually bullets and other single-tick effects), but by and large these systems rely on a single universal tick that updates the game. The reason for this is mainly, as mdma noted in his comment on Roman's answer, that the actual engine does a lot more than simple individual rendering operations. Similarly a physics engine will update the entire world, calculating collision volumes taking sub-tick movement into account.

Loading a sprite is usually a matter of loading a whole map's worth of resources and then addressing that sprite by name within the map's resource repository.

Collision is usually handled by a separate physics engine, see above.

Mike Burton
+6  A: 

It couples all of your rendering and logic code together when they have little in common beyond being conceptually tied to the same "entity". As your class gets bigger, you can find yourself with a huge monolithic Player that's a nightmare to maintain. Splitting out along domain boundaries (rendering, AI) makes it more manageable without having to give up much since those domains don't have a lot of overlap.

Beyond maintainability, there's some other considerations:

  1. If you want to process rendering and AI on different threads, mixing their state into the same class is just begging for nasty multi-threading problems.

  2. If you're using a language like C++, highly-coupled classes like this can kill your compile times.

  3. Depending on how your code and objects are laid out in memory, splitting objects into separate components for each domain can give you better cache coherency and much better performance.

Here's lots more info if you're curious.

munificent
I love the page you linked to with "lots more info"!! Still thinking about it all but it is **definitely** helpful to not only be told what to do, but given a decent example and discussion of the pitfalls and other considerations.
Ricket
Thanks! That's a book I'm working on, but it's more or less on hiatus right now. Let me know if anything is unclear in the chapter.
munificent
+1 Just for point 3 which is the key reason, especially on consoles. Encapsulation can lead to poor cache performance esp when storing flags like IsVisible per object then loading the entire cache line for that obj just to check if should draw it etc
zebrabox
Good point. Even better than doing an `isVisible` check is making separate collections of just the renderable components, that way invisible ones don't even need to be traversed at all.
munificent
@munificent. Indeed. The fastest code is the code that never gets called :) For an excellent exploration of this read http://research.scee.net/files/presentations/gcapaustralia09/Pitfalls_of_Object_Oriented_Programming_GCAP_09.pdf
zebrabox
A: 

Having fallen for this pattern before here is my input:

For a game, performance will be the biggest drawback. You probably want to keep all the drawings contexts, objects, etc in one place and optimise one larger block of code.

If for some reason you wanted to render on different platforms, then this approach fails because of lack of separation, again a plug-in renderer is a better approach

James Westgate