For Cocoa Game developers

Moderator
Posts: 3,572
Joined: 2003.06
Post: #16
unknown Wrote:I am really skeptical that any single design pattern could "fit all".
True, that is indeed a bold statement to have made. Let's say it this way instead:

If there *were* a one-size-fits-all approach, it would be MVC.

Better? Smile

[edit] Of course, pertaining to this discussion, I do believe MVC fits all sizes of games. [/edit]
Quote this message in a reply
⌘-R in Chief
Posts: 1,253
Joined: 2002.05
Post: #17
Hehe .
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #18
Maybe I am missing something, then, because I've never been able to get pure MVC to work all that well for games. How do you keep the one view class that draws everything from having to know all sorts of intimate details about all of the model classes it draws? What I see there is either a nightmare of trying to maintain that view with all of its couplings to the models, or a nightmare of trying to abstract the models to such an extreme that they can all be drawn in precisely the same way by the view. Having each model know how to draw itself seems like a tradeoff where you get a little bit more code duplication, but a lot fewer maintainability headaches when your models need to draw themselves differently.

...sorry for the thread derailment, but this is turning into a pretty interesting side discussion. Smile
Quote this message in a reply
Member
Posts: 257
Joined: 2004.06
Post: #19
maximile Wrote:Now I just have to make all of those pieces do the right things. I'm pretty new to OO programming, but eager to learn, so I thought it would be a good idea to have one class for "piece", which contains all the drawing code and a few simple rules, and then a class that inherits from it for each type of piece which contains all the code about legal moves. Does that sound like a reasonable way to go about it?

Well, I might as well throw in my $0.02 USD into the discussion as well since I've done a couple board game type games. Anyway, for the type of game that you're doing, yes, I'd separate out the drawing code from the logic code. It makes it so much easier when you port it to another platform. Really, all the logic part needs to do is keep track of where the pieces are, can this piece move here, and what happens when you movie piece x to square y. Then the part that drives all the UI and graphics can tell the logic part "piece x has moved to here -- can it do that?" as well as update the display of the board.

I actually have a couple real world examples of this for two of my projects: Hot Spot X and Chemical Bonds. The first, someone wanted to port it over to Win32 so all he had to do (and is still doing, last I heard) is just write the GUI layer that talks to the game logic. He doesn't need to mess with any of the code that drives the game. With Chemical Bonds, I'm in the process of moving it over to Cocoa and it was pretty easy to just take the core game code and have Obj-C code that handles all the graphics and user input talk to it.

Of course, it's your code so you should do whatever you want Smile I'm just saying what I do for my stuff.

The brains and fingers behind Malarkey Software (plus caretaker of the world's two brattiest felines).
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #20
MVC is about separation of responsibility, which is "always" a good thing.

As with anything in programming, there is a project size threshold below which it gives diminishing returns. For OO programming, I think that threshold is generally a few hundred lines. For MVC I think it's probably higher, maybe closer to a thousand lines.

That said, it's a hard-fought conclusion I've come to, that's radically improved the design (and therefore long-term maintainability) of my code. I highly recommend it.

If your project grows beyond the threshold (whatever it might be for you, your language, and your product), you *will* need to transform it to adhere to MVC if you want to be able to continue to maintain and extend it. You might as well get that done early. It's not like it adds a massive overhead to the early coding process.
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #21
Quote:How do you keep the one view class that draws everything from having to know all sorts of intimate details about all of the model classes it draws?

Let's see... Here's an example I put up to draw sprites with in another post a few weeks ago. It's not tested, so I don't know that it works, but it gets the idea across without fluff here:

Code:
static void DrawSprite(Sprite *sprite)
{
    GLfloat    halfWidth = sprite->width * 0.5f;
    GLfloat    halfHeight = sprite->height * 0.5f;
    
    glPushMatrix();
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glBindTexture(GL_TEXTURE_2D, sprite->imageID);
    glColor4f(1.0f, 1.0f, 1.0f, sprite->opacity);
    glTranslatef(sprite->x, sprite->y, 0.0f);
    glScalef(sprite->scale, sprite->scale, 1.0f);
    glRotatef(sprite->heading, 0.0f, 0.0f, 1.0f);
    glBegin(GL_QUADS);
        glTexCoord2f(0.0f, 0.0f);
        glVertex2f(-halfWidth, -halfHeight); // lower left
        glTexCoord2f(1.0f, 0.0f);
        glVertex2f(halfWidth, -halfHeight); // lower right
        glTexCoord2f(1.0f, 1.0f);
        glVertex2f(halfWidth, halfHeight); // upper right
        glTexCoord2f(0.0f, 1.0f);
        glVertex2f(-halfWidth, halfHeight); // upper left
    glEnd();
    glPopMatrix();
}

That's pretty darn generic. It won't do *everything* you might ask of it, but it's a start. For instance, what if you had animated sprites? Well, you could add in texture coordinates that DrawSprite could just as easily access instead of just the whole image as in the 0.0f's and 1.0f's above. The coordinates could be calculated in the sprites' update. If you used animated sprites all over the place, then you could set up a centralized animation system in the view class as well. Here is actual code I have used in a sprite based game a while back which calculates the animation in the update:
Code:
static void UpdateSprite(Sprite *sprite)
{
    unsigned int    frame, row, col;
    float            s, t;
    
    // there are 64 frames, so convert here
    // - starts with zero
    if((gameState.time - sprite->timeOfLastFrame ) >= sprite->timeOfNextFrame)
    {
        sprite->timeOfLastFrame = gameState.time;
        sprite->currentFrame++;
        if (sprite->currentFrame >= 64)
            sprite->currentFrame -= 64;
    }
    frame = sprite->currentFrame;
    row = frame / 8;
    col = frame - (row * 8);
    s = col * 0.125f;
    
    // starts in the upper left corner, goes across and then down
    t = (1.0f - 0.125f) - (row * 0.125f);
    sprite->sLeft   = s;
    sprite->sRight  = s + 0.125f;
    sprite->tBottom = t;
    sprite->tTop    = t + 0.125f;
    
    // move the sprite
    sprite->x += sprite->velX * gameState.frameDelta;
    sprite->y += sprite->velY * gameState.frameDelta;
    sprite->rot += 10.0 * gameState.frameDelta;
    while(sprite->rot > 360.0f)
            sprite->rot -= 360.0f;
}

Notice there is no drawing done in the sprite update. DrawSprite and UpdateSprite access the same model: Sprite. Sprite is just a struct in this instance, but it could be its own proper object. It is added to a database of Sprites that is iterated over by the scene graph manager (for lack of a better term at the moment). `Scene graph' manager is actually blowing it out of proportion really... Simple array iterator is a little more accurate Wink

Model: Sprite
View: DrawSprite
Controller: UpdateSprite

Quote:Having each model know how to draw itself seems like a tradeoff where you get a little bit more code duplication, but a lot fewer maintainability headaches when your models need to draw themselves differently.
I used to think that was going to be a problem too, so when I started moving over to this approach I also put in a callback where the object could optionally draw itself. After I started realizing how easy and logical it is to have models and sprites that can be drawn in a generic fashion, I found that I never used that callback and removed it from future designs entirely. In retrospect, I had more headaches doing it the non-MVC way!
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #22
ThemsAllTook Wrote:Maybe I am missing something, then, because I've never been able to get pure MVC to work all that well for games. How do you keep the one view class that draws everything from having to know all sorts of intimate details about all of the model classes it draws? What I see there is either a nightmare of trying to maintain that view with all of its couplings to the models, or a nightmare of trying to abstract the models to such an extreme that they can all be drawn in precisely the same way by the view. Having each model know how to draw itself seems like a tradeoff where you get a little bit more code duplication, but a lot fewer maintainability headaches when your models need to draw themselves differently.

First off, let me say that I don't think that having the models effectively be "structs" (guts open to the public) is necessarily a bad thing. One of the issues with a game is that lots and lots of things interact in complex ways. If you try to hide all that away behind access controls like a good little OO programmer, you'll find yourself in difficulties quickly. So long as the controllers that mess with the models are well defined, and the views talk only to the controllers rather than poking around in the model directly, I don't see any problems.

That said, in most games I've made, there have only been a handful of "kinds" of object (eg. level, 3d object, particle), each of which is drawable completely generically. In general, I've found that each object will have a reference to an "appearance" which defines how it should be drawn, and a "transformation" which defines the properties unique to the model object.

You then end up with code (vastly simplified) a bit like this in a controller:

Code:
foreach (object in all_objects):
    renderer = Renderer.for_kind(object.kind)
    renderer.draw(object.appearance, object.transformation)

Which nicely separates all the drawing code from all the model code. If necessary, the renderer for that object kind can delegate further to a collection of classes based on the kind of appearance it receives.
Quote this message in a reply
Member
Posts: 312
Joined: 2006.10
Post: #23
When working with NSViews, and sub-classes of it, is it generally a view, model or controller? Just with working with it the last few days, I've seen it acts like a mixture of a view and a controller (that is, a controller of only the data inside the view - not other windows or buttons). Event handling is easily done it because you can override the methods such as mouseDown, keyDown, ect... inside of the view class.
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #24
bronxbomber92 Wrote:When working with NSViews, and sub-classes of it, is it generally a view, model or controller?
It is generally supposed to be a view, thus the `view' part of the name NSView. But in practice, if you don't know how to keep the MVC separation yourself, or it is not practical for a given purpose, then it can easily contain (represent?) any of the three elements. Sometimes even Apple's examples mix them up -- usually with a note that they're doing so. Plus, there often exists multiple levels of MVC: MVC of the system UI (the Cocoa MVC paradigm), which you are referring to, and the MVC of your game, which we are currently discussing.

[edit] That might seem extra confusing. The game, as a whole, would be seen as the model in the Cocoa layer of MVC. [/edit]
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #25
NSView is, as the name implies, a view.

If you're putting controller code in there, you really shouldn't be Smile

Note that event handling is not necessarily controller code -- views are by nature stateful, and code to determine what a new state is does belong in the view.

If you can't take your view out and put it in another application, though, you've done something wrong.
Quote this message in a reply
Member
Posts: 312
Joined: 2006.10
Post: #26
Is there a document for handling events (mouse) outside of the view, maybe specific to the model?

Edit - I think I found a doc: http://developer.apple.com/documentation...ementID_43

Edit 2 - Seems the guide mainly focuses on handling event through the method mouseDragged, ect.. from inside the view, but inside of another method, thus making the the view not generic in most cases...
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #27
You can capture all events by overriding sendEvent in NSApplication. There are reasons why one might wish to do that, but if you don't have a good reason to, then don't. Wink
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #28
Jake and Keith make it sound so easy... Well, I have some projects in the works that are going to absolutely necessitate taking the draw-method-separated-from-model approach, so I guess I'll have more perspective once I've written a real implementation of it.
Quote this message in a reply
Member
Posts: 312
Joined: 2006.10
Post: #29
Would you think this would work?

Code:
    NSPoint locationInWindow;
    NSPoint endLocationInWindow;
    theEvent = [[view window] nextEventMatchingMask:(NSLeftMouseDownMask| NSLeftMouseDraggedMask |
                                                NSLeftMouseUpMask | NSRightMouseDownMask |
                                                 NSRightMouseDraggedMask | NSRightMouseUpMask )];
    if( ([theEvent type] == NSLeftMouseDownMask ||NSRightMouseDownMask ) && [theEvent clickCount] == 1 )
    {
        locationInWindow = [view convertPoint:[theEvent locationInWindow] fromView:nil];
    }
    else if( [theEvent type] == NSLeftMouseDraggedMask || NSRightMouseDraggedMask )
    {
        endLocationInWindow = [view convertPoint:[theEvent locationInWindow] fromView:nil];

    }

I don't think it would, but would be nice to know Rasp


Quote:Jake and Keith make it sound so easy... Well, I have some projects in the works that are going to absolutely necessitate taking the draw-method-separated-from-model approach, so I guess I'll have more perspective once I've written a real implementation of it.
Yeah, the do seem to make sound very easy Smile
Quote this message in a reply
Moderator
Posts: 3,572
Joined: 2003.06
Post: #30
bronxbomber92 Wrote:Would you think this would work? ...
No, that's not really the way to do it. You can do it better like this in your NSView:
Quote:- (BOOL)acceptsFirstResponder
{
return YES;
}

- (BOOL)becomeFirstResponder
{
return YES;
}

- (BOOL)resignFirstResponder
{
return YES;
}

- (BOOL)acceptsFirstMouse: (NSEvent *)event
{
return YES;
}

- (void)mouseDown: (NSEvent *)theEvent
{
mouseDown[align=left] = YES;
}

- (void)mouseUp: (NSEvent *)theEvent
{
mouseDown[align=left] = NO;
}

- (void)mouseMoved: (NSEvent *)theEvent
{
[self mouseChangedLocation:theEvent];
}

- (void)mouseDragged: (NSEvent *)theEvent
{
[self mouseChangedLocation:theEvent];
}

- (void)rightMouseDragged: (NSEvent *)theEvent
{
[self mouseChangedLocation:theEvent];
}

- (void)otherMouseDragged: (NSEvent *)theEvent
{
[self mouseChangedLocation:theEvent];
}

- (void)mouseChangedLocation: (NSEvent *)theEvent
{
NSPoint mouseLoc;

if (fullScreen) return;
mouseLoc = [theEvent locationInWindow];
mouseX = mouseLoc.x;
mouseY = mouseLoc.y;
}

// can call this in awakefromnib, reshape, and when coming out of captured fullscreen, but it's not required
- (void)updateMouseLocOutsideEventStream
{
NSPoint mouseLoc = [[self window] mouseLocationOutsideOfEventStream];
mouseX = mouseLoc.x;
mouseY = mouseLoc.y;
}

- (void)scrollWheel: (NSEvent *)theEvent
{
float wheelDelta;

wheelDelta = [theEvent deltaX] +[theEvent deltaY] + [theEvent deltaZ];
wheel += wheelDelta;
while (wheel > 1000) wheel -= 1000;
while (wheel < 0) wheel += 1000;
}

Sorry, the forum is giving me a real hard time about using too many images because of stuff like a ( directly following a : , and the tabs disappeared in the preview. I give up... Sad
Quote this message in a reply
Post Reply