|
Sliding Collision |
Eric Johnson
Member #14,841
January 2013
|
Hi there. I'm attempting to implement "sliding collision"--collision where, if you hold down and right while colliding with a wall to the right, you'll stop moving right, but will continue moving (or "sliding") down. This kind of collision is seen in many top-down 2D games (like A Link to the Past and other Zelda games). I'm not sure what this type of collision is actually called, but I'm going with "sliding collision" for now. Here's a (poorly drawn) visualization of what I mean: {"name":"collision_visualized.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/c\/2c09512ab285f9de23c8767e683eb94d.png","w":800,"h":286,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/2\/c\/2c09512ab285f9de23c8767e683eb94d"} The yellow square is the player, moving down and to the right. The player then collides with a solid wall (the red square). While colliding, down and right are still being held, but collision is cancelled on the right, and movement continues downward. Finally, when the player is below the red wall, movement down and to the right resumes as expected. Here's what I have already working with a single player and a single wall (the colors and outlines are different in the game than in the above visualization): 1#include <cmath>
2#include <iostream>
3
4#include <allegro5/allegro.h>
5#include <allegro5/allegro_primitives.h>
6
7using std::abs;
8using std::cout;
9
10void checkReturn(const bool condition) {
11
12 if (!condition) {
13
14 cout << "Error: something failed.\n";
15
16 exit(EXIT_FAILURE);
17 }
18}
19
20bool isColliding(int ax, int ay, int ah, int aw, int bx, int by, int bw, int bh) {
21
22 return (abs(ax - bx) * 2 < (aw + bw)) && (abs(ay - by) * 2 < (ah + bh));
23}
24
25int main(void) {
26
27 checkReturn(al_init());
28
29 ALLEGRO_TIMER *timer;
30 ALLEGRO_DISPLAY *display;
31 ALLEGRO_EVENT_QUEUE *event_queue;
32
33 checkReturn((timer = al_create_timer(1.0 / 60.0)));
34 checkReturn((display = al_create_display(800, 450)));
35 checkReturn((event_queue = al_create_event_queue()));
36
37 checkReturn(al_install_keyboard());
38 checkReturn(al_init_primitives_addon());
39
40 al_register_event_source(event_queue, al_get_keyboard_event_source());
41 al_register_event_source(event_queue, al_get_timer_event_source(timer));
42 al_register_event_source(event_queue, al_get_display_event_source(display));
43
44 al_start_timer(timer);
45
46 bool render = true;
47 bool running = true;
48
49 bool key[4] = {false, false, false, false};
50
51 // Player variables.
52 int p_x = 0; // X axis
53 int p_y = 0; // Y axis
54 int p_w = 64; // Width
55 int p_h = 64; // Height
56 int p_s = 4; // Speed
57
58 // Wall variables (same as above).
59 int w_x = 256;
60 int w_y = 256;
61 int w_w = 64;
62 int w_h = 64;
63
64 enum {UP = 0, DOWN, LEFT, RIGHT};
65
66 while (running) {
67
68 ALLEGRO_EVENT event;
69
70 al_wait_for_event(event_queue, &event);
71
72 if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
73
74 running = false;
75 }
76 else if (event.type == ALLEGRO_EVENT_KEY_DOWN) {
77
78 switch (event.keyboard.keycode) {
79
80 case ALLEGRO_KEY_UP:
81
82 key[UP] = true;
83 break;
84
85 case ALLEGRO_KEY_DOWN:
86
87 key[DOWN] = true;
88 break;
89
90 case ALLEGRO_KEY_LEFT:
91
92 key[LEFT] = true;
93 break;
94
95 case ALLEGRO_KEY_RIGHT:
96
97 key[RIGHT] = true;
98 break;
99
100 case ALLEGRO_KEY_ESCAPE:
101
102 running = false;
103 break;
104 }
105 }
106 else if (event.type == ALLEGRO_EVENT_KEY_UP) {
107
108 switch (event.keyboard.keycode) {
109
110 case ALLEGRO_KEY_UP:
111
112 key[UP] = false;
113 break;
114
115 case ALLEGRO_KEY_DOWN:
116
117 key[DOWN] = false;
118 break;
119
120 case ALLEGRO_KEY_LEFT:
121
122 key[LEFT] = false;
123 break;
124
125 case ALLEGRO_KEY_RIGHT:
126
127 key[RIGHT] = false;
128 break;
129 }
130 }
131 else if (event.type == ALLEGRO_EVENT_TIMER) {
132
133 render = true;
134
135 if (key[UP]) {
136
137 p_y -= p_s;
138
139 while (isColliding(p_x, p_y, p_w, p_h, w_x, w_y, w_w, w_h)) {
140
141 ++p_y;
142 }
143 }
144
145 if (key[DOWN]) {
146
147 p_y += p_s;
148
149 while (isColliding(p_x, p_y, p_w, p_h, w_x, w_y, w_w, w_h)) {
150
151 --p_y;
152 }
153 }
154
155 if (key[LEFT]) {
156
157 p_x -= p_s;
158
159 while (isColliding(p_x, p_y, p_w, p_h, w_x, w_y, w_w, w_h)) {
160
161 ++p_x;
162 }
163 }
164
165 if (key[RIGHT]) {
166
167 p_x += p_s;
168
169 while (isColliding(p_x, p_y, p_w, p_h, w_x, w_y, w_w, w_h)) {
170
171 --p_x;
172 }
173 }
174 }
175
176 if (render && al_is_event_queue_empty(event_queue)) {
177
178 render = false;
179
180 al_clear_to_color(al_map_rgb(255, 255, 255));
181
182 // Draw player rectangle and outline.
183 al_draw_filled_rectangle(p_x, p_y, p_x + p_w, p_y + p_h, al_map_rgb(255, 255, 0));
184 al_draw_rectangle(p_x + 4, p_y + 4, p_x + p_w - 4, p_y + p_h - 4, al_map_rgb(255, 0, 0), 8);
185
186 // Draw wall rectangle and outline.
187 al_draw_filled_rectangle(w_x, w_y, w_x + w_w, w_y + w_h, al_map_rgb(0, 255, 0));
188 al_draw_rectangle(w_x + 4, w_y + 4, w_x + w_w - 4, w_y + w_h - 4, al_map_rgb(0, 0, 255), 8);
189
190 al_flip_display();
191 }
192 }
193
194 // Free memory.
195 al_destroy_timer(timer);
196 al_destroy_display(display);
197 al_destroy_event_queue(event_queue);
198
199 return 0;
200}
The above works as expected. But now when I make multiples of the wall, things get a bit more complicated. 1#include <cmath>
2#include <iostream>
3
4#include <allegro5/allegro.h>
5#include <allegro5/allegro_primitives.h>
6
7using std::abs;
8using std::cout;
9
10void checkReturn(const bool condition) {
11
12 if (!condition) {
13
14 cout << "Error: something failed.\n";
15
16 exit(EXIT_FAILURE);
17 }
18}
19
20bool isColliding(int ax, int ay, int ah, int aw, int bx, int by, int bw, int bh) {
21
22 return (abs(ax - bx) * 2 < (aw + bw)) && (abs(ay - by) * 2 < (ah + bh));
23}
24
25int main(void) {
26
27 checkReturn(al_init());
28
29 ALLEGRO_TIMER *timer;
30 ALLEGRO_DISPLAY *display;
31 ALLEGRO_EVENT_QUEUE *event_queue;
32
33 checkReturn((timer = al_create_timer(1.0 / 60.0)));
34 checkReturn((display = al_create_display(800, 450)));
35 checkReturn((event_queue = al_create_event_queue()));
36
37 checkReturn(al_install_keyboard());
38 checkReturn(al_init_primitives_addon());
39
40 al_register_event_source(event_queue, al_get_keyboard_event_source());
41 al_register_event_source(event_queue, al_get_timer_event_source(timer));
42 al_register_event_source(event_queue, al_get_display_event_source(display));
43
44 al_start_timer(timer);
45
46 bool render = true;
47 bool running = true;
48
49 bool key[4] = {false, false, false, false};
50
51 // Player variables.
52 int p_x = 0; // X axis
53 int p_y = 0; // Y axis
54 int p_w = 64; // Width
55 int p_h = 64; // Height
56 int p_s = 4; // Speed
57
58 // Wall variables (same as above).
59 int number_of_walls = 1;
60 int w_x[number_of_walls] = {256};
61 int w_y[number_of_walls] = {256};
62 int w_w[number_of_walls] = {64};
63 int w_h[number_of_walls] = {64};
64
65 enum {UP = 0, DOWN, LEFT, RIGHT};
66
67 while (running) {
68
69 ALLEGRO_EVENT event;
70
71 al_wait_for_event(event_queue, &event);
72
73 if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
74
75 running = false;
76 }
77 else if (event.type == ALLEGRO_EVENT_KEY_DOWN) {
78
79 switch (event.keyboard.keycode) {
80
81 case ALLEGRO_KEY_UP:
82
83 key[UP] = true;
84 break;
85
86 case ALLEGRO_KEY_DOWN:
87
88 key[DOWN] = true;
89 break;
90
91 case ALLEGRO_KEY_LEFT:
92
93 key[LEFT] = true;
94 break;
95
96 case ALLEGRO_KEY_RIGHT:
97
98 key[RIGHT] = true;
99 break;
100
101 case ALLEGRO_KEY_ESCAPE:
102
103 running = false;
104 break;
105 }
106 }
107 else if (event.type == ALLEGRO_EVENT_KEY_UP) {
108
109 switch (event.keyboard.keycode) {
110
111 case ALLEGRO_KEY_UP:
112
113 key[UP] = false;
114 break;
115
116 case ALLEGRO_KEY_DOWN:
117
118 key[DOWN] = false;
119 break;
120
121 case ALLEGRO_KEY_LEFT:
122
123 key[LEFT] = false;
124 break;
125
126 case ALLEGRO_KEY_RIGHT:
127
128 key[RIGHT] = false;
129 break;
130 }
131 }
132 else if (event.type == ALLEGRO_EVENT_TIMER) {
133
134 render = true;
135
136 if (key[UP]) {
137
138 p_y -= p_s;
139 }
140
141 if (key[DOWN]) {
142
143 p_y += p_s;
144 }
145
146 if (key[LEFT]) {
147
148 p_x -= p_s;
149 }
150
151 if (key[RIGHT]) {
152
153 p_x += p_s;
154 }
155
156 for (int i = 0; i < number_of_walls; ++i) {
157
158 while (isColliding(p_x, p_y, p_w, p_h, w_x[i], w_y[i], w_w[i], w_h[i])) {
159
160 if (key[UP]) {
161
162 ++p_y;
163 }
164
165 if (key[DOWN]) {
166
167 --p_y;
168 }
169
170 if (key[LEFT]) {
171
172 ++p_x;
173 }
174
175 if (key[RIGHT]) {
176
177 --p_x;
178 }
179 }
180 }
181 }
182
183 if (render && al_is_event_queue_empty(event_queue)) {
184
185 render = false;
186
187 al_clear_to_color(al_map_rgb(255, 255, 255));
188
189 // Draw player rectangle and outline.
190 al_draw_filled_rectangle(p_x, p_y, p_x + p_w, p_y + p_h, al_map_rgb(255, 255, 0));
191 al_draw_rectangle(p_x + 4, p_y + 4, p_x + p_w - 4, p_y + p_h - 4, al_map_rgb(255, 0, 0), 8);
192
193 al_hold_bitmap_drawing(true);
194
195 for (int i = 0; i < number_of_walls; ++i) {
196
197 // Draw wall rectangle and outline.
198 al_draw_filled_rectangle(w_x[i], w_y[i], w_x[i] + w_w[i], w_y[i] + w_h[i], al_map_rgb(0, 255, 0));
199 al_draw_rectangle(w_x[i] + 4, w_y[i] + 4, w_x[i] + w_w[i] - 4, w_y[i] + w_h[i] - 4, al_map_rgb(0, 0, 255), 8);
200 }
201
202 al_hold_bitmap_drawing(false);
203
204 al_flip_display();
205 }
206 }
207
208 // Free memory.
209 al_destroy_timer(timer);
210 al_destroy_display(display);
211 al_destroy_event_queue(event_queue);
212
213 return 0;
214}
I made the wall variables an array. Drawing works no problem, but collision doesn't work as expected. I limited the number of walls to just 1, to test it against my earlier code, but now the player collides with a wall and just becomes "stuck"; it doesn't "slide" like I expected. What am I doing wrong? I haven't done collision like this before, so I'm surely making mistakes, but I appreciate any and all feedback and suggestions. Thank you! Edit 1#include <cmath>
2#include <iostream>
3
4#include <allegro5/allegro.h>
5#include <allegro5/allegro_primitives.h>
6
7using std::abs;
8using std::cout;
9
10void checkReturn(const bool condition) {
11
12 if (!condition) {
13
14 cout << "Error: something failed.\n";
15
16 exit(EXIT_FAILURE);
17 }
18}
19
20bool isColliding(int ax, int ay, int ah, int aw, int bx, int by, int bw, int bh) {
21
22 return (abs(ax - bx) * 2 < (aw + bw)) && (abs(ay - by) * 2 < (ah + bh));
23}
24
25int main(void) {
26
27 checkReturn(al_init());
28
29 ALLEGRO_TIMER *timer;
30 ALLEGRO_DISPLAY *display;
31 ALLEGRO_EVENT_QUEUE *event_queue;
32
33 checkReturn((timer = al_create_timer(1.0 / 60.0)));
34 checkReturn((display = al_create_display(800, 450)));
35 checkReturn((event_queue = al_create_event_queue()));
36
37 checkReturn(al_install_keyboard());
38 checkReturn(al_init_primitives_addon());
39
40 al_register_event_source(event_queue, al_get_keyboard_event_source());
41 al_register_event_source(event_queue, al_get_timer_event_source(timer));
42 al_register_event_source(event_queue, al_get_display_event_source(display));
43
44 al_start_timer(timer);
45
46 bool render = true;
47 bool running = true;
48
49 bool key[4] = {false, false, false, false};
50
51 // Player variables.
52 int p_x = 0; // X axis
53 int p_y = 0; // Y axis
54 int p_w = 64; // Width
55 int p_h = 64; // Height
56 int p_s = 4; // Speed
57
58 int number_of_walls = 2;
59
60 // Wall variables (same as above).
61 int w_x[number_of_walls] = {256, 64 * 6};
62 int w_y[number_of_walls] = {256, 256};
63 int w_w[number_of_walls] = {64, 64};
64 int w_h[number_of_walls] = {64, 64};
65
66 enum {UP = 0, DOWN, LEFT, RIGHT};
67
68 while (running) {
69
70 ALLEGRO_EVENT event;
71
72 al_wait_for_event(event_queue, &event);
73
74 if (event.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
75
76 running = false;
77 }
78 else if (event.type == ALLEGRO_EVENT_KEY_DOWN) {
79
80 switch (event.keyboard.keycode) {
81
82 case ALLEGRO_KEY_UP:
83
84 key[UP] = true;
85 break;
86
87 case ALLEGRO_KEY_DOWN:
88
89 key[DOWN] = true;
90 break;
91
92 case ALLEGRO_KEY_LEFT:
93
94 key[LEFT] = true;
95 break;
96
97 case ALLEGRO_KEY_RIGHT:
98
99 key[RIGHT] = true;
100 break;
101
102 case ALLEGRO_KEY_ESCAPE:
103
104 running = false;
105 break;
106 }
107 }
108 else if (event.type == ALLEGRO_EVENT_KEY_UP) {
109
110 switch (event.keyboard.keycode) {
111
112 case ALLEGRO_KEY_UP:
113
114 key[UP] = false;
115 break;
116
117 case ALLEGRO_KEY_DOWN:
118
119 key[DOWN] = false;
120 break;
121
122 case ALLEGRO_KEY_LEFT:
123
124 key[LEFT] = false;
125 break;
126
127 case ALLEGRO_KEY_RIGHT:
128
129 key[RIGHT] = false;
130 break;
131 }
132 }
133 else if (event.type == ALLEGRO_EVENT_TIMER) {
134
135 render = true;
136
137 if (key[UP]) {
138
139 p_y -= p_s;
140
141 for (int i = 0; i < number_of_walls; ++i) {
142
143 while (isColliding(p_x, p_y, p_w, p_h, w_x[i], w_y[i], w_w[i], w_h[i])) {
144
145 ++p_y;
146 }
147 }
148 }
149
150 if (key[DOWN]) {
151
152 p_y += p_s;
153
154 for (int i = 0; i < number_of_walls; ++i) {
155
156 while (isColliding(p_x, p_y, p_w, p_h, w_x[i], w_y[i], w_w[i], w_h[i])) {
157
158 --p_y;
159 }
160 }
161 }
162
163 if (key[LEFT]) {
164
165 p_x -= p_s;
166
167 for (int i = 0; i < number_of_walls; ++i) {
168
169 while (isColliding(p_x, p_y, p_w, p_h, w_x[i], w_y[i], w_w[i], w_h[i])) {
170
171 ++p_x;
172 }
173 }
174 }
175
176 if (key[RIGHT]) {
177
178 p_x += p_s;
179
180 for (int i = 0; i < number_of_walls; ++i) {
181
182 while (isColliding(p_x, p_y, p_w, p_h, w_x[i], w_y[i], w_w[i], w_h[i])) {
183
184 --p_x;
185 }
186 }
187 }
188 }
189
190 if (render && al_is_event_queue_empty(event_queue)) {
191
192 render = false;
193
194 al_clear_to_color(al_map_rgb(255, 255, 255));
195
196 // Draw player rectangle and outline.
197 al_draw_filled_rectangle(p_x, p_y, p_x + p_w, p_y + p_h, al_map_rgb(255, 255, 0));
198 al_draw_rectangle(p_x + 4, p_y + 4, p_x + p_w - 4, p_y + p_h - 4, al_map_rgb(255, 0, 0), 8);
199
200 for (int i = 0; i < number_of_walls; ++i) {
201
202 // Draw wall rectangle and outline.
203 al_draw_filled_rectangle(w_x[i], w_y[i], w_x[i] + w_w[i], w_y[i] + w_h[i], al_map_rgb(0, 255, 0));
204 al_draw_rectangle(w_x[i] + 4, w_y[i] + 4, w_x[i] + w_w[i] - 4, w_y[i] + w_h[i] - 4, al_map_rgb(0, 0, 255), 8);
205 }
206
207 al_flip_display();
208 }
209 }
210
211 // Free memory.
212 al_destroy_timer(timer);
213 al_destroy_display(display);
214 al_destroy_event_queue(event_queue);
215
216 return 0;
217}
It's not pretty, but it works. I've solved my own question now, but if you know of a way to improve the above code to make it more succinct, I'd appreciate you telling me. Thanks!
|
|