Making Enemies shoot bullets

Started by
10 comments, last by BeerNutts 9 years, 2 months ago

I'm having a little trouble getting all my enemies to shoot bullets. I thought I would just need a vector (like the for the player) but all I get is 1 enemy shooting instead of all of them shooting. I'm pretty sure it doesn't work because I'm dealing with more than 1 enemy at the same time so having all these enemies drawing from 1 vector is probably not going to work.

I just would like to know how I should go about this? What process should I use?

Advertisement

It would help if you could post a code snippet and say in what language you are programming this. Each of the enemies should shoot in the direction he's looking right? Depending on how you encode orientation (quaternions, matrix, euler angles, axis-angle) you might need to do different things to get the vector you need. The fact that only one of the "enemies shoots bullets" would hint at the fact that apart from the orientation, you get the positions of the bullets wrong. Here's some code:


class Bullet
{
private:
    static std::vector<Bullet*> bullets;
    Mesh*  mesh;
//...
    Vector3 direction;
    Vector3 position;
public:
    //...
    Bullet(const Vector3& position, const Vector3& direction);
}

class Enemy
{
private:
    Vector3 position;
    Vector3 direction;
public:

    void shoot()
    {
         
         Bullet = new Bullet(position, direction);
         //...
    }
}


It would help if you could post a code snippet and say in what language you are programming this. Each of the enemies should shoot in the direction he's looking right? Depending on how you encode orientation (quaternions, matrix, euler angles, axis-angle) you might need to do different things to get the vector you need. The fact that only one of the "enemies shoots bullets" would hint at the fact that apart from the orientation, you get the positions of the bullets wrong. Here's some code:


class Bullet
{
private:
    static std::vector<Bullet*> bullets;
    Mesh*  mesh;
//...
    Vector3 direction;
    Vector3 position;
public:
    //...
    Bullet(const Vector3& position, const Vector3& direction);
}

class Enemy
{
private:
    Vector3 position;
    Vector3 direction;
public:

    void shoot()
    {
         
         Bullet = new Bullet(position, direction);
         //...
    }
}

Yeah I forgot to mention its in C++ because I was just looking for a general thing. Now you have your shoot function, do I loop through my vector of enemies and call enemies.shoot()?

Well it depends on what you want to do, if I were you I'd implement some simple AI for the enemies as it doesn't make any sense for them to shoot non-stop. So basically you'll have a few states the enemies can be in, like idle, attacking, chasing etc.

Since it's in c++ here's a sketch of what I would recommend:


#include <set>
class Bullet
{
private:
	Vector3 position;
	//encodes both the direction and speed of the projectile
	//basically velocity
	Vector3 direction;
	static std::set<Bullet*> bullets;
        static double speed;
	//...stuff...
public:
	Bullet(const Vector3& position, const Vector3& direction)
		:position(position), direction(direction*speed)
	{
		bullets.insert(this);
		//...stuff...
	}

	void update(double dtime)
	{
		position += direction*dtime;
		//...stuff...
	}

	~Bullet()
	{
		bullets.erase(bullets.find(this));
		//...stuff...
	}
};

class Enemy;

class EnemyState
{
public:
	//...stuff...
	virtual void update(Enemy* enemy) = 0;
};

class EnemyStateIdle :public EnemyState
{
	//...stuff...
	void update(Enemy* enemy)
	{
		//do nothing
	}
};

class EnemyStateAttack : public EnemyState
{
private:
	
public:
	//...stuff...
	void update(Enemy* enemy)
	{
		enemy->shoot();
	}
};

class Enemy
{
private:
	Vector3 position;
	Vector3 direction;
	EnemyState* state;
	static std::set<Enemy*> enemies;

private:
	static EnemyStateIdle idleState;
	static EnemyStateAttack attackState;
public:

	void shoot()
	{
		//offset so the bullet won't appear in the enemy's center
		double offset;//=...
		Bullet* temp = new Bullet(position + offset*direction, direction);
		//...stuff...
	}

	void think()
	{
		//if player is in range do:
		if (state == &idleState)
		{
			state = &attackState;
		}
		//if the player is out of range do:
		if (state == &attackState)
		{
			state = &idleState;
		}
	}

	void update()
	{
		think();
		state->update(this);
	}

};

Well it depends on what you want to do, if I were you I'd implement some simple AI for the enemies as it doesn't make any sense for them to shoot non-stop. So basically you'll have a few states the enemies can be in, like idle, attacking, chasing etc.

Since it's in c++ here's a sketch of what I would recommend:


#include <set>
class Bullet
{
private:
	Vector3 position;
	//encodes both the direction and speed of the projectile
	//basically velocity
	Vector3 direction;
	static std::set<Bullet*> bullets;
        static double speed;
	//...stuff...
public:
	Bullet(const Vector3& position, const Vector3& direction)
		:position(position), direction(direction*speed)
	{
		bullets.insert(this);
		//...stuff...
	}

	void update(double dtime)
	{
		position += direction*dtime;
		//...stuff...
	}

	~Bullet()
	{
		bullets.erase(bullets.find(this));
		//...stuff...
	}
};

class Enemy;

class EnemyState
{
public:
	//...stuff...
	virtual void update(Enemy* enemy) = 0;
};

class EnemyStateIdle :public EnemyState
{
	//...stuff...
	void update(Enemy* enemy)
	{
		//do nothing
	}
};

class EnemyStateAttack : public EnemyState
{
private:
	
public:
	//...stuff...
	void update(Enemy* enemy)
	{
		enemy->shoot();
	}
};

class Enemy
{
private:
	Vector3 position;
	Vector3 direction;
	EnemyState* state;
	static std::set<Enemy*> enemies;

private:
	static EnemyStateIdle idleState;
	static EnemyStateAttack attackState;
public:

	void shoot()
	{
		//offset so the bullet won't appear in the enemy's center
		double offset;//=...
		Bullet* temp = new Bullet(position + offset*direction, direction);
		//...stuff...
	}

	void think()
	{
		//if player in range
		if (state == &idleState)
		{
			state = &attackState;
		}
		//if player is out of range
		if (state == &attackState)
		{
			state = &idleState;
		}
	}

	void update()
	{
		think();
		state->update(this);
	}

};

Thanks, ill try this out. My game is a simple shooter so they just have to shoot when they appear on screen.

One thing to think about:

A bullet shot from a pistol travels with approximately 500 m/s, from a rifle with 700 m/s, or over 1000 m/s if it is a high speed projectile. Further, the bullet has a small extent. This has 2 consequences for a typical shooting game: You will never see the bullet flying. Second, you are not interested in the bullet as an object but in its trajectory and its damage parameters. So ... think of modeling the shot, not the bullet.

That said, it is even true for slowly flying projectiles like an arrow. The only difference is that one may observe the arrow flying. Nevertheless, the aspect of interest is not the arrow by itself but the shot.

BTW:



//encodes both the direction and speed of the projectile
//basically velocity
Vector3 direction;

If something is "basically a velocity" but not a direction ... then why, ever, is it named direction instead of velocity? The fact that you need to write down an excusing comment should be enough hint that refactoring is to be done now.

It's a hint to make bullet inherit from a class with direction and position (the other reason being that in the beginning I thought like let's have it direction and an unit vector and add a double for speed and just multiply the vector when needed, but then I decided to not clutter the code as it's meant to be an example - basically I was trying to not emphasize a design for the class - keep it as generic as possible and just get the idea through), but I guess not explaining wasn't really helpful. I was thinking of adding a bunch of stuff like making a generic Entity class from which various things would inherit, have lookup by ids and stuff but eventually decided it would be too much useless code that would only serve to distract from the main idea.


It's a hint to make bullet inherit from a class with direction and position [...]

Don't confuse OOD with inheritance. Many books overemphasize inheritance because it was a new feature with OOP. That has embed the idea that in general good OOD requires inheritance everywhere. Well, in fact good teaching should show you that composition should be preferred. That does not mean that inheritance is devilish. Instead it means that one simply should think twice whether inheritance is the tool to use.

However, I still hint at the fact that the OP don't want to model a bullet itself here. Think of the several roles. A Bullet class typically means a specialized item, i.e. a game object where an agent can interact with. E.g. something that can be picked up, and inserted into a magazine as ammunition. As an item the bullet has a position and orientation, for sure. If it lies on the ground it is just placed in the world. If it is hold in the hand its placement is updated by forward kinematics. When being shot, the bullet looses its role as item and becomes a damage causer instead. Although, as a real world object, the bullet is still the same, the change of its role makes many of its features as an item meaningless but makes other features become relevant. One of these features is the kind of collision detection (changing from bounding box to trajectory); also the collision response of a bullet thrown by hand is different from a shot bullet; the latter causes damage, the former not. A bullet thrown by hand follows a ballistic curve while shooting a bullet gives a straight trajectory (for the sake of a simple shooting game). And so on ...

Wrangling all this into one class is not the way to go. Hence, modeling the shot is what is wanted here. It defines how the placement is to be updated (if used so at all), how to run collision detection, and how to handle a collision response. Classifying shots as themselves gives the engine the possibility to handle them explicitly. Naming such a class "Bullet" would simply be misleading.

There are examples of engines that use the design I am talking about (Game objects inheriting from a base class Entity that has some basic variables), I am not saying it's god sent, but it's not wrong to use it in this case per say - especially since we're talking example + simple problem.

However, I still hint at the fact that the OP don't want to model a bullet itself here.

I cannot infer this from his original post, and he failed to mention it when I posted my code. I should also mention that I am thinking in terms of projectile in this case and not bullet in the sense of "hitscan"(and I am thinking in 3d). As for my naming conventions, maybe they are not the best suited, but I'm trying to get the idea across here and not any specific design patterns - you could revise my code if you believe it would help the OP.

What I actually want is to urge the OP not to simply copy your example code but to think about it: Why does it help, how does it help, and what other consequences it might have; if possible, even how to refine it. That's the reason why I started my first answer with "One thing to think about". It has nothing to do with your code in itself.

This topic is closed to new replies.

Advertisement