4

So I am making a Super Mario Bros NES clone in pygame and for all enemies that are drawn to the window, I have a class hierarchy that is 5 classes deep. Going in the order of:

  1. Object (A base class for all things drawn on the screen)
  2. Character (A class for any type of character, playable or not)
  3. Enemy (A class for any enemy in the game)
  4. BasicEnemy (A class for any enemy that only moves horizontally, as they both act the same, just different visuals. This includes Goombas and Koopas).

Then the deepest classes are Goomba and Koopa, with the class structure stated above. I am doing it this way because all objects that are drawn to the screen all have common properties, as well as all characters, enemies and basic enemies. For example, all enemies move horizontally through the exact same method (adding their x velocity to their x position). I have read in many places that too much inheritance is bad. I'm not sure if what I am doing is too much. And if it is too much, how should I be doing it?

  • I don't think it's too deep. It seems like decent number of layers. One suggestion I have is to make sure behavior is in the highest class it can be. Certainly other characters can move. If you have a class that isn't supposed to jump, make their jumpHeight = 0 or something like that. – BAM Aug 3 '15 at 21:50
  • gameprogrammingpatterns.com/component.html PS: this is not the right place, go to gamedev.stackexchange.com – Den Aug 4 '15 at 9:10
  • @Den This is one of those questions that actually is on topic at both. It can be asked at both too as long as the question is worded such here to seek answers from a software developers perspective, and at GameDev to seek answers from a Game Developers perspective. – maple_shaft Aug 4 '15 at 16:07
  • @maple_shaft you are right. – Den Aug 4 '15 at 16:26
8

I would have your objects implement interfaces like ICharacter, IEnemy, IHorizontalMover. The relevant design guideline is known as Favor Composition over Inheritance, and it should allow your design to be more flexible. One difference is that interfaces tend to specify behavior and any given object can implement as much or as little as it needs.

If you feel that this causes code duplication that you did not have when a base class just handled mostly non-overridden methods, then you should be using forwarding methods, where your implementing classes forward method calls to a private class (or stateless static class) that provides a function common to several implementations. This keeps the codebase DRY (meaning Don't Repeat Yourself). It can feel like you are writing more boilerplate lines to achieve what inheritance takes care of for you behind the scenes, but it is generally considered worth the effort, leaving the codebase more flexible and open to change than a pure inheritance design.

  • I didn't realize python doesn't support interfaces, what would be the python equivalent to this? I will update my question accordingly. – Robbie Aug 3 '15 at 22:42
  • 3
    @Robbie I think you're right that python doesn't have interfaces. Interfaces don't really make sense in dynamically typed languages anyway. – Rob Aug 3 '15 at 23:45
  • In fact, I'm going to just take this answer as its correct for other languages. – Robbie Aug 4 '15 at 1:48
  • 1
    @Robbie In Python, you usually just attempt to call the method, and if someone messed up enough to pass something that doesn't have it, you get a TypeError. This is known as Duck Typing. – jpmc26 Aug 4 '15 at 4:38
6

At first glance it sounds reasonable to use inheritance to separate different types of entities: this is the "real-world" approach to OOAD that most colleges teach in CS101. In other words, classes are named based on real-world objects that we can see: desk, dresser, door, etc.

However, that makes little sense in the context of a video game. There are really two categories of concern with the objects you are modeling:

  • The objects appear on screen and need to be drawn, so it makes sense to model them using classes. After all they have both properties (a sprite or 3D model) and behavior (e.g. movement) which make them candidates for classes.

  • The objects interact. A player object might interact with a brick block (this is a Mario clone, after all) by standing on it or smashing it. The object might interact with an enemy by stomping on it.

When I analyze these potential classes, I see a lot of the same concerns. Some object move, some do not. Some have "intelligence" behind them (AI or human), some do not. I do not see much value in the hierarchy you presented: the concerns are spread out too much.

I recommend moving the concerns about object control outside of the objects themselves. Here is how I would design it, based loosely on MVC.

Actor would replace Object in your hierarchy. The idea is an actor has some role on the game field. That role might be "do nothing" in the case of a brick that is unsmashable and performs no actions. It might be able to remove itself from the game field (brick gets smashed). It might move: this could be a platform that patrols on a loop, an enemy with an AI, or a player that receives input from a hardware device.

ActorView is passed in to an Actor and controls how it is displayed. This could be as simple as "always display this sprite" or "render this 3D model based on certain criteria."

ActorController is the "brain" of the Actor. One could have a "do nothing" implementation that never changes state (e.g. an unbreakable brick), a simple implementation that can remove the actor (e.g. a breakable brick), a more complex implementation (e.g. a platform that patrols back and forth), an AI implementation (e.g. an enemy), or a player (use hardware input).

Now you can use composition: Actor should really just be one class that takes a view and a controller. The ActorView might have a small number of subclasses that can be reused. Do you really need a different view for each static brick? No, you can simply reuse one class and pass a different bitmap in each time. ActorController could have a small number of subclasses for each broad type of Actor: static, scripted (moves, breaks), AI, player.

  • +1 for debunking the nonsense "classes are real world objects" I got in school (and later realized how absurd it is). – jpmc26 Aug 4 '15 at 4:42
  • "There are really two categories...". I disagree. There are n categories. Mesh category, data optimized for rendering. Physics category, data optimized for collision handling, possibly implemented via another library like Bullet. Tens of categories specific to concrete games. "It makes sense to model them using classes", only if performance does not matter. The solution is to model full systems/components using classes, not going into detail by modeling each game entity as class. This aproach's name is "Component Entity System". – cubuspl42 Aug 4 '15 at 9:14
1

Instead of trying to figure out the exact class hierarchy you need, how about letting it grow organically?

The big advantage of inheritance is avoiding repetition. When you put functionality in a base class, you can share that functionality among many different classes without having to put the same code in each one.

So just inherit from Object, write your code, and then start creating intermediary classes where it makes sense to avoid repetition. When you find two objects doing the same thing, create an abstract class between them and Object and pull the common behavior into that abstract class.

If your hierarchy begins to get too complex, with common behavior among objects that aren't that closely related, that's when you can start to pull behavior out into separate objects and use composition to achieve that behavior...again, in order to avoid repetition.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.