2011年10月20日星期四

Architecting 15 types of in-game enemies (without messing up the code) Part 1 [EddyL]

Architecting 15 types of in-game enemies (without messing up the code) Part 1 [EddyL]:
There are 15 types of enemies in my iOS game, Thick Lips, and each has its special super power. This post is going to show how I architect the game with design patterns, so that the 15 types of enemies wouldn’t mess up the code.





I assume you have basic knowledge of object-oriented programming. The post will be splitted into 2 parts.

For part 1, I’ll show you why we should not use inheritance, the well known design pattern built into most modern programming language, to solve the problem.

For part 2, I’ll show you how I use composition to solve this problem.

What is my game about?


The iOS game, Thick Lips, is an adaption to the classic whack-a-mole style game with levels. It is easy to play (just tap to destroy enemies), but is challenging to master. When the player proceeds to the next level, he is rewarded with a new type of enemy having a new and more challenging kind of super power. There are total 15 types of enemies.




It’s challenging to architect 15 types of super power


To explain how challenging it is to architect so many types of super power, let me introduce some of the enemies to you.











Don’t use Inheritance for this kind of problem


Inheritance is the most well-known design pattern for programmers. Yet, inheritance is not suitable for this problem, as it just messes up the codes. Let me explain it:

The base class, “Enemy”


Let’s use the class “Enemy” to represent the basic type of enemy. Player can just tap the enemy to destroy it. “Enemy” has a method named “EnemyDidTapped” to handle tapping related action.




It’s still ok for “SpinyEnemy”


Then, we use “SpinyEnemy” to represent enemies that player should not tap. “SpinyEnemy” inherits from “Enemy” and it overrides the method “EnemyDidTapped” to handle forbidding tapping.



It goes crazy for “HalfSpiny”


Here comes the problem. How should we represent Half Spiny that switch between spiny and non-spiny for every 2 seconds? Should it inherit from “Enemy” or “SpinyEnemy”? It seems HalfSpinyEnemy should contain both world of EnemyDidTapped!



How about the fast-moving HalfSpiny?


In addition, the fast-moving guy could be represented by the “MovingEnemy” class, inheriting “Enemy”. It contains a method “moveEnemy” to handle enemy moving.




So, how about fast-moving half spiny? Should it inherit from “SpinyEnemy”, “Enemy”, or “MovingEnemy”? It needs the method “EnemyDidTapped” of “SpinyEnemy”/”Enemy”, and also the method “moveEnemy” of “MovingEnemy”!




Conclusion


So you get it. Using inheritance to solve this kind of problem will drive you crazy in 2 main possible ways:

  1. It ends up creating a lot of classes with many code duplication.
  2. It ends up creating 1 single class with excess if-statements to handle different types of enemy.

In part 2, I’ll show you how I use composition to solve this kind of problem. Part 2 is coming soon… :D



没有评论:

发表评论