Getting And Setting The Size Of An Array Of Objects


motorollin

Member
Joined
Jul 31, 2007
Messages
163
I have created an Enemy class. I want lots of enemies on screen, so I have created an array of enemies:

CODE
Enemy myEnemy[5];


In my main loop I have lines like the following to make all of the enemies do their processing:
CODE
for ( int i = 0; i <= 4; i++ ) if ( myEnemy.alive == true ) myEnemy.move();


This works. However, I want to be able to add enemies as and when I want to, and then change the for loops to check the size of the array. In the Lazy Foo tutorials I have seen the array.size() function which returns the current size of the array, and the array.resize() function which resizes it. However my Enemy class doesn't recognise these functions. How can I achieve this?

TIA
 
Actually I think I might be able to use the "alive" variable I added to the Enemy class. When the enemy is shot its alive variable is set to false so it doesn't get drawn, moved, or checked for collisions any more.

I suppose what I could create the array with a really high number, like "Enemy myEnemy[100]", which is more enemies than I would ever need, then set myEnemy[0].alive -> myEnemy[5].alive to true to create five enemies. When an enemy is killed its alive variable is set to false. When an enemy needs to be created I could do this:

CODE
void create_enemy()
{
for ( int i=0; i<=99; i++ )
{
if ( myEnemy.alive == false )
{
myEnemy.alive = true;
return true;
}
}
return false;
}

if ( create_enemy() == true )
{
//An enemy has been created
}
else
{
//All 100 of the enemies in the array are already alive,
//so no enemy was created this time.
}


Any thoughts?
 
motorollin said:
Actually I think I might be able to use the "alive" variable I added to the Enemy class. When the enemy is shot its alive variable is set to false so it doesn't get drawn, moved, or checked for collisions any more.

I suppose what I could create the array with a really high number, like "Enemy myEnemy[100]", which is more enemies than I would ever need, then set myEnemy[0].alive -> myEnemy[5].alive to true to create five enemies. When an enemy is killed its alive variable is set to false. When an enemy needs to be created I could do this:

CODE
void create_enemy()
{
for ( int i=0; i<=99; i++ )
{
if ( myEnemy.alive == false )
{
myEnemy.alive = true;
return true;
}
}
return false;
}

if ( create_enemy() == true )
{
//An enemy has been created
}
else
{
//All 100 of the enemies in the array are already alive,
//so no enemy was created this time.
}


Any thoughts?



Going through the loop 100 times to find alive enemies is very inefficient. Imagine when there is only 1 enemy on screen and you are going through a 100 loop each time. Not the way to go.

Why not create yor own array template class with a size function or use the standard template library?

Alternatively why not use a linked list and just traverse through it until you hit the end? Just add and delete nodes as neccessary.
 
Last edited by a moderator:
motorollin said:
I have created an Enemy class. I want lots of enemies on screen, so I have created an array of enemies:

CODE
Enemy myEnemy[5];
In my main loop I have lines like the following to make all of the enemies do their processing:
CODE
for ( int i = 0; i <= 4; i++ ) if ( myEnemy.alive == true ) myEnemy.move();


This works. However, I want to be able to add enemies as and when I want to, and then change the for loops to check the size of the array. In the Lazy Foo tutorials I have seen the array.size() function which returns the current size of the array, and the array.resize() function which resizes it. However my Enemy class doesn't recognise these functions. How can I achieve this?

TIA


I think what you really want use is a vector. it will allow you to add dynamically and it has a size member.
 
Last edited by a moderator:
tridion said:
Going through the loop 100 times to find alive enemies is very inefficient. Imagine when there is only 1 enemy on screen and you are going through a 100 loop each time. Not the way to go.

Fair point.

tridion said:
Why not create yor own array template class with a size function or use the standard template library?

Alternatively why not use a linked list and just traverse through it until you hit the end? Just add and delete nodes as neccessary.
Would you mind pointing me to some examples? I'm very new to this language so while your suggestions sound logical I have no idea how to implement them.

[EDIT]

[/EDIT]
@Pickle
Again, would you mind giving me an example? I know how to create an array of SDL_Rect, but how would I then link that to my enemy class?
Thanks
moto
 
Last edited by a moderator:
motorollin said:
tridion said:
Going through the loop 100 times to find alive enemies is very inefficient. Imagine when there is only 1 enemy on screen and you are going through a 100 loop each time. Not the way to go.

Fair point.
In the case where there's only 1 enemy, 99 misses per frame is not "very inefficient". If you are only going to have a few enemies and things on screen then this is a perfectly suitable method. It is extremely unlikely to cause a bottleneck compared to any sort of graphics operations or physics or so on.

It is possible for a linked list to be worse in certain circumstances.
 
Last edited by a moderator:
I wanted to add in my opinion you should have a vector of class CEnemy, which then contains your SDL_rect. If an enemy dies just remove that object, when you want to add an enemy just add one to the vector.

Pickle said:
Heres a rough example, google it im sure you will come up with better examples:

CODE

#include <vector>

std::vector<CSpriteStatic> oTiles; // a vector hold all of the tile objects
std::vector<CSpriteStatic>* ptr_tiles = &oTiles; // a pointer to the vector

CSpriteStatic tile( x, y, 0 ); //Create a new tile
ptiles->resize( ptiles->size() + 1, tile ); // Add the tile (you can also remove)

// example of going though all of the tiles in the vector, im checking to see if the tiles are in the camera(screen)
for( unsigned int t=0; t<ptiles->size(); t++ )
{
if( Check_RectCollision( CAMERA, ptiles->at(t).GetCollisionBox() ) )
{
ptiles->at(t).Show( SCREEN, CAMERA );
}
}
 
Last edited by a moderator:
Example usage of vectors:

CODE

#include <vector>
using namespace std;

int main(int argc,char* argv[])
{
vector<int> blah; // Dynamic array of int objects

blah.push_back((int)52);// Enlarges the vector and adds the int at the last point
blah[0] = 10; // replaces the 52 with 10
blah.pop_back(); // removes the last element in the vector (10 atm)
blah.size(); // gets the size of the vector (num of elements)
blah.clear(); // removes all elements in vector
blah.resize(20); // resizes the vector to hold 20 elements

return 0;
}
 
"Linked list" for the win!

jeff

(Actually do note -- arrays are _very_ fast to work with; if you're expecting less than say 7 or 8 enemies, then the array method is probably always faster than a more heavily designed method.. but your'e limited by array size. You could work around that by malloc'ing your array as needed to give it whjatever size you need. But in general, it is better to save code time and use a useful data structure.. so a linked list or somesuch woudl be best. Vector is pretty good too, and not too slow, but does have some overhead..)
 
Dr_Ian said:
motorollin said:
tridion said:
Going through the loop 100 times to find alive enemies is very inefficient. Imagine when there is only 1 enemy on screen and you are going through a 100 loop each time. Not the way to go.

Fair point.
In the case where there's only 1 enemy, 99 misses per frame is not "very inefficient". If you are only going to have a few enemies and things on screen then this is a perfectly suitable method. It is extremely unlikely to cause a bottleneck compared to any sort of graphics operations or physics or so on.

It is possible for a linked list to be worse in certain circumstances.



Well I'll agree to disagree. I've been a professional C/C++/C# programmer for 14 years and worked on a lot of real time applications where it would be considered very inefficient. Accepted, that it might be acceptable for GP2X development but I've always considered it best practice to do things as efficiently as possible regardless of it's neccessity.
 
Last edited by a moderator:
Thanks all for the advice. I did Google linked lists but it is bit beyond me atm. So I have got it working with a vector:
CODE

vector<Enemy> enemies; //container for the enemies
enemies.resize(3); //Initial number of enemies

//Inside my main loop:
for ( int i = 0; i<=enemies.size()-1; i++ ) enemies.move();
for ( int i = 0; i<=enemies.size()-1; i++ ) enemies.show();
//Check for collisions
for ( int i = 0; i <= enemies.size()-1; i++ )
{
if ( collision( myPlayer.col_l , enemies.col_l, myPlayer.col_r , enemies.col_r, myPlayer.col_t , enemies.col_t, myPlayer.col_b , enemies.col_b ) )
{
enemies.erase(i);
}
}


This test just gets rid of the enemy if it collides with the player. However, the enemies.erase(i) line doesn't work. It results in error "700 main.c no matching function for call to `std::vector<Enemy, std::allocator<Enemy> >::erase(int&)' "

Any thoughts?

[EDIT]
Corrected the vector names in the code example - copy/paste error ;-)
[/EDIT]
 
Ok enemies.erase(enemies.begin()+i) works. But when the last enemy is destroyed (causing enemies to become empty?) the game just quits :blink:
 
More to point re: the above .. if you are goign to brute-force scan an array fo (say) 100 members, and only 5 of them are populated.. it is _very_ inefficient to poll all the rest.

But it would also be pretty easy to sort them to the top once in awhile, and then break your brute-force scan after you run into a NULL or flag value.

But lets cut to the chase.. if you're new to high speed C coding .. dont' sweat it; get your job done and working. Optimize it later. You'll do better next project, no worries :)

jeff
 
The STL linked list class is called list. If you're removing items from the from the middle of a list, this will be more efficient as it just involves changing a couple of pointers, not shuffling the remaining items up.
 
Ok I coded around my problem with the game quitting. I just changed my for loops from 0 -> enemies.size()-1 to 1 -> enemies.size()-1. So enemies[0] never gets moved, shown or checked for collision. Because it can't be seen or destroyed, it remains in the vector, so all of the (visible) enemies can be destroyed and the game doesn't quit. I still want to understand this problem, but hey, it works now :) I will look in to lists when the time comes to optimize my game, but for now I want to get back to writing my game :D
 
I don't see why you can't delete the last entry

this works:
CODE

#include <iostream>
#include <vector>

using namespace std;

int main(int argc, char** argv)
{
vector<int> v;
for (int i=0; i<10; i++)
{
v.push_back(i);
cout << "Size = " << v.size() << endl;
}

for (int i=9; i>=0; i--)
{
v.erase(v.begin()+i);
cout << "Size = " << v.size() << endl;
}

return 0;
}
 
motorollin said:
Thanks all for the advice. I did Google linked lists but it is bit beyond me atm. So I have got it working with a vector:
CODE

vector<Enemy> enemies; //container for the enemies
enemies.resize(3); //Initial number of enemies

//Inside my main loop:
for ( int i = 0; i<=enemies.size()-1; i++ ) enemies.move();
for ( int i = 0; i<=enemies.size()-1; i++ ) enemies.show();
//Check for collisions
for ( int i = 0; i <= enemies.size()-1; i++ )
{
if ( collision( myPlayer.col_l , enemies.col_l, myPlayer.col_r , enemies.col_r, myPlayer.col_t , enemies.col_t, myPlayer.col_b , enemies.col_b ) )
{
enemies.erase(i);
}
}
This test just gets rid of the enemy if it collides with the player. However, the enemies.erase(i) line doesn't work. It results in error "700 main.c no matching function for call to `std::vector<Enemy, std::allocator<Enemy> >::erase(int&)' "

Any thoughts?

[EDIT]
Corrected the vector names in the code example - copy/paste error ;-)
[/EDIT]


CODE

for ( int i = 0; i<=enemies.size()-1; i++ ) enemies.move();



can be written as

CODE

for ( int i = 0; i<enemies.size(); i++ ) enemies.move();



Also given that
CODE

enemies.size()



is called 3 times the value should be stored in a local variable (since nothing you are doing will change the size).

Also this approach is very procedural (maybe you haven't got as far as oo yet, although you are using classes). So you could have a class called Enemies which has an array/vector/storage (whatever you choose) as a member, then you could have a method which takes MyPlayer as a constant reference then all the moving, showing, erasing, etc can be done inside the Enemies class (where it belongs).

In an ideal world all you member variables should be private and accessed via constant methods.

motorollin said:
Ok enemies.erase(enemies.begin()+i) works. But when the last enemy is destroyed (causing enemies to become empty?) the game just quits :blink:
When iterating over an array removing elements you should always iterate backwards otherwise your indexing will go to pot as soon as one is removed.
 
Last edited by a moderator:
Back
Top