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

Author Topic: Starting sfml with two little games  (Read 785 times)

0 Members and 1 Guest are viewing this topic.

Rolf

  • Newbie
  • *
  • Posts: 6
    • View Profile
Starting sfml with two little games
« on: May 09, 2019, 07:58:48 pm »
Hi friends , i'm new to sfml and i want to program some little 2d games, so i took the Asteroids code from the 16 sfml games youtube videos and developed it a bit. The result is in the link (Asteroids and spaceshooter).

https://github.com/RafaelRodMar

What i want to know is if all is ok before doing another. I don't know if i am doing the time handling correctly because i don't multiply speed by delta time.

It seems to work but i need someone with knowledge to tell me if something is wrong.

Hapax

  • Hero Member
  • *****
  • Posts: 2791
  • My number of posts is shown in hexadecimal.
    • View Profile
Re: Starting sfml with two little games
« Reply #1 on: May 10, 2019, 12:51:02 pm »
Hi and welcome! :)

The first thing to mention would be that you should be considering splitting games into smaller chunks. Your game is almost entire within the main function and all code is in one file. Separating it into the parts it needs can make it easier to maintain and also focus on a specific area. For an example, your entity class could be separated into its file(s).
Don't be too disheartened by this comment. My first game was the same. 8)

Another big thing to note is that it's generally thought of as bad to use almost any form of global variables. Remember that you can create objects/variables inside main and then pass them to functions that need them.
This is even more serious with SFML resources; it should not be done. Again, you can create them inside main and pass them around (by reference or pointer). Another option is to use global pointers and then create them within main using those global pointers. This can be a little less safe however.

It seems that you are using a fixed timestep. The fact that it repeats multiple times per frame to "spend" the elapsed time in equal chunks is, I would say, the right approach.
It seems, however, that some things have been caught up in that part. For example, for each frame (once through the window-is-open loop code), you only need to do the event loop once and the window's clear, draw, display once. You have included the event loop inside the timestep update and, although this sounds like it might run more often so isn't a problem, it will actually not test for events until an entire timestep has passed so could cause a small amount of "lag".

I don't know if you are drawing over every pixel in the window but for an Asteroid game, I suspect not, so you should be including the window's clear() just before drawing to the window.

I noticed that you have a state system in place, which can be a very confusing area to implement. You have taken the simple method of "iffing" dependant on a variable and this method does work fine. However, you may want to look into more advanced methods for implementing states as your games get more complicated and have more and more states.
Also, again, this state checking in inside the timeframe thing but I mentioned this above. You probably don't need fixed timestep in your menu, for example.

I was looking through Asteroids' main.cpp as I wrote this so it may not also apply to "space shooter".
Selba Ward - SFML drawables
Kairos - Timing Library
Rectangular Boundary Collision - Rectangular SAT Collision

@Hapaxiation - Hapaxia on Twitter

Rolf

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Starting sfml with two little games
« Reply #2 on: May 10, 2019, 04:54:19 pm »
Thanks Hapax  :D

So, splitting, no global variables, event loop only once per frame and states. I have work for the weekend.

I use the background image instead of the windows.clear() thing. And the space shooter is mostly a copy of the asteroids one.

Stauricus

  • Full Member
  • ***
  • Posts: 155
    • View Profile
    • Email
Re: Starting sfml with two little games
« Reply #3 on: May 10, 2019, 08:11:01 pm »
you should clear the window every time before drawing again.

window.clear();
window.draw(your sprites, etc);
window.display();

Hapax

  • Hero Member
  • *****
  • Posts: 2791
  • My number of posts is shown in hexadecimal.
    • View Profile
Re: Starting sfml with two little games
« Reply #4 on: May 11, 2019, 01:29:10 am »
If you are drawing a background that will always cover the entire window and all of its pixels then clear is not necessary but, however, it can be cleared anyway, just to be safe and shouldn't affect the performance.

The skeleton version of a main window loop with a fixed time step would look something like this:
while (window.isOpen())
{
    sf::Event event;
    while (window.pollEvent(event))
    {
        // handle events
    }

    sf::Time frameTime += getFrameTime(); // "getFrameTime()" would be the time since last frame
    while (frameTime >= dt) // dt is fixed frame time (delta time)
    {
        frameTime -= dt;
        // update everything using dt as the time frame
    }

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

One other thing that I forgot to mention is that you should be avoiding rand() as much as possible as it has a number of failings; it's not even fairly choosing the numbers!
Instead, you can use the newer random number generators. Here's an example:
http://www.cplusplus.com/reference/random/uniform_int_distribution/
Selba Ward - SFML drawables
Kairos - Timing Library
Rectangular Boundary Collision - Rectangular SAT Collision

@Hapaxiation - Hapaxia on Twitter

Rolf

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Starting sfml with two little games
« Reply #5 on: May 12, 2019, 10:17:54 pm »
I changed a lot of things, the time things are still confusing but i made it work. Now it can be splitted in some files easily. Entity class needs some changes. No state manager for now. And i clear the window always.

https://github.com/RafaelRodMar/SpaceShooter/blob/master/main.cpp

Hapax

  • Hero Member
  • *****
  • Posts: 2791
  • My number of posts is shown in hexadecimal.
    • View Profile
Re: Starting sfml with two little games
« Reply #6 on: May 13, 2019, 12:46:58 am »
Good job. Looks nicer already!

You're right. It looks like a pretty simple job to separate into files now, with all those classes. :)

Although it's not going to make any effective difference, I'm curious as why your logic for timestep requires that more than (while( elapsed > timePerFrame )) the required time has passed rather than at least that amount ((while( elapsed >= timePerFrame )). I would've thought that if elapsed is equal to timePerFrame, it should be performing the update. This is, of course, just a quibble since it's highly unlikely to ever be equal with it being in microseconds but still... :P

You should probably be passing larger object around as reference rather than by value.
Your text function takes copies of all of its parameters (except the window) before using them to draw that text. Most are okay but string should probably be passed as a reference since it looks like this might be called a lot. (const reference since it isn't modified)
The important one, though, is sf::Font. That should almost never be passed by value. It's a massive resource and don't expect you want to be copying that every time you want to draw a single text object.

If you're interested, I have a timing library (Kairos) that may help or simplify your timestep implementation.
It includes Timestep (I use this myself for pretty everything), that uses the library's own clock implementation (that allow pausable clocks that can have their speed adjusted and even go backwards!), allowing everything to be taken care of automatically, including helping with frame interpolation.
However, it also includes Timestep Lite, which is a lightweight and stand-alone class (only need the header and source file for that class so you can copy those into your project instead of using the entire library) that can be used with any clock you use. It still allows for changing delta times including negative values.

Feel free to have a look at the examples to see how simple they are 8)
Also, feel free to have a look at the code if you're interested to see how I implemented it.
Selba Ward - SFML drawables
Kairos - Timing Library
Rectangular Boundary Collision - Rectangular SAT Collision

@Hapaxiation - Hapaxia on Twitter

Rolf

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Starting sfml with two little games
« Reply #7 on: May 14, 2019, 09:58:33 pm »
I have a lot of bad practices. Always forget to pass things by reference (i don't like pointers and references  ::) ). I've been trying the random number generators but couldn't make it to work. I'll study it more.

I may use Kairos in the future, when I have better knowledge of sfml, to make the code clearer.

Changed > for >= too. I will restructure the asteroids on thrusday.

Hapax

  • Hero Member
  • *****
  • Posts: 2791
  • My number of posts is shown in hexadecimal.
    • View Profile
Re: Starting sfml with two little games
« Reply #8 on: May 15, 2019, 01:42:54 am »
Just to be clear, Kairos isn't tied to SFML and doesn't use it; it's a timing library only and doesn't use SFML internally. It does, however, allow you to work with SFML.

References just mean that you are working on the original variable that was passed into the function instead of a temporary copy, which allows you to modify external variables in a function, like you do with the window.
Pointers are similar but different and tend to be more confusing at first although they can be avoided completely for a lot of applications.

Since you are having trouble with the newer (better) random stuff, I have put together an example for you:
#include <random>
#include <iostream>

namespace
{

std::default_random_engine generator;

} // namespace

void randomSeed()
{
        std::random_device rd;
        generator.seed(rd());
}

double getRandomDouble(double min, double max)
{
        std::uniform_real_distribution<double> distribution(min, max);
        return distribution(generator);
        // can be shortened to just: return std::uniform_int_distribution<double>(min, max)(generator);
}

int getRandomInt(int min, int max)
{
        std::uniform_int_distribution<int> distribution(min, max);
        return distribution(generator);
        // can be shortened to just: return std::uniform_int_distribution<int>(min, max)(generator);
}

int main()
{
        // randomise the seed so that the same random numbers aren't given in each run of the program (think srand)
        randomSeed();
       
        // use functions
        std::cout << "Random integer between 0 and 100: " << getRandomInt(0, 100) << std::endl;
        std::cout << "Random double between 0 and 1000: " << getRandomDouble(0.0, 1000.0) << std::endl;

        // use inline
        std::cout << "Random inline random integer between 0 and 1000000: " << std::uniform_int_distribution<int>(0, 1000000)(generator) << std::endl;
        std::cout << "Random inline random double between 0 and 1: " << std::uniform_real_distribution<double>(0.0, 1.0)(generator) << std::endl;

        // throw 4 dice
        std::cout << "DICE:" << std::endl;
        std::uniform_int_distribution<int> diceRoll(1, 6);
        for (std::size_t i{ 0u }; i < 4u; ++i)
                std::cout << diceRoll(generator) << std::endl;
}
You only really need the one generator so it can be reused by all random numbers.
You pretty much always should be using uniform distribution although you have to choose whether or not it's an integer or a real number.
In this example, it shows you:
- how to generate a random seed (think srand(time(NULL)) or whatever),
- how to use simple functions to make getting a random number really clear in the code,
- how to use direct inline calls to get the number without creating functions, and
- how to re-use the same distribution if you expect the range to stay the same.

Hope that helps.
Selba Ward - SFML drawables
Kairos - Timing Library
Rectangular Boundary Collision - Rectangular SAT Collision

@Hapaxiation - Hapaxia on Twitter

Rolf

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: Starting sfml with two little games
« Reply #9 on: May 16, 2019, 08:58:34 pm »
Thanks , it helps. I did a little program and it works.

Code: [Select]
#include <iostream>
#include <random>

std::default_random_engine random_engine;
std::uniform_int_distribution<> coin(0,1);
int flip_coin(){return coin(random_engine);}

int main()
{
    random_engine.seed(27);
    for(int i=0;i<100;i++)
        std::cout << flip_coin() << " ";
    std::cout << std::endl;

    random_engine.seed(52);
    for(int i=0;i<100;i++)
        std::cout << flip_coin() << " ";
    std::cout << std::endl;

}