Thread overview
deleting items from 2d arrays
Aug 14, 2009
Michael P.
Aug 14, 2009
Daniel Keep
Aug 14, 2009
Michael P.
Aug 16, 2009
Sergey Gromov
Aug 16, 2009
Daniel Keep
Aug 14, 2009
Sergey Gromov
August 14, 2009
Okay, so I'm making a breakout type game in D. Using Derelict.
I have a 2d array of Block type variables(not important what's in them) declared like this:
Block[][] level;
and later load into like this:
level = loadLevel( "levels.txt", levelNumber );
Anyways, what I want to do is that when the ball hits one of the blocks in the array, I remove it. When a line no longer has any blocks in it, I remove that line/row. When there are no more lines, I load the next level.
Any ideas on how I could achieve this?

foreach( Block[] ba; level )
{
	foreach( Block b; ba )
	{
		if( checkCollision( ball, b.p ) )
		{
			//remove the block??
		}
	}
}

The level array has 12 lines of 'x' amount of bricks. x can be as great as 10. -Michael P.
August 14, 2009
On Thu, Aug 13, 2009 at 10:59 PM, Michael P.<baseball.mjp@gmail.com> wrote:
> Okay, so I'm making a breakout type game in D. Using Derelict.
> I have a 2d array of Block type variables(not important what's in them) declared like this:
> Block[][] level;
> and later load into like this:
> level = loadLevel( "levels.txt", levelNumber );
> Anyways, what I want to do is that when the ball hits one of the blocks in the array, I remove it. When a line no longer has any blocks in it, I remove that line/row. When there are no more lines, I load the next level.
> Any ideas on how I could achieve this?
>
> foreach( Block[] ba; level )
> {
>        foreach( Block b; ba )
>        {
>                if( checkCollision( ball, b.p ) )
>                {
>                        //remove the block??
>                }
>        }
> }
>
> The level array has 12 lines of 'x' amount of bricks. x can be as great as 10.

Okay.  First you need a remove function to take lines out of the array.  Here's a general one:

import std.c.string; // phobos
import tango.stdc.string; // tango

void remove(T)(ref T[] arr, size_t idx)
{
    assert(arr.length > 0);

    if(idx == 0)
        arr = arr[1 .. $];
    else if(idx == arr.length - 1)
        arr = arr[0 .. $ - 1];

    memmove(&arr[idx], &arr[idx + 1], T.sizeof * (arr.length - idx - 1));
    arr = arr[0 .. $ - 1];
}

You use it like arr.remove(idx) (or alternately, remove(arr, idx)) and
the given element will be removed, everything after it will be shifted
down a slot, and the length of the array will be reduced by one.

Now you need to do collision with the blocks.  If Block is a class, we can use 'null' in the array to indicate that there is not block there; if it's a struct, you'll have to keep an 'active' member in the struct or somesuch.  Either way, a quadratic algorithm for detecting collision probably isn't necessary.  You can calculate which blocks the ball is near with a little math.

const BlockHeight = 24; // fill these in
const BlockWidth = 60; // with whatever you're using

void checkRemoveLine(int row)
{
    foreach(ref b; level[row])
        if(b.active)
            return;

    // none of the blocks were active
    level.remove(row);
}

void removeBlock(int row, int col)
{
    level[row][col].active = false;
    checkRemoveLine(row);
}

void collidePoint(int x, int y)
{
    auto row = y / BlockHeight;
    auto col = x / BlockWidth;

    // assuming Block is a struct..
    if(row < level.length && level[row][col].active)
        removeBlock(row, col);
}

const BallDiam = 10; // this makes the ball 10 pixels across const HalfBallDiam = BallDiam / 2;

void collide(Ball ball)
{
    // collide all four corners of the ball's colbox
    collidePoint(ball.x + HalfBallDiam, ball. y + HalfBallDiam);
    collidePoint(ball.x - HalfBallDiam, ball. y + HalfBallDiam);
    collidePoint(ball.x - HalfBallDiam, ball. y - HalfBallDiam);
    collidePoint(ball.x + HalfBallDiam, ball. y - HalfBallDiam);
}

Your main game loop would probably look something like this:

foreach(levelFile; levelFiles)
{
    level = loadLevel(levelFile); // load the level array

    // while there are still lines..
    while(level.length > 0)
    {
        getUserInput();
        updateStuff();
        doCollision(); // ball runs into things yay
        drawGraphics();
    }
}

Well this has ended up being a bit long ;)
August 14, 2009

Michael P. wrote:
> Okay, so I'm making a breakout type game in D. Using Derelict.
> I have a 2d array of Block type variables(not important what's in them) declared like this:
> Block[][] level;
> and later load into like this:
> level = loadLevel( "levels.txt", levelNumber );
> Anyways, what I want to do is that when the ball hits one of the blocks in the array, I remove it. When a line no longer has any blocks in it, I remove that line/row. When there are no more lines, I load the next level.
> Any ideas on how I could achieve this?
> 
> foreach( Block[] ba; level )
> {
> 	foreach( Block b; ba )
> 	{
> 		if( checkCollision( ball, b.p ) )
> 		{
> 			//remove the block??
> 		}
> 	}
> }
> 
> The level array has 12 lines of 'x' amount of bricks. x can be as great as 10. -Michael P.

That depends on what you mean by "remove" and what Block is.

If Block is a class, you can remove instances from the array simply by setting that slot to null.

foreach( row ; level )
{
  foreach( ref block ; row )
  {
    if( checkCollision( ball, block.p ) )
        block = null;
  }
}

If you want to actually remove the Block from the array, and their relative ordering isn't important, you can do this:

void dropElement(T)(ref T[] arr, size_t i)
{
    assert( i < arr.length );
    arr[i] = arr[$-1];
    arr = arr[0..$-1];
}

That moves the last element into the place of the one you don't want, then drops the last element of the array.

If the relative ordering DOES matter, then you have to copy the later elements down the array and drop the last element.
August 14, 2009
Thu, 13 Aug 2009 22:59:37 -0400, Michael P. wrote:

> foreach( Block[] ba; level )
> {
> 	foreach( Block b; ba )
> 	{
> 		if( checkCollision( ball, b.p ) )
> 		{
> 			//remove the block??
> 		}
> 	}
> }

I like Daniel's answer better.  But if order matters you could do this:

for( int i = 0; i < level.length; )
{
	for( int j = 0; j < level[ i ].length; )
	{
		if( checkCollision( ball, level[ i ][ j ].p ) )
		{
			level[ i ] = level[ i ][ 0 .. j ] ~ level[ i ][ j+1 .. $ ];
		}
		else
		{
			j++;
		}
	}
	if( level[ i ].length == 0 )
	{
		level = level[ 0 .. i ] ~ level[ i+1 .. $ ];
	}
	else
	{
		i++;
	}
}
August 14, 2009
Daniel Keep Wrote:

> 
> 
> Michael P. wrote:
> > Okay, so I'm making a breakout type game in D. Using Derelict.
> > I have a 2d array of Block type variables(not important what's in them) declared like this:
> > Block[][] level;
> > and later load into like this:
> > level = loadLevel( "levels.txt", levelNumber );
> > Anyways, what I want to do is that when the ball hits one of the blocks in the array, I remove it. When a line no longer has any blocks in it, I remove that line/row. When there are no more lines, I load the next level.
> > Any ideas on how I could achieve this?
> > 
> > foreach( Block[] ba; level )
> > {
> > 	foreach( Block b; ba )
> > 	{
> > 		if( checkCollision( ball, b.p ) )
> > 		{
> > 			//remove the block??
> > 		}
> > 	}
> > }
> > 
> > The level array has 12 lines of 'x' amount of bricks. x can be as great as 10. -Michael P.
> 
> That depends on what you mean by "remove" and what Block is.
> 
> If Block is a class, you can remove instances from the array simply by setting that slot to null.
> 
> foreach( row ; level )
> {
>   foreach( ref block ; row )
>   {
>     if( checkCollision( ball, block.p ) )
>         block = null;
>   }
> }
> 
> If you want to actually remove the Block from the array, and their relative ordering isn't important, you can do this:
> 
> void dropElement(T)(ref T[] arr, size_t i)
> {
>     assert( i < arr.length );
>     arr[i] = arr[$-1];
>     arr = arr[0..$-1];
> }
> 
> That moves the last element into the place of the one you don't want, then drops the last element of the array.
> 
> If the relative ordering DOES matter, then you have to copy the later elements down the array and drop the last element.

This function worked quite well.
I ended up just using a normal array, because it made things easier than having a 2D array. The order of where the Blocks were in the array didn't matter.
I ended up doing this:
//Check for collision between blocks
for( int i = 0; i < level.length; i++ )
{
	if( checkCollision( ball, level[i].p ) )
	{
		//remove the block
		ballyVel = -ballyVel;
		dropElement( level, i );
		i--;
	}
}
.....
//remove element from arrray
void dropElement(T)(ref T[] arr, size_t i)
{
    assert( i < arr.length );
    arr[i] = arr[$-1];
    arr = arr[0..$-1];
}
August 16, 2009
Fri, 14 Aug 2009 13:55:18 +1000, Daniel Keep wrote:

> void dropElement(T)(ref T[] arr, size_t i)
> {
>     assert( i < arr.length );
>     arr[i] = arr[$-1];
>     arr = arr[0..$-1];
> }

I think it's important to erase the last element after the move to make sure no dangling references to unused data are left in a memory area you don't manage anymore:

| void dropElement(T)(ref T[] arr, size_t i)
| {
|     assert( i < arr.length );
|     arr[i] = arr[$-1];
    arr[$-1] = T.init;
|     arr = arr[0..$-1];
| }

This is a sad consequence of conservative garbage collection.
August 16, 2009

Sergey Gromov wrote:
> Fri, 14 Aug 2009 13:55:18 +1000, Daniel Keep wrote:
> 
>> void dropElement(T)(ref T[] arr, size_t i)
>> {
>>     assert( i < arr.length );
>>     arr[i] = arr[$-1];
>>     arr = arr[0..$-1];
>> }
> 
> I think it's important to erase the last element after the move to make sure no dangling references to unused data are left in a memory area you don't manage anymore:
> 
> | void dropElement(T)(ref T[] arr, size_t i)
> | {
> |     assert( i < arr.length );
> |     arr[i] = arr[$-1];
>     arr[$-1] = T.init;
> |     arr = arr[0..$-1];
> | }
> 
> This is a sad consequence of conservative garbage collection.

Excellent point; I forgot about that.