OOP Pac-Man example - how do I make the ghosts *see* the maze? SOLVED!

Started by
18 comments, last by darenking 18 years, 10 months ago
Hello, I'm not actually writing a Pac-Man game, I'm writing a platform game, hopefully a very sophisticated one, but let's assume I'm writing a Pac-Man game, as it makes a nice clean example. (I'm using C++ with Allegro, but this question probably applies to any OOP language.) Please forgive my terminology which is probably wrong... Let's say I have my program structured like this: Maze, Ghosts, various other objects including bitmaps, intput objects etc Engine main() In otherwords, my main() function creates the game Engine object, which has the Maze, the Ghosts and various other objects as member variables. So there are in effect three levels, with the Maze and the Ghosts on the same level. It all works fine: the game engine deals with input and moves the Ghost sprites around, it also draws the Ghosts and Maze to the screen as the bitmaps and the screen buffer are also member variables of the game Engine. Trouble is, if the Maze and the Ghosts are all member objects of the game Engine, they can't communicate with each other. The maze doesn't care about the ghosts, just as a real maze wouldn't, so that is fine... but if I want the ghosts to have methods that move them around (based on input or AI from the engine) restricted by the walls of the maze, they need to be able to ask the maze questions, ie is there a wall located at point x,y in the maze, or a space? Theoretically I can think of three ways to solve this problem, without going into anything really complicated (I want to keep things simple). 1, which I have done as a temporary solution: declare the Maze object as a global, ugh! 3, Maybe I could keep the structure as in the 'diagram' above, and solve the problem by sending the ghost objects a pointer (or reference?) to the Maze object? 2, Rather than make the Maze, Ghosts, etc members of the game Engine, maybe I should just declare them all in main()? Then it would look more like this, if you compare with the 'diagram' above: Engine, Maze, Ghosts, various other objects main() Oh, but then the Ghosts still wouldn't be able to see the maze. Having said all that, I haven't even considered that I want the Ghosts to be able to bump into *eachother*, which suggests that I should keep to the first of the two structures above, but have their movement controlled within the Engine. The Ghosts can't 'see' each other unless the methods that restrict their movements are situated externally, ie the Engine. Have I just answered my own question here, or is there more to it, or a better way of structuring the program? Hmm... *goes for a lie down* [Edited by - darenking on June 19, 2005 3:51:33 AM]
Advertisement
Quote:Original post by joebarnslondon
Hello,

< snip >

Hmm...

*goes for a lie down*


I see two possible solutions:

1) the engine takes care of moving the ghosts. Nothing wrong with this - it is even quite good, IMHO, because you don't have any relationship bewteen the maze and the ghost classes (and no relathionship means no dependencies). Thus, you'll probably have a method "moveGhostInMaze(aGhost, theMaze)".

2) the ghosts know what a maze is, and are able to move themselves inside the maze. Again, there is nothing wrong here: if you consider that ghosts are rather sensible creatures, why don't you enable them to see the walls? Having a Ghost::moveInMaze(maze)" method don't hurt. The bad thing is that it adds a new dependency, but it is not very important one.

Since your question is more about design than about pac-man and its ghost - and I believe the example you took is not the best one, because there are many solutions to this problem, and a lot of them are good enough - I suggest you to

* list all the thing you want to add in your game (the different entities, considering that the world itself is an entity, as well as a spoon if your game feature spoons; don't try to be OO here: if you have a ninja and a zombie, don't treat them as a generalized creature). Don't mess up with the technical part of your game (no, neither your joystick nor the h4x0rz DirectX driver you just wrote are game entities).
* see how thay have to communicate (trace arrows between entities and put verbs on them)
* try to see the other relathionship (is a, has a) between these entities
* try remove circular dependencies (they are bad, and most of the time not needed; for example, a spoon don't know to who it belongs)

Once you have done this, you'll have a good base for your design. What you've just done is to create your game model; now, you have represent this model on screen (the view), and to implement feedback (from the controller) (yes, the Model/View/Controler paradigm can be easily applied to game software design).

HTH,
My personal opinion:

- no ghosts can exist outside of a maze
- therefore a maze always exists before ghosts exist
- so ghosts are members of maze, and pass a reference to the maze to the ghosts when ghosts are created
- ghosts can now refer to their encompassing maze when making decisions
- the maze has a function that can inform the ghosts about the player
- the maze can have an AllGhosts() function that returns a list of ghosts in the maze, to allow for coordination between ghosts
Quote:
neither your joystick nor the h4x0rz DirectX driver you just wrote are game entities


Do you mean that they should not be objects? Because they are not part of the game world?

For example, I wrote a Joystick class, which treats the arrow keys and the joystick as an object. Maybe this isn't needed? I guess I could just read the joystick and keyboard in a method in the Engine class. But putting them in a class keeps them away from everything else. This is going to become a very complicated project, and I intend to write lots of games using this engine, so surely it is a good idea to simplify things by creating classes for joystick and screen display and so on? It certainly keeps the Engine code simpler. Otherwise the Engine class is going to be huge! Especially if I make the joystick/keyboard quite sophisitcated, with user definable keys and stuff.

I guess they're not game entities in that they're not part of the game's internal world. But maybe it is OK to think of the "game", ie the thing that my program is creating, as a sort of virual arcade machine, like a coin-up (though without the coins). If you think of it like that, maybe it is OK conceptually to think of the joystick etc as "game" entities?

As I say, I plan to write lots of games using this engine, and I want them to be able to grow in complexity, so need to simplify the main engine as much as I can.

Surely on hugely complex 3D games they must have tons of objects that aren't monsters, guns etc, but are more abstract in some way?

Thoughts?
Not all objects you use will be game entities.

Personally what I've done in the past, is have a "Manager" object, which contains any "game global" objects (aggregation). This includes a "map" object or something.

Then I pass a reference to Manager in the constructor of the entity objects, so they all "know" about it (stored in a member of the entity base class).

Then the object can use manager.map to "see" the map.

One advantage of this over using a global, is that if the finishes a new game, I can just delete the manager object (or let it fall out of scope; it's normally stack-allocated). Then when the player restarts, all the objects are automatically necessarily reconstructed, so you can't get any problems with state "leaking" out of one game into another.

Mark
markr, so basically I could either keep my current structure or use a structure like yours and, either way, let the ghosts "see" the maze by sending them a pointer to the maze?

OK, I've decided to experiment with this sort of structure:

Ghosts
|
Maze - other objects along here
|
Engine
|
main()

So Maze is a member of the game Engine, and the Ghosts are members of the Maze.

All well and good: the Maze can see the Ghosts, so when the Engine calls the Maze.Paint() function, the Maze can paint itself (walls etc) and can also paint the ghosts as they are member objects of the Maze.

One thing I'm still stuck with though: how to make the Ghosts be able to look back down the chain at the Maze, to check the walls and stuff?

Presumably I need the Maze object to send a pointer to itself, ie to the Maze, to the Ghosts as it creates them; the Ghosts will store this pointer and be able to see the Maze so it can enquire about the walls.

Two questions then:
1 How can I get an object (the Maze) to send the Ghosts a pointer to itself?
2 Do I actually use a pointer, or a reference?

markr or anyone else who's listening, over to you...
When you create the ghosts, which will be somewhere in one of Maze's functions, pass the 'this' pointer to the Ghost's constructor. The constructor for each Ghost will store that pointer for later. (Or use a reference instead of a pointer - whichever you prefer.)
Thanks for that, Kylotan. I'll read up on the 'this' pointer and set to work, I reckon it will be fairly straightforward.

But I'm interested too in Mark's Manager object thingy. I can sort of see how it works but can't quite get my head round it.

Quote:
...have a "Manager" object, which contains any "game global" objects (aggregation). This includes a "map" object or something.
Then I pass a reference to Manager in the constructor of the entity objects, so they all "know" about it (stored in a member of the entity base class).
Then the object can use manager.map to "see" the map.


I think I would do it slightly different. In the Pac-Man example, rather than have the Ghosts as members of the Maze object, there would be an object called Manager (a member of the game Engine), and the Maze and Ghosts would sit alongside each other, as members of the Manager. Like this:

Maze and lots of Ghosts, all up here (Ghosts receive a pointer to Maze)
|
Manager - other objects along here beside it
|
Engine
|
main()

When the Manager creates the Maze and Ghosts, it will create the Maze first, then create each ghost, sending them a pointer to the Maze. (Wouldn't need the This pointer.)

Can anyone see any reason why this wouldn't work? Before I get knee-deep in code and tear my hair out and stuff?

Advantage is, Engine just has to tell Manager to update or draw itself or whatever, and as my game grows in complexity, the Engine won't get too bloated.
ghosts dont actually see the maze, they actually respond to were pacman is at, so move closer like left up left up, but it repeats it if he just stands there unless the ghost has a direct route

This topic is closed to new replies.

Advertisement