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

Author Topic: This is bizarre... Why would NOT drawing something be causing a lag? 6/9 SOLVED!  (Read 336 times)

0 Members and 1 Guest are viewing this topic.

RetroRain

  • Newbie
  • *
  • Posts: 14
    • View Profile
I've been trying to recreate the game, called Snake, in C++, using the SFML library.

I was programming it my way, with certain code aspects taken from this video:

https://www.youtube.com/watch?v=OBBrp43TX3A

I was having a lagging problem.  The snake wouldn't move downwards smoothly without lagging.  And I couldn't figure out why.

So I was forced to strip my code down similar to the code in the video.

Even after modifying every line, and testing, I kept getting the lagging.

Even when the code was exactly the same as the one in the video, I kept getting the lagging (I omitted the fruit code and my aesthetics were different, but the code was the same).

After a few hours of testing, I finally traced where the lagging is coming from.

This is what is bizarre.

Why would NOT drawing something, be causing a lag?

If you omit (or comment out) this block of code, or any of the invididual lines here, lagging occurs.  This code is meant to draw the background tiles.

    for (int i=0; i<N; i++)
      for (int j=0; j<M; j++)
        { sprite1.setPosition(i*size, j*size);  window.draw(sprite1); }
 

The source code and folder with images is downloadable at the YouTube video link I posted above.  But you can simply use any 16x16 pixel square to represent the snake (sprite2) and background (sprite1).

Here is the code.  It's actually not that long at all, and the code block above appears right after window.clear().

#include <SFML/Graphics.hpp>
#include <time.h>
using namespace sf;

int N=30,M=20;
int size=16;
int w = size*N;
int h = size*M;

int dir,num=4;

struct Snake
{ int x,y;}  s[100];


void Tick()
 {
    for (int i=num;i>0;--i)
     {s[i].x=s[i-1].x; s[i].y=s[i-1].y;}

    if (dir==0) s[0].y+=1;
    if (dir==1) s[0].x-=1;
    if (dir==2) s[0].x+=1;
    if (dir==3) s[0].y-=1;
 }

int main()
{
    srand(time(0));

    RenderWindow window(VideoMode(w, h), "Snake Game!");

    Texture t1,t2;
    t1.loadFromFile("images/white.png");
    t2.loadFromFile("images/red.png");

    Sprite sprite1(t1);
    Sprite sprite2(t2);

    Clock clock;
    float time = 0;
    float timer=0, delay=0.1;

    while (window.isOpen())
    {
        time = clock.getElapsedTime().asSeconds();
        clock.restart();
        timer+=time;

        Event e;
        while (window.pollEvent(e))
        {
            if (e.type == Event::Closed)
                window.close();
        }

        if (Keyboard::isKeyPressed(Keyboard::Left)) dir=1;
        if (Keyboard::isKeyPressed(Keyboard::Right)) dir=2;
        if (Keyboard::isKeyPressed(Keyboard::Up)) dir=3;
        if (Keyboard::isKeyPressed(Keyboard::Down)) dir=0;

        if (timer>delay) {timer=0; Tick();}

   ////// draw  ///////
    window.clear();

      for (int i=0; i<N; i++)                       //IF YOU COMMENT OUT THIS LINE
         for (int j=0; j<M; j++)                   //OR THIS LINE
           { sprite1.setPosition(i*size, j*size);  window.draw(sprite1); }     //OR THIS LINE, LAGS OCCUR.

    //BUT IF YOU KEEP THIS BLOCK IN, THE LAGS DON'T OCCUR.  THAT DOESN'T MAKE ANY SENSE TO ME.

    for (int i=0;i<num;i++)
        { sprite2.setPosition(s[i].x*size, s[i].y*size);  window.draw(sprite2); }

    window.display();
    }

    return 0;
}
 

I'm simply trying to understand why NOT drawing something, is causing a problem?  But if the code is left in there, there is no problem at all.

In other words, if you decide to not draw the background tiles, a lagging problem will occur with the snake.  The snake will freeze and move irregulary.  But if the background tiles are drawn, the snake moves smoothly and perfectly along.

The only thing that comes to mind, that would even cause a potential problem, is the code that deals with the clock and timing.  But it still doesn't make any sense to me.

By omitting the background tile drawing, this should not impact the movement of the snake in the slightest.

Does anyone know what exactly is going on here?

The only reason I am posting this topic, is because I do find it very bizarre that leaving out a code that is meant to draw something is very weird.  What if I didn't want a background?  NOT drawing something shouldn't be causing a problem.

Thank you for taking the time to look at this.
« Last Edit: June 09, 2020, 05:48:11 pm by RetroRain »

Hapax

  • Hero Member
  • *****
  • Posts: 2999
  • My number of posts is shown in hexadecimal.
    • View Profile
Re: This is bizarre... Why would NOT drawing something be causing a lag?
« Reply #1 on: June 08, 2020, 06:30:01 pm »
Have you tried commenting out the entire block in one go to see if the delay is still there?

One thing I can see is that if you comment out the draw line, the next draw loop is looped within that loop.

i.e.
without:
{ sprite1.setPosition(i*size, j*size);  window.draw(sprite1); }
it becomes:
for (int i=0; i<N; i++)
    for (int j=0; j<M; j++)
        for (int i=0;i<num;i++)
            { sprite2.setPosition(s[i].x*size, s[i].y*size);  window.draw(sprite2); }

This means that the draw sprite2 line is drawn a lot more times (more than just sprite1 and sprite2 should in total).

One thing to remember to do is to add braces to all code blocks whether they are one line or not. I know that I've used compilers that get confused without them, especially for loops.

So, it'd look like this:
    window.clear();

    for (int i=0; i<N; i++)
    {
        for (int j=0; j<M; j++)
        {
            sprite1.setPosition(i*size, j*size);
            window.draw(sprite1);
        }
    }
    for (int i=0;i<num;i++)
    {
        sprite2.setPosition(s[i].x*size, s[i].y*size);
        window.draw(sprite2);
    }

    window.display();
This makes is obvious which block you may be commenting and which code would be used otherwise.
Selba Ward - SFML drawables
Kairos - Timing Library
Rectangular Boundary Collision - Rectangular SAT Collision

@Hapaxiation - Hapaxia on Twitter

RetroRain

  • Newbie
  • *
  • Posts: 14
    • View Profile
Re: This is bizarre... Why would NOT drawing something be causing a lag?
« Reply #2 on: June 09, 2020, 02:21:06 am »
I think I get what you are trying to tell me.

There definitely is a flaw in the code logic here.  One that I was just blind to.

This line, by itself, is flawed logic:

for (int i=0;i<num;i++)
{ sprite2.setPosition(s[i].x*size, s[i].y*size);  window.draw(sprite2); }
 

But when I used this nested loop...

for (int i=0; i<N; i++)
    for (int j=0; j<M; j++)
        for (int i=0;i<num;i++)
            { sprite2.setPosition(s[i].x*size, s[i].y*size);  window.draw(sprite2); }

there was no lag.

I tried all sorts of various combinations of commenting out single lines, the whole blocks, and removing/adding braces, etc.

I'm still a little bit confused by all of this, but I feel like I have a better idea of why this is happening.

It definitely has to do with the nested loop code logic.

Thank you Hapax for seeing something that I was just blind to.

That is why I posted it on here to begin with.  Something little like this was driving me nuts.

I appreciate it.

RetroRain

  • Newbie
  • *
  • Posts: 14
    • View Profile
Re: This is bizarre... Why would NOT drawing something be causing a lag?
« Reply #3 on: June 09, 2020, 05:47:34 pm »
UPDATE - 6/9/20: SOLVED!

The lagging is a result of the timing mechanism.

It has nothing to do with the nested loops.  The nested loops happened to work, because the snake was being drawn right after the nested loop, so the timing was in-sync.

This is the original code:
   if (timer>delay) {timer=0; Tick();}

   ////// draw  ///////
    window.clear();

      for (int i=0; i<N; i++)
         for (int j=0; j<M; j++)
           { sprite1.setPosition(i*size, j*size);  window.draw(sprite1); }

    for (int i=0;i<num;i++)
        { sprite2.setPosition(s[i].x*size, s[i].y*size);  window.draw(sprite2); }

    window.display();

The drawing code is OUTSIDE of the timer.  The reason for the lag, is because the drawing is happening every frame, but the logic for the snake movement is not happening every frame.  It is happening when the timer is greater than the delay, as shown above.

However, if I go like this:

   if (timer>delay)
   {
     timer=0;
     Tick();

   ////// draw  ///////
    window.clear();

      for (int i=0; i<N; i++)
         for (int j=0; j<M; j++)
           { sprite1.setPosition(i*size, j*size);  window.draw(sprite1); }

    for (int i=0;i<num;i++)
        { sprite2.setPosition(s[i].x*size, s[i].y*size);  window.draw(sprite2); }

    window.display();
    }

Now the drawing code is in-sync with the timer mechanism, hence no more lagging.

I can't believe it didn't see this initially, but I just figured it out now, as I am currently reprogramming my snake game from scratch.

So the answer to my question has now been solved.

The nested loop that draws the background gives the program the time it needs to draw the snake, so there is no lag.

Otherwise, once the timing mechanism is introduced, everything should be in-sync with the timer.

Hapax, thank you again for your help.

You gave me that detail of the nested loop, which in turn led me in the right direction to figuring out why this annoying lag was happening.

You guys here are awesome.  You've always been helpful.

I only hope that if anyone out there is having the same problem that I had, and they don't understand WHY it is happening, they will be able to stumble upon this thread, to understand why it is happening.

Thank you. :)

Hapax

  • Hero Member
  • *****
  • Posts: 2999
  • My number of posts is shown in hexadecimal.
    • View Profile
You're welcome. I'm glad I could help.

I have some problem with your solution though.
This means that you are only updating the window on some of the main loops. I see that this allows you to update multiple times before updating but consider approaching it from a different perspective...

Instead, think of the main loop as a way for your window to be updated, including all events and fully drawing "painting" (as its called in at least one OS) the window. This means that every loop will loop events and clear, draw, and display to the window. This keeps the window up-to-date and responsive. If required, you can slow down the loop (rather than only updating on certain loops) by using vertical synchronization or setting a maximum frame rate.

Then, in your update part of the loop, you are free to update (the logic/physics etc.) multiple times in that one loop. Loop the update ;)

In addition, if you find that drawing a lot of things is causing lag, consider these two options:
1) create a render texture and update that as often or not as you want to (like you do to your window) but always draw that one render texture to the window each loop.
2) use a vertex array to "batch" all of your sprites so that all of your sprites can be drawn using a single drawn call.



For a more detailed explanation about the updates and the loop, read about timesteps and how to fix them here:
https://gafferongames.com/post/fix_your_timestep/

If after reading that, you would like to simplify how to implement the process, consider using Kairos timing library.
Or, at least, Kairos' "Timestep Lite", which you could drop into your code.
Selba Ward - SFML drawables
Kairos - Timing Library
Rectangular Boundary Collision - Rectangular SAT Collision

@Hapaxiation - Hapaxia on Twitter

RetroRain

  • Newbie
  • *
  • Posts: 14
    • View Profile
Hapax, sorry for the late reply.  I have been away from programming the past several days, and I didn't expect anymore replies to this topic.

If I understand what you are trying to tell me, is that the window clearing, drawing, and displaying should be happening every frame (every loop cycle of the while loop), but the actual game logic can be handled however I need it to be handled?

I'm going to try some modifcations to my code, as I want optimal performance.

Yes, I know about the RenderTexture class.  I've been using it to draw my levels.  I have found out in the past that having multiple calls to the window drawing can slow down the speed of the program.  But drawing to a texture first, and then simply drawing that texture to the window makes things faster.

I will definitely look into the vertex arrays (I've used SFML's vertex arrays to draw stars in a space fight game that I attempted).

Thanks for the info.
« Last Edit: June 22, 2020, 02:07:38 am by RetroRain »

Hapax

  • Hero Member
  • *****
  • Posts: 2999
  • My number of posts is shown in hexadecimal.
    • View Profile
If I understand what you are trying to tell me, is that the window clearing, drawing, and displaying should be happening every frame (every loop cycle of the while loop)
Yes, absolutely.

the actual game logic can be handled however I need it to be handled?
Indeed. The game logic can be updated multiple times per game loop.

Yes, I know about the RenderTexture class.  I've been using it to draw my levels.  I have found out in the past that having multiple calls to the window drawing can slow down the speed of the program.  But drawing to a texture first, and then simply drawing that texture to the window makes things faster.
The point I was trying to make is that whenever any render target is changed, it should be cleared, drawn and displayed. This is the same for both your window and your render texture. When I said "display" the render texture, I mean using the display() method, not showing it on the window.
Remember, if you clear a render target (render texture or window, for example), it should also be displayed after you've finished drawing to it. And, you should always clear it first after displaying it is the last thing that happened. clear/draw/display

You can draw the "render sprite" (the sprite used to draw the render texture to the window) window every frame but the texture itself needs to treated with the clear/draw/display cycle every time (you change it).

I will definitely look into the vertex arrays (I've used SFML's vertex arrays to draw stars in a space fight game that I attempted).
If you've already used them (for points, I presume), moving on to using larger primitives should be comfortable enough for you. Instead of one vertex per output (as in point primitive), it would be multiple vertices per output (3 for triangle primitive and 4 for quads). The strips/fans can be a little more complicated at first but can reduce the number of required once you understand them.
Selba Ward - SFML drawables
Kairos - Timing Library
Rectangular Boundary Collision - Rectangular SAT Collision

@Hapaxiation - Hapaxia on Twitter

RetroRain

  • Newbie
  • *
  • Posts: 14
    • View Profile
The point I was trying to make is that whenever any render target is changed, it should be cleared, drawn and displayed. This is the same for both your window and your render texture. When I said "display" the render texture, I mean using the display() method, not showing it on the window.
Remember, if you clear a render target (render texture or window, for example), it should also be displayed after you've finished drawing to it. And, you should always clear it first after displaying it is the last thing that happened. clear/draw/display

You can draw the "render sprite" (the sprite used to draw the render texture to the window) window every frame but the texture itself needs to treated with the clear/draw/display cycle every time (you change it).

Yes, I know.  I always clear, draw, and display.  I have a whole function for drawing my levels that takes care of it.  I clear the texture, draw to it, display it, and then assign it to a sprite for drawing to the window on every frame.  I only draw the level when I need to, so I have a flag that is set when the texture is drawn.

If you've already used them (for points, I presume), moving on to using larger primitives should be comfortable enough for you. Instead of one vertex per output (as in point primitive), it would be multiple vertices per output (3 for triangle primitive and 4 for quads). The strips/fans can be a little more complicated at first but can reduce the number of required once you understand them.

Yes, I'll look into them when I need them.  What is interesting, is that after my recent modifications to my game (having it the way that it was in the first post), there is no longer any lagging going on.  But that could also be due to my code doing more since then.  I'm not sure, but my game works fine now with the clock timing.

Thank you for your help Hapax.