slopecollide.cpp (9958B)
1 // Physics slope experiment with lash_game_sprite 2 3 #include "SDL.h" 4 #include "SDL_gfxPrimitives.h" 5 #include "../lash_game_sprite.h" 6 #include <math.h> 7 #include <stdio.h> 8 #include <string.h> 9 10 #define SCREEN_WIDTH 800 11 #define SCREEN_HEIGHT 600 12 #define BALL_RADIUS 30 13 #define BPP 32 14 #define FPS 60 15 #define GRAVITY 9.8 16 #define PIXELS_METER 100 17 #define COLLISION_CUTOFF 0.3 18 #define BOUNCE 0.25 19 20 /// \todo bounce was never implemented in this test 21 22 int applyBounce() { 23 24 return 0; 25 } 26 27 28 29 int main(int argc, char *argv[]) { 30 31 int i; 32 33 unsigned long frametime; 34 unsigned long lastframetime; 35 unsigned long startframetime; 36 float minframetime = 1000 / (float)FPS; 37 int run = 1; 38 int state = 0; 39 40 Lash_Sprite_2D_Simple *sprite; 41 42 SDL_Event event; 43 SDL_Surface *screen; 44 45 SDL_Rect screen_r; 46 SDL_Rect ball_r; 47 48 uint32_t screen_c; 49 uint32_t ball_c; 50 uint32_t slope_c; 51 52 uint32_t ball_slope_offset_y; 53 float p_ball_force_net; 54 float p_ball_force_norm; 55 float slope_friction; 56 57 float slope_angle_1, slope_angle_2, slope_gap; 58 float momentum_spill; 59 float bounce_vel; 60 61 uint8_t slope_v_size = 5; 62 Sint16 *slope_v_x; 63 Sint16 *slope_v_y; 64 65 float ballxinit; 66 float ballyinit_1, ballyinit_2; 67 float monitoroldv, monitorv, monitorrv, monitora, monitorra; 68 69 char debugstring[255]; 70 unsigned int framesrun; 71 unsigned long timestart; 72 unsigned long monitorframesturn; 73 74 float *debugframevels; 75 float *debugframevelrads; 76 77 if (argc < 4) { 78 printf("Usage: %s slope_angle_left_in_degrees slope_angle_right_in_degrees ball_mass_in_grammes slope_friction_coeffcient\n", argv[0]); 79 return 1; 80 } 81 82 83 sprite = new Lash_Sprite_2D_Simple(BALL_RADIUS * 2, BALL_RADIUS * 2, 0.f, 0.f); 84 sprite->setMass(atof(argv[3])); 85 86 //radians 87 slope_angle_1 = (atof(argv[1]) * M_PI) / 180; 88 slope_angle_2 = (atof(argv[2]) * M_PI) / 180; 89 slope_gap = M_PI - slope_angle_1 - slope_angle_2; 90 91 slope_friction = atof(argv[4]); 92 if (!slope_friction) 93 slope_friction = 0.f; 94 95 SDL_Init(SDL_INIT_VIDEO); 96 screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, BPP, SDL_HWSURFACE|SDL_DOUBLEBUF); 97 98 if (screen == NULL) 99 return 1; 100 101 slope_v_x = (Sint16*)malloc(sizeof(Sint16)*slope_v_size); 102 slope_v_y = (Sint16*)malloc(sizeof(Sint16)*slope_v_size); 103 104 if (slope_v_x == NULL || slope_v_y == NULL) 105 return 1; 106 107 debugframevels = (float*)malloc(sizeof(float) * 10000); // should be ample 108 debugframevelrads = (float*)malloc(sizeof(float) * 10000); 109 110 monitorframesturn = 0; 111 112 screen_r.x = 0; 113 screen_r.y = 0; 114 screen_r.w = SCREEN_WIDTH; 115 screen_r.h = SCREEN_HEIGHT; 116 117 ball_r.x = 0; 118 ball_r.y = 0; 119 ball_r.w = BALL_RADIUS * 2; 120 ball_r.h = BALL_RADIUS * 2; 121 122 screen_c = SDL_MapRGBA(screen->format, 255, 255, 255, 255); 123 slope_c = SDL_MapRGBA(screen->format, 0, 0, 255, 255); 124 ball_c = SDL_MapRGBA(screen->format, 255, 0, 0, 255); 125 126 *slope_v_y = SCREEN_HEIGHT; 127 *(slope_v_y + 1) = (SCREEN_HEIGHT / 2) - ((SCREEN_WIDTH / 2) * tan(slope_angle_1)); 128 *(slope_v_y + 2) = SCREEN_HEIGHT / 2; 129 *(slope_v_y + 3) = (SCREEN_HEIGHT / 2) - ((SCREEN_WIDTH / 2) * tan(slope_angle_2)); 130 *(slope_v_y + 4) = SCREEN_HEIGHT; 131 132 *slope_v_x = 0; 133 *(slope_v_x + 1) = 0; 134 *(slope_v_x + 2) = SCREEN_WIDTH / 2; 135 *(slope_v_x + 3) = SCREEN_WIDTH; 136 *(slope_v_x + 4) = SCREEN_WIDTH; 137 138 ballxinit = BALL_RADIUS; 139 ballyinit_1 = (BALL_RADIUS / cos(slope_angle_1)); 140 ballyinit_2 = (BALL_RADIUS / cos(slope_angle_2)); 141 sprite->setY((int)((tan(slope_angle_1) * BALL_RADIUS) + *(slope_v_y + 1) - ballyinit_1)); 142 sprite->setX((int)ballxinit); 143 144 p_ball_force_net = sin(slope_angle_1) * (GRAVITY * sprite->getMass()); 145 p_ball_force_norm = cos(slope_angle_1) * (GRAVITY * sprite->getMass()); 146 if (p_ball_force_net < 0.f) 147 p_ball_force_net = 0.f; 148 149 // divide by FPS twice, as it's m/s² 150 sprite->setAcceleration((((p_ball_force_net / sprite->getMass()) * PIXELS_METER) / FPS) / FPS, -slope_angle_1); 151 // resistance is friction translated to m/s 152 sprite->setResistance(((p_ball_force_norm / sprite->getMass()) * slope_friction) / FPS); 153 154 float tmpav, tmpar; 155 sprite->getAcceleration(&tmpav, &tmpar, 0); 156 printf("# FPS: %d\n# Pixels per meter: %d\n# Sprite: x %.1fpx, y %.1fpx, mass %.2fkg\n# Slope left: angle %f, resistance %fm/s, distance %f\n# Slope right: angle %f, distance %f\n# Object force %.2fN, accel: %f m/s/s | %f px/frame, radians %f\n", FPS, PIXELS_METER, sprite->getX(), sprite->getY(), sprite->getMass(), slope_angle_1, sprite->getResistance(), (SCREEN_WIDTH / 2) / cos(slope_angle_1), slope_angle_2, (SCREEN_WIDTH / 2) / cos(slope_angle_2), p_ball_force_net, ((float)p_ball_force_net / sprite->getMass()), tmpav * FPS, tmpar); 157 158 SDL_WM_SetCaption("Slope Physics Test", 0); 159 160 while (run) { 161 162 frametime = SDL_GetTicks(); 163 164 while (SDL_PollEvent(&event)) { 165 switch(event.type) { 166 case SDL_KEYDOWN: 167 if (event.key.keysym.sym == SDLK_ESCAPE) { 168 run = 0; 169 } else if (event.key.keysym.sym == SDLK_SPACE) { 170 sprite->walkRight(); 171 framesrun = 0; 172 timestart = frametime; 173 } else if (event.key.keysym.sym == SDLK_r) { 174 sprite->stop(); 175 sprite->setVel(0.f, 0.f); 176 sprite->setY((int)((tan(slope_angle_1) * BALL_RADIUS) + *(slope_v_y + 1) - ballyinit_1)); 177 sprite->setX((int)ballxinit); 178 sprite->setAcceleration((((p_ball_force_net / sprite->getMass()) * PIXELS_METER) / FPS) / FPS, -slope_angle_1); 179 state = 0; 180 } else if (event.key.keysym.sym == SDLK_s) { 181 state = 2; 182 } 183 break; 184 } 185 186 } 187 188 if (sprite->isWalking(0)) { 189 190 sprite->move(); 191 sprite->accelerate(); 192 193 if (state != 2) { 194 float newaccelangle; 195 float newvelangle; 196 float newymod; 197 const char *directionstring = "ltr\0"; 198 int newstate; 199 int newdirection = 0; 200 201 // check if we're to land 202 if (sprite->isBouncing()) { 203 if (state == 1) { 204 newymod = ballyinit_1 - ballyinit_2; 205 newvelangle = slope_angle_2; 206 } 207 else { 208 newymod = ballyinit_2 - ballyinit_1; 209 newvelangle = -slope_angle_1 + M_PI; 210 } 211 if (ball->getY() <= tan(newvelangle) * abs(ball->getX() - SCREEN_WIDTH / 2) - newymod) { 212 ball->finish(); 213 sprite->setAcceleration((((p_ball_force_net / sprite->getMass()) * PIXELS_METER) / FPS) / FPS, newaccelangle); 214 sprite->setVel(); 215 } 216 } 217 218 // enter right slope 219 if (sprite->getX() > SCREEN_WIDTH / 2 && state != 1) { 220 221 newaccelangle = slope_angle_2 - M_PI; 222 newvelangle = slope_angle_2; 223 newymod = ballyinit_1 - ballyinit_2; 224 directionstring = "ltr\0"; 225 newstate = 1; 226 newdirection = 1; 227 228 // enter left slope (from right slope) 229 } else if (sprite->getX() < SCREEN_WIDTH / 2 && state != 0) { 230 231 newaccelangle = -slope_angle_1; 232 newvelangle = -slope_angle_1 - M_PI; 233 newymod = ballyinit_2 - ballyinit_1; 234 directionstring = "rtl\0"; 235 newstate = 0; 236 newdirection = 1; 237 238 } 239 240 if (newdirection) { 241 momentum_spill = sprite->getVel() * sin(slope_gap); 242 monitoroldv = monitorv; 243 sprite->getVel(&monitorv, &monitorrv); 244 sprite->getAcceleration(&monitora, &monitorra, 0); 245 sprite->setVel(sprite->getVel() - momentum_spill, newvelangle); 246 247 if (sprite->getVel() < COLLISION_CUTOFF - (COLLISION_CUTOFF * slope_friction)) { 248 sprite->stop(); 249 } else { 250 251 bounce_vel = momentum_spill * BOUNCE; 252 if (applyBounce(sprite, momentum_spill, monitoroldv)) { 253 254 } else { 255 sprite->setAcceleration((((p_ball_force_net / sprite->getMass()) * PIXELS_METER) / FPS) / FPS, newaccelangle); 256 } 257 258 if (bounce_vel * monitoroldv > 1.0) { 259 sprite->bounce(); 260 sprite->addVel(bounce_vel, monitorrv + M_PI); 261 sprite->setAcceleration(((GRAVITY * PIXELS_METER) / FPS) / FPS, 3 * (M_PI_2)); 262 } else { 263 264 } 265 266 } 267 268 printf("# %s spill %f from vel %f (%f post-accel) new vel %f a %f (%f at %d frames)\n", directionstring, momentum_spill, monitoroldv, monitorv, sprite->getVel(), monitora, monitorra, framesrun); 269 270 sprite->setY(sprite->getY() + newymod); 271 monitorframesturn = framesrun; 272 state = newstate; 273 } 274 } // if state != 2 275 276 277 // if screen boundaries are hit 278 if (sprite->getY() >= SCREEN_HEIGHT - BALL_RADIUS || sprite->getX() >= SCREEN_WIDTH - BALL_RADIUS || sprite->getVel() == 0.f) { 279 if (sprite->getY() >= SCREEN_HEIGHT - BALL_RADIUS || sprite->getX() >= SCREEN_WIDTH - BALL_RADIUS) { 280 sprite->setX(SCREEN_WIDTH - BALL_RADIUS); 281 } 282 sprite->stop(); 283 284 } 285 286 if (debugframevels != NULL) { 287 sprite->getVel(&tmpav, &tmpar); 288 *(debugframevels + framesrun) = tmpav; 289 *(debugframevelrads + framesrun) = tmpar; 290 } 291 292 // if we're done 293 if (!sprite->isWalking(0) || state == 2) { 294 if (state == 2) 295 printf("# ABORTED!\n"); 296 printf("# End velocity: %f px/frame (%f m/s) after %.2f secs, %d frames\n", *(debugframevels + framesrun - 1), ((*(debugframevels + framesrun - 1) / PIXELS_METER) * FPS), (frametime - timestart) / (float)1000, framesrun); 297 298 sprite->stop(); 299 300 if (debugframevels != NULL) { 301 for (i = 0; i <= framesrun; i++) { 302 //printf("Frame %d: %f @ %f\n", i, *(debugframevels+i), (*(debugframevelrads+i) / M_PI) * 180); 303 printf("%d\t%f\n", i, *(debugframevels+i), (*(debugframevelrads+i) / M_PI) * 180); 304 } 305 } 306 } 307 308 framesrun++; 309 310 } 311 312 sprintf(debugstring, "Frametime %lu", frametime - lastframetime); 313 SDL_FillRect(screen, &screen_r, screen_c); 314 filledPolygonColor(screen, slope_v_x, slope_v_y, slope_v_size, 0x0000ffff); 315 filledCircleColor(screen, sprite->getXPixels(), sprite->getYPixels(), BALL_RADIUS, 0xff0000ff); 316 stringColor(screen, 10, SCREEN_HEIGHT - 15, debugstring, 0xffffff99); 317 SDL_Flip(screen); 318 319 lastframetime = frametime; 320 321 //if (lastframetime - frametime < minframetime) 322 //SDL_Delay (minframetime - (SDL_GetTicks () - frametime)); 323 SDL_Delay(minframetime); 324 325 326 } 327 328 delete(sprite); 329 SDL_FreeSurface(screen); 330 SDL_Quit(); 331 332 return 0; 333 }