enemy system for shooter

Started by
2 comments, last by darookie 19 years, 3 months ago
hey guys... i'm working on a 2d shooter, and i'm trying to figure how i can best implement my enemies. i was wondering, would it be better to have one generic Enemy class that works for everything, or having a separate class for each type of enemy? i think it would be easier to have a separate class for each type of enemy, but then again, i can't think of any difference between types of enemy that would justify making 'Enemy' an abstract class (other than the boss) thoughts?
Advertisement
I would suggest googling for 'Inheritance' or read up on it somwhere, or whatnot. Inheritence lets you declare a base class, in your case Enemy, and put the things that are similar inside of it. Draw, Move, Think, position, etc. and then "inherit" other classes off of it, saying "This is a Ogre enemy, but its still an enemy" and you put the things specific to ogre in it, like Stench, Ugly (note: not real examples :)) and it will still have draw, move, think, inside of it. Theres more features of it, so i would suggest reading up on it, thats just the basics. Gladi could help give back to the gamedev community! :-D
Quantumplation
Just think of different enemy types. Depending on the diversity you'll want to have an abstract or basic class.

Possible types:

o Boss
o following a path
o tracking the player
o Bullets
o grouping (flocks)

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

You don't necessarily need different enemy classes. One class is sufficient - you can (and should) use a data-driven design approach. Your enemies could for example have a micro-language that defines their behaviour (e.g. movement patterns or AI-behaviour).
Example:
// pattern commandstruct Command {    int Id;      // command id    int Param1;  // generic parameter 1    int Param2;  // generic parameter 2};// pattern entry - processed each game logic tickstruct PatternEntry {    int       NumCommands;  // Number of commands in this entry    Command * CommandList;  // List of 'NumCommands' commands    PatternEntry * Next;    // Linked list → pointer to next entry};const int CMD_IDLE             = 0;const int CMD_MOVE             = 1;const int CMD_SHOOT_UP         = 2;const int CMD_SHOOT_DIAG_LEFT  = 3;const int CMD_SHOOT_DIAG_RIGHT = 4;const int CMD_SHOOT_DOWN       = 5;const int CMD_MOVE_TO_PLAYER   = 6;...// simple commands processingvoid Enemy::ProcessPattern() {     bool gotoNext = true;     // loop through all current commands     for (int i = 0; i < currentEntry->NumCommands; ++i) {         Command * command = currentEntry->CommandList;         switch (command->Id) {            // sample: IDLE waits Param2 ticks, Param1 is initially set to Param2 and is decremented each loop            case CMD_IDLE:                // decrement counter                if (command->Param1 > 0) {                    --command->Param1;                    gotoNext = false;     // prevent from loading next entry                    // set for-loop condition to false to leave it                    i = 9999;                    continue;                }                // idle counter elapsed → reset Param1                else {                                     command->Param1 = command->Param2;                }                break;                // move: Param1 is dx, Param2 is dy            case CMD_MOVE:                this->Move(command->Param1, command->Param2);                break;                // move to player: Param1 is velocity x, Param2 is velocity y            case CMD_MOVE_TO_PLAYER:                {                   int x, y;                   this->GetPlayerPosition(x, y);                   if (x < this->positionX)                        x = -1;                   else if (x > this->positionX)                        x = 1;                   else x = 0;                   if (y < this->positionY)                        y = -1;                   else if (y > this->positionY)                        y = 1;                   else y = 0;                   this->Move(command->Param1 * x, command->Param2 * y);                }                break;           ....         }     }      // move to next entry     if (gotoNext) {        currentEntry = currentEntry->Next;     }}


Instead of a switch statement you could also use a std::map that maps command Ids to member functions. The good thing about these patterns is that you can define them in text files on disk, which you can modify and test without recompilation.
Plus you only need one pattern processor for all enemies and still create a unique behaviour for each enemy type.

HTH,
Pat

This topic is closed to new replies.

Advertisement