Public Group

# [SDL] Stuck at Stage 2 - only Stage1 and 2 are being shown

This topic is 3801 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hello, I've been making this game for some time now. The concept: Rectangles (Blocks) are falling from the sky and the player must avoid them. When the Block(s) are out of the screen, the stage is changed. So, I've set up a Block class and a Stage class, along with others (Timer, InputState, Player) Here is main:
int main( int argc, char* args[] )
{
if( !init() )
return 1;

SDL_WM_SetCaption( "Blocks", NULL );
screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, 32, SDL_FULLSCREEN );

Player player;

timer.start = SDL_GetTicks();
timer.running = true;

std::vector< Stage > stages;
std::vector< Block > blocks;

const int NUMBER_OF_STAGES = 3;
Stage* currentStage = &(stages[0]);

// THE MAIN LOOP
while( !state.m_Exit )
{
getInput( state, timer, player );

for( std::vector<Block>::const_iterator iter = blocks.begin(); iter < blocks.end(); ++iter )
{
if( collision( player.returnRect(), iter->returnRect() ) )
timer.running = false;
}

if( timer.running )
{
if( (SDL_GetTicks() - timer.start) % timer.interval == 0 )
{
applySurface( 0, 0, background, screen );
player.Display();

for( std::vector< Block >::const_iterator iter = blocks.begin(); iter < blocks.end(); ++iter )
{
iter->rect->y += 5 + rand() % 4;
iter->Display();
}

if( changeStage( blocks ) )
{
blocks.empty();
currentStage->completed = true;
}

if ( SDL_Flip( screen ) == -1 )
return 1;
}
}
}

return 0;
}

In the main loop, I check if the stages has to be changed and I send blocks (a vector of Block) in the function changeStage. Here is changeStage:
bool changeStage( std::vector< Block > blocks )
{
const int BLOCKS_OUT_MAX = blocks.size();
int blocksOut = 0;

for( std::vector< Block >::const_iterator iter = blocks.begin(); iter < blocks.end(); ++iter )
{
if( iter->rect->y > SCREEN_HEIGHT )
++blocksOut;
}

if( blocksOut == BLOCKS_OUT_MAX )
return true;

return false;
}


As I've said above, it only reaches Stage2 (2 blocks falling) but 2 blocks keep falling and they are not increased to 3, 4, etc. Thanks for your help.

##### Share on other sites
As you've not stated this i just want to ask a quick question. Is your problem that the code for changeStage incorrectly return false when it should return true?

Try to confrim this by stepping through, if is works as you thought then the problem must be in addBlocks function. It appears as if your using that function to change the 'blocks' vector but i can't see how. i.e your not passing it by reference. So how is a new block added to the 'blocks' vector?

What i'm trying to say is this, is the problem that its not moving on the stage? or that it is but not adding extra block when it should?

##### Share on other sites
Quote:
 Original post by JimmyDeemoTry to confrim this by stepping through, if is works as you thought then the problem must be in addBlocks function. It appears as if your using that function to change the 'blocks' vector but i can't see how. i.e your not passing it by reference. So how is a new block added to the 'blocks' vector?

This way:
void addBlocks( std::vector< Block >& blocks, std::vector< Stage >& stages, Stage* currentStage ){	int numberOfBlocks = 0;	for( std::vector< Stage >::iterator iter = stages.begin(); iter < stages.end(); ++iter ) 	// check which stages have been completed to choose number of blocks	{		if( !iter->completed )		{			currentStage = &(*iter);			numberOfBlocks = iter->ReturnNumBlocks();			break;		}	}/*******************************************************************************/	int width = 0; // the sum of all the blocks widths	for( int i = 0; i < numberOfBlocks; ++i )	{		blocks.push_back( Block() );		width += blocks.rect->w;	}	if( width > SCREEN_WIDTH )	{		int bestWidth = SCREEN_WIDTH / blocks.size() - blocks.size() * 5;		for( int i = 0; i < int(blocks.size()); ++i )		{			blocks.rect->w = 10 + rand() % (bestWidth - 10 + 1);		}	}	if( blocks.size() > 0 )	{		for( int i = 0; i < int(blocks.size()); ++i )		{			for( int j = 0; j < int(blocks.size()); ++j )			{				if( j == i )					continue;				bool changeJ = false;				while( collision( blocks.returnRect(), blocks[j].returnRect() ) )				{					blocks.rect->x = rand() % (SCREEN_WIDTH - blocks.rect->w );					changeJ = true;				}				if( changeJ )					j = -1;			}		}	}}

Actually I am passing a reference to the function.

I think it's this piece of code that makes the problem:
for( std::vector< Stage >::iterator iter = stages.begin(); iter < stages.end(); ++iter ) 	// check which stages have been completed to choose number of blocks	{		if( !iter->completed )		{			currentStage = &(*iter);			numberOfBlocks = iter->ReturnNumBlocks();			break;		}	}

I think it has something to do with that break;. If I don't write it, it takes me directly to the 3rd stage (3 blocks). Still have the problem.

Edit: Just realized what you meant, and changed the function to bool changeStage( std::vector< Block >& blocks ). The same happens. :(

##### Share on other sites
Ok so it is detecting that the stage has finished ok. But the value its getting for the next stage is wrong.

In your addBlock function is the !iter->completed giving values that you expect?

As you using the ReturnNumBlocks function, i assume you are setting the number of blocks associated with that stage somewhere. Perhaps on construction of the stage object. Check that is the right value i.e. 2 for stage 2, 3 for stage 3.

If you remove the break then it will get you to stage three, or whatever the last stage happens to be in your vector because your letting the loop carry on.

Still difficult to tell whats going on, but i'm just thinking of ideas.

##### Share on other sites
1. I've changed the Stage constructor, because ID was initialized to 1, I changed it to 0.
2. I changed the addBlocks function. I removed this code:
for( std::vector< Stage >::iterator iter = stages.begin(); iter < stages.end(); ++iter ) 	// check which stages have been completed to choose number of blocks	{		if( !iter->completed )		{			currentStage = &(*iter);			numberOfBlocks = iter->ReturnNumBlocks();			break;		}	}

3. I renamed the changeStage function to allBlocksOut.
4. I am changing the current stage in the main loop.

Here it is:
#include "SDL/SDL.h"#include "SDL/SDL_image.h"#include "SDL/SDL_ttf.h"#include <string>#include <vector>#include <ctime>#include <iostream>#include "Timer.h"#include "Player.h"#include "Block.h"#include "functions.h"#include "InputState.h"#include "Stage.h"const int SCREEN_WIDTH = 1024;const int SCREEN_HEIGHT = 768;SDL_Surface* screen = NULL;SDL_Surface* background = NULL;Timer timer;InputState state;SDL_Event event;Uint8* keystates = SDL_GetKeyState( NULL );void getInput( InputState& state, Timer& timer, Player& player );void addBlocks( std::vector< Block >& blocks, Stage* currentStage );void addStages( std::vector< Stage >& stages, int elements );bool allBlocksOut( const std::vector< Block >& blocks );int main( int argc, char* args[] ){	if( !init() )		return 1;		SDL_WM_SetCaption( "Blocks", NULL );	screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, 32, SDL_FULLSCREEN );	background = loadImage( "background.png" );	Player player;		timer.start = SDL_GetTicks();	timer.running = true;	std::vector< Stage > stages;	std::vector< Block > blocks;		const int NUMBER_OF_STAGES = 4;	addStages( stages, NUMBER_OF_STAGES );	Stage* currentStage = &(stages[0]);	addBlocks( blocks, currentStage );	// THE MAIN LOOP	while( !state.m_Exit )	{		getInput( state, timer, player );				for( std::vector<Block>::const_iterator iter = blocks.begin(); iter < blocks.end(); ++iter )		{			if( collision( player.returnRect(), iter->returnRect() ) )				timer.running = false;		}				if( timer.running )		{			if( (SDL_GetTicks() - timer.start) % timer.interval == 0 )			{				applySurface( 0, 0, background, screen );				player.Display();				for( std::vector< Block >::const_iterator iter = blocks.begin(); iter < blocks.end(); ++iter )				{					iter->rect->y += 5 + rand() % 4;					iter->Display();				}								if( allBlocksOut( blocks ) )				{					//blocks.empty();										if( currentStage->ReturnNumBlocks() < NUMBER_OF_STAGES )					{							currentStage->completed = true;						currentStage = &(stages[currentStage->ReturnStageNum() + 1]);					}					addBlocks( blocks, currentStage );				}				if ( SDL_Flip( screen ) == -1 )					return 1;			}		}	}	return 0;}void getInput( InputState& state, Timer& timer, Player& player ){     state.Clear();          if( SDL_PollEvent( &event ) )     {         if( keystates[ SDLK_ESCAPE ] )         {             state.m_Exit = true;         }		 if( keystates[ SDLK_RETURN ] )		 {			 if( timer.running )			 {				 timer.running = false;				 timer.start = NULL;			 }			 else			 {				 timer.running = true;				 timer.start = SDL_GetTicks();			 }		 }				 if( timer.running ) // get input to move the player		 {			if( keystates[ SDLK_LEFT ] )				player.Move( -30 );			if( keystates[ SDLK_RIGHT ] )				player.Move( 30 );		 }	 }}void addBlocks( std::vector< Block >& blocks, Stage* currentStage ){	int numberOfBlocks = currentStage->ReturnNumBlocks();	int width = 0; // the sum of all the blocks widths	for( int i = 0; i < numberOfBlocks; ++i )	{		blocks.push_back( Block() );		width += blocks.rect->w;	}	if( width > SCREEN_WIDTH )	{		int bestWidth = SCREEN_WIDTH / blocks.size() - blocks.size() * 5;		for( int i = 0; i < int(blocks.size()); ++i )		{			blocks.rect->w = 10 + rand() % (bestWidth - 10 + 1);		}	}	if( blocks.size() > 0 )	{		for( int i = 0; i < int(blocks.size()); ++i )		{			for( int j = 0; j < int(blocks.size()); ++j )			{				if( j == i )					continue;				bool changeJ = false;				while( collision( blocks.returnRect(), blocks[j].returnRect() ) )				{					blocks.rect->x = rand() % (SCREEN_WIDTH - blocks.rect->w );					changeJ = true;				}				if( changeJ )					j = -1;			}		}	}}void addStages( std::vector< Stage >& stages, int elements ){	for( int i = 0; i < elements; ++i )		stages.push_back( Stage() );}bool allBlocksOut( const std::vector< Block >& blocks ){		const int BLOCKS_OUT_MAX = blocks.size();	int blocksOut = 0;	for( std::vector< Block >::const_iterator iter = blocks.begin(); iter < blocks.end(); ++iter )	{		if( iter->rect->y > SCREEN_HEIGHT )			++blocksOut;	}	if( blocksOut == BLOCKS_OUT_MAX )		return true;	return false;}

Thanks for your help. I really appreciate it. :)

##### Share on other sites
				if( changeStage( blocks ) )				{					blocks.empty();					currentStage->completed = true;					addBlocks( blocks, stages, currentStage );				}

There is your bug: .empty() of a vector does not empty it out; it instead tests (and returns true or false) whether it is already empty.

But there are certainly a lot of other *problems*. In particular, some things in the code are just too complicated.

Example:

bool changeStage( std::vector< Block > blocks ){		const int BLOCKS_OUT_MAX = blocks.size();	int blocksOut = 0;	for( std::vector< Block >::const_iterator iter = blocks.begin(); iter < blocks.end(); ++iter )	{		if( iter->rect->y > SCREEN_HEIGHT )			++blocksOut;	}	if( blocksOut == BLOCKS_OUT_MAX )		return true;	return false;}

The apparent intent here is "return whether all blocks meet the condition". The standard library can help us out here: it provides a function, std::count_if, which implements "given a sequence and a condition, return how many elements meet the condition", and then we can check if that accounts for everything.

But the necessary logic is simpler than that: we can instead say "return whether no blocks fail the condition", i.e., meet the opposite condition. That lets us use the standard library std::find_if, which implements "given a sequence and a condition, return an iterator to the first thing meeting the condition", and see if it's within the container. (Well, both standard library approaches are about the same, I guess, but this way is easier when you implement it yourself.)

What we do is first implement a function to check if a block is on screen (which is probably going to be useful elsewhere anyway). Then we re-implement in terms of that. Finally, we change the name, to avoid exactly the same kind of confusion that caused the bug ;)

// std::find_if and std::count_if both live in <algorithm>.bool offScreen(const Block& b) {  return b.rect->y > SCREEN_HEIGHT;}bool onScreen(const Block& b) {  return !offScreen(b);}// Naming it this way prevents you from imagining that it will do the actual// stage-changing. Also, why not pass by reference :)bool stageFinished(const std::vector<Block>& blocks) {  // Either of these approaches:  return std::find_if(blocks.begin(), blocks.end(), onScreen) == blocks.end();  // return std::count_if(blocks.begin(), blocks.end(), offScreen) == blocks.size();}

Notice BTW that extracting the BLOCKS_OUT_MAX constant in your original code doesn't really buy you anything in terms of readability.

##### Share on other sites
Alright I decided to change the logic of the game, because I didn't think it would be fun (+ I was having some trouble doing something). So I changed some code.

main has become this:
while( !state.m_Exit )	{		getInput( state, timer, player );                 		/*just testing for nowfor( std::vector<Block>::const_iterator iter = blocks.begin(); iter < blocks.end(); ++iter )		{			if( collision( player.returnRect(), iter->returnRect() ) )				timer.running = false;		}*/				if( timer.running )		{			if( (SDL_GetTicks() - timer.start) % timer.interval == 0 )			{				applySurface( 0, 0, background, screen );				player.Display();				//draw the blocks				drawBlocks( blocks, speed );				if( allBlocksOut( blocks ) )				{																				setPosition( blocks ); /*new*/				}				if ( SDL_Flip( screen ) == -1 )					return 1;			}		}	}

So this time I'm not emptying the vector. Instead, I'm changing the position of the Block(s).

setPosition
void setPosition( std::vector< Block >& blocks ){	bool used[3] = {false, false, false};	for( std::vector< Block >::iterator iter = blocks.begin(); iter < blocks.end(); ++iter )	{		int pos = rand() % 3;				while( used[pos] )		{			pos = rand() % 3;		}		used[pos] = true;		switch( pos )		{		case 0:			iter->rect->x = 0;			break;		case 1:			iter->rect->x = 220;			break;		case 2:			iter->rect->x = 440;			break;		}		iter->rect->y = 0;	}}

Now this thing works once (until the block(s)--2) leave the screen. But they don't come back. I can move the player but the blocks are nowhere to be found.

##### Share on other sites
Hmm... Are you sure that '0' on the y-axis is the top of your screen? Have you tried other y-values?

For that matter, are you sure that you have no more than three items in your block vector? If I've read your code correctly, I think that it should end up in a theoretically-infinite loop if there are more than three elements in the vector...

##### Share on other sites
Quote:
 Original post by ThaumaturgeHmm... Are you sure that '0' on the y-axis is the top of your screen? Have you tried other y-values?

But does it matter anyway? After the blocks are out of the screen, in the next loop, the blocks should be moved down.

Quote:
 For that matter, are you sure that you have no more than three items in your block vector? If I've read your code correctly, I think that it should end up in a theoretically-infinite loop if there are more than three elements in the vector...

No, the vector has 2 items in it.

Edit: I did some testing. I changed a part of the main loop to this:
if( timer.running )		{			if( (SDL_GetTicks() - timer.start) % timer.interval == 0 )			{				applySurface( 0, 0, background, screen );				player.Display();				//draw the blocks				drawBlocks( blocks, speed );								if( blocks[0].rect->y > 300  || blocks[1].rect->y > SCREEN_HEIGHT )					blocks[0].rect->y = 100; /*!!!!*/				if ( SDL_Flip( screen ) == -1 )					return 1;			}		}

When the first blocks reaches 300, it goes back to 100, then does it again until the 2nd block goes out of the stage. But when it goes out, the game stops running. I meant the blocks stop moving.

[Edited by - sheep19 on March 24, 2008 8:35:28 AM]

1. 1
2. 2
JoeJ
17
3. 3
4. 4
5. 5
frob
11

• 13
• 16
• 13
• 20
• 12
• ### Forum Statistics

• Total Topics
632178
• Total Posts
3004608

×