PDA

View Full Version : For Cocoa Game developers


Taxxodium
2007.02.11, 07:41 AM
This may be of big interest to you:

http://www.macdevcenter.com/pub/a/mac/2006/12/19/building-a-game-engine-with-cocoa.html
http://www.macdevcenter.com/pub/a/mac/2007/01/05/building-a-game-engine-with-cocoa-part-2.html
http://www.macdevcenter.com/pub/a/mac/2007/02/02/building-a-game-engine-with-cocoa-part-3.html

maximile
2007.02.12, 10:04 PM
Thanks... that inspired me to start work on this:

http://www.maximile.net/apps/CCSS.jpg

I'm getting a bit stuck with all these classes and so on, but I'll persevere.

Malarkey
2007.02.13, 06:52 PM
Looks purty. What is that? Shogi?

OneSadCookie
2007.02.13, 07:31 PM
Not Shogi (it has a 9x9 board, no colors, pieces that indicate direction, etc.) I'm guessing it's some kind of Chinese equivalent, but I'm interested too :)

AnotherJake
2007.02.13, 10:38 PM
Yeah, that looks pretty interesting Maximile, what is it?

maximile
2007.02.13, 11:04 PM
It's Xiangqi (http://en.wikipedia.org/wiki/Xiangqi), or Chinese Chess. I couldn't find a good Mac version, so I thought I'd have a go at making one. It's really a very cool board game.

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?

AnotherJake
2007.02.13, 11:58 PM
Does that sound like a reasonable way to go about it?
No.

Graphics and game logic should be kept completely separate, broadly speaking. The graphics should follow the logic, not be integrated with it.

Take modern chess games for example -- which seem closely related to your game. There are many chess `engines' out there which consist mostly (if not entirely) of logic and some simple input and output mechanisms with virtually no graphic representation; mostly to be operated from the command line. Then, OTOH, you have graphical game engines which can launch those engines as a separate sub-process (so to speak) and interact with them to know where to place the pieces on screen. The chess engines can be run completely from the command line, but the visual chess games can't do anything but look pretty all by themselves. They have to work together to make a conventional end product. Take Big Bang Chess for example: It's a great game all on its own, and looks super-duper impressive and smart and everything, but all it's really doing is presenting a face for gnuchess underneath, which is freely available and actually does all the heavy lifting (no offense to the Big Bang Chess programmers ;-) ).

leRiCl
2007.02.14, 12:11 AM
No.

Graphics and game logic should be kept completely separate, broadly speaking. The graphics should follow the logic, not be integrated with it.


This apply even if I make other types of games? :s All the programs I have made so far ties graphics && logic together. (eg... drawing code in a method of classes...)

How do you seperate them? You have a function that fetches all the data in your objects and draw them seperately? But that'd slow it down a little... Because when I draw the code, it also process the game logic simultaneously. If I seperate them like that then it would take more loops....
:S

Very nice chess game there! (I am a Xiangqi player myself... :))

AnotherJake
2007.02.14, 01:34 AM
This apply even if I make other types of games?
It should, yes. Logic and drawing are very common to keep separated. The most common logical way to do this is to have an `update' method and a `draw' method. Every visible object will have geometry data which is affected by the logic in some way, and drawn in another. The object's update method does not care at all about how the object is drawn, it simply determines where it's at and where it's supposed to be, etc. Whereas the draw method does not care at all about where the object is or what it's doing, only what it's drawing parameters are, such as rotation, scale, geometry, etc. The two need not have anything to do with each other. In fact, the draw method could/should be centralized and generic so-as to handle drawing of all objects of a particular type. IOW, drawing code need not even be contained within the objects themselves, but rather all by itself, off in some other remote module, perhaps even another program entirely!

OneSadCookie
2007.02.14, 02:44 AM
Yes, this is absolutely the number one rule to follow to get your game design working out nicely. It's basically just the game version of the MVC (model-view-controller) OO design pattern.

ThemsAllTook
2007.02.14, 10:08 AM
While having a 'draw' method as part of the same class as the logic has its limitations, in practice, it's worked perfectly for me in any reasonably simple game. If you're not doing something that actively necessitates separation, it may well be wasted effort to try to do it. Call me unprofessional or a bad coder or whatever all you like, but I'm speaking from practical, real-world experience.

AnotherJake
2007.02.14, 11:13 AM
Well, a game is just like any other program in that respect -- you can do it however you like! Hehe, it's not like MVC is a religious edict handed down by the programming gods or something. Doing it that way doesn't scale well though, and can present problems later on.

Here are some example situations to add to the discussion:

- What if you wanted to multi-thread your application? The logic (the controller) and the drawing code (the view) could easily share the objects' data (the model) and be threaded without much issue.

- Then, as another example, what about if you wanted to draw all your objects using a new shader? If all the drawing code is in one place and generic then it'd be a simple five minute matter of updating just that code and testing it. If all your drawing code is wrapped up in each individual class, that could take many hours, if not days to do.

- What if you wanted to use some special feature that only works on newer machines but would like to retain the older method for older machines? Here it would be nice to just add a single conditional branch in the centralized code base to add the feature, rather than in every single class.

- What if you wanted to port your game to another platform that uses a different graphics API, such as, oh I don't know, DirectX... Again, central drawing code base, relatively easy change (hopefully).

- What happens if your current graphics API changes and breaks your game? Again, if it's all generic, centralized drawing code then it would likely be easier to change a few lines rather than hundreds, if not thousands of individualized lines.

And the biggest advantage to centralized drawing: Write less code!

Again, this is all not to say that you'd be a bad programmer if you didn't do it this way, but there are many advantages to it. :)

ThemsAllTook
2007.02.14, 11:44 AM
What if you don't need to do any of that for the game you're writing?

Again, I fully understand the benefits of that approach, but when they're not actually applicable, I don't see the point. This is probably just my minimalist approach to coding talking, but I generally try not to design for "what-ifs". The scope of a project obviously has everything to do with this... There really isn't a one-size-fits-all solution. You can always try to make a solution that would be appropriate for a large-scale game project fit a much simpler one, but in my experience, that usually results in wasted effort... that is, unless you happen to already have the code written from another project, and can simply copy and paste and make a few tweaks.

AnotherJake
2007.02.14, 11:55 AM
It's not a matter of not `needing' to do it.

Not to be a contrarian, but I'm not sure you understand the approach I am describing. MVC *is* minimalist, and not overkill. It *is* actually a one-size-fits-all approach. If I were to design a small (tiny even) sprite game, I would make classes for each type of avatar which would only change their respective data, but not draw. I would then make one single class to draw them all using the data that the avatar classes had managed. That class would be all of about fifty lines. That's minimalist! It's easy! Being as how I've done it both ways (yours and mine), I have come to learn that MVC is *far* easier than distributing drawing code in each class. Admittedly, it might take a bit to wrap one's mind around, but it's well worth the effort :)

unknown
2007.02.14, 12:15 PM
It *is* actually a one-size-fits-all approach.

I am really skeptical that any single design pattern could "fit all".

AnotherJake
2007.02.14, 12:23 PM
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? :)

Of course, pertaining to this discussion, I do believe MVC fits all sizes of games.

FreakSoftware
2007.02.14, 03:21 PM
Hehe .

ThemsAllTook
2007.02.14, 03:29 PM
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. :)

Malarkey
2007.02.14, 04:30 PM
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 (http://www.malarkeysoftware.com/projects_HotSpotX.html) and Chemical Bonds (http://www.malarkeysoftware.com/projects_ChemicalBonds.html). 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 :) I'm just saying what I do for my stuff.

OneSadCookie
2007.02.14, 04:58 PM
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.

AnotherJake
2007.02.14, 05:05 PM
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:

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:
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 ;)

Model: Sprite
View: DrawSprite
Controller: UpdateSprite

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!

OneSadCookie
2007.02.14, 05:06 PM
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:

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.

bronxbomber92
2007.02.14, 05:21 PM
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.

AnotherJake
2007.02.14, 05:30 PM
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.

That might seem extra confusing. The game, as a whole, would be seen as the model in the Cocoa layer of MVC.

OneSadCookie
2007.02.14, 05:33 PM
NSView is, as the name implies, a view.

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

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.

bronxbomber92
2007.02.14, 05:39 PM
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/Cocoa/Conceptual/EventOverview/Introduction/chapter_1_section_1.html#//apple_ref/doc/uid/10000060i-CH1-DontLinkElementID_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...

AnotherJake
2007.02.14, 05:56 PM
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. ;)

ThemsAllTook
2007.02.14, 05:59 PM
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.

bronxbomber92
2007.02.14, 06:00 PM
Would you think this would work?

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 :p


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 :)

AnotherJake
2007.02.14, 07:14 PM
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:
- (BOOL)acceptsFirstResponder
{
return YES;
}

- (BOOL)becomeFirstResponder
{
return YES;
}

- (BOOL)resignFirstResponder
{
return YES;
}

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

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

- (void)mouseUp: (NSEvent *)theEvent
{
mouseDown[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... :(

bronxbomber92
2007.02.14, 07:19 PM
But then, you lose portability in a sense. Say you want to create a line or path. You could put what I did (if it worked :/) in a method, create a BezierPath then a create a path from those points. Your way, you have to create the path in the View, and that loses the ability to use mouse event for saying creating a rect or circle or any other geometric figure without having a bunch of if-else if statements.

See how the view loses portability?

Edit - I would say in many other cases where you only need mouse input for one task, then that would be pretty portable.

AnotherJake
2007.02.14, 07:45 PM
I really don't see what you're trying to get at. Could you clarify?

You get a constant supply of mouse coordinates and mouse downs and mouse ups as they occur in the view that is being worked with. You record where they happen in your model as you please, which can be used in any view that accesses that model. That seems pretty `portable' to me, but again, perhaps I don't understand what you're saying.

GolfHacker
2007.03.03, 09:15 PM
MVC is not the only way to do game design (which is the impression I get reading this thread). I've done nothing but OO design and programming for 15 years. I don't use the MVC pattern in my games or other programs, and yet they are quite portable. But then I'm relying on cross-platform libraries to take care of all the low-level details.

My OO design more closely follows the Bridge pattern. In my design, I have a graphics library consisting of a set of basic shape classes (rectangles, billboards, spheres, disks, cones, etc) that all inherit from a common base class. These classes encapsulate the OpenGL rendering, including lighting, texturing, etc. If I needed to, I could rewrite the internals of these classes to use DirectX, SDL, or anything to do the rendering, and the rest of my code would be unaffected. I can also reuse these graphic classes for any game, since they are independent of game type (2D, 3D, action, puzzle, etc).

I then have higher-level classes for the game objects that implement the game-specific object behavior (movement, AI, etc). The game object classes have graphic objects as class members (Bridge pattern). They also have separate methods for updating the object state and drawing the object (which I agree is desirable to keep separate), but in my design both methods are on the same class. The draw method just invokes draw on the low-level graphic objects.

This way, the game objects are completely encapsulated in a truly OO way, and yet the low-level rendering code is still kept separate from the game logic. As an OO guy, this feels so much more natural to me. I can call update() on an object to make it update itself, and I can call draw() on it to make it render itself. The game objects have access to the "world" or "map" object, so they can do their own collision detection and change their own behavior based on their environment. This makes then fairly autonomous. I can also serialize/deserialize each game object, which makes it very easy to save and restore the game at any point.

I think OO is quite suited for game development, and it works as well for games as it does any other kind of software development. OO is just a different mindset and a different way of programming.

AnotherJake
2007.03.04, 04:45 AM
OOP and MVC are not different from each other. MVC is merely a conceptual technique to use. You don't *have* to use it. It's not a dogma. It's good stuff though, for various reasons already discussed. I never really gave a crap about it myself until I started multi-threading, where traditional OOP approaches that I used to use, like you describe them, broke down. MVC separation fit much better with multi-threading, and that's what really sold me on it.

GolfHacker
2007.03.04, 08:35 PM
I'm not knocking MVC. It's a fine approach to software design, for all the reasons that were given. But for the benefit of ThemsAllTook and others who are interested in using a more OO design, I just wanted to rebut some comments on the thread that gave the impression MVC is the only way. It isn't. There are a number of OO design patterns that will achieve the same results (namely, separation of responsibility).

OneSadCookie
2007.03.04, 08:38 PM
Your proposed design does not separate all the responsibilities that I feel it is important to separate. I do not consider it an alternative to a proper MVC design, and I would not design any new games in that way.

nich
2007.03.08, 04:03 AM
oo methods comparison
http://students.cs.byu.edu/~pbiggs/survey.html
http://www.cetus-links.org/oo_ooa_ood_methods.html
http://en.wikipedia.org/wiki/Object-oriented_programming