Pong game (SFML)

Pages: 12
I picked up this book on SFML, "Beginning C++ Game Programming" & I am learning a lot. I am able to rewrite the code like a good little parrot, but this one sample code for Pong is really bugging me by this point.

All sorts of crazy things are happening, from the score going up when ball is missed, to the ball snagging on top of screen & right side of screen at times. There is this other thing that happens when the paddle hits the ball at just the right time...the ball then travels horizontally across the paddle. I wrote the code from book & downloaded the source and both do similar things.

https://github.com/PacktPublishing/Beginning-Cpp-Game-Programming-Second-Edition


PROBLEM 1:
1
2
3
4
5
6
void Ball::reboundBottom()
{
	m_Position.y = 0;
	m_Position.x = 500;
	m_DirectionY = -m_DirectionY;
}

I think part of the culprit is here. The direction of the ball is changed once it is missed by the paddle & passes the bottom of the screen (top of ball > bottom of screen). BUT the problem is that it was going down when missed & then restarts from top again & then reverses it's direction to go up from (500, 0). Since its already at top it then quickly goes back down & I think that is where it wigs out and even adds many points to the score on occasions when the ball is positioned back to the top.

 
m_DirectionY = m_DirectionY;


I changed the code to this (or leave out that line) & it seems to work better now, but there are still more issues. My brain hurts now to figure out the other two problems, but they are below...maybe I will try again tomorrow before looking at this post.



PROBLEM 2: Score sometimes goes up multiple times when the ball hits the top, but it should only go up by 1 point.
I am thinking quick fix to put some bool with .2s delay to enable/disable the +1 point increase, but you guys might see the problem & know better.

PROBLEM 3: Ball gets snagged on paddle & travels horizontally across the paddle.

Part of problem 3 might be the bottom snippet? Shouldn't that be an if/else instead of 2 if's? The speed of the ball is too slow, so I set it to 2000.f and I sometimes hit left/right aggressively to make it wig out faster, although you don't have to wait too long to see it either. If you run their code, just don't touch the paddle and intentionally miss the ball and you will see the craziness even without paddle movement (meaning part of the problem is in the ball code).

1
2
3
4
5
6
7
8
9
10
11
12
void Bat::update(Time dt)
{
	if (m_MovingLeft) {
		m_Position.x -= m_Speed * dt.asSeconds();
	}

	if (m_MovingRight) {
		m_Position.x += m_Speed * dt.asSeconds();
	}

	m_Shape.setPosition(m_Position);
}
Last edited on
Inverting the direction is a nice idea but it doesn't work so well if the ball gets too far inside the wall or if it hits the paddle on the side. This is quite likely to happen when you use a variable step time (dt) between updates, because if the ball is close to the wall and the update that moves the ball inside the wall happens to have an above average dt value it might move the ball so far inside the wall that it's unlikely to get moved out again on the next update so instead it still collides with the wall and gets its direction reversed and the whole thing repeats again and again without the ball leaving the wall.

To avoid this you might want to take the position of the ball and the object that it collides with into consideration. Either in your collision detection code (do not detect collisions if the object is colliding but is moving out of the object) or check which side the ball is on and make it move in that direction (e.g. if the ball center is above the paddle center then make the ball go up). Or you could hard code it for each object (collisions with the paddle always makes the ball go up, the left wall makes the ball go right, etc.). I haven't studied the code enough in detail to say which approach would be better/easier.

The problem with multiple points might be related to the first problem. You'll get one point per game update for as long as the ball is stuck inside the top wall.
Last edited on
PROBLEM 1:
I would think that reboundBottom() implies a change of the direction. So that line 3/4 are rather wrong.

PROBLEM 2:
It should bounce from the top as well.

PROBLEM 3:
When it hits the paddle you need that rebound as well (in x-direction).

Shouldn't that be an if/else instead of 2 if's?
I would have expected that the direction is used instead.

When it hits the paddle the x-direction need to change. When hitting top/bottom the y-direction needs to change. When it hits the left/right border the score for the opponent needs to be raised by 1.
> All sorts of crazy things are happening
Good!

You wouldn't have learnt anything had you just typed in the code and it worked just like that.

Most competent s/w developers can do your typical student homework in a single edit and have it work first time. But this is very much the exception rather than the rule.

Real s/w development is 10% writing the original code and 90% debugging it to fix all the problems you didn't anticipate at the outset.

So debugging skills are very much a necessary part of the whole processs, so now is a good time to start honing those skills.

You already have some good insights, go with it and see where you get to.


The code you have is a https://en.wikipedia.org/wiki/Minimum_viable_product
It's enough to give you the confidence that it could work.
But there's work to get it to where it's polished, but you'll feel a lot more satisfaction if you manage to solve the problems.
Shouldn't that be an if/else instead of 2 if's?

If you press both left and right at the same time then the effect of both if statements will cancel each other out and the paddle will not move. If you change it to use "else if" it would instead move left when both keys are pressed.


I would have expected that the direction is used instead.

The "bat" (paddle) doesn't seem to have a direction the same way the ball does. Instead it has m_MovingLeft and m_MovingRight depending on which keys are pressed.
Last edited on
@Peter
Guess I overlooked the 'bat' part...

Doesn't the paddle/bat usually move up and down?
Last edited on
The game is more like breakout/dx-ball except no bricks.
This has all the 'smell' of being coded before being designed. Particularly where logic is involved as here, IMO it's most important that you design (including an algorithm for play etc) before coding is even started. Once you're satisfied with the design, then you code from that design. Code in small parts and compile and test before moving to the next part.

Thanks.

Pong is typically played with vertical paddles, but this one has horizontal ones and is called Pong in the book and yea it is more like Breakout or Brick Breaker when it comes to just the paddle control.

I see that working with dt introduces a host of other problems and requires extreme caution. There is no way I could predicted this stuff when I just read this section of code, without actually trying it out. It all looked fine on paper.

1) I added this to the code & the scoring seems stable now. I would have to do something similar for the sides, where if the ball passes the boundary, then I just reset it to the edge of the boundary position....for the occasional problems with the sides.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void Ball::update(Time dt)
{
	// Update the ball position variables
	if (m_Position.y >= 0)
		m_Position.y += m_DirectionY * m_Speed * dt.asSeconds();
	else
	{ 
		m_Position.y = 0;
		m_DirectionY = abs(m_DirectionY);
	}

	m_Position.x += m_DirectionX * m_Speed * dt.asSeconds();
	 
	// Move the ball and the bat
	m_Shape.setPosition(m_Position);
}


2)
Shouldn't that be an if/else instead of 2 if's?

If you press both left and right at the same time then the effect of both if statements will cancel each other out and the paddle will not move. If you change it to use "else if" it would instead move left when both keys are pressed.


No way! I tried hitting both at the same time just now & somehow magically you are right, it does nothing and they cancel each other out. BUT, how the heck????????
Is this because Keyboard::isKeyPressed goes low for both left & right keys because they are both being pressed at the same time? I have not checked & will have to go back to place some values to the screen to satisfy myself. I would have suspected both of them to go high and for both if's to go through...scratching my head?

3) I don't know any VB Studio debugging yet, other than to place a watch, which I don't know if I am even doing it right because I can just hover the mouse over a variable and see the values at times. Is there a good video tutorial that I can learn VB debugger? I would love to be able to see in some list EXACTLY what the values are & what lines/classes/functions are triggered....to be able to step through it line by line and have a deeper insight into what is going on.
Last edited on
protoseepp wrote:
I don't know any VB Studio debugging yet
Debugging in Visual Studio (Windows) | Microsoft Docs
https://docs.microsoft.com/en-us/visualstudio/debugger/

Learning to debug, no matter what your programming tools are, is probably one of the most important things one can learn. Using Visual Studio makes debugging easier than most compilers/IDEs.

I'd definitely recommend from that main page above starting with:

Debugging for absolute beginners
https://docs.microsoft.com/en-us/visualstudio/debugger/debugging-absolute-beginners?view=vs-2022
Is this because Keyboard::isKeyPressed goes low for both left & right keys because they are both being pressed at the same time? I have not checked & will have to go back to place some values to the screen to satisfy myself. I would have suspected both of them to go high and for both if's to go through...scratching my head?

No, it's because both go "high" and the code in both if statements are executed. But because the first if subtracts as much as the second if adds you end up with the same amount you started with.

x - y + y = x
Last edited on
Thanks, I will have to review the debugging for sure as it is very valuable.

Peter thanks, it makes sense. No flickering on the screen either for the paddle because the increment/decrement is so small, which is an added bonus.

4) One other question for this one that I just memorized but is still bugging me. I know that it sets the origin of the original text box, so it can more readily be centered on the screen.

1
2
3
4
5
messageText.setString("Press Enter to start!");     

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





FloatRect sf::Text::getGlobalBounds ( ) const

Get the global bounding rectangle of the entity.

The returned rectangle is in global coordinates, which means that it takes into account the transformations (translation, rotation, scale, ...) that are applied to the entity. In other words, this function returns the bounds of the text in the global 2D world's coordinate system.

Returns
Global bounding rectangle of the entity


To me the top & left should always be zero if we are talking about the object itself, any value that is different is some type of offset? When I print these two values to the screen for the FloatRect text they are:

textRect.left = 0;
textRect.top = 10

What does that really mean, that there is an imaginary text box there & the text written in a certain font does not start at the top-most edge of the text box (at top=0), but 10 pixels down inside of it??????
But then if that was the case then he should be subtracting that number & not adding it in...

"textRect.top + textRect.height / 2.0f"


So then I consider the reverse, if he is adding "textRect.top + textRect.height" then it might mean that the font actually spills over outside the textbox......naaaaaa otherwise textRect.top = -10 is what I would have suspected to return & I don't think the text box in SFML would have been programmed as such. I think SFML would have just neatly surrounded the text. Which then leaves me baffled??????????




5) I love this book and I am already eye balling my next one, probably geared more toward openGL & then after that book some DirectX11. Any other really good gaming book? After finishing this book, I will want to read something that produces more refined gameplay mechanics, at least a Mario type game to start. I know I can do it in Unity or other game engines easier, but I want to do it in C++ for the experience, the love of it & motivation.

"Game Programming in C++: Creating 3D Games (Game Design) 1st Edition "


6) Any really good in-depth Mario bros how to video/coding tutorials? I found this one guy, Mitchell Sternke. He did an amazing job at redoing Mario in C++ with painstaking graphics import, but his resource link is broken on the Github page. I have not ran his code, but now he has an "mario.xml" in his resource folder. I have not ran the code yet, is it possible he is scraping the resources from the net now?
Any way to get those resources or does someone know this guy or can contact him to ask?

This is the broken link...

These are needed to run the game in it's current form. I've shared them publicly in my Dropbox: http://bit.ly/1myJJk0 Copy everything from there to the /resources directory.


https://www.youtube.com/watch?v=_ls3CICgYbQ&t=1s
https://github.com/MitchellSternke/Mario
https://github.com/MitchellSternke
Last edited on
No flickering on the screen either for the paddle because the increment/decrement is so small, which is an added bonus.

The paddle is not drawn (window.draw(bat.getShape())) and the screen is not updated (window.display()) until later so flicker because of this is nothing you have to worry about.

4)
It describes the visible portion of the text. If you change the text to "___" it will have more padding at the top and less height.

I noticed the code calls getLocalBounds() but you quoted the text for getGlobalBounds(). The getGlobalBounds() function takes transformations (such as setOrigin) into account while getLocalBounds() does not. Before calling setOrigin they return the same values but after they return different values.

But then if that was the case he should be subtracting that number & not adding it in...

getLocalBounds() does not take the position of the text into account (but getGlobalBounds() does). Instead it returns the position relative to the top-left corner of the sf::Text.

textRect.left gives you the relative x-coordinate where the visible portion of the text starts.
textRect.width / 2.0f is half the width of the visible portion of the text.
So textRect.left + textRect.width / 2.0f is the relative x-coordinate of the middle part of the visible portion of the text.


   textRect.width
 |----------------|
  _            _   
 | |          | |  
 | |_ _____  _| |_ 
 | __/ _ \ \/ / __|
 | ||  __/>  <| |_ 
  \__\___/_/\_\\__|
 ^       ^
 |       |
 |       textRect.left + textRect.width / 2.0f 
 |
 textRect.left
 

This is probably good enough for this situation but if you changed text from one frame to another and repositioned it using this method it might look a bit weird. For example, if you switched from "nano" to "kano" it would look like "kano was written further down than "nano" because the different heights makes the calculated y origin to differ.

                                _                     
   _ __   __ _ _ __   ___      | |                    
  | '_ \ / _` | '_ \ / _ \     | | ____ _ _ __   ___  
 >| | | | (_| | | | | (_) |   >| |/ / _` | '_ \ / _ \ 
  |_| |_|\__,_|_| |_|\___/     |   < (_| | | | | (_) |
                               |_|\_\__,_|_| |_|\___/  
                                                    


The ASCII art letters were generated using https://patorjk.com/.
Last edited on
OK, yes the increment/decrement to cancel each other out, then the draw at the end.

Yes, it is the local and not the global. I need to evaluate the global once again in a different game to see how they compare. I changed the wording in my original post to make it clearer.


FloatRect sf::Text::getLocalBounds ( ) const

Get the local bounding rectangle of the entity.

The returned rectangle is in local coordinates, which means that it ignores the transformations (translation, rotation, scale, ...) that are applied to the entity. In other words, this function returns the bounds of the entity in the entity's coordinate system.

Returns
Local bounding rectangle of the entity


I just double checked, here it is right from the book...getLocalBounds();
1
2
3
4
5
6
7
8
// Position the text
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);
scoreText.setPosition(20, 20);




Earlier I went back to the program and printed out all the values & you are right the left & top seem to follow the original text box.

Window x = 1920
Window y = 1080
0messageText.getLocalBounds().left = 0
0messageText.getLocalBounds().top = 12
textRect.left = 0
textRect.top = 12
textRect.height = 64
messageText.getLocalBounds().left = 0
messageText.getLocalBounds().top = 12
messageText.getLocalBounds().height = 64
messageText.getGlobalBounds().left = 0
messageText.getGlobalBounds().top = 12
messageText.getGlobalBounds().height = 64


Left is zero & and the only value worth any weight is the width, and is pretty much straight forward.

How about the top, which this time since I had changed fonts is 12 now.

So, we are saying the height of this text box is 64 pixels, but the first pixel drawn is 12 pixels down from the top (textRect.top = 12).

So does that mean really there is 12 pixels of empty space between the very top of the text box & where it hits the first visible pixel from the top of the font? But why would I want to include empty spaces in trying to figure out the mid of the VISIBLE part of the text? ......so that now really the math should be subtraction (instead of addition like he has it), because we have 12 dead empty pixel rows at the top?

textRect.height - textRect.top - = 52 pixels
(64) - (12)
Because there are 12 dead rows of pixels at the top, so that now you can really center it on the visible font/pixels and omit those 12 dead/empty pixels?

MY POSSIBLY WRONG CALCULATION/ASSUMPTIONS:
messageText.setOrigin(textRect.width - textRect.left / 2.0f,
textRect.height - textRect.top / 2.0f);

Or is his math still correct & I am still missing something?.....I checked and I am wrong and so I don't understand why his math is right when I thought we should be subtracting the padding? Unless it is not empty space & the font spills outside the text box????

After your mention, I checked & just a dash "-" gives:
textRect.top = 41

And "_" underscore gives:
textRect.top = 69

...seems like more padding/empty pixels???? So indeed the clues now leads me to believe it is padding, just like you said.
But lets just write out the correct math to make it clearer.


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

messageText.setOrigin(0 + 935 / 2.0f,
12 + 64 / 2.0f);

messageText.setOrigin(467.5f, 38.0f);


The x (467.5f) part forget about because it is just like taking the original width 935/2 = 467.5f.
But the y (38.0f) part is where I need to question. Because if we forget about this .top nonsense, then 62/2 = 31......is the middle origin, but no your telling me the box has some padding (aka empty space if I am understanding???)...empty space from top of text box & bottom first pixel of font that is 12 pixels in height. So now instead of subtract we need to make that box BIGGER by adding (12 + 64)/2 = 38............BIGGER than the 31 which means the font/text is bigger and we need this bigger mid-point origin.

I added more to the printout of my screen for analysis:

Window x = 1920
Window y = 1080

BEFORE FloatRect formed:
messageText.getLocalBounds().left = 0
messageText.getLocalBounds().top = 12
messageText.getLocalBounds().height = 64
messageText.getLocalBounds().width = 935

AFTER FloatRect formed:
textRect.left = 0
textRect.top = 12
textRect.height = 64
textRect.width = 935
messageText.getLocalBounds().left = 0
messageText.getLocalBounds().top = 12
messageText.getLocalBounds().height = 64
messageText.getLocalBounds().width = 935
messageText.getGlobalBounds().left = 0
messageText.getGlobalBounds().top = 12
messageText.getGlobalBounds().height = 64
messageText.getGlobalBounds().width = 935

AFTER messageText.setOrigin(textRect.left + textRect.width / 2.0f, textRect.top + textRect.height / 2.0f); :
textRect.left = 0
textRect.top = 12
textRect.height = 64
textRect.width = 935
messageText.getLocalBounds().left = 0
messageText.getLocalBounds().top = 12
messageText.getLocalBounds().height = 64
messageText.getLocalBounds().width = 935
messageText.getGlobalBounds().left = 492.5
messageText.getGlobalBounds().top = 508
messageText.getGlobalBounds().height = 64
messageText.getGlobalBounds().width = 935
Last edited on
The font matters. The characters in the text matters.

I changed the text in Chapter03 to start with (-) or dot (.) and then messageText.getLocalBounds().left returned 1.


So, we are saying the height of this text box is 64 pixels, but the first pixel drawn is 12 pixels down from the top (textRect.top = 12).

The top of the box that is 64 pixels high is located 12 pixels down.


Top  
left
corner
_|_        ___
 |          |
            | 12 pixels
  ____     _|_
 | ## |     |
 |#  #|     |  
 |####|     |  64 pixels 
 |#__#|    _|_
 
 


does that mean really there is 12 pixels of empty space between the very top of the text box & where it hits the first visible pixel from the top of the font?

There are essentially two boxes. One outer box and an inner box. The inner box contains the visible content while the outer box contains the inner box plus some padding. Assuming the text is only one line, the height of the outer box would always be the same regardless of text content (I think it's essentially the value that you pass to setCharacterSize).

In your example the inner box would be 64 pixels high and the top would be 12 pixels from the top of the outer box.


so that now really the math should be subtraction (instead of addition like he has it), because we have 12 dead empty pixel rows at the top?

No. Note that the coordinates that you pass to setOrigin are based on the top-left corner of the outer box.
Last edited on
I get it now why they had to do it that way. Each font and even each character would be different and so they probably take the largest possible height for a given font/character/setting and draw the character from the bottom. So the top is the padding that you have to add because it is changing per character and per character in the strings.

When you take your attention away from shape centering to font centering then you can easily see where the problem is and why they had to do it this way.

I mean still strictly speaking the center of your A there is really 64/2 = 32 if you wanted to get dead center, but they give you padding in case you want to insert lets say capital letters and it gives you an average mid point where more characters are considered.


Do you know SFML, how did you know this?

Did you see that Mario game and how smooth and great of a job he did? Have you created any games?

Much appreciate your help!!!!
Do you know SFML, how did you know this?

I have never made an attempt at learning SFML but I have experience with SDL and have quite good experience with programming in general so that helps a lot. I know how to test things, I know how to look things up, etc. To be fair, I do not have zero experience with SFML because I have helped others before (probably why I had SFML installed on my computer already).

Did you see that Mario game and how smooth and great of a job he did?

No, I didn't. Is that Chapter17?

I tried now after you mentioned it but I had problems getting it to work. With the compiler I use you're not allowed to put ClassName:: in front when declaring functions or constructors inside the class definition (I think only MSVC allows this). Then there was some inconsistent spelling with HUD.h and Hud.h (which would work on Windows but not elsewhere). These problems were easy to fix but when I run the game it complains about some errors in the fragment shader among other things and the player just falls through the floor so I gave up.

I used SFML 2.4.1 which is like 5-6 years old so maybe I just need a newer version.

Have you created any games?

See link on my profile page.
Last edited on
Even in the source code there might be errors, for instance there was Ball:: in the header file function name for 2 functions that should not be there, so you need to fix that in order to get it to run.

The Mario game link is in my #6 question from above. Check out the YouTube video of his game he made with C++....all posted again below. The source code for the Mario game is in the Github link, we just need the source graphics art to run it I think.


6) Any really good in-depth Mario bros how to video/coding tutorials? I found this one guy, Mitchell Sternke. He did an amazing job at redoing Mario in C++ with painstaking graphics import, but his resource link is broken on the Github page. I have not ran his code, but now he has an "mario.xml" in his resource folder. I have not ran the code yet, is it possible he is scraping the resources from the net now?
Any way to get those resources or does someone know this guy or can contact him to ask?

This is the broken link...

These are needed to run the game in it's current form. I've shared them publicly in my Dropbox: http://bit.ly/1myJJk0 Copy everything from there to the /resources directory.


https://www.youtube.com/watch?v=_ls3CICgYbQ&t=1s

https://github.com/MitchellSternke/Mario

https://github.com/MitchellSternke
I think I skip it. I find it quite boring testing games that are just copies of other games to be honest. I understand it can be fun to create but copying the graphics and everything does not interest me (copying the game idea is fine though). I don't say it's wrong to do it or anything, it just doesn't interest me personally.

You can of course learn from it, but the important thing is to understand how it works so that you can adjust it to your needs. Not every game is the same.

Any really good in-depth Mario bros how to video/coding tutorials?

I'm sorry, I have nothing to recommend. The tutorial I followed when I learned this stuff was Tonypa's tile based games tutorials but that was many years ago and it used Flash. ;)
https://web.archive.org/web/20050216015419/http://www.tonypa.pri.ee/tbw/start.html
Last edited on
Pages: 12