Collision problem SDL

Hey guys,

so the problem... well watching the 1 minute short video I uploaded to explain the problem may give you a better understanding of what is wrong - https://www.youtube.com/watch?v=G1H7Hr1ibAU

but I will explain my problem, the first object or the black rectangle seems to go through the red rectangle ie no collisions are detected, but this only happens after a certain speed. When I press b ( speed up key ) about 3 times I get this undefined behaviour but the strange thing is when it is running at normal speed the collisions on all sides are detected, there is some error in my logic in my code.

As this is a logic question could someone give me a hint as to what the bug may be,

thanks


also please excuse the dangerous use of globals, this is just an exercise to get better with collisions

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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239

#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;
SDL_Surface* spriteOneSurface;
SDL_Surface* spriteTwoSurface;
SDL_Texture* spriteOneTexture;
SDL_Texture* spriteTwoTexture;
SDL_Rect spriteOneRect;
SDL_Rect spriteTwoRect;
bool spriteOneUp = false;
bool spriteOneDown = false;
bool spriteOneLeft = false;
bool spriteOneRight = true;
bool spriteTwoUp = false;
bool spriteTwoDown = true;
bool spriteTwoleft = false;
bool spiteTwoRight = false;
bool spriteOnePassing = false;
bool collideLeft = false;
bool collideRight = false;
int velocity = 2;
int velocityFirstObj = 2;


void render(){

    SDL_RenderClear(renderer);
    SDL_SetRenderDrawColor(renderer,255,255,255,255);
    SDL_RenderCopy(renderer,spriteOneTexture,NULL,&spriteOneRect);
    SDL_RenderCopy(renderer,spriteTwoTexture,NULL,&spriteTwoRect);
    SDL_RenderPresent(renderer);
}

bool collisonBounds(SDL_Rect& r);
bool objectsCollision();

void moveSpriteOne(){

    if(collisonBounds(spriteOneRect)){

        if(spriteOneRight){

            spriteOneRight = false;
            spriteOneLeft = true;
        }else{

            spriteOneLeft = false;
            spriteOneRight = true;
        }
    }
    // check if black rect hits red rect from side
    if(objectsCollision() && !spriteOnePassing && collideRight){

        spriteOneRight = false;
        spriteOneLeft = true;
        collideRight = false;
    }
    // same but left side
    if(objectsCollision() && !spriteOnePassing && collideLeft){

        spriteOneLeft = false;
        spriteOneRight = true;
        collideLeft = false;
    }
    if(spriteOnePassing)
        cout << "passing" << endl;

    if(spriteOneRight)
        spriteOneRect.x+=velocityFirstObj;
    if(spriteOneLeft)
        spriteOneRect.x-=velocityFirstObj;
}

void moveSpriteTwo(){

   if(collisonBounds(spriteTwoRect)){


        if(spriteTwoDown){

            spriteTwoDown = false;
            spriteTwoUp = true;
        }else{

            spriteTwoUp = false;
            spriteTwoDown = true;
        }
   }

    if(spriteTwoDown)
        spriteTwoRect.y+=velocity;
    else
        spriteTwoRect.y-=velocity;
}

bool collisonBounds(SDL_Rect& rect){

  if( (rect.x + rect.w) >= 799)
    return true;

  if(rect.x <= 0 )
    return true;

  if(rect.y <= 0 && !spriteOnePassing)
    return true;

  if(spriteOnePassing && &rect == &spriteTwoRect){

    // cout << "here**" << endl;
    // cout << "rect.y == " << rect.y << endl;
    // cout << " two y + h == " << (spriteOneRect.y + spriteOneRect.h)+4 << endl;
     if(rect.y <= (spriteOneRect.y + spriteOneRect.h )+4){
        //cout << "yes!!!!!!!!!!!!!!!!!!!!!!!" << endl;
        return true;
     }
  }

  if(rect.y + rect.h >= 599)
    return true;

   return false;
}

void debug(){

   cout << "SPRITE ONE ::  y = " << spriteOneRect.y << " x = " << spriteOneRect.x << " y + h = "
   << spriteOneRect.y + spriteOneRect.h << endl;
   cout << "SPRITE TWO ::  y = " << spriteTwoRect.y << " x = " << spriteTwoRect.x << endl;

}

bool isObjectOver(){

   if(spriteTwoRect.x > (spriteOneRect.x + spriteOneRect.w) )
    return false;

   if( (spriteTwoRect.x + spriteTwoRect.w )< spriteOneRect.x )
    return false;

    return true;
}

bool objectsCollision(){

   spriteOnePassing = false;

   if( ( (spriteOneRect.y + spriteOneRect.h) < spriteTwoRect.y) && isObjectOver() ){
      //cout << "true" << endl;
      spriteOnePassing = true;
      return true;
   }else
    // cout << "false" << endl;

   if(spriteOneRect.x + spriteOneRect.w == spriteTwoRect.x){
      collideRight = true;
      cout << "RIGHT" << endl;
      cout << "spriteOneRect.x + spriteOneRect.w == " << spriteOneRect.x + spriteOneRect.w << endl;
      cout << "spriteTwoRect.x == " << spriteTwoRect.x << endl;
      return true;
   }
   if(spriteOneRect.x == spriteTwoRect.x + spriteTwoRect.w){
      collideLeft = true;
      cout << "LEFT" << endl;
      return true;
   }
    return false;
}


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);

    spriteOneSurface = IMG_Load("blockOne.png");
    spriteTwoSurface = IMG_Load("blockTwo.png");
    spriteOneTexture = SDL_CreateTextureFromSurface(renderer,spriteOneSurface);
    spriteTwoTexture = SDL_CreateTextureFromSurface(renderer,spriteTwoSurface);
    spriteOneRect.x = 20;
    spriteOneRect.y = 20;
    SDL_QueryTexture(spriteOneTexture,NULL,NULL,&spriteOneRect.w,&spriteOneRect.h);
    spriteTwoRect.x = 350;
    spriteTwoRect.y = 20;
    SDL_QueryTexture(spriteTwoTexture,NULL,NULL,&spriteTwoRect.w,&spriteTwoRect.h);
}

void cleanUp(){

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

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

   init();
   bool quit = false;

   while(!quit){


      SDL_PollEvent(&event);

      if(event.type == SDL_QUIT)
        break;

       if(event.type == SDL_KEYDOWN){

          int bKey = event.key.keysym.sym;
          if(bKey == SDLK_b)
            velocityFirstObj += 2;

       }

      moveSpriteOne();
      moveSpriteTwo();
      SDL_Delay(2);
      render();
   }

   cleanUp();
}
Last edited on
objectsCollision() checks if the object's edges match up exactly. But when you speed up the game, the positions move by more than 1. That means they can go from "not touching at all" to "one has smashed through the edge of the other." So objectsCollision() should check whether the objects intersect at all. Even that isn't 100% reliable: if the velocity is so fast that the black rect's velocity is more than the red rect's width, then it could pass completely through the red rectangle in a single time quantum. But you can probably ignore that, or limit the velocity.

There are other ways to clean this up substantially. If each rectangle has an x and y velocity, then when a rect collides, you just set vx = -vx and vy = -vy. That works regardless of how a rectangle moves.

objectsCollision() should take the rectangles as parameters. Also, if you start with a function that determines if two line segments on the same axis overlap, then objectsCollision becomes trivial. Something like (untested):
// Return true is line segment [s1,e1] intersects line segment [s2,e2]
1
2
3
4
5
6
7
8
9
10
11
12
bool intersects(int s1, e1, s2, e2) {
    if (e1 < s2) return false; // segment 1 is to the right of segment 2
    if (e2 < s1) return false; // segment 2 is to the right of segment 1
   return true;  // If one isn't to the right of the other, then they must intersect.
}


objectsCollision(SDL_Rect &r1, SDL_Rect &r2)
{
    // Two rectangles intersect if they intersect it both their x and y axes.
    return intersects(r1.x, r1.x+r1.w, r2.x, r2.x+r2.w) && intersects(r1.y, r1.y+r1.h, r2.y, r2.y+r2.h);
}


You still have to decide which rectangle ran into which. What do you do if they ram corners?
Thanks Hayden :)

yeah agreed , I think my code can be simplified, way too long and overly complicated for what I'm trying to achieve,

You still have to decide which rectangle ran into which. What do you do if they ram corners?


corners of the rectangles? or corners of the window?
ok so I tidied up the code a little but I still still to have a major bug, when the black rectangle is moving at a certain speed ( speed up using the accelerate key which in this case 's b) the black rectangle does collide with the red rectangle but the red rectangle fails to continue moving instead the red rectangle lingers in mid air

this is what is happening - https://www.youtube.com/watch?v=E2rT7vtyK7Y

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
188
189
190
191
192
193
194
195
196
197
198
199
200

#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;
SDL_Surface* spriteOneSurface;
SDL_Surface* spriteTwoSurface;
SDL_Texture* spriteOneTexture;
SDL_Texture* spriteTwoTexture;
SDL_Rect spriteOneRect;
SDL_Rect spriteTwoRect;
bool spriteOneUp = false;
bool spriteOneDown = false;
bool spriteOneLeft = false;
bool spriteOneRight = true;
bool spriteTwoUp = false;
bool spriteTwoDown = true;
bool spriteTwoleft = false;
bool spiteTwoRight = false;
int velocity = 2;
int velocityFirstObj = 2;

void render(){

    SDL_RenderClear(renderer);
    SDL_SetRenderDrawColor(renderer,255,255,255,255);
    SDL_RenderCopy(renderer,spriteOneTexture,NULL,&spriteOneRect);
    SDL_RenderCopy(renderer,spriteTwoTexture,NULL,&spriteTwoRect);
    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);

    spriteOneSurface = IMG_Load("blockOne.png");
    spriteTwoSurface = IMG_Load("blockTwo.png");
    spriteOneTexture = SDL_CreateTextureFromSurface(renderer,spriteOneSurface);
    spriteTwoTexture = SDL_CreateTextureFromSurface(renderer,spriteTwoSurface);
    spriteOneRect.x = 20;
    spriteOneRect.y = 20;
    SDL_QueryTexture(spriteOneTexture,NULL,NULL,&spriteOneRect.w,&spriteOneRect.h);
    spriteTwoRect.x = 350;
    spriteTwoRect.y = 20;
    SDL_QueryTexture(spriteTwoTexture,NULL,NULL,&spriteTwoRect.w,&spriteTwoRect.h);
}

void cleanUp(){

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

bool objectsColliding(SDL_Rect& one, SDL_Rect& two){

    if(one.y >= two.y + two.h)
        return false;
    if(one.x >= two.x + two.w)
        return false;
    if(one.y + one.h <= two.y)
        return false;
    if(one.x + one.w <= two.x)
        return false;

    cout << "returning true :: collision" << endl;
    return true;
}


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(SDL_Rect& rect){

  if(&rect == &spriteOneRect){

    if(collisionBounds(rect)){

        if(spriteOneLeft){

            spriteOneLeft = false;
            spriteOneRight = true;
        }else{

           spriteOneRight = false;
           spriteOneLeft = true;
        }

    }

    if(objectsColliding(spriteOneRect,spriteTwoRect)){

        if(spriteOneLeft){

            spriteOneLeft = false;
            spriteOneRight = true;
        }else{

           spriteOneRight = false;
           spriteOneLeft = true;
        }

    }

    if(spriteOneLeft)
        spriteOneRect.x-= velocityFirstObj;
    else
        spriteOneRect.x+= velocityFirstObj;
  }

  if(&rect == &spriteTwoRect){

    if(collisionBounds(rect)){

        if(spriteTwoDown){

            spriteTwoDown = false;
            spriteTwoUp = true;
        }else{
            spriteTwoUp = false;
            spriteTwoDown = true;
        }
    }

    if(objectsColliding(spriteOneRect,spriteTwoRect)){

        if(spriteTwoUp){

            spriteTwoUp = false;
            spriteTwoDown = true;
        }else{

           spriteTwoDown = false;
           spriteTwoUp = true;
        }
    }

    if(spriteTwoDown)
        spriteTwoRect.y+=2;
    else
        spriteTwoRect.y-=2;
  }

}

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

   init();
   bool quit = false;

   while(!quit){


      SDL_PollEvent(&event);

      if(event.type == SDL_QUIT)
        break;

       if(event.type == SDL_KEYDOWN){

          int bKey = event.key.keysym.sym;
          if(bKey == SDLK_b)
            velocityFirstObj += 2;

       }
      moveObject(spriteTwoRect);
      moveObject(spriteOneRect);
      SDL_Delay(2);
      render();
   }
   cleanUp();
}
Last edited on
I think what's happening is that every time there is a collision, both the red and black boxes change directions. Because the black box moves so much faster than the red one, every time it bounces back, the red box changes too. So, the red box never moves very far.

BTW,

Why have spriteTwoDown and spriteTwoUp? (and similar for spriteOne) You only need one of these variables, because one is always the opposite of the other. Changing directions is simply spriteTwoUp = !spriteTwoUp;
very true, that is the problem, now the question is why does the red rectangle change direction also?

also I added a bool value to make sure the change of direction only happens one the name of the var is collideOnce, but it does seem quite a hackish fix that may add future problems

but back to the question as to why the red rectangle also moves down when a collision is detected, so we first call moveObject with the black rectangle passed as a argumentr, when a collision occurs we move the black rectangle in the opposite direction, now a collision is not happening but shouldn't this mean that the red rectangle doesn't change direction? I would only expect the red rectangle to change direction if it collides with the top of the black rectangle because this is when the call to objectsColliding returns true in moveObject with the red rectangle as an argument???

thanks


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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#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;
SDL_Surface* spriteOneSurface;
SDL_Surface* spriteTwoSurface;
SDL_Texture* spriteOneTexture;
SDL_Texture* spriteTwoTexture;
SDL_Rect spriteOneRect;
SDL_Rect spriteTwoRect;
bool spriteOneUp = false;
bool spriteOneDown = false;
bool spriteOneLeft = false;
bool spriteOneRight = true;
bool spriteTwoUp = false;
bool spriteTwoDown = true;
bool spriteTwoleft = false;
bool spiteTwoRight = false;
bool spriteOnePassing = false;
bool collideOnce = false;

int velocity = 2;
int velocityFirstObj = 2;

void render(){

    SDL_RenderClear(renderer);
    SDL_SetRenderDrawColor(renderer,255,255,255,255);
    SDL_RenderCopy(renderer,spriteOneTexture,NULL,&spriteOneRect);
    SDL_RenderCopy(renderer,spriteTwoTexture,NULL,&spriteTwoRect);
    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);

    spriteOneSurface = IMG_Load("blockOne.png");
    spriteTwoSurface = IMG_Load("blockTwo.png");
    spriteOneTexture = SDL_CreateTextureFromSurface(renderer,spriteOneSurface);
    spriteTwoTexture = SDL_CreateTextureFromSurface(renderer,spriteTwoSurface);
    spriteOneRect.x = 20;
    spriteOneRect.y = 20;
    SDL_QueryTexture(spriteOneTexture,NULL,NULL,&spriteOneRect.w,&spriteOneRect.h);
    spriteTwoRect.x = 350;
    spriteTwoRect.y = 20;
    SDL_QueryTexture(spriteTwoTexture,NULL,NULL,&spriteTwoRect.w,&spriteTwoRect.h);
}

void cleanUp(){

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

bool objectsColliding(SDL_Rect& one, SDL_Rect& two){

    if(one.y >= two.y + two.h)
        return false;
    if(one.x >= two.x + two.w)
        return false;
    if(one.y + one.h <= two.y)
        return false;
    if(one.x + one.w <= two.x)
        return false;

    cout << "returning true :: collision" << endl;
    return true;
}


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(SDL_Rect& rect){

  if(&rect == &spriteOneRect){

    if(collisionBounds(rect)){

        if(spriteOneLeft){

            spriteOneLeft = false;
            spriteOneRight = true;
        }else{

           spriteOneRight = false;
           spriteOneLeft = true;
        }

    }

    if(objectsColliding(spriteOneRect,spriteTwoRect)){

        if(spriteOneLeft){

            spriteOneLeft = false;
            spriteOneRight = true;
        }else{

           spriteOneRight = false;
           spriteOneLeft = true;
        }

    }

    if(spriteOneLeft)
        spriteOneRect.x-= velocityFirstObj;
    else
        spriteOneRect.x+= velocityFirstObj;
  }

  if(&rect == &spriteTwoRect){

    if(collisionBounds(rect)){

        if(spriteTwoDown){

            spriteTwoDown = false;
            spriteTwoUp = true;
            collideOnce = false;
        }else{
            spriteTwoUp = false;
            spriteTwoDown = true;
        }

    }

    if(objectsColliding(spriteOneRect,spriteTwoRect) && !collideOnce){

        collideOnce = true;
        if(spriteTwoUp){

            spriteTwoUp = false;
            spriteTwoDown = true;
        }else{

           spriteTwoDown = false;
           spriteTwoUp = true;
        }
    }
    if(spriteTwoDown)
        spriteTwoRect.y+=2;
    else
        spriteTwoRect.y-=2;
  }
}

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

   init();
   bool quit = false;

   while(!quit){

      SDL_PollEvent(&event);

      if(event.type == SDL_QUIT)
        break;

       if(event.type == SDL_KEYDOWN){

          int bKey = event.key.keysym.sym;
          if(bKey == SDLK_b)
            velocityFirstObj += 2;

       }
      moveObject(spriteOneRect);
      render();
      moveObject(spriteTwoRect);
      SDL_Delay(20);

   }
   cleanUp();
}
I would do the following:

1. Give each rectangle 2 motion vectors. Start by assigning red horizontal to 0 and black vertical to 0 to get everything tested. (variables to add to x and y each cycle).

2. Let the value (+/-) of the vector determine whether the object is going up or down, left or right. Change directions with direction *= -1;

3. When checking for collisions with other rectangles, specify which is the moving rectangle and which is the one being collided with. Also, pass the "next location" coordinates of the moving rectangle, not the current coordinates. That way you answer the question "will I collide if I make this move?"

4. If a collision would occur, change the direction of movement. I might consider just changing the direction and leaving the rectangle sit for a cycle. What happens if you are between a wall and another rectangle and there is no place to move? If you keep reversing directions in a single cycle, you will either place a rectangle out of bounds or go into an infinite loop trying to find out where to put it. If you just stop for a cycle, you can at least let the other rectangle move. It may take a while, but eventually the stuck rectangle will get a chance to move.

5. The location of render() seems to be weird. You should render at the end of the loop.

6. I think collideOnce was a valiant effort, but I don't think that at the end of the day it will give you what you want.


Last edited on
when a collision occurs we move the black rectangle in the opposite direction, now a collision is not happening
Not necessarily. Suppose the red rectangle hits the bottom of the black rectangle dead center. You reverse the direction of the black rectangle, but that still doesn't remove it completely from the red one. It just sort of slides off to the side. Since they still overlap, the red rectangle changes direction too.

Like I said before, you have to figure out which rectangle hits which, and change the hitter's direction.

Okay, here's a thought on how to do this.

You have to consider both of their moves simultaneously. So during each pass through the loop:
- figure out where the rectangles would be if they don't collide.
- Determine if that results in them overlapping. That indicates a collision.
- Do some math to figure out when the leading edge of each rectangle crosses the side edge of the other.
- Whichever rectangle cross the side of the other last is the one that's responsible for the collision. That's the one that changes direction.

Again, this will fall apart if either rectangle is moving so fast that it moves completely past the other in a single time quantum, but you could control that by limiting the max speed.

By the way, when it changes direction, the math is a little harder. You can't just say "previous point + velocity in the opposite direction" like you're doing now. It actually moves forward to the point of intersection, and then travels backward to some point. For example, if it's at position 20 and it's 5 units away from the edge of the other square and the velocity is 10, then it moves 5 units to the edge, switches direction and moves 5 units back, resulting in no net change in position. Your code would have it jump 10 units backwards to position 10.

Hope this helps.
the main take away is I will need to figure out which rectangle hit the other, so if the red rectangle is above the black one that means the red rectangle hits the black, likewise if the black and red rectangles are level horizontally and if a collision occurs that means the black rectangle hits the red rectangle,

I could use a couple of bools right? like if a right side or left side collision occurs I could set a bool let's say sideCollision to true and reverse direction of the black object only if sideCollision is set to true, again likewise for the red rectangle colliding with the black rectangle I could have a topCollision bool, would that be a good idea? It may make the code look less elegant but as long as it works and is efficient I don't mind making that sacrifice. ( on 2nd thought, I just tried it and it failed )

For example, if it's at position 20 and it's 5 units away from the edge of the other square and the velocity is 10, then it moves 5 units to the edge, switches direction and moves 5 units back, resulting in no net change in position.


ah ok I think I got you, so even though we have not overlapped and collided with 5 units to spare we move back 5 but at this point the objects are still technically colliding right? so that is the reason why both rectangles change direction?

also yes I moved render to see if it would fix the problem but of course to no avail, will need to move it back to the end of the loop.

thanks guys both really helpful ideas :)
Last edited on
also Doug I tried doing your suggestion too and it worked !! :) well there is still a small bug that pops up every now and then but it's much better than what it was

the bug is at 0:33 seconds of the video, you can see the black rectangle keeps on hitting against the red rectangle for some reason - https://www.youtube.com/watch?v=f68U6PB6cVc

When checking for collisions with other rectangles, specify which is the moving rectangle and which is the one being collided with. Also, pass the "next location" coordinates of the moving rectangle, not the current coordinates. That way you answer the question "will I collide if I make this move?"


This pretty much helped solve the issue ( almost ) but that brings me to the question why and how does it solve the issue? I made a new function which checks for when a collision is about to take place and I replaced the objectsColliding with this new function( I will put the updated code below ), but again why does checking before a collision even takes place fix the problem ie making sure only the object that is hit moves??

on a side note does anybody see the source of the bug in the video in my code? @dhayden
Again, this will fall apart if either rectangle is moving so fast that it moves completely past the other in a single time quantum, but you could control that by limiting the max speed.


would this perhaps be the source of the bug ^^

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

bool willObjectsCollide(SDL_Rect& one, SDL_Rect& two,int deltaX,int deltaY){

    if(one.y >= two.y + two.h)
        return false;
    if(one.x - deltaX >= two.x + two.w)
        return false;
    if(one.y + one.h <= two.y - deltaY )
        return false;
    if(one.x + one.w + deltaX <= two.x)
        return false;

    cout << "returning true :: will be a collision" << endl;
    return true;
}


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(SDL_Rect& rect){

  if(&rect == &spriteOneRect){

    if(collisionBounds(rect)){

        if(spriteOneLeft){

            spriteOneLeft = false;
            spriteOneRight = true;
        }else{

           spriteOneRight = false;
           spriteOneLeft = true;
        }

    }


    if(willObjectsCollide(spriteOneRect,spriteTwoRect,velocityFirstObj,0)){

        if(spriteOneLeft){

            spriteOneLeft = false;
            spriteOneRight = true;
        }else{

           spriteOneRight = false;
           spriteOneLeft = true;
        }
    }

    if(spriteOneLeft)
        spriteOneRect.x-= velocityFirstObj;
    else
        spriteOneRect.x+= velocityFirstObj;
  }

  if(&rect == &spriteTwoRect){

    if(collisionBounds(rect)){

        if(spriteTwoDown){

            spriteTwoDown = false;
            spriteTwoUp = true;
            collideOnce = false;
        }else{
            spriteTwoUp = false;
            spriteTwoDown = true;
        }

    }


    if(willObjectsCollide(spriteOneRect,spriteTwoRect,0,2) && !collideOnce){

        collideOnce = true;
        if(spriteTwoUp){

            spriteTwoUp = false;
            spriteTwoDown = true;
        }else{

           spriteTwoDown = false;
           spriteTwoUp = true;
        }

    }

    if(spriteTwoDown)
        spriteTwoRect.y+=2;
    else
        spriteTwoRect.y-=2;
  }
}
Last edited on
Your old code was doing this:

Check if sprite 1 is already out of bounds. If so, change direction
Check if there is already a collision. If so, change sprite 1 direction
Move sprite 1
Repeat the above steps with sprite 2

Because you only check for collisions at the beginning of the cycles, for there to be a collision or out of bounds, a sprite must have moved to the offending position in the previous cycle. You have to detect that a collision is about to occur, and handle that at that time.

As far as the issue at 33 sec., it's hard to tell how fast the black box is moving. It might be so fast that it rebounds off of the right hand wall and doesn't appear to move.

@dhayden and I both gave you suggestions about how to handle movement in the collision case. You implement either suggestion. The behavior you're questioning is related to this exact issue.

I actually like @dhaden's suggestion more than my own. If you are moving 25 to the right but a collision is 15 away, move to 10 to the left of the collision and change your velocity to 25 left.

Don't try it now, but with some thought you could even handle a situation where the border is 2 to the right and the other box is 4 to the left and you have to move 25. By looping back and forth you would figure out that you have 4 or 5 collisions and which direction you would still be moving in.

A suggestion. Dont' name the willObjectsCollide arguments "one" and "two". Ambiguous names lead to problems. Maybe something like "movingSprite" and "collisionSprite".
Okay, here's a thought on how to do this.

You have to consider both of their moves simultaneously. So during each pass through the loop:
- figure out where the rectangles would be if they don't collide.
- Determine if that results in them overlapping. That indicates a collision.
- Do some math to figure out when the leading edge of each rectangle crosses the side edge of the other.
- Whichever rectangle cross the side of the other last is the one that's responsible for the collision. That's the one that changes direction.


I'm kind of not sure about point one, figure out where the rectangles would be if they don't collide. this is interesting but I'm not too sure what is meant by this point,

if they don't collide how could you determine if that results in them overlapping?

also kind of lost on this



when it changes direction, the math is a little harder. You can't just say "previous point + velocity in the opposite direction" like you're doing now. It actually moves forward to the point of intersection, and then travels backward to some point. For example, if it's at position 20 and it's 5 units away from the edge of the other square and the velocity is 10, then it moves 5 units to the edge, switches direction and moves 5 units back, resulting in no net change in position. Your code would have it jump 10 units backwards to position 10.


sorry my logic skills are pretty weak :(
Last edited on
I'm kind of not sure about point one
You're doing it already. :) You move the rectangles. Then you check to see if they have collided. If so, you change directions. What I mean is that you move the rectangles, assuming that they will not collide. Then you check to see if you were wrong.

As for changing directions, the key here is to consider what happens between ticks of your clock. Here's a simple diagram showing a ball moving to the right and bouncing off a wall. The speed is 5 spaces per second. Here's how your code models it:
----o      |    t=1.0
---------o |    t=2.0
----o      |    t=3.0

At t=2, the ball is 2 spaces away from hitting the wall. Your code says "ah ha! If it keeps moving like this, then at t=3 it will be past the wall. So I'll change directions and move it 5 spaces to the left instead of to the right."

But that isn't what would happen in real life. Here's what it looks like in real life. I've slowed the clock between t=2 and t=3:
----o      |    t=1
---------o |    t=2
----------o|    t=2.2
-----------o    t=2.4
----------o|    t=2.6
---------o |    t=2.8
--------o  |    t=3.0

The ball doesn't suddenly change directions at t=2. Instead, it moves two spaces to the right, bounces off the wall, and rebounds 3 spaces to the left. As a result, the final position at t=3 is just 1 space to the left of where it was at t=2.

So your code should model this behavior.

Topic archived. No new replies allowed.