You hit Duke. Duke is killed.
November 21, 2009 7:18 PM   Subscribe

I'm trying to teach myself Java by writing a simple roguelike. Can you help me figure out the architecture?

I'm totally new to Java. I just installed NetBeans this evening. I've been tinkering around with the Swing library.

One of the reasons I want to learn Java is to enhance my architectural chops. I'm reasonably comfortable with OOP (mainly in PHP5, which lacks some of the more exotic OO features), and I've read a couple of books on design patterns (and I know that patterns are not a magic bullet), but I still struggle with architecture.

The specific concern I'm wondering about is how to delegate control to various parts of the program as the player negotiates menus, enters different modes and interfaces in the game, etc.

For example, the first thing the player will see is the main menu. From here, they can start a new game, load a saved one, or view the high score table.

Let's say they choose the latter. My program needs to pass control over to the class responsible for displaying the high score view. When the user dismisses that view, the system should automatically return control to the main menu.

Now let's say they start a new game. Control is passed over to the main game engine, which renders a map, player statistics, etc.

The user presses "r" to read an item from their inventory. Control is passed over to a menuing widget which displays all readable items in their backpack, and allows them to select one with the up/down keys. The player can cancel the action with Esc (which simply returns control to the previous subsystem in the chain—i.e., the main game engine), or select the highlighted item by pressing Enter.

Perhaps the player later encounters a minigame—let's say they can play blackjack to earn money. In this case, control gets passed to the blackjack class. When they decide they've had enough, control is released to whatever class spawned the blackjack game.

(Or they enter a city mode from an overworld map, then return to the overworld when leaving the city...you get the idea.)

And, of course, when the player dies, the game engine terminates and control is automatically returned to the main menu.

In other words, it's much like a call stack. I've seen a similar system used in many programs, and I think it will work well for mine.

But the web apps I'm used to programming are much more linear creatures—they only receive input (e.g., POST variables) at runtime. State is entirely simulated—when it's time to move to the next step in a process, the user is simply forwarded to a new page (which is, effectively, a whole new program).

So: does my proposed architecture (sketchy though it may be) make sense? Is there a name for it? And, most importantly, how do I implement it?

Bonus points if you can combine this with an elegant way to separate the view/UI entirely from the game logic, so I can (for example) build the initial design with a text-only interface, and then graft a graphical mode on top later.
posted by ixohoxi to Computers & Internet (19 answers total) 19 users marked this as a favorite
 
Very interesting question - I had the same ambition, also to educate myself. Still have it, in fact.

Quickly reading through your post, the ideas seem sound.

Can't say much else now, but AFAIK Dungeon Crawl Stone Soup is cleanly designed and implemented Roguelike. You probably want to have a peek at it.

For laffs, have a look at the Nethack source. It's even more arcane than the game itself.
posted by krilli at 7:23 PM on November 21, 2009


Oh, and. The wise old engineers have a saying. It's my favorite. Build it wrong, but build it.
posted by krilli at 7:23 PM on November 21, 2009 [1 favorite]


... and start a Github project for it, and use Git for revision control! It's excellent! You get much more freedom to experiment if you know that any changes are rewindable and undoable, and you can branch off into weird experiments as much as you like and still have an easy time of merging all of it together.
posted by krilli at 7:27 PM on November 21, 2009


Your "Bonus points" goal, to keep the game engine and view separate, should be a primary goal, not just a bonus. It will help you clearly think about the whole system more clearly, and be easier to test, debug, and as you mention, create an alternate interface. The two patterns you might want to review are MVC (model/view/controller) and MVP (model/view/presenter) They both encourage the separation of the game data itself and your GUI code.
posted by mrgoldenbrown at 7:46 PM on November 21, 2009


Response by poster: mrgoldenbrown—well, yeah, that is a primary goal, but I'm significantly more comfortable figuring that part out on my own, which makes it secondary for the purposes of this question.

I'm halfway familiar with MVC, and I'm actually reading about it in another tab at the moment. I'm mainly interested in how to delegate control as I've described.
posted by ixohoxi at 7:53 PM on November 21, 2009


Best answer: Program architecture is dictated in part by platform, in part by the problem you're trying to solve, leaving you, the designer, with perhaps 1 percent left to fill in.

In this case, the obvious approach is threefold:
* Model
* View
* Controller.

Etch this into your brain, as it is the UI design pattern.

The Model, roughly, is the interesting part. It's how you represent the game state, encode the rules and enforce only valid actions (ie no moving through solid objects). The Model need not be a single class; you'll probably want to use an OO model for the actors; the player, the monsters and their AI routines. Most of your actual game design happens here.

The View is the set of classes that determine how to render your game. Essentially, given a specific game state, display interesting information to the user. In your case, the 2d overlay. Properly done, you can declare a public set of functions (in Java, an Interface), and then glue them to low level routines like printchar or Swing. It's tempting here to add a function to game objects to return the character to print, but I think you're better off with a Visitor pattern to do the mapping. It's nearly the same code either way.

The Controller is the hardest one to understand, because many people are used to writing programs that compute a result then terminate. The controller takes input from your player, interprets it as an action to take in the model, interacts with the model in some way and asks the view to recalculate the view. The first challenge novices face is deciding that writing a function that doesn't terminate is the right thing to do. The next step is to do this efficiently. Traditionally, the game loop is how game controllers are written, where they just check what buttons are pressed and then go without the user; you need to wait. For that, we have the Observer pattern, another part of the Swing library. The controller implements the callbacks for when a key is pressed and builds a context. You may need to build a small interpreter to determine things like which direction to throw an object, etc; just build a second view to handle the formation of the action to take.

My suggestion to you is to skip the initial menu and hardcode a character; once you've done a lot of the rest of the program I think you'll be in a better position to understand what needs to be done to make that happen. There's a guide to roguelike creation you can follow and they suggest starting simple and working your way up.

As far as minigames go, within roguelikes they're generally implemented within the game world. Sokoban in nethack, for example. If you wanted to do a minigame, you'd write a new model and view, and treat the controller as a state machine.
posted by pwnguin at 7:58 PM on November 21, 2009 [3 favorites]


Best answer: Echoing what has been said before, make it a primary goal (not a bonus) to make as strong an interface cut as you can between the display/control and the game information. Think of it like a FPS game, with a game client that gets all its information from the game server and has to ask politely for it. Don't send keystrokes to the server, don't have the server sending back terminal escape sequences (not that you should do terminal stuff; roll your own display stuff, but you see what I mean).

What you seem to be worrying about a bit too much is the modes of whatever is handling the input (the controller). Basically, one mode of control logic is active at the time, and one of the things that it can do (besides causing changes in the view and model) is end the current mode and select the next.

So you have a GameClient object, which fields input and shovels it off to the current ControlMode. The ControlMode turns the crank a bit (passing control to a server that is dedicated to turning the actual game crank), and returns some kind of code to the GameClient which can indicate, among other things, what ControlMode should handle the next bit of input.

E.g., the main loop is "like this":
while(stillPlaying)
{
       input = getInput();
       modeRet = currentControlMode.handleInput(input);
       handleModeRet(modeRet);
}

posted by fleacircus at 8:14 PM on November 21, 2009


Response by poster: pwnguin & fleacircus—

I've actually done a fair bit of reading about MVC. I understand that it separates data from presentation from application logic, and I fully appreciate the benefits of that. But none of the articles I've read explain how to actually do that, in code—how to build a working application that embodies the principle. I need concrete examples that I can take apart and put back together.

Is the stack-like system I've described compatible with (or contraindicated by, or tangential to) MVC?

fleacircus—

I suspected the answer would be something like the loop you've illustrated—the main class keeps track of which subsystem is active, collects input, and pipes input to the active subsystem (until a new one becomes active).

I presume that, in your example, the actual change to a new controller would happen in handleModeRet, if modeRet indicates that this is the right thing to do? (What is modeRet? It's not an M or a V or C—right? Just an arbitrary data structure that can accommodate whatever data I need to pass back to the main loop, or what?)

And, in the context of my proposed stack, modeRet could tell the main loop to either:

a. Push a new controller onto the stack and transfer control to it (e.g., the user has just entered an inventory menu), or

b. Pop the active controller off the stack and return control to the previous controller (e.g. the user has just canceled the inventory menu).

Is that right?

Sorry if my terminology is imprecise.
posted by ixohoxi at 8:43 PM on November 21, 2009


You're biting off two unrelated problems at once. Get your MVC chops down and then work on the state changes.

It seems as if you might be trying to re-invent an ordinary call stack with your "active subsystem" idea; not everything need change when going OOP.
posted by fydfyd at 9:06 PM on November 21, 2009


Best answer: ixohoxi, you mention you're somewhat familar with design patterns. I think the Command pattern will help you here a bit. It is very standard approach to handling Actions (such as you describe with wanting to pass control around when the user performs a specific function). If you look at examples from the Java tutorial or standard Java library, you'll see often that's how they handle menu items and actions.

The Command pattern allows you to encapsulate the behavior you want inside the Command itself.
posted by miasma at 9:09 PM on November 21, 2009


Best answer: Ok, if you simply want advice on how to handle modes and interfaces within the game, it's quite simple.

Java is an object oriented language. You have a huge library of GUI widgets available, and throwing the roguelike text motif away for a moment, you build an InventoryWindow class to show the inventory. The inventory class you design inherits from some ListSelection GUI widget and overrides the constructor with additional parameters: the inventory to show, in this case. Then when asked, your program will just construct a new InventoryWindow and call a paint() function or something to make it render. For things that don't require changing out the game logic, this is the way to go.

Switching between various program modes is not hard. Imagine the typical tabbed UI. There's a large region that represents the panel into which the view is rendered, and the tabs area that lets you select which panel is to be rendered. You want the same thing, except replacing the tab interface with internal game logic. This is all contained within the controller, perhaps as different functions. A state machine or simple function call stack would probably suffice.

Swing uses an observer / listener pattern to register keyboard and mouse events, in a manner you may recognize as similar to javascript hooks within the browser. This is where you glue the view to the Model. When you click the read button (or press 'r'), an onclick event (or keypress('r') event) is generated and all listeners are notified. Your controller wakes up, hits switch(keypress) and in a long chain of options jumps to the read handler, which queries the model about your inventory for readable objects, and constructs a new InventoryWindow. Then magic happens as I don't recall how you return the selected item but I know it's possible.

At that point you have formulated a move you believe is valid according to the game rules so you hand it to the model and have it process the new game state.
posted by pwnguin at 9:48 PM on November 21, 2009


Oh, and on the subject of roguelike development, this is a damn fine wiki, especially the How to Write a Roguelike in 15 Steps piece.
posted by pwnguin at 9:53 PM on November 21, 2009 [1 favorite]


Response by poster: Argh. I've ended up where I always end up when I try to tackle this stuff: confused and frustrated. Every time I think I'm starting to get a bead on it, it's suddenly over there. I feel like I'm getting ten different answers to five different questions, none of which were the actual questions I asked. (And I know you guys are trying to help, and that it's almost certainly my questions and my assumptions—not your answers—which are deficient. So please don't think I'm bitching at you.)

Perhaps I am biting off too much at once. Something tells me that I'm going to smack my head at the simplicity of it all when I finally grok it—this stuff can't be that complicated—but I'm coming from such a different background that I probably need to unlearn some fundamental assumptions.

I guess I should spend some more time getting comfortable with Swing and with Java in general, for starters. I think I'll build a simple clone of the old Food Fight BBS door. I'm also going to look at the source code for some Java roguelikes and see how they do things.

(Yup; I've been working my way through RogueBasin. Tons of great stuff there.)

Thanks for your comments! I don't get it all, but it definitely gives me fertile ground for further research. (And I certainly don't mean that to preclude further comments; I'll be listening as long as people have things to add.)
posted by ixohoxi at 10:27 PM on November 21, 2009


Best answer: ten different answers to five different questions, none of which were the actual questions I asked.

This is pretty much design patterns. They're somewhere between overkill and required. Most of them are simple and obvious in Lisp or ML. But since you asked for architecture and design, that's how I framed things. OO and Java tends to make things overly complicated.

One suggestion: build a puzzle game like minesweeper or lights out or sudoku. It's roughly the same architecture as a roguelike without the highly complicated game rules.
posted by pwnguin at 11:09 PM on November 21, 2009


Best answer: A couple more random things:

1. Most Roguelikes I have seen are written in procedural languages and not OO. The OO model allows for a lot of extra complexity in a Rougelike that the procedural implementations have proven you don't absolutely need.

2. Avoid analysis paralysis by building the smallest possible roguelike. Then refactor it or rewrite it, building the next-smallest possible roguelike. And so on.

I realize this isn't exactly in the spirit of your question. These are just some dusty old thoughts I have re. Roguelike architecture.

As I think more, I realize that if I were teaching computer science, I'd have a Roguelike implementation a mandatory project. In a graduate-level course. And watch them learn to build it wrong.
posted by krilli at 5:54 AM on November 22, 2009


Best answer: I feel your pain. I was in exactly your shoes several months ago, except rather than java, it was ruby that I was trying to learn, and the MVC pattern threw me for a bit.
Here are what my thoughts were when reading your question, but also keep in mind that different programmers have different styles, the way I think may not be the way you think.

The first part of my advice is very similar to those above ... namely simplify, simplify, simplify. For example, I would skip the whole menu part, sure its important, but to me its fluff at this point. Instead focus on a very small thing at first. Maybe just the ability to move the character in a small room. The room is fixed size, the character has no attributes other than moving.

So your model is essentially the state of the room, including the current position of the character, and prevents the characters position from passing through the wall. The model also contains the methods that do the state transitions (i.e. moveCharacter(x, y) or something).

The initial view should render the room, then the character. The view should also have methods for re-rendering. In this case, just redrawing the character location.

The controller will listen for input from the player and ask the model to change based upon that input. Note: the wording here was very deliberate, and a restatement of what others have said. The controller doesn't change the model directly (i.e. the controller does not maintain model state), it asks the model to change its state. It's the controllers job to take 'w' key press and decide that means call the model's moveCharacter(x_loc, y_loc) with (x, y-1). It's also the controllers job to notify the view if something needs to be re-rendered based on the model changing state. So after the model has informed the controller the state change has been completed (i.e., the return value of the moveCharacter method could be the current location of the character), the view is updated. In this example, a successful execution of the moveCharacter(x, y-1) would result in a character.reRender(oldloc, newloc) call to the view component.

If done correctly, it will be a breeze (relatively speaking) to change and add behaviors. So having different sized rooms, items existing in the room, secret passageways, changing the player controls (adding arrow keys for controls, or mouse events), changing from character based display to graphics, etc.

Hopefully this helps make the connection between the MVC pattern and the actual architectural steps you'd need to take. I know I struggled making that connection as well.
posted by forforf at 6:43 AM on November 22, 2009


I implemented Rogue as a Java applet while learning Java. In 1987.
It is as close to original Rogue as I could make it with only a little research.
It is not very MVC, but has been running for a long time at self-link.

The source (except for hackable high-score reporting) is here.
It is mostly close to its originals (early 1980s Rogue) but I rewrote the most horribly-implemented parts from scratch -- all random stuff is in one class (Randomx), there is true vision (vs light-up-a room-when-you enter it) and monster wandering was totally replaced.

There is a 'note' file that describes some of my experiences with it.
posted by hexatron at 8:29 AM on November 22, 2009


Response by poster: hexatron—thanks, but it looks like the source link is borked?
posted by ixohoxi at 9:48 AM on November 22, 2009


The source again
And if the .zip borks:
On this page click jrogue.zip
posted by hexatron at 10:38 AM on November 22, 2009


« Older Cream, Whey and What?   |   (UK) mobile phone operator? Newer »
This thread is closed to new comments.