Sign in to follow this  

2d shooter

This topic is 3811 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Here's a pic to give you a general idea of my code : wxcv So far I've got the same "Enemy" that comes in a loop and I'm currently thinking of ways to add more kinds. A simple one would be to add subclasses like Enemy1,Enemy2 etc. but it's not good since I would need to explicitely instanciate them in the code with for example "new Enemy1();new Enemy1234().." when I'd prefer something like "SpawnEnemyNumber(n)", but how to implement this ? Thanks in advance

Share this post


Link to post
Share on other sites
One possible solution to your problem might be the following:

Create an abstract class called AbstractEnemy for example.
That way your concrete enemy could implement method behaving differently
by declaring them as abstract methods in AbstractEnemy.
In this class implement a static method called spawn. The method spawn
takes the type of enemy you'd like to create and returns an instance
of the according concrete enemy.

This may look something like this:

<code>
class AbstractEnemy {

public:
static AbstractEnemy spawn(int iEnemyType)
{
switch (iEnemyType)
{
case 0:
return new BadGuy();
case 1:
return new ReallyBadBuy();
// and so on
}
}

abstract void Shoot();
abstract void Move();
// and so on
};

class BadGuy : public AbstractEnemy
{
public:
void Shoot()
{
// do something
}

void Move()
{
// do something
}
};
</code>

Hope this helps and gives you some idea on how to do what you need.

Michael

Share this post


Link to post
Share on other sites
I think that you're looking for lists or vectors. They are essentially resizeable arrays. In particular, look at std::vector. There's plenty of information about it on the web. For example, some code could be (warning, not tested):

std::vector<Enemy*> enemies; //A list of pointers to enemies

//Add two Enemies to enemies
enemies.push_back( new Enemy() );
enemies.push_back( new Enemy() );

std::vector<Enemy*>::iterator itr; //An iterator that can traverse all of enemies

for(itr = enemies.begin(); itr != enemies.end(); itr++)
{
itr->SomeFunction();//Calls each Enemies SomeFunction
DisplayObject(itr.get());//Displays each Enemy
}



So it behaves sorta like an array, except that you can use iterators instead of indexes, and they can resize themselves to fit the number that you add via push_back or other operations.

Edit: I think I misread the first post. Instead of manually typing in 'new Enemy###();', have a level file that defines what to create.

[Edited by - Ezbez on July 10, 2007 7:10:49 AM]

Share this post


Link to post
Share on other sites
You're making a mistake here: your proposed SpawnEnemyNumber(n) function still needs to check which enemy you want to create, so inside that function, you still need to create the specified type explicitely. And while I think it's good to provide an interface for spawning new enemies, doing so by passing an integer isn't the most clear way. SpawnEnemy(15) isn't very descriptive, is it? An enumeration would be better: it's much easier to see which enemy type SpawnEnemy(SHIELD_CARRIER) will create.


So, for example:

enum EnemyType
{
SHIELD_CARRIER,
MACHINE_GUNNER
}

void SpawnEnemy(EnemyType type)
{
if(type == SHIELD_CARRIER)
enemies.push_back(new EnemyShieldCarrier());
else if(type == MACHINE_GUNNER)
enemies.push_back(new MachineGunner());
}




EDIT: Perhaps it's possible, with some hackwork, to make classes register themselves, so the spawn function only has to look up with what class the given value is associated. This would allow you to keep the code for each object inside it's class only, so you don't need to do an explicit check for it somewhere else anymore. I wonder, however, if it's worth the hassle at this point. I also wonder how this could be achieved. Perhaps some global macro trickery or such, I don't know.

Share this post


Link to post
Share on other sites
Not sure whether this is quite what you're asking, but: It probably depends on whether the different enemy types need different code as opposed to different values for variables. If the only differences are in appearance and speed, say, then you can use one class and maybe pass a list/dictionary/etc. of variable settings to make an enemy. If the enemies have substantially different abilities then you might want several classes that inherit the basics, like location and movement, and with some adding new features. For instance I might have one enemy with a built-in weapon of some kind, and another that's an NPC version of the player, with the ability to pick up weapons.

By the way, you might look at the shareware 2D shooter "Soldat" for inspiration on the game style.

Share this post


Link to post
Share on other sites
Function pointers can come in handy here. They allow you to call specific code without subclassing or switch statements, and have very little overhead. Here's an example of how you could apply them to your enemy class:


struct Enemy : public Objext
{
typedef void(Enemy::*t_func)(); // define function pointer
t_func move_func; // function pointer instance
void cylon(); // make Object act as a cylon
void borg(); // etc.
Enemy(t_func func) : move_func(func) {} // constructor
void Move() { (*this.*move_func)(); } // call function pointer
};

Enemy creep1(Enemy::cylon); // create a cylon
creep1.Move(); // indirectly calls Enemy::cylon()
...


Share this post


Link to post
Share on other sites

This topic is 3811 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this