views:

1165

answers:

5

Working on a game in Objective-C/Cocoa for OS X, and I finished the prototype as much as it's worth being finished. It's a mess of code, being my first game, but everything works. I've been reading up on the best way to put things together, and MVC seems to make the most sense, but I'm a bit confused.

Where does it start? With the controller? That seems to make the most sense to me, but how is it started? In my mess of a prototype, I have everything starting from the init of the view and going from there. Would I just do the same for the controller, and put what's needed in the init? Or is there something else I can use to do this? If it's started from the init, how do I init the controller?

How would I set up the game world? I currently use two arrays, one for the world (Walls, Floors, Doors, Water, Lava, etc.), and one for the items (I'll be adding a third for characters). The map (a .plist) is loaded, and then the objects are created and added to the array it belongs to. Where do the arrays go? In the prototype, they're also part of the view, so I guess you could say I combined the two (View and Controller) together. Would there be a Map object created for each map? Would there be a Maps object that contains all of the maps?

How does it all work together? The player presses a key, which moves the character in the game. The view would be handling the input, right? Would you send that to the controller, which would check for everything (walls, monsters, etc) in the map/other arrays, and then return the result? Or would you send it to the player, which would go to the controller, which would do all of the the checks, and then return the result?

I thought I had it pretty nicely laid out in my head, but the more I think about it, the less solid my ideas become and the more confused I get. By all means do not hesitate to draw something up if you think it will get the point across more efficiently.

If you've taken the time to read all of this, thank you for your patience. From what I've gathered, most people that write code don't use any sort of design. After reading up on this, I can see why some people would avoid it, it's confusing and people seem to think it isn't worth the time. I personally think that the advantages totally outnumber the disadvantages (are there any?) and it only makes sense to keep things organized in a way that you won't have to do a total rewrite every time you want to implement a new feature. You wouldn't build a house, car, or an appliance without a design, why would you write a complex program without one?

I asked this question because I want to do it the right way, instead of hacking and half-assing my way to "victory".

+2  A: 

You should create a model for the the whole game. It should contain everything about the game except the GUI interaction. The views, on the other side, contain all GUI stuff without knowing anything about the game flow.

The whole point is that models and views are expected to be reusable. The model classes should play with any GUI (and maybe even the console or command line). The view classes should be able to be used with other similar-looking games. Models and views should be completely decoupled.

Then, the controller fills the gap. It reacts on user input, asks the model classes to perform a specific game move, and asks the views to show the new situation. The controller is not expected to be reusable. It's the glue which holds the game together. The controller ensures that model classes and view classes remain indenpendent and reusable.

In addition, don't try to make the design perfect from the start. Don't hesitate to refactor at any time. The faster a bad design decision gets corrected, the less evil it does. Designing everything upfront means that a bad design decisions won't be corrected at all, unless you make a perfect design upfront, which is simply improssible even with decades of experience.

Always remember the third design rule of the X Window System: "The only thing worse than generalizing from one example is generalizing from no examples at all."

vog
That's what I've been trying to do, but I keep getting hung up on where to put things.
Sneakyness
BTW you're not going to have a single model, view or controller for the entire game. You're going to have a monster model, monster view, monster controller... everything should be broken down by purpose, in an object-oriented sense. Your array of monsters would be part of the model in some other model, like the level model.
emddudley
@emddudley: You usually do have _one_ model, but it consists of multiple classes and/or functions. :-)
vog
@Sneakyness: Just try it and refactor as you see you've made a mistake. Don't try to make a perfect design. Design has to be tried in code. If you don't restrain to refactor, any bad design decision is easily corrected.
vog
@vog: Thanks vog, you're right.
emddudley
Vog, it's an extremely complex game, and I'm trying to spend more than just an hour or two on the Design and then coding. I understand not to try and make a perfect design, but I'd like to have a better idea of how to lay things out. Pieces are falling into place. Nobody has answered my question as to where it all starts, or how, though.
Sneakyness
A: 

For your game the model would include things like the current position of the character, the number of health points, and other values that involve the "state" of the game. It would notify the view whenever something changed so that the view could update itself. The view would simply be the code required to show everything to the user. The controller is responsible for responding to user input, and updating the model when necessary.

The controller is the central component in this, and it is what should instantiate the model and view.

When the player presses a key, the view should simply pass that command onto the controller, which decides what to do.

You are wrong that most people don't design. Perhaps most amateurs, or perhaps the people who ask the most questions online, but not anyone working on a project that is even somewhat sophisticated. Professional programmers are all experienced in software design; without it they literally would not be able to do their job (write software to do X).

Your game sounds like it is complicated enough to warrant an architecture pattern like MVC. There are some instances where a piece of software is simple enough that MVC is overkill and needlessly complicates things, but the threshold for using MVC is pretty low.

emddudley
Yeah, I chose to write a Roguelike for my first game/program. Turns out it was a hard choice, but I personally think it is the right choice.
Sneakyness
A: 

You might find this article Introduction to MVC design using C# useful. Although the example is in C#, the principle should apply.

mqbt
That still tells me absolutely nothing as to which one starts everything. Or even how to start everything.
Sneakyness
+1  A: 

You may be interested in a presentation I gave to ACCU '09 - "Adopting Model-View-Controller in Cocoa and Objective-C".

Where does it start? With the controller? That seems to make the most sense to me, but how is it started?

Create a new Cocoa app project and you'll see that there's already a controller class provided by the template - it's the app delegate class. Now look in MainMenu.xib. There's an instance of the app delegate, and it's connected to the "File's Owner" object's delegate outlet. In this case the NSApplication is the File's Owner; it's the thing that wanted MainMenu to be unpacked. So this really is the delegate of the application.

That means we've got something which is a controller object, can talk to the NSApplication instance, and can have outlets to all the other objects in the XIB. That makes it a great place to set up the initial state of the application - i.e. to be the "entry point" for your app. In fact the entry point should be the -applicationDidFinishLaunching: method. That's called once the application has finished all of the stuff needed to get your app into a stable, running state - in other words Cocoa is happy that it's done what it needs to and everything else is up to you.

-applicationDidFinishLaunching: is the place where you want to create or restore the initial Model, which is the representation of the application's state (you could also think of it as representing the user's document, if that analogy is suitable for your app - document-based apps are only a little more complex than the default) and tell the View how to represent things to the user. In many apps you don't need to load the whole Model when the app has launched; for a start it can be slow and use more memory than you need, and secondly the first View probably doesn't display every single bit about the Model. So you just load the bits you need in order to show the user what's up; you're Controlling the interaction between the View and the Model.

If you need to display other information in a different View - for example if your main View is a master view and you need to show a detail editor - then when the user tells you what they want to do you need to set that up. They tell you by performing some action, which you could handle in the app delegate. You then create a new Controller to back the new View, and tell it where to get the Model information it needs. You could hold the other View objects in a separate XIB, so they're only loaded when they're needed.

How would I set up the game world? I currently use two arrays, one for the world (Walls, Floors, Doors, Water, Lava, etc.), and one for the items (I'll be adding a third for characters). The map (a .plist) is loaded, and then the objects are created and added to the array it belongs to. Where do the arrays go? In the prototype, they're also part of the view, so I guess you could say I combined the two (View and Controller) together. Would there be a Map object created for each map? Would there be a Maps object that contains all of the maps?

We can work out what objects we're modeling by analysing your statement above - you may not realise it, but you've sketched out a specification :-). There's a world which contains walls, doors etc., so we know we need objects for those, and that they should belong to a World object. But we also have items and characters - how do they interact with a world? Can a place contain water and a character? If so, perhaps the world is made up of Locations, and each Location can have a wall or a door or whatever, and it can also have items and characters. Note that if I write it like this, it seems that the item belongs to the location, not the location to the item. I would say "the mat has a cat on it" rather than "the cat has a mat underneath it".

So simply think about what you want your game world to represent, and the relationships between the things in the game. This is called domain modeling, because you're describing the things in the game world rather than trying to describe things in the software world. If it helps, write down a few sentences describing the game world, and look for the verbs and nouns like I did in the last paragraph.

Now some of your nouns will become objects in the software, some will become attributes of other objects. The verbs will be actions (i.e. methods). But either way, it will be easier to think about if you consider what you're trying to model first, instead of jumping straight down to the software.

How does it all work together? The player presses a key, which moves the character in the game. The view would be handling the input, right? Would you send that to the controller, which would check for everything (walls, monsters, etc) in the map/other arrays, and then return the result? Or would you send it to the player, which would go to the controller, which would do all of the the checks, and then return the result?

I like to follow the "tell, don't ask" policy, which says that you command an object to do something rather than asking it to give you the information to make the decision. That way, if the behaviour changes, you only need to modify the object being told. What this means for your example is that the View handles a keypress event (it does because they're handled by NSControl), and it tells the Controller that this event occurred. Let's say the View receives a "left arrow" keypress, and the Controller decides this means the player should move left. I would just tell the player "move left", and let the player sort out what happens when moving left means bumping into the wall or a monster.

To explain why I want to do it that way around, imagine that you add in game 1.1 the ability for the player to swim. Now the player has some ableToSwim property, so you need to change the player. If you're telling the player to move left, then you update the player to know what moving left over water means depending on whether they can swim. If instead the Controller asks the player about moving left and makes the decision, then the Controller needs to know to ask about being able to swim, and needs to know what it means near water. As does any other controller object in the game that might interact with a player, as does the controller in game for iPhone ;-).

Graham Lee
I liked it, thanks. But, this still does not tell me where it starts. How is the controller started? From the awakefromnib of the view? There's something I must be missing.
Sneakyness
Ok so I use applicationDidFinishLaunching, but how? do I use that just like you would use awakefromnib?
Sneakyness
Re -applicationDidFinishLaunching: it's effectively just a function that gets called back by Cocoa. Think of it as being the entry point just like main() is in a C program, except that all the objects you created in your XIB can already be used.
Graham Lee
I understand that, but how do I use it? Furthermore after some discussion with a coding friend, I've decided it might be much less of a hassle to abandon this implementation and continue with my hybrid approach, instead just cleaning things up. I'd really like to talk to you about it if you have the time. Aim is UnsavedDocument. I thank you for everything you've done so far. I'm close to getting to the bottom of this I know it.
Sneakyness
I'm giving you the answer for spending so much time writing this, but I've really gotten nowhere with this question, which is a disappointment, but I'll live. Thank you for your time.
Sneakyness
So what I'd do in -applicationDidFinishLaunching: is reload any state which is important in your app (that's the model) and set up the view necessary to display that. It's fine to carry on with what you've got, but MVC gives you a clean design that's easy to extend - imagine if you wanted to change the way your maps were drawn right now, without changing how those maps were represented in RAM. MVC makes that a lot simpler.
Graham Lee
+1  A: 

It seems like your main confusion is how things are constructed during app start up. A lot of people get stuck on this, because it feels like something magic is going on, but let me see if I can break it down.

  1. App is launched by user
  2. C main() function is called
  3. main() calls NSApplicationMain()
  4. NSApplicationMain() load the MainMenu.nib (or another nib specified in the info.plist)
  5. Nib loading initializes all of the objects defined in the nib (including the application delegate)
  6. Nib loading makes all the connections defined in the nib
  7. Nib loading calls awakeFromNib on all the objects it just created
  8. -applicationWillFinishLaunching: is called in the application delegate.
  9. NSApplication (or a subclass specified in the Info.plist) is initialized
  10. -applicationDidFinishLaunching: is called in the application delegate.

Note that the application delegate is initialized BEFORE the NSApplication (sub)class. That is why application(Will|Did)FinishLaunching: takes a notification as opposed to the NSApplication it is a delegate of.

The general consequence of this is that your views are created for you via nibs, and you controllers are created as a side effect of nib launching since they tend to either be root level objects in the nibs, or the nib's File's Owners. Your model is usually created either in the application delegate or as a singleton that is lazily initialized when something first access it.

Louis Gerbarg
The application delegate isn't specified in Info.plist, and isn't instantiated by NSApplicationMain(). It's serialized in the Nib and connected to NSApp via File's Owner. It also can't be sent -applicationWillFinishLaunching: before NSApp is initialized, as the application doesn't yet exist to willFinishLaunching.
Graham Lee
You are correct that it is not declared in the Info.plist, I will change that and reorder appropriately. applicationWillFinishLaunching: absolutely can be sent before the application has been initialized and that is its documented behavior: "Sent by the default notification center immediately before the application object is initialized."
Louis Gerbarg
So, I just tested and it appears that NSApp is initialized before applicationWillFinishLaunching:, but that does not need to be true, it is not the documented behavior, and I am pretty certain that is not how it worked in older releases of OS X.
Louis Gerbarg