I am new to XNA and this is a philosophical question. In most examples I have seen, game assets are defined as private class variables, loaded in the LoadContent method, and then drawn with the Draw method. If I have a large game with a lot of screens, there could be quite a bit of declarations at the top of this class.
With that said, here are my questions
Should I use the content pipeline over Texture2D.FromFile().
What are the advantages other than faster loading.
Should I call Content.Load(Of T)([some asset name]) outside the LoadContent() sub.
How are you handling loading assets for different screens? Are you declaring all assets at the top?
Thanks in advanced,
Eric
Using the Content Pipeline allows you to compile your textures with your binary, which saves space, load time, and protects your assets from editing/unauthorized use if you care about that. On the flipside, if you wanted an asset to be editable (like texture packs), FromFile() is effective. The file must exist in the expected directory with normal use of course.
It is good practice but ultimately your decision on where you choose to load content. Remember that content loading requires reading from disk, which is not something you want to be doing every frame for sure, and not really something we like doing during the game. You will want to set up your Game State Management so that content can be loaded completely during loading screens or game startup and not during the game itself. Of course, this is precisely what level loading screens are for! If you're very clever you can sneak loading in during pauses in gameplay, a la Metroid Prime's 'door loading'. Depending on the scope and assets of your game, though, you shouldn't really need to load dynamically like that.
Finally, about dumping assets: the answer is the great trope of OO programming: abstraction. If you have trouble organizing members then move them into an inherited class or a subclass as necessary (and only when sensible). In my game design I rarely have more than 2 Texture2Ds, 1 SoundBank, and perhaps a VertexBuffer/IndexBuffer per class. If I have designed things well, these are stored in a base class like "Sprite" from which any visual objects inherit. In my latest set of tools, I've gone one level deeper, so now it looks like "Player.base(which is Sprite).Animation.Texture" if you want to access the actual texture... but you don't need to because all animation/drawing is handled completely by the Animation class and updated by Sprite along with Position, Rotation, Scale, Bounding, etc.
So, break down your game into objects. If you are storing a Texture2D PlayerTex and Vector2 PlayerPos in your Game class and in Draw you are drawing PlayerTex at PlayerPos, you are not taking advantage of OO programming. Store PlayerTex and PlayerPos in a Player class which also defines every other aspect and behavior (methods) of the player. Now all you need in Game is Player myPlayer, and in Draw you call myPlayer.Draw(SpriteBatch .. etc). You can take it even further! Here are some classes pretty much every game will have: Entity (base class of all dynamic objects), Level (stores scenery and Entities of each level and handles their interaction), GameScreen (stores and increments its Level member upon completion of each), ScreenManager (stores a stack of Screens to update, like GameScreen, but also MenuScreen, PauseScreen, LoadingScreen)... The list goes on. At this point all your Game1 class does is update ScreenManager, and if you inherit ScreenManager from IDrawableGameComponent, you don't even have to do that.
I hope I haven't dived too far into the deep end of OO 101, but if you're having trouble keeping track of all the members of your main class, you should start to break things down. It's a fundamental OO skill.
If all else fails, learn to use the #region <name>/#endregion tags liberally. Honestly, use them anyway, they make everything better.
Related
Currently I am trying my best programming a little video game in the style of the old Zelda games. However, I am having some trouble with the whole OOP style of thinking. To be more specific, I don't really know how to "design" the screens.
Let's say I have a class for my sprites and loaded a Wall-sprite to have a border for a certain area, should I make an extra "wall"-class, or is the wall being a "sprite" already enough? I thought it might be senseless to define an extra class since it would not have any different variables than the actual sprite class (as my wall IS only a sprite) so I didn't consider it to be a useful idea.
I am asking this because I have a little problem with the collision detection as well: What I currently do is loading a sprite for an object only once and rendering it multiple times at several locations. But the problem is that this causes the collision only to be detected at the last position the sprite was rendered at.
It gives me more problems when I render 2 cave-entrances somewhere but my game only checks for the 2nd entrance if I "entered" it.
So I thought making an extra "entrance"-class and creating 2 completely different objects of it that are treated separately might help, but should I then also create 30 objects for my wall-sprites?
Hmmm, there is really two questions, well three, but the OOP-thinking is too non-specific for a good question. So let's see if we can answer it by answering your valid ones.
Good OO Design is centered around "Patterns" (Common solutions to a variety of Problems) in the case of your sprite re-use in OO this would be known as a "Fly-weight" Pattern. Three important structural elements in good OO and understanding them is key to "getting it". They are:
Interfaces - They are free (relatively) of operational code, and provide only method and constructor signatures(generally) to allow for clean separation of coding concerns.
Classes - Only the reusable parts(ideally) of an object they are "the Mold or Pattern" that objects are instantiated (or patterned) from.
Objects - Instances (this chair or that chair as opposed, to Chair as an ideal) of the same form of Class (the ideal Chair). Objects (ideally) should keep as instance values only that which differentiates it from other instances of the same ideal.
However, because your original sprite is not an object you are having this collision problem, because it actually is the same instance rendered again and again, the graphics pipeline does not keep all of its previous locations as separate things it actual just stores pixels(usually), once they've been translated.
In this case if each instance is an object, each instance would have its location as a local instance variable(s), while its graphical representation and collision detection method would be common to all instances of the class.
Don't think of it as having 30 whole copies in memory at once, you only have thirty copies of the instance variables. This is true if you use OO or not; in a procedural solution to get proper collision detection you would have to maintain an array of all of the places you rendered that sprite and iterate through each time, further your code would be less cleanly separated and you would have to iterate through the array for every sprite interaction as well as for updating moving sprites. With OO you could handle this with one class method call that recurses it children.
A good class structure for this simple example might be:
An Abstract Sprite Class (abstract because you will never use a non-specific Sprite) containing only code common to all sprites
A Concrete Wall Sprite Class that extends Sprite, only the code for non-moving wall sprites.
A Concrete Trigger Sprite Class (whose graphic might be clear or null) for behaviors that need to be triggered in "open spaces"
A Concrete Agent Sprite Class for moving sprites (might implement a movable interface meaning all derivatives of the class have a move() method.
A Concrete Character class that extends agent for a movable sprite that is driven by user commands.
It may seem confusing at first, but it's actually cleaner, simpler, and more maintainable doing it the OO way.
:)
This is sort of a methodology question.
When writing software that makes use of object-oriented libraries to abstract OpenGL and the like, should you (or would you) choose to extend the OpenGL helper objects and add your own business logic onto them, or create your basic business objects and then have the OpenGL helpers as a property of them.
For example
Let's say I have an OpenGL library that provides a class
OpenGLBillboard
which draws a 2D sprite that faces the camera
if I have some sort of business object that is going to be rendered using that class, is it wise to simply extend OpenGLBillboard and build on top of it, or should I just have an OpenGLBillboard object that is a property on what is essentially a base class in my software?
It seems like inheritance in this case could be dangerous because if the base class ever has to change due to requirements, the refactoring could be painful ... whereas if I just go the property route, I have a little more upfront boilerplate to write.
Your thoughts?
Thanks
P.S. forgive me for saying "business object". I just didn't want to say "model" a whole lot in a topic about 3D and have people be confused as to my meaning.
Personally, I would probably have my business object contain (privately) a "Billboard" instance, and not tie it directly to OpenGL at all.
The business object would just be asked to render, and redirect it's methods through the Billboard instance. This would allow you to swap out a different implementation (if you decide to go with something other than a Billboard) or a different backend (ie: a Direct3D rendering pipeline) without changing the business object at all.
I'm working on a multiview app for iPhone and currently have my views (VIEW) set up and their transitions (CONTROLLER?) working nicely. Now I'd like to add objects for the actual program data (MODEL).
My question is: How should I structure my data to adhere to the Model View Controller (MVC) design pattern? I know I should create separate classes to implement my data structures and that my controller classes can pass messages to them from the view, but are there any other organizational considerations I should examine? Especially those particular to Cocoa Touch, Xcode, or iOS?
Other particulars: Playback of pre-recorded and perhaps user-generated audio will also be essential. I know these are model elements, but how exactly they relate to the "V" and the "C" I'm still a bit fuzzy on. I suppose when a user action requires audio playback, the CONTROLLER should pass a message to the MODEL to ready the appropriate sounds, but where exactly should regulation of the playback live? In a "PlayerController" separate from the ViewController I imagine?
Many thanks and pardon my MVC noobery.
Caleb gives a good introduction and overview of how to think about the problem. In your particular case, here are some of the pieces you would might have given your description:
Clip (M) - Responsible for holding the actual audio data. It would know how to interpret the data and given information about it, but it wouldn't actually play anything.
Player (V) - Actually plays a clip on the speakers. Yes, that's a kind of view in MVC. Audio is just another kind of presentation. That said, you'd never call it "PlayerView" because that would suggest it were a subclass of UIView.
PlayerView (V) - A screen representation of the Player. Knows nothing about Clips.
ClipManager (C) - An object that would keep track of all the clips in the system and manage fetching them from the network, adding and removing them to caches, etc.
PlayerViewController (C) - Retrieves a Clip from the ClipManager, and coordinates a Player and a PlayerView to display and play it, as well as any other UI elements (like a "back button" or the like).
This is just an example of how you might break it down for some theoretical audio player app. There are many correct MVC ways to do it, but this is one way to think about it.
Lord John Worfin (and, I'm sure, someone before him) said: "Character is what you are in the dark." Well, a model is what an application is when nobody is looking -- it's the data and logic that defines how the app behaves regardless of how it's presented on screen.
Imagine that you decide to add a command-line interface to your application. You'd still want to use the same structures for managing your data, and your logic for sorting, sifting, and calculating based on the data should still be the same too. The code in your app that remains important/useful no matter how the user sees or interacts with the app is the model.
A model can be very simple and made up entirely of standard objects. iOS apps are often more about retrieving, storing, and displaying data than they are about crunching numbers, so it's not unusual to have a model that's just an array of dictionaries, or a hierarchy of dictionaries that's several levels deep. If you look at Core Data's NSManagedObject class, it's similar in many respects to NSMutableDictionary. So, don't be afraid to use standard objects if they're appropriate.
That said, you can certainly also create your own model objects, and that's useful if you have certain requirements that you want to enforce on your data, or if you want to be able to derive information from the data.
Beginners often wonder how each controller gets access to the model. Some folks advocate using the singleton pattern for this, mainly because it provides a single, shared, globally accessible object. I don't recommend this. Instead, have some high-level object in your app such as the app delegate create/load the model (which will likely be a graph of many individual objects) and give a pointer to the model to any view controllers that need it. If those controllers in turn create other view controllers, they can again provide a pointer to the model (or part of it) to their child controllers.
I hope that helps.
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".
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 ;-).
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.
App is launched by user
C main() function is called
main() calls NSApplicationMain()
NSApplicationMain() load the MainMenu.nib (or another nib specified in the info.plist)
Nib loading initializes all of the objects defined in the nib (including the application delegate)
Nib loading makes all the connections defined in the nib
Nib loading calls awakeFromNib on all the objects it just created
-applicationWillFinishLaunching: is called in the application delegate.
NSApplication (or a subclass specified in the Info.plist) is initialized
-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.
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."
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.
You might find this article Introduction to MVC design using C# useful. Although the example is in C#, the principle should apply.
I'll be as direct as I can concerning this problem, because there must be something I'm totally missing coming from a structured programming background.
Say I have a Player class. This Player class does things like changing its position in a game world. I call this method warp() which takes a Position class instance as a parameter to modify the internal position of the Player. This makes total sense to me in OO terms because I'm asking the player "to do" something.
The issue comes when I need to do other things in addition to just modifying the players position. For example, say I need to send that warp event to other players in an online game. Should that code also be within Player's warp() method? If not, then I would imagine declaring some kind of secondary method within say the Server class like warpPlayer(player, position). Doing this seems to reduce everything a player does to itself as a series of getters and setters, or am I just wrong here? Is this something that's totally normal? I've read countless times that a class that exposes everything as a series of getters/setters indicates a pretty poor abstraction (being used as a data structure instead of a class).
The same problem comes when you need to persist data, saving it to a file. Since "saving" a player to a file is at a different level of abstraction than the Player class, does it make sense to have a save() method within the player class? If not, declaring it externally like savePlayer(player) means that the savePlayer method would need a way to get every piece of data it needs out of the Player class, which ends up exposing the entire private implementation of the class.
Because OOP is the design methodology most used today (I assume?), there's got to be something I'm missing concerning these issues. I've discussed it with my peers who also do light development, and they too have also had these exact same issues with OOP. Maybe it's just that structured programming background that keeps us from understanding the full benefits of OOP as something more than providing methods to set and get private data so that it's changed and retrieved from one place.
Thanks in advance, and hopefully I don't sound too much like an idiot. For those who really need to know the languages involved with this design, it's Java on the server side and ActionScript 3 on the client side.
I advise you not to fear the fact, that player will be a class of getters and setters. What is object anyway? It's compilation of attributes and behaviours. In fact the more simple your classes are, the more benefits of an OOP you'll get in the development process.
I would breakdown your tasks/features into classes like that:
Player:
has hitpoints attribute
has position attribute
can walkTo(position), firing "walk" events
can healUp(hitpoints)
can takeDamage(hitpoints), firing "isHurt" event
can be checked for still living, like isAlive() method
Fighter extends Player (you should be able to cast Player to Fighter, when it's needed) :
has strength and other fighting params to calculate damage
can attack() firing "attack" event
World keeps track of all players:
listens to "walk" events (and prevents illegal movements)
listents to "isHurt" events (and checks if they are still alive)
Battle handles battles between two fighters:
constructor with two fighters as parameters (you only want to construct battle between players that are really fighting with each other)
listens to "attack" events from both players, calculates damage, and executes takeDamage method of the defending player
PlayerPersister extends AbstractPersister:
saves player's state in database
restores player's state from database
Of course, you game's breakdown will be much more complicated, but i hope this helps you to start thinking of problems in "more OOP" way :)
Don't worry too much about the Player class being a bunch of setters and getters. The Player class is a model class, and model classes tend to be like that. It's important that your model classes are small and clean, because they will be reused all over the program.
I think you should use the warpPlayer(player, position) approach you suggested. It keeps the Player class clean. If you don't want to pass the player into a function, maybe you could have a PlayerController class that contains a Player object and a warp(Position p) method. That way you can add event posting to the controller, and keep it out of the model.
As for saving the player, I'd do it by making Player implement some sort of serialisation interface. The player class is responsible for serializing and unserializing itself, and some other class would be responsible for writing the serialised data to/from a file.
I would probably consider having a Game object that keeps track of the player object. So you can do something like game.WarpPlayerTo(WarpLocations.Forest); If there are multiple players, maybe pass a player object or guid with it. I feel you can still keep it OO, and a game object would solve most of your issues I think.
The problems you are describing don't belong just to game design, but to software architecture in general. The common approach is to have a Dependency Injection (DI) and Inversion of Control (IoC) mechanisms. In short what you are trying to achieve is to be able to access a local Service of sorts from your objects, in order for example to propagate some event (e.g warp), log, etc.
Inversion of control means in short that instead of creating your objects directly, you tell some service to create them for you, that service in turn uses dependency injection to inform the objects about the services that they depend on.
If you are sharing data between different PCs for multiplayer, then a core function of the program is holding and synchronising that piece of state between the PCs. If you keep these values scattered about in many different classes, it will be difficult to synchronise.
In that case, I would advise that you design the data that needs to be synchronised between all the clients, and store that in a single class (e.g. GameState). This object will handle all the synchronisation between different PCs as well as allowing your local code to request changes to the data. It will then "drive" the game objects (Player, EnemyTank, etc) from its own state. [edit: the reason for this is that keeping this state as small as possible and transferring it efficiently between the clients will be a key part of your design. By keeping it all in one place it makes it much easier to do this, and encourages you to only put the absolute essentials in that class so that your comms don't become bloated with unnecessary data]
If you're not doing multiplayer, and you find that changing the player's position needs to update multiple objects (e.g. you want the camera to know that the player has moved so that it can follow him), then a good approach is to make the player responsible for its own position, but raise events/messages that other objects can subscribe/listen to in order to know when the player's position changes. So you move the player, and the camera gets a callback telling it that the player's position has been updated.
Another approach for this would be that the camera simply reads the player's position every frame in order to updaet itself - but this isn't as loosely coupled and flexible as using events.
Sometimes the trick to OOP is understanding what is an object, and what is functionality of an object. I think its often pretty easy for us to conceptually latch onto objects like Player, Monster, Item, etc as the "objects" in the system and then we need to create objects like Environment, Transporter, etc to link those objects together and it can get out-of-control depending on how the concepts work together, and what we need to accomplish.
The really good engineers I have worked with in the past have had a way of seeing systems as collections of objects. Sometimes in one system they would be business objects (like item, invoice, etc) and sometimes they would be objects that encapsulated processing logic (DyeInjectionProcessor, PersistanceManager) which cut across several operations and "objects" in the system. In both cases the metaphors worked for that particular system and made the overall process easier to implement, describe, and maintain.
The real power of OOP is in making things easier to express and manage in large complex systems. These are the OOP principles to target, and not worry as much whether it fits a rigid object hierarchy.
I havent worked in game design, so perhaps this advice will not work as well, in the systems I do work on and develop it has been a very beneficial change to think of OOP in terms of simplification and encapsulation rather than 1 real world object to 1 OOP class.
I'd like to expand on GrayWizardx's last paragraph to say that not all objects need to have the same level of complexity. It may very well fit your design to have objects that are simple collections of get/set properties. On the other hand, it is important to remember that objects can represent tasks or collections of tasks rather than real-world entities.
For example, a player object might not be responsible for moving the player, but instead representing its position and current state. A PlayerMovement object might contain logic for changing a player's position on screen or within the game world.
Before I start simply repeating what's already been said, I'll point towards the SOLID principles of OOP design (Aviad P. already mentioned two of them). They might provide some high-level guidelines for creating a good object model for a game.