• Create Account

Enemy shooting

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

19 replies to this topic

#1Suen  Members   -  Reputation: 160

Like
0Likes
Like

Posted 30 August 2012 - 01:22 PM

I'm creating a very simple 2D game in C++ (with a given external library) where I might have something like 20-30 enemy sprites on the screen from the beginning. I keep track on whether the enemy is alive or not through a member variable of the enemy object (simply a bool variable).

Now I want to have one random enemy of these 20-30 enemies to shoot a bullet and I want this to happen periodically. So for example every two seconds one enemy should be shooting a bullet. First time it might be the first enemy, second time might be the 10th enemy etc. I thought I could keep this simple by just generating a random number within a limit (which is the number of enemies) and use that number to determine which enemy that will shoot. The issue here is that an enemy can only shoot if it is alive, and if I have...say 20 enemies and 19 of them are dead I will keep generating random numbers until I get that very specific number of the remaining enemy that is alive and that feels like quite a waste of computations, basically a very inefficient way of doing it.

Surely there are better ways of doing this? The problem is that I'm not quite sure on what to search for on the net when it comes to this.

#2EngineProgrammer  Members   -  Reputation: 295

Like
0Likes
Like

Posted 30 August 2012 - 01:38 PM



int m_FrameTime;  // Initialized on zero in the cpp

int m_ShootCounter; // Initialized on a certain number of frames.

cpp:

// In the constructor(or initialize)

m_FrameTime = 0;

m_ShootCounter = 2; // 2 frames for example

//in the gamecycle(or gametick):

if(m_FrameTime > m_ShootCounter) // When my frametime reached the amount of frames of the shootcounter, search for a random enemy

{

if(m_pEnemyList->Size() > 0 )

{

int random = rand()%(m_pEnemyList->Size()-1); // Creates a random number from zero to the maximum number of enemies

/*NOTE: array starts from zero until the 'size-1', so if the last enemy is random-ed and you don't add a -1 to it, you are out of the array! */

m_pEnemyList->At(random)->ShootBullet();

}

m_FrameTime = 0 // Reset frameTime

}

++m_FrameTime;



Edited by EngineProgrammer, 30 August 2012 - 01:44 PM.

#3CC Ricers  Members   -  Reputation: 1487

Like
0Likes
Like

Posted 30 August 2012 - 01:46 PM

I guess that you are storing your enemies in an array since they can either be set as alive or dead.

Just read EngineProgrammer's answer, and his code has an m_pEnemyList. It's not defined in his code, but it seems to be a vector.

Going with that, you add your living enemies to the vector, while still using the array as a data pool to spawn new enemies and keep dead ones. The vector would have pointers to the enemies that are alive in the array, and you would query a random index for the vector.

New game in progress: Project SeedWorld

My development blog: Electronic Meteor

#4Suen  Members   -  Reputation: 160

Like
0Likes
Like

Posted 30 August 2012 - 02:02 PM

I guess that you are storing your enemies in an array since they can either be set as alive or dead.

Just read EngineProgrammer's answer, and his code has an m_pEnemyList. It's not defined in his code, but it seems to be a vector.

Going with that, you add your living enemies to the vector, while still using the array as a data pool to spawn new enemies and keep dead ones. The vector would have pointers to the enemies that are alive in the array, and you would query a random index for the vector.

You are correct that I have an array where I have stored my enemies in. Going by the two answers given by you, I would at the start of the game have a vector of pointers to all of my enemies and as soon as a certain enemy is dead I should remove the pointer to this enemy from this vector, correct?. Yes this does basically reduce the amount of enemies that can shoot each time so it does solve the problem. But doesn't this also mean that each time I remove an element from the vector all the data in it has to be moved due to the nature of the stl vector? Is there a way of avoiding all that shuffling of data inside the vector? Or perhaps this movement of data in the vector isn't expensive due to the fact that it only consist of pointers?

#5CC Ricers  Members   -  Reputation: 1487

Like
0Likes
Like

Posted 30 August 2012 - 02:13 PM

I guess that you are storing your enemies in an array since they can either be set as alive or dead.

Just read EngineProgrammer's answer, and his code has an m_pEnemyList. It's not defined in his code, but it seems to be a vector.

Going with that, you add your living enemies to the vector, while still using the array as a data pool to spawn new enemies and keep dead ones. The vector would have pointers to the enemies that are alive in the array, and you would query a random index for the vector.

You are correct that I have an array where I have stored my enemies in. Going by the two answers given by you, I would at the start of the game have a vector of pointers to all of my enemies and as soon as a certain enemy is dead I should remove the pointer to this enemy from this vector, correct?. Yes this does basically reduce the amount of enemies that can shoot each time so it does solve the problem. But doesn't this also mean that each time I remove an element from the vector all the data in it has to be moved due to the nature of the stl vector? Is there a way of avoiding all that shuffling of data inside the vector? Or perhaps this movement of data in the vector isn't expensive due to the fact that it only consist of pointers?

Yes, the vector would have to move all the data after the erased element down one position. The operation takes linear time whether it holds pointers or not, but for 30 enemies this is not a big deal and you won't notice a performance hit.

New game in progress: Project SeedWorld

My development blog: Electronic Meteor

#6fastcall22  Crossbones+   -  Reputation: 8836

Like
1Likes
Like

Posted 30 August 2012 - 02:16 PM

But doesn't this also mean that each time I remove an element from the vector all the data in it has to be moved due to the nature of the stl vector? Is there a way of avoiding all that shuffling of data inside the vector? Or perhaps this movement of data in the vector isn't expensive due to the fact that it only consist of pointers?

If you do not care too much about the ordering of the pointers inside the element, you can swap the element to be deleted with the last element in the array then erase the last element in the array.
gzip: H4sIAAAAAAAEAG1QTUvEMBC991e8nvaiFfYoS7yo sLCo6MnjtJ1ugmkiyWRL/72z3T1YEQIJ8z4zA2Xp yPvt1qBpGrRFIJZkk9FyRyUzHCbKIHgn4hnZOrm1 TD0mG0HCCs+QGDGWziKXI6Wm2n++GYwUVH2mrGEE PnGCVQ8K8+JYfXA6URDEQfMZh5h6g5eoAlWJdeEI bbH2qYZf7XMUfw8f/Q0oMeZYNL9/WHF0uFEshvMr XYujd9SycFb+F18QcSOvlJauZ8ejqevdnV7/d550 e0t6prmunh73Bu+vz4c/XUeOQXfJgvKNkhf95U8/ Dtgmy5IBAAA=

#7TheResolute  Members   -  Reputation: 187

Like
3Likes
Like

Posted 30 August 2012 - 02:18 PM

EngineProgrammer provided a nice solution

Something far more simple would be to:
keep track of how many enemies are alive (variable initialized with number of enemies and decremented each time one dies);
using the same process he described to do this at certain intervals: generate a random number using the number of living enemies as your cap, and iterate through your enemies array, skipping dead ones, while decrementing the random number, and have the enemy shoot when your random number is 0
Hope that makes sense

Some pseudo code could help:
int totalEnemies = 30;
int livingEnemies = totalEnemies;
Enemy[30] enemies;

// in code region dealing with enemy death
livingEnemies--;
//

// on iterval for enemy to shoot
if(livingEnemies > 0)
{
int randNum = Math.Rand(0, livingEnemies);  // generates a random integer between bounds, lower exclusive, upper inclusive
for(int i = 0; i < totalEnemies; i++)
{
if(enemies[i].isAlive)
{
randNum--;
if(randNum == 0)
{
// Enemy shoots
}
}
}
}
//


Edited by TheResolute, 30 August 2012 - 02:31 PM.

#8EngineProgrammer  Members   -  Reputation: 295

Like
0Likes
Like

Posted 30 August 2012 - 02:20 PM

I disagree with deleting an enemy at run-time.

Assume you are creating a game World Of Warcraft: There is a mob you are killing. The mod has like 40items carrying, position, rotation, etc. All stored in data members. And then... You kill it in run-time. The mob is gone.. And the world continues happily ever after. But then... unexpected The mob has a re-spawn!! BAM! Enemy has been allocated again! 40items, position, rotation, and many more data members created in Run-time.

I think we all know what will happen? If not: the game have a possibility of lag. always try to avoid lag so never ever allocate an object at run-time. ( unless it's a temporary local data variable or something.

Solution: The enemy has a "bool m_IsKilled" or somethng. You only calls Ticks, Paints, Checks on the Enemy when he is still alive.

#9EngineProgrammer  Members   -  Reputation: 295

Like
0Likes
Like

Posted 30 August 2012 - 02:34 PM

Some pseudo code could help:

int totalEnemies = 30;
int livingEnemies = totalEnemies;
Enemy[30] enemies;

// in code region dealing with enemy death
livingEnemies--;
//

// on iterval for enemy to shoot
if(livingEnemies > 0)
{
int randNum = Math.Rand(0, livingEnemies);  // generates a random integer between bounds, lower exclusive, upper inclusive
for(int i = 0; i < totalEnemies; i++)
{
if(enemies[i].isAlive)
{
randNum--;
if(randNum == 0)
{
// Enemy shoots
}
}
}
}
//


Won't work.

So you have 30 enemies. Let us say I have killed the 2nd, 4th, 6th one. So 27 enemies left.

the rand() seeder will only give me a number from 0-27.
Meaning, if I get the number 0, 4, 6 those enemies will try to shoot but they are dead..
Also means the last 3 enemies will never be able to shoot a bullet.

#10TheResolute  Members   -  Reputation: 187

Like
2Likes
Like

Posted 30 August 2012 - 02:49 PM

Won't work.

So you have 30 enemies. Let us say I have killed the 2nd, 4th, 6th one. So 27 enemies left.

the rand() seeder will only give me a number from 0-27.
Meaning, if I get the number 0, 4, 6 those enemies will try to shoot but they are dead..
Also means the last 3 enemies will never be able to shoot a bullet.

You must have misread the code or I made a mistake, but it is not accessing the array using the random number as an index, it is iterating through the array and every time an enemy is alive, it decrements its counter and if the counter is zero, meaning that this enemy is the nth living one, it will then have it shoot

#11Suen  Members   -  Reputation: 160

Like
0Likes
Like

Posted 30 August 2012 - 02:51 PM

I disagree with deleting an enemy at run-time.

Assume you are creating a game World Of Warcraft: There is a mob you are killing. The mod has like 40items carrying, position, rotation, etc. All stored in data members. And then... You kill it in run-time. The mob is gone.. And the world continues happily ever after. But then... unexpected The mob has a re-spawn!! BAM! Enemy has been allocated again! 40items, position, rotation, and many more data members created in Run-time.

I think we all know what will happen? If not: the game have a possibility of lag. always try to avoid lag so never ever allocate an object at run-time. ( unless it's a temporary local data variable or something.

Solution: The enemy has a "bool m_IsKilled" or somethng. You only calls Ticks, Paints, Checks on the Enemy when he is still alive.

I agree with this, I've been avoiding this so far. Besides while I don't have much knowledge in this as far as I've understood it is that aside from the fact that I have to allocate/deallocate what could be expensive objects (not in my case but still) I would also be corrupting the heap or in other words I assume there would be a heap fragmentation. TheResolute, I appreciate the help but as pointed out there is a problem because I am not limited to kill enemies in a specific order. So I could end up with bullets coming out of nowhere and have enemies that can not shoot.

To summarize this: I do not want to allocate/deallocate my objects. I allocate once and only once, and deallocate only once. Allocation/deallocation at run-time is highly undesired in my case, unfortunately I am still doing that in my code in some other part of the game but I will change that soon. My point is that the vector has to move all the data (pointers) down one point upon erasing an element and it would have been nice if one could avoid that. My target is not modern hardware. While I don't have any specifications of the hardware itself all I know is that it is not modern (for example I am supposed to assume it only has a single core). I know that doesn't exactly say very much but still...

Perhaps it doesn't matter much despite that with only 30 enemies or so.

Edit: I suppose I might have misread TheResolute's code as well (as I thought of the same thing as EngineProgrammer), will check through it again now.

Edited by Suen, 30 August 2012 - 02:58 PM.

#12Álvaro  Crossbones+   -  Reputation: 18617

Like
1Likes
Like

Posted 30 August 2012 - 02:52 PM

[...]
Won't work.

So you have 30 enemies. Let us say I have killed the 2nd, 4th, 6th one. So 27 enemies left.

the rand() seeder will only give me a number from 0-27.
Meaning, if I get the number 0, 4, 6 those enemies will try to shoot but they are dead..
Also means the last 3 enemies will never be able to shoot a bullet.

It works just fine. Note that the code loops over the enemies to skip the dead ones.

#13EngineProgrammer  Members   -  Reputation: 295

Like
0Likes
Like

Posted 30 August 2012 - 03:01 PM

Won't work.

So you have 30 enemies. Let us say I have killed the 2nd, 4th, 6th one. So 27 enemies left.

the rand() seeder will only give me a number from 0-27.
Meaning, if I get the number 0, 4, 6 those enemies will try to shoot but they are dead..
Also means the last 3 enemies will never be able to shoot a bullet.

You must have misread the code or I made a mistake, but it is not accessing the array using the random number as an index, it is iterating through the array and every time an enemy is alive, it decrements its counter and if the counter is zero, meaning that this enemy is the nth living one, it will then have it shoot

Ah my bad, I interpreted the working of it wrong. A good solution indeed!

I agree with this, I've been avoiding this so far. Besides while I don't have much knowledge in this as far as I've understood it is that aside from the fact that I have to allocate/deallocate what could be expensive objects (not in my case but still) I would also be corrupting the heap or in other words I assume there would be a heap fragmentation. TheResolute, I appreciate the help but as pointed out there is a problem because I am not limited to kill enemies in a specific order. So I could end up with bullets coming out of nowhere and have enemies that can not shoot.
To summarize this: I do not want to allocate/deallocate my objects. I allocate once and only once, and deallocate only once. Allocation/deallocation at run-time is highly undesired in my case, fortunately I am still doing that in my code in some other part of the game but I will change that soon. My point is that the vector has to move all the data (pointers) down one point upon erasing an element and it would have been nice if one could avoide that. My target is not modern hardware. While I don't have any specifications of the hardware itself all I know is that it is not modern (for example I am supposed to assume it only has a single core). I know that doesn't exactly say very much but still...
Perhaps it doesn't matter that much despite that with only 30 enemies or so.

For this application the deleting won't make any difference. But if you want to write games you always need to think big.

When I'm writing a class I think: "What If I expand this class in the future, what way/how should, I write my code so I don't need to change allot when I expand it."
As for TheResolute his answer, his idea is not working with the index of the array but using the random number as a delay to shoot.

Edited by EngineProgrammer, 31 August 2012 - 10:44 AM.

#14caldiar  Members   -  Reputation: 715

Like
0Likes
Like

Posted 30 August 2012 - 03:04 PM

You could make a second, smaller array that keeps a subset of the total number of monsters only holding references to those that are currently alive.

Then your logic could go through the 'live array' randomly.

Alternatively, if you're not a fan of keeping a separate tally of live monsters, you could initialize each monster with a random timer on allocation. After the timer is up, it could ask a 'monster manager' or some similar construct if it's time to shoot its gun. The first monster to ask is the first allowed and then you can have a cooldown in the controller preventing from two monsters being able to shoot their gun at the same exact time (unless that's not a problem, in that case just initialize every monster with a random attack timer and let them shoot every n seconds).

Something like:
//monster controller
{
public:
bool can_shoot()
{
if(can_shoot_again)
{
return true;
can_shoot_again = false until seconds_until_can_shoot_again passes
}
return false
}
int seconds_until_can_shoot_again
}
//monster1 thinking
{
random think time is 3 seconds.
if( 3 seconds is up and monster_controller::can_shoot)
{
shoot_my_gun()
reset_timer
}
}
//monster2 thinking
{
random think time is 4 seconds.
if( 4 seconds is up and monster_controller::can_shoot)
{
shoot_my_gun()
reset_timer
}
}


sorry for the crappy pseudo-code but the idea is the first monster to ask to be able to shoot locks the system for the other monsters until the system cooldown passes and only live monsters will call their think functions (and ask if they can shoot).

Edited by caldiar, 30 August 2012 - 03:06 PM.

#15Suen  Members   -  Reputation: 160

Like
0Likes
Like

Posted 30 August 2012 - 03:10 PM

Won't work.

So you have 30 enemies. Let us say I have killed the 2nd, 4th, 6th one. So 27 enemies left.

the rand() seeder will only give me a number from 0-27.
Meaning, if I get the number 0, 4, 6 those enemies will try to shoot but they are dead..
Also means the last 3 enemies will never be able to shoot a bullet.

You must have misread the code or I made a mistake, but it is not accessing the array using the random number as an index, it is iterating through the array and every time an enemy is alive, it decrements its counter and if the counter is zero, meaning that this enemy is the nth living one, it will then have it shoot

Ah my bad, I interpreted the working of it wrong. A good solution indeed!

I agree with this, I've been avoiding this so far. Besides while I don't have much knowledge in this as far as I've understood it is that aside from the fact that I have to allocate/deallocate what could be expensive objects (not in my case but still) I would also be corrupting the heap or in other words I assume there would be a heap fragmentation. TheResolute, I appreciate the help but as pointed out there is a problem because I am not limited to kill enemies in a specific order. So I could end up with bullets coming out of nowhere and have enemies that can not shoot.
To summarize this: I do not want to allocate/deallocate my objects. I allocate once and only once, and deallocate only once. Allocation/deallocation at run-time is highly undesired in my case, fortunately I am still doing that in my code in some other part of the game but I will change that soon. My point is that the vector has to move all the data (pointers) down one point upon erasing an element and it would have been nice if one could avoide that. My target is not modern hardware. While I don't have any specifications of the hardware itself all I know is that it is not modern (for example I am supposed to assume it only has a single core). I know that doesn't exactly say very much but still...
Perhaps it doesn't matter that much despite that with only 30 enemies or so.

For this application the deleting won't make any difference. But if you want to write games you always need to think big.

When I'm writing a class I think: "What If I expand this class in the future, what way/how should, I write my code so I don't need to change allot when I expand it."
As for TheResolute his answer, his idea is not working with the index of the array but using the random number as a delay to shoot. The disadvantage of this is you can't shoot every 2 frames. I will be 10frames, 1frame, 3frames sometimes. But in a gamecycle of 60FPS that doesn't really matter.

Having looked at his code again it is indeed a good solution. Furthermore if my mind is not somehow drifting out in space (as I am tired now) then it doesn't even seem like I need to use a container in that solution. Thanks TheResolute, being tired and reading through code is never a good thing

#16Suen  Members   -  Reputation: 160

Like
0Likes
Like

Posted 30 August 2012 - 03:15 PM

You could make a second, smaller array that keeps a subset of the total number of monsters only holding references to those that are currently alive.

Then your logic could go through the 'live array' randomly.

Alternatively, if you're not a fan of keeping a separate tally of live monsters, you could initialize each monster with a random timer on allocation. After the timer is up, it could ask a 'monster manager' or some similar construct if it's time to shoot its gun. The first monster to ask is the first allowed and then you can have a cooldown in the controller preventing from two monsters being able to shoot their gun at the same exact time (unless that's not a problem, in that case just initialize every monster with a random attack timer and let them shoot every n seconds).

Something like:

sorry for the crappy pseudo-code but the idea is the first monster to ask to be able to shoot locks the system for the other monsters until the system cooldown passes and only live monsters will call their think functions (and ask if they can shoot).

This also sound like it could be an interesting idea. The change is that I want to be able to have three enemies that can shoot at once, no more so adjustments would have to be done for that although it shouldn't be that hard to figure it out.

#17BeerNutts  Crossbones+   -  Reputation: 4064

Like
0Likes
Like

Posted 31 August 2012 - 10:00 AM

Firstly, keep in mind, this is a simple shooter game, not WOW-style MMORPG. You're not allocating tons and tons of enemies, and this sounds like a beginner project, so I'd suggest a little KISS (keep it simple stupid).

I would suggest using an std::list to hold your active enemies. When you want to spawn an enemy, add it to that list. When an enemy dies (or goes off the screen if you operate that way), remove it form the list.

Since you will be iterating over all the enemies when you update, simply choose a random number X from 0 to std::list::size(), and when updating each enemy, if your updating enemy X, make him shoot.

Something like this:
void EnemyUpdate()
{
int EnemyToFire = -1;
int EnemyCount = 0;

if (TimeForEnemyToFire) {
EnemyToFire = rand()%EnemyList.size();
}

for (std::list<Enemy>::iterator it = EnemyList.begin();
it != EnemyList.end(); it++) {
// Move Enemy
// Handle Enemy Colliding with player or players bullet;  if dead, don't continue

// Check if this enemy should fire a bullet; will never happen if EnemtToFire = -1
if (EnemyCount == EnemyToFire) {
EnemyShoot(*it);
}
}
}


Edited by BeerNutts, 31 August 2012 - 10:04 AM.

My Gamedev Journal: 2D Game Making, the Easy Way

---(Old Blog, still has good info): 2dGameMaking
-----
"No one ever posts on that message board; it's too crowded." - Yoga Berra (sorta)

#18EngineProgrammer  Members   -  Reputation: 295

Like
0Likes
Like

Posted 31 August 2012 - 11:40 AM

Firstly, keep in mind, this is a simple shooter game, not WOW-style MMORPG. You're not allocating tons and tons of enemies, and this sounds like a beginner project, so I'd suggest a little KISS (keep it simple stupid).

I would suggest using an std::list to hold your active enemies. When you want to spawn an enemy, add it to that list. When an enemy dies (or goes off the screen if you operate that way), remove it form the list.

Since you will be iterating over all the enemies when you update, simply choose a random number X from 0 to std::list::size(), and when updating each enemy, if your updating enemy X, make him shoot.

Something like this:

Spoiler

Even in a simple project you need to take all the advantage you can get. And certainly when you are a beginner.
Out my experience of programming I've learned your code should always be expandable. Even for a small project.

My first year at college we need to re-write an existing game. ( I re-wrote DBZ supersonic warriors )
I only had 2 unit classes: class Goku, class Vegeta.
For only those 2 classes I used a virtual class. When I came into my examination the docent asked me "Why did you use polymorfism for only 2 classes? You are wasting some performance there"
I told him "I Always think big. Who knows I want to add a unit later on and this way I don't need to change allot of my code.
He totally agree with me. (docent has 10 years experience from a game company.)
He told me that is a very good answer and a good attitude of programming. ( I'm not bragging, just telling my story )

In a simple/small project the code is very clear. You don't need to teach yourself bad habits by not using virtual-classes, list-classes, supportive-classes. Always think big! When your game is finished, and someone says: "Too bad that 'type' of unit isn't in that game". You totally want to add that unit then.. So be sure you make your code expandable.

Well that was my opinion. I only have 2 years of experience though.

Edited by EngineProgrammer, 31 August 2012 - 11:41 AM.

#19Suen  Members   -  Reputation: 160

Like
0Likes
Like

Posted 31 August 2012 - 12:26 PM

Firstly, keep in mind, this is a simple shooter game, not WOW-style MMORPG. You're not allocating tons and tons of enemies, and this sounds like a beginner project, so I'd suggest a little KISS (keep it simple stupid).

I would suggest using an std::list to hold your active enemies. When you want to spawn an enemy, add it to that list. When an enemy dies (or goes off the screen if you operate that way), remove it form the list.

Since you will be iterating over all the enemies when you update, simply choose a random number X from 0 to std::list::size(), and when updating each enemy, if your updating enemy X, make him shoot.

Something like this:

void EnemyUpdate()
{
int EnemyToFire = -1;
int EnemyCount = 0;

if (TimeForEnemyToFire) {
EnemyToFire = rand()%EnemyList.size();
}

for (std::list<Enemy>::iterator it = EnemyList.begin();
it != EnemyList.end(); it++) {
// Move Enemy
// Handle Enemy Colliding with player or players bullet;  if dead, don't continue

// Check if this enemy should fire a bullet; will never happen if EnemtToFire = -1
if (EnemyCount == EnemyToFire) {
EnemyShoot(*it);
}
}
}


First of all I'd like to mention (for the whole thread) that I have a set of "conditions" I should try and follow to my best. Sure I could keep it simple and somewhat inefficient. More than likely I won't notice any performance hit but there are some things I want to account for. So even though allocating...say several small objects every second or so might not make any huge difference I'd like to avoid it if possible.

The solution provided here would work yes. But there are some things in it I'm not sure of if they would help. The first is thing is about adding and removing enemies from my list. I felt this is unnecessary, my enemies have already been allocated once and they are all seen on the screen. Once these enemies are killed a new group of enemies appear. Instead of allocating new enemies I could just use the ones I allocated in the beginning and change their location on the screen. Yes the disadvantage here is that for each frame I will be updating some of the properties of all my enemies (including dead enemies) and each time I draw my group of enemies I need an if-statement that makes sure that only enemies that are alive are drawn and branches is something I should try and avoid if not necessary. Yes if I use a vector or a linked list I could allocate/deallocate at run-time and avoid updating dead enemies and avoid checking enemy state when drawing (since they would be removed from the container) and thus I avoid that if-statement but the disadvantage is that if I use a vector then each time I remove an enemy from the vector elements in the vector would have to be moved down one step and so I would be calling my Enemy cctor frequently for an x number of enemies.

In case of a std::list I think I read that it is implemented as a double-linked list and if I'm not talking out of nowhere now then I believe that if I want to read my elements from the list I would incur cache misses frequently since the elements in the list are not adjacent in memory and cache misses I want to avoid even more than branches. I'm still fairly new to this so I could just be understanding it wrong so any corrections are welcome.

Yesterday I tried out TheResolute's method and it worked fine...but the problem was that less enemies meant less shooting...and in the end when I only had one enemy left it would just stand there doing nothing for quite a while.

I then went on and implemented EngineProgrammer's method. What I basically did was to create a vector of Enemy pointers and then added corresponding elements to this vector. When it is time for an enemy to fire I generate a random number as shown in his example and a random enemy fires a bullet. If an enemy is hit I loop through my vector of Enemy pointers until I find the right enemy to be removed and then remove it from the vector. This basically means that regardless of how many enemies I have on the screen one of them (on random) is always guaranteed to fire. If all my enemies die I just fill the vector of Enemy pointers with the same (now dead) enemies again. Basically I just reset them (position, life status etc.)

EngineProgrammer's solution is the best solution I have so far but of course there are two disadvantages here. The first is that every time an enemy is hit I need to go through my vector (of Enemy pointers) element by element and check with an if-statement(again, if possible I would want to avoid it but obviously there are situations where I can't do so, I don't mean to say that I can not absolutely use it, just to not use it if not needed) to find the correct enemy to remove. This should have a linear time complexity if I am not mistaken. The second disadvantage is that the data in the vector has to be moved around each time I remove an element from it. The ideal situation would have been to avoid these two issues. On the other hand there is an additional advantage (over the fact that the solution works); because the vector basically keeps track of enemies that are alive I can use it to only update and draw enemies that are alive and thus getting rid of the issues mentioned in the second paragraph (updating dead enemies, checking each time whether to draw an enemy or not depending on the state of the enemy)

Opinions on this would be appreciated, otherwise I'm grateful for all opinions and help that has been given so far.

#20Suen  Members   -  Reputation: 160

Like
0Likes
Like

Posted 03 September 2012 - 09:26 AM

So having worked a little bit on this today by playing around with stl vector I noticed one way of improving EngineProgrammer's solution. I still have to loop through my vector of Enemy pointers to find the right element. Once found, instead of removing it at it's place, I swap it's position with the last element in the vector and then remove it (see below). This at least avoids creating a gap in the middle of the internal storage of the vector which would require a reallocation to be done. Furthermore as already mentioned this solution also allowed me to only update and draw enemies that actually are alive so it has given me a few good benefits. This is the best improvement I can do for the time being but any opinions are still welcome of course. Thanks.

m_enemyIterator = m_enemiesAlive.begin();

while(m_enemyIterator != m_enemiesAlive.end())
{
if((*m_enemyIterator) == &m_army[enemyID])
{
std::swap(*m_enemyIterator, *(m_enemiesAlive.end()-1));
m_enemiesAlive.erase(m_enemiesAlive.end()-1);
break;
}
else
{
m_enemyIterator++;
}
}


Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

PARTNERS