Problem with my jumping simulation

Hey guys,

So I think I am finally getting the idea of vectors, a vector can represent a point in space but it can also represent a force or a movement of an object and to test my understanding of vectors I made a gravity/jumping simulation, where when the rectangle jumps gravity reduces the velocity

as mentioned I have a velocity vector which I add to my black rectangles vector ( which is just it's x and y coords) I then add the gravity vector to the velocity vector, I keep doing this until the rectangle reaches the floor again.

The simulation turned out pretty good, actually looks somewhat realistic especially for a first attempt,

anyway the problem or bug that is in my code, is that when I move the object with the move key, after I moved the object an arbitrary amount on the x axis then proceeded with a jump, the rectangle skips back a couple of places on the x axis, I can't spot the bug but hopefully someone can point me in the right direction, here is a video below to illustrate what is happening.

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


( full code )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187


#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>

using namespace std;

SDL_Renderer* renderer;
SDL_Window* window;
SDL_Texture* texture;
SDL_Surface* surface;
SDL_Event event;


class Vector2{

   public:
       int x;
       int y;

       Vector2(int x,int y): x(x),y(y){}
       Vector2(){x = 0;y = 0;}

       void add(Vector2& other){

          x += other.x;
          y += other.y;
       }

};

class BlackRect{

  public:
      bool objectLeft = false;
      bool objectRight = true;
      Vector2 vec;
      SDL_Rect spriteOneRect;
      SDL_Surface* spriteOneSurface;
      SDL_Texture* spriteOneTexture;


      BlackRect(int x,int y){

         vec.x = x;
         vec.y = y;
         spriteOneSurface = IMG_Load("blockOne.png");
         spriteOneTexture = SDL_CreateTextureFromSurface(renderer,spriteOneSurface);
         spriteOneRect.x = x;
         spriteOneRect.y = y;
         SDL_QueryTexture(spriteOneTexture,NULL,NULL,&spriteOneRect.w,&spriteOneRect.h);
      }
      BlackRect(){

        vec.x = 250;
        vec.y = 250;
      }

      void update(){

        spriteOneRect.x = vec.x;
        spriteOneRect.y = vec.y;
      }

};


void render(BlackRect& br){

    SDL_RenderClear(renderer);
    SDL_SetRenderDrawColor(renderer,255,255,255,255);
    SDL_RenderCopy(renderer,br.spriteOneTexture,NULL,&br.spriteOneRect);
    SDL_RenderPresent(renderer);
}

void init(){

    if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
        cout << "fail" << endl;

    if(!IMG_Init(IMG_INIT_PNG))
        cout << "image fail" << endl;

    window = SDL_CreateWindow("sdl",250,250,800,600,SDL_WINDOW_SHOWN);
    renderer = SDL_CreateRenderer(window,-1,SDL_RENDERER_ACCELERATED);

}

void cleanUp(){

  SDL_DestroyRenderer(renderer);
  SDL_DestroyWindow(window);
  window = NULL;
  renderer = NULL;
  SDL_Quit();
}




bool collisionBounds(SDL_Rect &rect){

  if(rect.y <= 0)
    return true;
  if(rect.y + rect.h >= 600)
    return true;
  if(rect.x <= 0)
    return true;
  if(rect.x + rect.w >= 800)
    return true;

  return false;
}

void moveObject(BlackRect& rect){

   if(collisionBounds(rect.spriteOneRect)){

       if(rect.objectRight){

          rect.objectRight = false;
          rect.objectLeft = true;
       }else{
         rect.objectLeft = false;
         rect.objectRight = true;
       }
   }

   if(rect.objectRight)
    rect.spriteOneRect.x += 2;

   if(rect.objectLeft)
    rect.spriteOneRect.x -=2;

}

void jump(BlackRect& rec,int groundY){

  Vector2 gravity(0,1);
  Vector2 velocity(2,-20);

  while(true){

    rec.vec.add(velocity);
    rec.update();
    render(rec);
    SDL_Delay(20);

    velocity.add(gravity);

    if(rec.vec.y == groundY)
        break;
  }

}

int SDL_main(int argc,char* argv[]){

   init();
   bool quit = false;
   BlackRect br(20,400);

   while(!quit){

      SDL_PollEvent(&event);

      if(event.type == SDL_QUIT)
        break;

       if(event.type == SDL_KEYDOWN){

          int key = event.key.keysym.sym;
          if(key == SDLK_b)
            moveObject(br);
          else if(key == SDLK_v)
            jump(br,400);

       }
      render(br);
      SDL_Delay(2);

   }
   cleanUp();
}

Your event handling code is incorrect.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SDL_Event event;
while(SDL_PollEvent(&event)){
    if(event.type == SDL_QUIT){
        cleanUp()
        return;
    }

    if(event.type == SDL_KEYDOWN){
        int key = event.key.keysym.sym;
        if(key == SDLK_b)
            moveObject(br);
        else if(key == SDLK_v)
            jump(br,400);
    }
}
I don't currently have SDL to run the code, but in moveObject, you update rect.spriteOneRect.x. But you don't update rec.vec.

Then, in jump, you then call rec.update() which resets the rect.spriteOneRect.x to vec.x, which at this point might have a particularly old value in it. I think that's your issue, although I could be wrong.

1
2
        vec.x = 250;
        vec.y = 250;
I suggest not having a variable called "vec". That doesn't tell me what vec is. I already know it's a vector because of its type. You should call it "pos" or "position" instead, because that's what the vector actually represents.

Also, I think you're going to get into trouble by doing your "rendering loop within a loop" thing. You should always be looping around the main loop of your program. If you are looping inside of an inner function like jump, then you won't be able to poll for any events while this happens.
Last edited on
Thanks Ganado :) that indeed was the problem I changed the move function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

void moveObject(BlackRect& rect){

   if(collisionBounds(rect.spriteOneRect)){

       if(rect.objectRight){

          rect.objectRight = false;
          rect.objectLeft = true;
       }else{
         rect.objectLeft = false;
         rect.objectRight = true;
       }
   }

   if(rect.objectRight)
    rect.vec.x += 2;

   if(rect.objectLeft)
    rect.vec.x -=2;

    rect.update();

}


and very true, I think position is a more informative name rather than vec.



I think you're going to get into trouble by doing your "rendering loop within a loop" thing. You should always be looping around the main loop of your program. If you are looping inside of an inner function like jump, then you won't be able to poll for any events while this happens.


that's a very good point, a work around? I'm guessing one way would be to call the function in the main loop with different parameters until I hit a base case that way I can still poll for events.

@Helios

what's the difference? cleanUp is going to be called once we break out of the while loop anyway right?
@Ganado

here is my second attempt which is also works fine, I removed the rendering from the while loop and kept it to the game loop

the code does not look as clean but it should be more effective right?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

void jumpSecond(BlackRect& rec,int groundY,bool& jumping,Vector2& velocity){

    if(rec.vec.y == groundY){
        jumping = false;
        // reset velocity after jump completes
        velocity.x = 2;
        velocity.y = -20;
    }
}


int SDL_main(int argc,char* argv[]){

   init();
   bool jumping = false;
   BlackRect br(20,400);
   Vector2 gravity(0,1);
   Vector2 velocity(2,-20);

      while(true)
    {
        while(SDL_PollEvent(&event))
        {
            if(event.type == SDL_QUIT)
            {
                cleanUp();
                return 0;
            }

            if(event.type == SDL_KEYDOWN)
            {

                int key = event.key.keysym.sym;
                if(key == SDLK_b)
                    moveObject(br);
                else if(key == SDLK_v)
                    jumping = true;
            }
        }

        if(jumping)
        {
            br.vec.add(velocity);
            br.update();
            velocity.add(gravity);
            jumpSecond(br,400,jumping,velocity);
        }
        render(br);
        SDL_Delay(10);
   }
   
}



also @Helios changed the code but why is polling the events in a while loop correct as opposed to checking it at every iteration of the game loop?

thanks
Last edited on
cleanUp is going to be called once we break out of the while loop anyway right?
That's not the point.

First, SDL_PollEvent() checks the event queue and returns 1 if there was an event, and the event data is copied to the parameter. If there wasn't an event, the function returns 0 and the parameter is left untouched. So, for example, if you press B once the first frame the function will get the keydown event. The second frame, if you still havent released the key, the function will return 0 because there were no new events, but the event struct will still maintain its previous value, so you will process it again by calling moveObject() again. This will keep happening until a new event enters the queue.

Second, SDL_PollEvent() is supposed to be called in a loop. You need to process events as soon as possible, since the event queue is limited and event processing is a latency-sensitive process. You don't want to wait to process the user's input. The code you had previously only processed a single event per frame, at most.
that makes sense :), so basically if there is a plethora of events in the queue, only one queue will be polled on each iteration, so best to put that in a while loop to check for all events in the queue well that's how I Interpreted it.
Topic archived. No new replies allowed.