A sneaky little bug

Previous topic - Next topic

Darmakwolf

A friend of mine described a silly game he wished existed - so I threw it together in GLBasic based on what he told me (It's just a pointless "you're a helicopter, shoot bad guys!" kind of game) - so I got done what I wanted for a basic foundation but I ran into a strange problem. I made it so in test-mode you can  spawn as many "bad guys" as you wanted, which parachute to the ground from the helicopter and land, marching in a random direction. You can fire at them and kill them in air and on the ground, and they have individual collision-detection which works great and at a steady 60FPS even with 1000+ "live" objects. The problem I'm having is occasionally men will simply disappear after shooting a lot of other men. It's only noticeable after 2-3 minutes of gameplay, and requires spawning quite a few bad guys and shooting them at random. Attached is the entire "project" (it's small.) with windows, linux, and OS X universal/x86 binaries plus the source code. If someone has the time, please look into what could be "evaporating" the objects!

Notes:
Use WASD to move the heli. SPACE spawns men - quickly!
Use the mouse to aim, click to fire.

Notes about the code:
it's complex! I don't program small projects with comments really. It's just kind of all in there. Basically I use a TYPE called "shell" which acts as a kind of hollow objects which can be created as anything, like a bullet or a bad guy. I usually use several types for things, but this system works for such a small game and has before. I'm thinking the problem is somewhere in the code where I check in a loop whether or not an object closest to the start of the array is "operational" (the op variable) and if it is not, it will "chop" the minimum starting number in the array it has to loop through and draw, to save on performance. Give it a look!




[attachment deleted by admin]

Slydog

#1
Cool demo!  :good:

It took a *quick* peek at the code, but didn't see anything obvious, but I never got into the details of what you are doing.
You look like you are spawning new objects (bullets and enemies), but I don't see where you remove them (DIMDEL?) from your array when not needed anymore. 

For this do you use that 'mb' variable to keep track of where your first bullet is (so you don't process bullets that are no longer used)?

You had a check if the bullet[mb] had a member variable (can't remember which) equal to zero, then increment mb.
But are there other situations that would cause multiple bullets being set as inactive in the same frame, or out of order?
This would cause your 'minimum bullet' (mb?) to get out of sync. 

But I didn't get a full understanding of what you were doing, so I'm not sure.

You could set your bullet array ahead of time to a maximum value, say 1000, and preallocate those bullets.
Have a new member variable in your TYPE called 'active' and when you need to spawn a new bullet iterate through the array to find the first non-'active' bullet and use that index (and set its 'active = TRUE').  Set the 'active = FALSE' when the bullet becomes off screen or inactive.
For the collision detect, loop through each of your bullets and only check ones that are active.

This may clean things up, plus possibly prevent situations where too many bullets are spawned that would cause performance issues.  Or you may have run out of memory by not removing old bullets.
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

Darmakwolf

Using DIMDEL immediately crashes the game when I remove anything from any array once a bad guy gets shot. If you could look further into it at some point and find a workaround it would be appreciate :)

Slydog

DIMDEL physically makes the array one smaller.
If you had any index variables pointing to the last entry, it would now point to a location beyond the array bounds, causing a crash.

You could update all your variables by decrementing them down by one each time you delete a bullet, but this could get messy.
In your situation you'd be better off not using any DIMDEL or DIMPUSH and force a fixed maximum quantity.  Use an 'active' flag to manage what bullets are active or not, like I mentioned in my other comment.

Create a 'GetAvailableBullet()' function to search the array to find the first non-active bullet and return it's index.  Use this index for your new bullet.  But watch out for the condition that ALL your bullets are active.  Then you'll either have to not allow the new bullet, or remove an older bullet first (which means you may have to add the current time when adding a new bullet so you know which bullets are the oldest).
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

Slydog

#4
Here's a quick sample of what I'm referring to:

Code (glbasic) Select

CONSTANT MAX_BULLETS% = 1000

TYPE TBullet
    active% = FALSE
    ... other variables you have already
ENDTYPE
GLOBAL bullets[] AS TBullet
DIM bullets[MAX_BULLETS]

FUNCTION MainLoop:
    LOCAL bx% = -1
    bx = GetAvailableBullet()
    bullets[bx].active = TRUE
    bullets[bx].x = .... etc
    RETURN
ENDFUNCTION


FUNCTION GetAvailableBullet%:
    LOCAL bx%   // bullet index
    FOR bx = 0 TO LEN(bullets[]) - 1
        IF NOT bullets[bx].active THEN RETURN     // Great, we found a 'non-active' bullet, so return the index
    NEXT
    // We got this far, so all bullets are active.  Now decide which bullet you want overwritten.
    RETURN 0   // For simplicity, we will always return the first bullet when none are available
ENDFUNCTION


[Edit]  I would create two TYPES and therefore two arrays, one for your bullets and one for your enemies.
They are quite different from a game point of view, but I understand they do share common requirements like the needing to display them and update their positions (ie, their sprite properties).  But all bullets are the same sprite, so displaying them is easy.
My current project (WIP) :: TwistedMaze <<  [Updated: 2015-11-25]

monono

I ran into the same mistake once. I now use a Poolarray. Instead of deleting a type instance I flag it deleted = true, stop drawing that and dimpush the index into the poolarray. When a new object is going to be created, it looks if there is any index in the poolarray. If so I use that position in the typearray for the new object and dimdel the poolarray index, if not I (for example / easiest not fastest way) dimpush a new instance into the typearray. That way u don´t have to go through the whole typearray to look for a free slot. Works fine if u do not need any z-ordering. If u do ask again ;)

ampos

I use this:

Code (glbasic) Select
for each o in objects
   drawsprite o.i,o.x,o.y
   dec o.life
   if o.life=0 then delete o
next


And I have tested upto 2000 objects drawn...

Darmakwolf

I ended up redoing the system so that everything had a (high) amount of pre-determined items, and it would deactivate off-screen objects and cycle through which ones it can use each spawn. Works fine and keeps the memory usage predictable! thanks for your input.

Kitty Hello

Awesome work. When I have lists of objects that can remove at some given point, I use the method to "copy" the last element of the array into the position of the element to be deleted. Then I DIMDEL array[], -1 // drop the last one. That's a very fast way, because the move is just one copy, and the dimdel from the tail takes zero time, since there's nothing to move up.
The drawing "order" is messed up, though. I use a quicksort before draing all enemies in my SEUCK thus for each frame.