Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Using sf::Clock on a moving ball.  (Read 4950 times)

0 Members and 1 Guest are viewing this topic.

dzzank

  • Newbie
  • *
  • Posts: 9
    • View Profile
    • Email
Using sf::Clock on a moving ball.
« on: April 29, 2014, 07:20:51 pm »
Hi! I'm trying to make a game, but on different computers the speed of the ball is different. I know I have to use the sf::Clock but I don't know how?
Can you help me?

#include <SFML/Graphics.hpp>
#include <vector>
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <string>
#include <sstream>

std::string convertInt(int number)
{
   std::stringstream ss;
   ss << number;
   return ss.str();
}

const float speed = 0.3;

int main(int argc, char** argv)
{
    sf::RenderWindow window(sf::VideoMode(800, 800), "Bounce!!!");

    int nr = 0;

    sf::Font font;

    if(!font.loadFromFile("arial.ttf"))
        return -1;

    std::vector<float> random_neg;
    std::vector<float> random_pos;

    random_neg.push_back(-0.3);
    random_neg.push_back(-0.2);
    random_pos.push_back(0.2);
    random_pos.push_back(0.3);

    float x1 = -0.2, y1 = -0.3;

    srand(time(0));

    int random1 = rand() % 2;
    int random2 = rand() % 2;

    sf::RectangleShape paddle;

    paddle.setFillColor(sf::Color::Blue);
    paddle.setPosition(350, 650);
    paddle.setSize(sf::Vector2f(100, 10));

    sf::CircleShape ball;

    ball.setRadius(10);
    ball.setPosition(335, 335);
    ball.setFillColor(sf::Color::Red);

    window.setMouseCursorVisible(false);

    while(window.isOpen())
    {
        sf::Event event;

        while(window.pollEvent(event))
        {
            switch(event.type)
            {
            case sf::Event::Closed:
                window.close();
                break;
            case sf::Event::KeyPressed:
                if(event.key.code == sf::Keyboard::Escape)
                    window.close();
                break;
            }
        }

        paddle.setPosition(sf::Mouse::getPosition(window).x, 650);

        if(paddle.getPosition().x <= 0)
            paddle.setPosition(0, 650);
        if(paddle.getPosition().x >= 700)
            paddle.setPosition(700, 650);

        if(ball.getPosition().y <= 0)
        {
            random1 = rand() % 2;
            y1 = random_pos[random1];
        }

        if(ball.getPosition().y >= 785)
        {
            random1 = rand() % 2;
            y1 = random_neg[random1];
        }

        if(ball.getPosition().x <= 0)
        {
            random1 = rand() % 2;
            x1 = random_pos[random2];
        }

        if(ball.getPosition().x >= 800)
        {
            random1 = rand() % 2;
            x1 = random_neg[random2];
        }

        if( ball.getPosition().x <= paddle.getPosition().x + 100 &&
           ball.getPosition().x >= paddle.getPosition().x - 40 &&
           ball.getPosition().y <= paddle.getPosition().y + 10 &&
           ball.getPosition().y >= paddle.getPosition().y - 10 )
        {
            ++nr;
            y1 = random_neg[random1];

            if(x1 < 0)
                x1 = random_neg[random2];
            else
                x1 = random_pos[random2];
            ball.move(x1, y1);
        }
        else
            ball.move(x1, y1);

        if(ball.getPosition().y >= 650)
        {
            ball.setPosition(300, 300);
            break;
        }

        std::string score0 = "Score: " + convertInt(nr);

        sf::Text score;
        score.setString(score0);
        score.setFont(font);
        score.setCharacterSize(15);
        score.setPosition(700, 750);
        score.setColor(sf::Color::Black);

        window.clear(sf::Color::Yellow);
        window.draw(paddle);
        window.draw(ball);
        window.draw(score);
        window.display();
    }
}

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re: Using sf::Clock on a moving ball.
« Reply #1 on: April 29, 2014, 08:25:57 pm »
Basically you have to take the time between frames into account when you update/draw your objects.

Here are some really good articles on the subject that I suggest you read:

http://gafferongames.com/game-physics/fix-your-timestep/
http://www.koonsolo.com/news/dewitters-gameloop/
http://gameprogrammingpatterns.com/game-loop.html

nomad5

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Using sf::Clock on a moving ball.
« Reply #2 on: April 30, 2014, 12:38:05 am »
Basically you have to take the time between frames into account when you update/draw your objects.

Here are some really good articles on the subject that I suggest you read:

http://gafferongames.com/game-physics/fix-your-timestep/
http://www.koonsolo.com/news/dewitters-gameloop/
http://gameprogrammingpatterns.com/game-loop.html

I see these posted a lot but I'm not entirely sure how I can incorporate them into how I draw objects.

                                for (int i = 0; i < 90; i++)
                                {
                    pOneSprite.move(sf::Vector2f(-1, 0));

                                        window.draw(pOneSprite);
                                        window.display();
                                        window.clear();
                                }

This basically moves and redraws pOneSprite by 1 pixel every loop iteration. How could I incorporate a "timestep" into something like this? I have the framerate set to 60 and I would like the visual speed to remain the same on a slower system.

« Last Edit: April 30, 2014, 12:46:27 am by nomad5 »

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: Using sf::Clock on a moving ball.
« Reply #3 on: April 30, 2014, 01:25:34 am »
The really simple and probably good enough solution:

// Our speed in pixels per second
float speed = 100.f;

// Our clock to time frames
sf::Clock clock;

while( window.isOpen() ) {
    // Event handling

    // Get elapsed time
    float delta = clock.restart().asSeconds();

    // Update sprite position
    sprite.move( speed * delta, 0.f );

    // Draw everything
    window.clear();
    window.draw( sprite );
    window.display();
}

Taken from the (unfortunately very easy to miss) SFML FAQ.

nomad5

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Using sf::Clock on a moving ball.
« Reply #4 on: April 30, 2014, 01:59:00 am »
The really simple and probably good enough solution:

//code example

Taken from the (unfortunately very easy to miss) SFML FAQ.

Appreciated, but I may still require some help.

If I'm not mistaken
sprite.move( speed * delta, 0.f );

speed * delta replaces my move 1 pixel? I'm having a hard time understanding it.

I have tried this solution before to no success, is it because of how I'm controlling how many pixels it will move with a for loop? The sprite is simply stationary when using this code:
                        for (int i = 0; i < 90; i++) // Goes right 1 pixel 90 times
                        {
                                float delta = clock.restart().asSeconds();
                                // Move token 1 pixel to right
                                pOneSprite.move(sf::Vector2f(speed * delta, 0.0f));

                                // Redraw frame
                                window.draw(boardSprite);
                                window.draw(pOneSprite);
                                window.display();
                                window.clear();
                        }

I assume I'm putting something in the wrong place?
« Last Edit: April 30, 2014, 02:02:29 am by nomad5 »

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: Using sf::Clock on a moving ball.
« Reply #5 on: April 30, 2014, 02:16:45 am »
clear() has to come before draw() and display().

(I'll assume it's obvious why that code would be very bad in a real program)

nomad5

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Using sf::Clock on a moving ball.
« Reply #6 on: April 30, 2014, 03:19:27 am »
Embarrassing, not sure how I managed that. In any case, I get some very strange behavior regarding the position of the sprite. It doesn't seem to move pixel by pixel from where I want it to, it appears higher up into the program.

It multiplies the amount of pixels it will move based on the amount of time spent, so my sprites get shot to the edges of the corner. At a lower framefrate cap, they reach outside of the window's view.
« Last Edit: April 30, 2014, 03:23:36 am by nomad5 »

Geheim

  • Full Member
  • ***
  • Posts: 201
    • View Profile
    • Email
Re: Using sf::Clock on a moving ball.
« Reply #7 on: April 30, 2014, 11:22:09 am »
speed describes how many pixels your sprite moves per second.

If you now use it in a for loop (why ever you do that?) it moves ofc 90 times faster than without the loop.
Set the speed to 1 and remove the for loop, then everything will work.
Failing to succeed does not mean failing to progress!

dzzank

  • Newbie
  • *
  • Posts: 9
    • View Profile
    • Email
Re: Using sf::Clock on a moving ball.
« Reply #8 on: April 30, 2014, 02:58:29 pm »
I tried to implement it but now the ball is moving only a few seconds and then stops on the left corner.

    while(window.isOpen())
    {
        sf::Event event;

        float delta = clock.restart().asSeconds();

        random_neg[0] *= delta;
        random_neg[1] *= delta;
        random_pos[0] *= delta;
        random_pos[1] *= delta;
.........

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: Using sf::Clock on a moving ball.
« Reply #9 on: April 30, 2014, 07:54:51 pm »
Show us a complete and minimal example.  From that snippet all we can tell is that you've left out the mistake.

dzzank

  • Newbie
  • *
  • Posts: 9
    • View Profile
    • Email
Re: Using sf::Clock on a moving ball.
« Reply #10 on: April 30, 2014, 08:00:45 pm »
#include <SFML/Graphics.hpp>
#include <vector>
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <string>
#include <sstream>

std::string convertInt(int number)
{
   std::stringstream ss;
   ss << number;
   return ss.str();
}

int main(int argc, char** argv)
{
    sf::RenderWindow window(sf::VideoMode(800, 800), "Bounce!");

    int nr = 0;
    int times = 10;

    sf::Font font;
    if(!font.loadFromFile("arial.ttf"))
        return -1;

    sf::Texture texture1;
    if(!texture1.loadFromFile("D:\\texture1.jpg"))
        return -1;

    sf::Texture texture2;
    if(!texture2.loadFromFile("D:\\texture2.png"))
        return -1;

    sf::Texture texture3;
    if(!texture3.loadFromFile("D:\\texture3.jpg"));

    sf::Sprite sprite;
    sprite.setTexture(texture1);

    sf::Clock clock;

    std::vector<float> random_neg;
    std::vector<float> random_pos;

    random_neg.push_back(-0.3);
    random_neg.push_back(-0.2);
    random_pos.push_back(0.2);
    random_pos.push_back(0.3);

    float x1 = -0.2, y1 = -0.3;

    srand(time(0));

    int random1 = rand() % 2;
    int random2 = rand() % 2;

    sf::Texture* text2 = &texture2;
    sf::Texture* text3 = &texture3;

    sf::RectangleShape paddle;

    paddle.setPosition(350, 650);
    paddle.setSize(sf::Vector2f(100, 10));
    paddle.setTexture(text2);

    sf::CircleShape ball;

    ball.setRadius(10);
    ball.setPosition(335, 335);
    ball.setTexture(text3);

    window.setMouseCursorVisible(false);

    while(window.isOpen())
    {
        sf::Event event;
        clock.restart();

        float delta = clock.restart().asSeconds();

        random_neg[0] *= delta;
        random_neg[1] *= delta;
        random_pos[0] *= delta;
        random_pos[1] *= delta;

        while(window.pollEvent(event))
        {
            switch(event.type)
            {
            case sf::Event::Closed:
                window.close();
                break;
            case sf::Event::KeyPressed:
                if(event.key.code == sf::Keyboard::Escape)
                    window.close();
                break;
            }
        }

        paddle.setPosition(sf::Mouse::getPosition(window).x, 650);

        if(paddle.getPosition().x <= 0)
            paddle.setPosition(0, 650);
        if(paddle.getPosition().x >= 700)
            paddle.setPosition(700, 650);

        if(ball.getPosition().y <= 0)
        {
            random1 = rand() % 2;
            y1 = random_pos[random1];
        }

        if(ball.getPosition().y >= 785)
        {
            random1 = rand() % 2;
            y1 = random_neg[random1];
        }

        if(ball.getPosition().x <= 0)
        {
            random1 = rand() % 2;
            x1 = random_pos[random2];
        }

        if(ball.getPosition().x >= 800)
        {
            random1 = rand() % 2;
            x1 = random_neg[random2];
        }

        if( ball.getPosition().x <= paddle.getPosition().x + 100 &&
           ball.getPosition().x >= paddle.getPosition().x &&
           ball.getPosition().y <= paddle.getPosition().y &&
           ball.getPosition().y >= paddle.getPosition().y - 10 )
        {
            ++times;

            if(times >= 1)
            {
                nr += 1;
                times = 0;
            }

            y1 = random_neg[random1];

            if(x1 < 0)
                x1 = random_neg[random2];
            else
                x1 = random_pos[random2];
            ball.move(x1, y1);
        }
        else
            ball.move(x1, y1);
    }
}

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: Using sf::Clock on a moving ball.
« Reply #11 on: April 30, 2014, 08:15:26 pm »
I'm very confused by what you're doing with the random numbers there, but you appear to be repeatedly multiplying your four random numbers by delta so they grow indefinitely until every possible movement shoots the ball outside the window (after which it wraps to the corner).

From an algorithmic standpoint, first you want to compute the direction based on all the user input and current positions, and THEN you want to multiply that direction by deltaTime to get the correct amount of movement for that frame, then pass it to move().

And the random stuff would be a lot more readable if you just used rand() calls with some modulos and other operations in the same line; I'm pretty sure you can get the same effects more concisely that way.  I'd write one or two lines as an example but it's too hard to figure out exactly what effect you were after.
« Last Edit: April 30, 2014, 08:16:57 pm by Ixrec »