www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - deleting items from 2d arrays

reply Michael P. <baseball.mjp gmail.com> writes:
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.
Aug 13 2009
next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
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) d=
eclared like this:
 Block[][] level;
 and later load into like this:
 level =3D loadLevel( "levels.txt", levelNumber );
 Anyways, what I want to do is that when the ball hits one of the blocks i=
n the array, I remove it. When a line no longer has any blocks in it, I rem= ove 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 )
 {
 =A0 =A0 =A0 =A0foreach( Block b; ba )
 =A0 =A0 =A0 =A0{
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if( checkCollision( ball, b.p ) )
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0{
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0//remove the block??
 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0}
 =A0 =A0 =A0 =A0}
 }

 The level array has 12 lines of 'x' amount of bricks. x can be as great a=
s 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 =3D=3D 0) arr =3D arr[1 .. $]; else if(idx =3D=3D arr.length - 1) arr =3D arr[0 .. $ - 1]; memmove(&arr[idx], &arr[idx + 1], T.sizeof * (arr.length - idx - 1)); arr =3D 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 =3D 24; // fill these in const BlockWidth =3D 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 =3D false; checkRemoveLine(row); } void collidePoint(int x, int y) { auto row =3D y / BlockHeight; auto col =3D x / BlockWidth; // assuming Block is a struct.. if(row < level.length && level[row][col].active) removeBlock(row, col); } const BallDiam =3D 10; // this makes the ball 10 pixels across const HalfBallDiam =3D 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 =3D 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 ;)
Aug 13 2009
prev sibling next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
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.
Aug 13 2009
next sibling parent Michael P. <baseball.mjp gmail.com> writes:
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]; }
Aug 14 2009
prev sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
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.
Aug 15 2009
parent Daniel Keep <daniel.keep.lists gmail.com> writes:
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.
Aug 15 2009
prev sibling parent Sergey Gromov <snake.scaly gmail.com> writes:
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++; } }
Aug 14 2009