Thread: Vertical Scroller laser cannon problem

1. Vertical Scroller laser cannon problem

I'm making an allegro vertical scroller at the moment, its going well and ive made alot of progress over the last day or 2. anyway, i've got a bit of a problem with the lasers. I'm in the process of adding weapons to the player, and the way I do it is to keep a record of the locations of all the laser beams on the screen in an array, then when it comes to refreshing, all the positions are known and u just cycle through them one at a time using a for loop. But, when a laser hits the top of the window, i needed a way of getting it to free up the memory slot in the array (i know that its not great practice for something like this, but the array is a fixed size and can take 100 coordinates at a time - given the rate they move off the screen, i thought 100 sounded enough) so that I could use it again for later shots.

The way I decided 2 do it was: everytime the main game loop executes, i would count the number of beams which had left the screen and then move the rest of the coordinates along by that many spaces.

e.g. Imagine the coordinates as:
0, 1, 2, 3, 4, ...

representing different beams, then suppose that i count up how many have left and find there are 2, then i would move everything along 2, so 2->0; 3->1 etc. Then when it comes 2 displaying them, i just count up 1, 2, 3 etc upto the number of rounds shot. (I know that this method should work, cos all the beams travel at the same speed and the player can't go faster than the beams, so the beams must leave in the order they were shot).

That was the idea anyway, although for some reason, its turned out that when i try shooting in the game, it more i shoot, the less it lets me shoot, im not entirely sure why, but here is some of my code:

Code:
```/* This is the whole 'shift everything along by an offset' thing */
for(idx1=0; idx1<shot_counter; idx1++)
{
if( cweapons.GetLaserPosition(1, idx1) < 0 )
{
offset++;
}
}

for(idx1=0; idx1<shot_counter; idx1++)
{
if( cweapons.GetLaserPosition(1, idx1) < 0 )
{
for(idx2=(idx1+offset); idx2 < shot_counter; idx2++)
{
posx = (int)cweapons.GetLaserPosition(0, idx2);
posy = (int)cweapons.GetLaserPosition(1, idx2);

cweapons.SetLaserPosition(0, idx2 - offset, posx);
cweapons.SetLaserPosition(1, idx2 - offset, posy);
}

shot_counter -= offset;
}
cweapons.DecrementLaserPosition(1, idx1);
}```
This following part is the way the shot is dealt with when the space button is pressed (dont worry about things like rof_index, thats just a way of regulating the rate of fire).

Code:
```if( (key[KEY_SPACE])&&(shot_counter < MAX_LASER_NUM)&&(rof_index > 1) )
{
/* Most of this isn't really related to the internals of the laser shots, more just getting the laser shots started */
posx = (int)cphysics.GetComPosition(0, 0, 1);
posy = (int)cphysics.GetComPosition(1, 0, 1);

/* Adjust posx so that shot comes out of middle of ship */
posx = posx + (cgraphics.p_player_ship->w/2);

if(laser_cannon == false)
{
posx = posx + (int)(0.60*cgraphics.p_player_ship->w/2);
laser_cannon = true;
}
else
{
posx = posx - (int)(0.61*cgraphics.p_player_ship->w/2);
laser_cannon = false;
}

cweapons.SetLaserPosition(0, shot_counter, posx);
cweapons.SetLaserPosition(1, shot_counter, posy);

/* Reset the rate of fire index */
rof_index = 0;

shot_counter++;
}```
And this is how I'm rendering it to the screen:
Code:
```for(idx1=0; idx1 < shot_counter; idx1++)
{
/* Add the laser shots to the screen buffer */
if( cweapons.GetLaserPosition(1, idx1) > 0 )
{
(int)cweapons.GetLaserPosition(1,idx1), cgraphics.p_laser->w, cgraphics.p_laser->h);
}
}```
Btw, I almost forgot to mention, in all the function calls etc, the index 0 corresponds to x and 1 corresponds to y (im sure you guys would have been able to figure that one out for yourselves, but I thought Id make things a little easier :P), lol. All the laser positions are stored in an array called 'm_laser_position[2][100]'

2. Since you're at the bottom of the screen, shooting up, why don't you make something to represent each shot, and then when it's Y coord is <= 0, remove that object? I'm not sure why you really need to keep track of the maximum number of shots -- unless you have something where say with the starting gun, they can only have 2 active shots at once, then they get a power up and can now have 3 lasers going at the same time, or something like that.

But anyway...
Code:
```struct shot
{
int ycoord;
int xcoord;
int speed;
...whatever..

struct shot *next; /* optionally, *prev, if you like double linked lists like I do */
};

void updateshots( struct shot *shots )
{
if( shots )
{
struct shots *n = NULL;
for( n = shots; n; n = n->next )
{
eraseshot( n );
n->ycord -= n->speed;
if( n->ycoord > 0 )
{
drawshot( n );
}
else
{
shot = n->next; /* reusing shot as a temp variable */
freeshot( n );
n = shot;
}
}
}
}```
Something like that should do the trick.

Quzah.

3. I think you're just asking for an easier way to handle a bunch of objects of the same type (easier than the multidimensional array you're using)? If so, look into std::list. Post back if you need more help.

4. You can make what I would call a smart array which is similar to a linked list except instead of using pointers you simply use indices. To track removed elements you use a stack.

Code:
```struct ArrayElement
{
unsinged int next;
unsigned int prev;
unsigned int index;
Object *pObject;
};```
Upon startup you init the constant sized array by setting all the pObjects to 0 and the next and prev to the correct values. Note that you will need some information denoting the index is invalid for the top of the array and the bottom. A value of MAX_INT could be used to denote this. It is a magical value which is bad practice so you may want to come up with something else.

The array works this way.

1. Check the stack for any removed elements.
2. If stack is not empty pop element off the stack and set pObject to passed in Object - update next of prev and prev of next in array based on data in the stack
3. If stack is empty, add item to the end of the array (arrayCount) and increment arrayCount.

Remove items
1. Delete the pObject for the node in question.
2. Update the prev element's next to point at current element's next
3. Update the next element's prev to point at current element's prev
4. Push the current element onto the stack.
5. If the current element was the top, set the top to current element's next.
6. If the current element was the bottom, set the bottom to the current element's prev.

Array traversal
1. Create functions that will return the top/bottom of the array
2. Create functions that will get the Next() element and the Prev() element so the array can be traversed in O(1) fashion without ever having to check for valid pObject pointers.

The array provides the following features when done correctly:
1. O(1) access and traversal
2. Easy to determine max memory footprint
3. Easy to traverse and use
4. Can be templated to hold any type of object
5. Excellent for particle systems, sprites, etc.
6. O(1) insertion and removal
7. During traversal you are guaranteed to only hit non-empty array indices. This means that you can do an O(1) traversal for rendering and updating.
8. It acts just like a doubly linked list but does not use pointers and has O(1) access, traversal, insertion and removal.

Some of the downsides to it are:
1. The array size is not dynamic and does have an upper limit
2. It is hard to implement without using magical values to represent top and bottom since elements are represented as indices into the array.
3. It can be a bit hard to debug and/or follow at first

I used something like this for my astroids game and while I had some memory issues to solve it worked out quite well and was very fast.

I will illustrate how it works for a very small array

Array
0 - prev = MAX_INT, next = 1
1 - prev = 0, next = 2
2 - prev = 1, next = 3
3 - prev = 2, next = 4
4 - prev = 3, next = MAX_INT
Top = 0
Bottom = 4
Stack = empty

Remove element 2
0 - prev = MAX_INT, next = 1
1 - prev = 0, next = 3
2 - EMPTY (still has next and prev data but isn't used so it doesn't matter)
3 - prev = 1, next = 4
4 - prev = 3, next = MAX_INT
Top = 0
Bottom = 4
Stack
- 2 prev = 1, next = 3

Notice that the stack essentially tracks the history of how the array was 'destroyed'. So if you select random indices and push them onto the stack you can rebuild the entire array by simply popping off the stack. When you pop the final element off the stack the array will look exactly as it did when you started.

Notice that the function for next becomes:

Code:
```...
unsigned int next = m_pArray[m_currentIndex].next;
if (next != MAX_INT)
{
m_currentIndex = next;
}
...```
Likewise Prev() becomes:

Code:
```...
unsigned int prev = m_pArray[m_currentIndex].prev;
if (prev != MAX_INT)
{
m_currentIndex = prev;
}
...```
A small loop to iterate the collection would be:

Code:
```unsigned int index = SmartArray->Begin();
unsigned int end = SmartArray->End();
while (index != end)
{
SmartArray[index]->Render();
index = SmartArray->Next();
}```

5. I never thought of putting them into linked lists, but thats a great idea. It means I don't have to have a limit on the number of lasers on the screen, definitely gonna use that method for missiles, aliens etc. makes things a whole lot easier.

I just coded it out and it worked a charm , thanks all.

edit: btw, there should definitely be a reputation thing on this message board, if there was, id definitely hand some out. lol. As far as I'm aware, there isn't.

6. We used to have something like that. A few of us still remember the days of red candy.

Quzah.