Help with decoupling a game design
Here's the current dependency graph (with freehand circles for TheTXI)
A game has players and a single board shared between them. A player also has access to the board to be able to add/move/remove units from it. A player has access to the units it owns, both on and off the board (A unit also knows it's owner, but that can probably be removed and just do a lookup). A board has units on it and knows units' positions. Units have abilities (Players possibly can as well)
The big one that I'm having trouble figuring out is Unit's Abilities. They should be able to affect anything in the game, healing/damaging a player/unit, reposition things on the board, possibly even the game itself (so far there isn't a need, but it could come up).
How can I have abilities that can affect anything without them having a reference to everything? I realize each ability can and should have references to only what it needs, but a unit class has abilities built in to them, so if a unit's ability affects the board, it will need to get a reference to the board from the unit somehow?
I'm trying to keep the design as flexible as possible, because the rules aren't set in stone yet (we're creating a game, and still pretty early on, so it's try something, see how it feels, change the rules until the game feels right)
Even whether there's a board/map at all is still up in the air, so the units should be decoupled from that, which they currently are. There is no global state or any "god object" (yet), and I'd like to keep it that way.
Specifically it's in Python, webapp, so while the question itself is language agnostic, any specifics based on a dynamic language wit开发者_如何学Goh first class functions are certainly welcome.
First, let me point you to the wonderful gamedev StackExchange. You might have more luck posting this question there.
As far as your question itself goes, I think one solution would be to pass notifications from the object triggering the ability up to the game object, which would then parse these notifications and hand them out to specific players, or boards, and from there to specific units.
It's hard to explain, so let me try and write it out in psuedocode...
Game {
getMe(); //Returns reference to singleton class.
list of players
list of boards
}
Board {
list of units
}
Unit {
int health
}
function Unit.notifyAbility(source, targeting-condition, ability-code) {
Game::getMe()->sendNotification(source, targeting-condition, ability-code);
}
function Game.sendNotification(source, targeting-condition, ability-code) {
for each unit in list of units {
if(unit matches targeting condition) {
apply ability-code
}
}
}
Targeting-condition and ability-code could themselves be data structures that pass relevant information. Even better, make them virtual classes and use some form of polymorphism to handle unique cases.
Example:
AbilityCode {
virtual function applyToUnit(target Unit)
virtual function applyToPlayer(target Unit)
}
AbilityGainHP: child of AbilityCode {
function applyToUnit(target Unit) { target.hp+= gainAmt; }
int gainAmt;
}
Hope this makes sense.
You might overthink this a bit.
Keep in mind, you want to be able to work with your code base, so it's important that you can easily understand your code, even if you haven't worked on that particular part for a bit.
If you describe your game in words, you'll most likely the use something like: It's a game for X players, played on a board of (specfics). Each player controls Y units.
So, start your design like this. The game (either your complete code, or just the PLAYING state of your program) needs to have references to the board and the players.
Now, the board will handle most of your problems. If a player interacts with the game, he'll most likely do something on the board.
Start with a simple system that allows you to move stuff around you should be able to move around, while not being able to move the stuff you shouldn't.
The abilities will most likely be a list containing the currently available selection out of the whole list. These will depend on the unit and maybe other elements of the board.
Depending on the type of game, you can either use events to inform the units that something has changed, or simply get the information when you need it.
Don't add more layers of abstraction than you actually need. From your description, the abilities seem to be the part you want to spend most time on, to ensure you can easily change and add to them.
Start with a simple design for the other parts, and refactor if you see that you need an extra layer at certain points. This will avoid analysis paralysis. Esp. when you want to make a "neat design" it's very easy to overcomplicate things.
You want to get the game running. You want to be able to read (and understand) your code easily. Your coding style and knowledge will change even while working on the project, and you'll also get new ideas while coding.
精彩评论