C++ Training project

I’m trying to develop small games in C++ for practice. I tried to make a copy of Timberman but I have a problem with the character inputs. The logic is simple: the character must cut the tree and with the right arrow cuts to the right and with the left arrow cuts to the left. The problem is that this input only works once when the game starts. Whether I press the right arrow or the left arrow, after the character cuts a tree trunk the input no longer works. I also tried adding some sound effects, and they don’t work either.
I share the code below.

#include <sstream>
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>

using namespace sf;

// Function declaration
void updateBranches(int seed);			// Function Prototype whit no return value and 1 int argument

const int NUM_BRANCHES = 6;				// There will be 6 moving branches on the tree
Sprite branches[NUM_BRANCHES];			// Array that can hold 6 sprite instances of branches

// Where is the player/branches: Left or Right
enum class side{ LEFT, RIGHT, NONE };	// We'll use this to describe the position of branches and player
side branchPositions[NUM_BRANCHES];		// This array will be used with a branchPositions function with 6 value of type side

int main()
{
	// -- WINDOW --
	VideoMode vm(1920, 1080);									// Create a VideoMode object	
	RenderWindow window(vm, "Timber!!!", Style::Fullscreen);	// Open a window for the game

	Texture textureBackground;									// Create a texture to hold a graphic on the GPU	
	textureBackground.loadFromFile("graphics/background.png");	// Load a graphic into the texture	

	Sprite spriteBackground;									// Create a sprite	
	spriteBackground.setTexture(textureBackground);				// Attach the texture to the sprite	
	spriteBackground.setPosition(0, 0);							// Set the spriteBackground to cover the screen

	// -- TREE --
	Texture textureTree;
	textureTree.loadFromFile("graphics/tree.png");

	Sprite spriteTree;
	spriteTree.setTexture(textureTree);
	spriteTree.setPosition(810, 0);

	// -- BEE --
	Texture textureBee;
	textureBee.loadFromFile("graphics/bee.png");

	Sprite spriteBee;
	spriteBee.setTexture(textureBee);
	spriteBee.setPosition(0, 800);
	
	bool beeActive = false;							// Is the bee currently moving?	
	float beeSpeed = 0.0f;							// How fast can the bee fly (pixels/seconds)

	// -- CLOUDS --
	Texture textureCloud;								// Make 3 cloud sprites from 1 texture	
	textureCloud.loadFromFile("graphics/cloud.png");	// Load 1 new texture
	
	Sprite spriteCloud1;
	Sprite spriteCloud2;
	Sprite spriteCloud3;
	spriteCloud1.setTexture(textureCloud);				// 3 new sprites with the same texture
	spriteCloud2.setTexture(textureCloud);
	spriteCloud3.setTexture(textureCloud);	
	spriteCloud1.setPosition(0, 0);						// Position the clouds on the left of the screen at different heights
	spriteCloud2.setPosition(0, 150);
	spriteCloud3.setPosition(0, 500);
	
	bool cloud1Active = false;							// Are the clouds currently on screen?
	bool cloud2Active = false;
	bool cloud3Active = false;
	
	float cloud1Speed = 0.0f;							// How fast is each cloud?
	float cloud2Speed = 0.0f;
	float cloud3Speed = 0.0f;

	// -- VARIABLES TO CONTROL TIME ITSELF --
	Clock clock;
	
	RectangleShape timeBar;												// Time bar
	float timeBarStartWidth = 400;
	float timeBarHeight = 80;
	timeBar.setSize(Vector2f(timeBarStartWidth, timeBarHeight));
	timeBar.setFillColor(Color::Red);
	timeBar.setPosition((1920 / 2) - timeBarStartWidth / 2, 980);

	Time getTimeTotal;
	float timeRemaining = 6.0f;
	float timeBarWidthPerSecond = timeBarStartWidth / timeRemaining;
	
	bool paused = true;													// Track whether the game is running

	// -- TEXT --
	int score = 0;

	Text messageText;
	Text scoreText;
	
	Font font;										// We need to choose a font
	font.loadFromFile("fonts/KOMIKAP.ttf");	

	messageText.setFont(font);							// Set the font to our message
	scoreText.setFont(font);
	
	messageText.setString("Press Enter to start!");		// Assign the actual message
	scoreText.setString("Score = 0");

	messageText.setCharacterSize(75);					// Make it really big
	scoreText.setCharacterSize(100);
	
	messageText.setFillColor(Color::White);				// Choose a color
	scoreText.setFillColor(Color::White);
	
	FloatRect textRect = messageText.getLocalBounds();	// Position the text

	messageText.setOrigin(
		textRect.left + textRect.width / 2.0f,
		textRect.top + textRect.height / 2.0f);

	messageText.setPosition(1920 / 2.0f, 1080 / 2.0f);

	scoreText.setPosition(20, 20);

	// -- BRANCHES --
	Texture textureBranch;
	textureBranch.loadFromFile("graphics/branch.png");
	
	for (int i = 0; i < NUM_BRANCHES; i++)		// Set the texture for each branch sprite
	{
		branches[i].setTexture(textureBranch);
		branches[i].setPosition(-2000, -2000); 
		branches[i].setOrigin(220, 20);			// Set the sprite's origin to dead centre. We can then spin it round without changing its position
	}	

	// -- PLAYER --
	Texture texturePlayer;
	texturePlayer.loadFromFile("graphics/player.png");

	Sprite spritePlayer;
	spritePlayer.setTexture(texturePlayer);
	spritePlayer.setPosition(580, 720);	
	side playerSide = side::LEFT;						// The player starts on the left

	// -- GRAVESTONE --
	Texture textureRIP;
	textureRIP.loadFromFile("graphics/rip.png");

	Sprite spriteRIP;
	spriteRIP.setTexture(textureRIP);
	spriteRIP.setPosition(600, 860);

	// -- AXE --
	Texture textureAxe;
	textureAxe.loadFromFile("graphics/axe.png");

	Sprite spriteAxe;
	spriteAxe.setTexture(textureAxe);
	spriteAxe.setPosition(700, 830);
	
	const float AXE_POSITION_LEFT = 700;			// Line the axe up with the tree
	const float AXE_POSITION_RIGHT = 1075;

	// -- FLYING LOG --
	Texture textureLog;
	textureLog.loadFromFile("graphics/log.png");

	Sprite spriteLog;
	spriteLog.setTexture(textureLog);
	spriteLog.setPosition(810, 720);
	
	bool logActive = false;							// Some other useful log related variables
	float logSpeedX = 1000;
	float logSpeedY = -1500;

	// -- CONTROL THE PLAYER INPUT --
	bool acceptInput = false;

	// -- SOUNDS --	
	SoundBuffer chopBuffer;								// The player chopping sound
	chopBuffer.loadFromFile("sounds/chop.waw");
	Sound chop;
	chop.setBuffer(chopBuffer);
	
	SoundBuffer deathBuffer;							// The player has met his end under a branch
	deathBuffer.loadFromFile("sounds/death.waw");
	Sound death;
	death.setBuffer(deathBuffer);
	
	SoundBuffer ootBuffer;								// Out of time
	ootBuffer.loadFromFile("sounds/out_of_time.waw");
	Sound outOfTime;
	outOfTime.setBuffer(ootBuffer);

	while (window.isOpen())
	{
		// -- INCREASING SCORE --
		Event event;

		while (window.pollEvent(event))
		{
			if (event.type == Event::KeyReleased && !paused)
			{
				// Listen for key presses again
				acceptInput = true;

				// Hide the axe
				spriteAxe.setPosition(2000, spriteAxe.getPosition().y);
			}
		}

		/******************************************************
		*
		* HANDLE THE PLAYER INPUT
		*
		******************************************************/

		if (Keyboard::isKeyPressed(Keyboard::Escape))
		{
			window.close();
		}

		// Start the game
		if (Keyboard::isKeyPressed(Keyboard::Return))
		{
			paused = false;

			// Reset the time and the score
			score = 0;
			timeRemaining = 6;

			// Make all the branches disappear starting in the second position
			for (int i = 1; i < NUM_BRANCHES; i++)
			{
				branchPositions[i] = side::NONE;
			}

			// Make sure the gravestone is hidden
			spriteRIP.setPosition(675, 2000);

			// Move the player into position
			spritePlayer.setPosition(580, 720);

			acceptInput = true;
		}

		// Wrap the player controls to make sure we are accepting input
		if (acceptInput)
		{
			// First handle pressing the right cursor key
			if (Keyboard::isKeyPressed(Keyboard::Right))
			{
				// Make sure the player is on the right
				playerSide = side::RIGHT;

				score++;

				// Add to the amount of time remaining
				timeRemaining += (2 / score) + .15f;

				spriteAxe.setPosition(AXE_POSITION_RIGHT, spriteAxe.getPosition().y);

				spritePlayer.setPosition(1200, 720);

				// Update the branches
				updateBranches(score);

				// Set the log flying to the left
				spriteLog.setPosition(810, 720);
				logSpeedX = -5000;
				logActive = true;

				acceptInput = false;

				// Play a chop sound
				chop.play();
			}

			// Handle the left cursor
			if (Keyboard::isKeyPressed(Keyboard::Left))
			{
				playerSide = side::LEFT;

				score++;

				// Add the amount of time remaining
				timeRemaining += (2 / score) + .15f;

				spriteAxe.setPosition(AXE_POSITION_LEFT, spriteAxe.getPosition().y);

				spritePlayer.setPosition(580, 720);

				// Update the branches
				updateBranches(score);

				// Set the log flying to the left
				spriteLog.setPosition(810, 720);
				logSpeedX = 5000;
				logActive = true;

				acceptInput = false;

				// Play a chop sound
				chop.play();
			}
		}

		/******************************************************
		*
		* UPDATE THE SCENE
		*
		******************************************************/

		if (!paused)
		{
			// Measure time
			Time dt = clock.restart();

			// Subtract from the amount of time remaining
			timeRemaining -= dt.asSeconds();
			// Size up the time bar
			timeBar.setSize(Vector2f(
				timeBarWidthPerSecond * timeRemaining,
				timeBarHeight));

			if (timeRemaining <= 0.0f)	// Test if time has run out
			{
				// Pause the game
				paused = true;

				// Change the message shown to the player
				messageText.setString("Out of time!!!");
				
				// Reposition the text based on its new size
				FloatRect textRect = messageText.getLocalBounds();
				messageText.setOrigin(
					textRect.left + textRect.width / 2.0f,
					textRect.top + textRect.height / 2.0f
				);

				messageText.setPosition(1920 / 2.0f, 1080 / 2.0f);


				// Play the out of time sound
				outOfTime.play();
			}

			// SET UP THE BEE
			if (!beeActive)								// Code to run when bee is inactive to activate it
			{
				// How fast is the bee
				srand((int)time(0) * 10);				// Seed the random number generator
				beeSpeed = (rand() % 200) + 200;		// Get a random number between 200 and 399 and assign the result to beeSpeed

				// How high is the bee
				srand((int)time(0) * 10);				// Seed the random number generator again
				float height = (rand() % 500) + 500;	// Get a random number between 500 and 999 and assign the result to height
				spriteBee.setPosition(2000, height);	// Set the position of the bee between x=2'000 and to whatever y=height

				beeActive = true;						// Set beeActive to true
			}
			else
				// Move the bee
			{
				spriteBee.setPosition(											// Get current x coordinate of the bee
					spriteBee.getPosition().x - (beeSpeed * dt.asSeconds()),	// Subtracts the beeSpeed (pixels/seconds) * dt.asSeconds from that coordinate
					spriteBee.getPosition().y);									// getPosition is used to keep the bee in the same y coordinate

				// Has the bee reached the left-hand edge of the screen?
				if (spriteBee.getPosition().x < -100)							// If returns less than -100 it will be out of view beeActive = false and a new bee will be set
				{
					// Set it up ready to be a whole new bee next frame
					beeActive = false;
				}
			}

			// MANAGE THE CLOUDS
			// Cloud 1
			if (!cloud1Active)
			{
				// How fast is the cloud
				srand((int)time(0) * 10);
				cloud1Speed = (rand() % 200);

				// How high is the cloud
				srand((int)time(0) * 10);
				float height = (rand() % 150);
				spriteCloud1.setPosition(-200, height);

				cloud1Active = true;
			}
			else
			{
				spriteCloud1.setPosition(
					spriteCloud1.getPosition().x + (cloud1Speed * dt.asSeconds()),
					spriteCloud1.getPosition().y);

				// Has the cloud reached the right hand edge of the screen?
				if (spriteCloud1.getPosition().x > 1920)
				{
					// Set it up to be a whole new cloud next frame
					cloud1Active = false;
				}
			}

			// Cloud 2
			if (!cloud2Active)
			{
				// How fast is the cloud
				srand((int)time(0) * 20);
				cloud2Speed = (rand() % 200);

				// How high is the cloud
				srand((int)time(0) * 20);
				float height = (rand() % 300) - 150;
				spriteCloud2.setPosition(-200, height);

				cloud2Active = true;
			}
			else
			{
				spriteCloud2.setPosition(
					spriteCloud2.getPosition().x + (cloud2Speed * dt.asSeconds()),
					spriteCloud2.getPosition().y);

				// Has the cloud reached the right hand edge of the screen?
				if (spriteCloud2.getPosition().x > 1920)
				{
					// Set it up to be a whole new cloud next frame
					cloud2Active = false;
				}
			}

			// Cloud 3
			if (!cloud3Active)
			{
				// How fast is the cloud
				srand((int)time(0) * 30);
				cloud3Speed = (rand() % 200);

				// How high is the cloud
				srand((int)time(0) * 30);
				float height = (rand() % 450) - 150;
				spriteCloud3.setPosition(-200, height);

				cloud3Active = true;
			}
			else
			{
				spriteCloud3.setPosition(
					spriteCloud3.getPosition().x + (cloud3Speed * dt.asSeconds()),
					spriteCloud3.getPosition().y);

				// Has the cloud reached the right hand edge of the screen?
				if (spriteCloud3.getPosition().x > 1920)
				{
					// Set it up to be a whole new cloud next frame
					cloud3Active = false;
				}
			}

			// Update the score text
			std::stringstream ss;
			ss << "Score = " << score;
			scoreText.setString(ss.str());

			// Update the branch sprites
			for (int i = 0; i < NUM_BRANCHES; i++)
			{
				float height = i * 150;

				if (branchPositions[i] == side::LEFT)
				{
					// Move the sprite to the left side
					branches[i].setPosition(610, height);

					// Flip the sprite round the other way
					branches[i].setOrigin(220, 40);
					branches[i].setRotation(180);
				}
				else if (branchPositions[i] == side::RIGHT)
				{
					// Move the sprite to the right side
					branches[i].setPosition(1330, height);

					// Set the sprite rotation to normal
					branches[i].setOrigin(220, 40);
					branches[i].setRotation(0);
				}
				else
				{
					// Hide the branch
					branches[i].setPosition(3000, height);
				}
			}

			// Handle a flying log
			if (logActive)
			{
				spriteLog.setPosition(
					spriteLog.getPosition().x + (logSpeedX * dt.asSeconds()),
					spriteLog.getPosition().y + (logSpeedY * dt.asSeconds()));

				// Has the log reached the right hand edge?
				if (spriteLog.getPosition().x < -100 || spriteLog.getPosition().x > 2000)
				{
					// Set it up ready to be a whole new log next frame
					logActive = false;
					spriteLog.setPosition(810, 720);
				}
			}

			// has the player been squished by a branch?
			if (branchPositions[5] == playerSide)
			{
				// death
				paused = true;
				acceptInput = false;

				// Draw the gravestone
				spriteRIP.setPosition(525, 760);

				// hide the player
				spritePlayer.setPosition(2000, 660);

				// Change the text of the message
				messageText.setString("SQUISHED!!");

				// Center it on the screen
				FloatRect textRect = messageText.getLocalBounds();

				messageText.setOrigin(
					textRect.left + textRect.width / 2.0f,
					textRect.top + textRect.height / 2.0f);

				messageText.setPosition(1920 / 2.0f, 1080 / 2.0f);

				// Play the death sound
				death.play();
			}
		} // End if(!paused)

		/******************************************************
		*
		* DRAW THE SCENE
		*
		******************************************************/

		// Clear everything from the last frame
		window.clear();

		// Draw our game scene here
		window.draw(spriteBackground);

		// Draw the clouds
		window.draw(spriteCloud1);
		window.draw(spriteCloud2);
		window.draw(spriteCloud3);

		// Draw the branches
		for (int i = 0; i < NUM_BRANCHES; i++)
		{
			window.draw(branches[i]);
		}

		// Draw the tree
		window.draw(spriteTree);

		// draw the player
		window.draw(spritePlayer);

		// Draw the axe
		window.draw(spriteAxe);

		// Draw the flying log
		window.draw(spriteLog);

		// Draw the gravestone
		window.draw(spriteRIP);

		// Draw the bee
		window.draw(spriteBee);

		// Draw the score
		window.draw(scoreText);

		// Draw the time bar
		window.draw(timeBar);

		if (paused)
		{
			// Draw our message
			window.draw(messageText);
		}

		// Show everything we just drew
		window.display();
	}

	return 0;
}

// Function definition
void updateBranches(int seed)
{
	// Move all the branches down one place
	for (int j = NUM_BRANCHES - 1; j > 0; j--)
	{
		branchPositions[j] = branchPositions[j - 1];
	}
	// Spawn a new branch at position 0
	// LEFT, RIGHT or NONE
	srand((int)time(0) + seed);
	int r = (rand() % 5);

	switch (r)
	{
	case 0:
		branchPositions[0] = side::LEFT;
		break;

	case 1:
		branchPositions[0] = side::RIGHT;
		break;

	default:
		branchPositions[0] = side::NONE;
		break;
	}
}

Thanks in advance

If I had to make a guess (I’m not familiar with SFML), I would check to make sure acceptInput is working the way you intend it to.

As a note, the cpp-fundamentals tag is meant to be used with the corresponding course which uses Raylib. To go further with this, I am primarily here to help with course material.

That said, when it comes to personal projects or questions outside of course material, you can always ask within our discord community to see if anyone else is willing to help. Though it does appear you have done that already.

In any case thanks for the suggestion to check the inputPlayer variable. I found the problem and solved it. Next time I’ll pay more attention to where I ask my questions.

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.

Privacy & Terms