// File: lunar.cxx // Written by Michael Main, Sep 15, 2005 // This is a demonstration game. The user tries to land a lunar lander. #include // Provides bgi graphics #include // Provides toupper function #include // Provides M_PI #include // Provides cout #include "sprite.h" // Provides Sprite class using namespace std; using namespace edu_colorado_main_intro; /////////////////////////////////////////////////////////////////////////////// // Physical constants const double FORCE_MAIN_THRUSTER = 30000; // kg m/s^2 const double FORCE_SIDE_THRUSTER = 100; // kg rad/s^2 const double GRAVITY = -9.8/6; // m/s^2 const double LANDER_MASS = 1000; // kg const double STARTING_FUEL = 250; // kg const double MAIN_FUEL_BURN_RATE = 1; // kg/sec const double SIDE_FUEL_BURN_RATE = 0.01; // kg/sec const int DELAY = 100; // ms const double DELTA_T = DELAY/1000.0; // s // Graphical constants const double SCALE = 0.65; const int WINDOW_WIDTH = static_cast(851*SCALE); const int WINDOW_HEIGHT = static_cast(608*SCALE); const int GROUND_ZERO_Y = static_cast(566*SCALE); const int ROCKET_SIZE = static_cast(100*SCALE); const double PIXELS_PER_METER = 10*SCALE; const int BACKGROUND_BYTES = 2069656; const int IMAGES = 16; /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // Set or modify the variables that keep track of the state of the lander void update_accelerations( double& ax, double& ay, double& ar, bool main, bool left, bool right, double mass, double r ); void update_fuel(double& fuel, bool& main, bool& left, bool& right); // Allow the user to change the controls void change_one_control(bool& thruster, int event, bool action); void change_controls(bool& main, bool& left, bool& right); // Draw the background image void draw_background( ); // Write some of the state data to the screen void draw_stats( double vx, double vy, // Current velocities (m/s) double vr, // Current rotational velocitie (radians/s) double fuel, // Remaining fuel (kg) double t // Current time (s) ); /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// //The main program int main( ) { bool main = false, left = false, right = false; // Are the thrusters on? double fuel = STARTING_FUEL; // Remaining fuel (kg) double vx = 0, vy = 0; // Velocity (m/s) double r = M_PI/2; // Rotational pos radians double vr = 0; // Rotational radians/s double t = 0; // Time since start double ax, ay; // Accelerations (m/s^2) double ar; // Rotational acc (rad/s^2) Sprite lander; // Our lander initwindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Mario Lander", 0, 0, true); lander.set_images(static_cast(SCALE*100), "rocket", 8, IMAGES); lander.set_drawing_transformations( PIXELS_PER_METER, WINDOW_WIDTH/2.0, -PIXELS_PER_METER, GROUND_ZERO_Y ); lander.set_drawing_mode(OR_PUT); lander.set_position( 0, (static_cast(GROUND_ZERO_Y-100)/PIXELS_PER_METER) ); lander.set_angle_in_radians(M_PI/2); do { // Adjust the control variables: main, left, and right change_controls(main, left, right); lander.change_frame((main?4:0)+(left?2:0)+(right?1:0)); // Adjust the physical variables: positions, velocities, fuel, t update_fuel(fuel, main, left, right); update_accelerations(ax, ay, ar, main, left, right, LANDER_MASS+fuel, r); lander.move_horizontal(vx*DELTA_T); lander.move_vertical(vy*DELTA_T); lander.rotate(vr*DELTA_T); vx += ax*DELTA_T; vy += ay*DELTA_T; vr += ar*DELTA_T; t += DELTA_T; // Redraw the screen and delay draw_background( ); draw_stats(vx,vy,vr,fuel,t); lander.draw( ); swapbuffers( ); delay(DELAY); } while(true); return 0; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void change_controls(bool& main, bool& left, bool& right) { change_one_control(left, WM_LBUTTONDOWN, true); change_one_control(left, WM_LBUTTONUP, false); change_one_control(right, WM_RBUTTONDOWN, true); change_one_control(right, WM_RBUTTONUP, false); if (kbhit( )) { switch (toupper(getch( ))) { case ' ': main = !main; break; case 'Q': exit(0); default: // Any other key pauses until another key is pressed. getch( ); break; } } } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void change_one_control(bool& thruster, int event, bool action) { if (ismouseclick(event)) { thruster = action; clearmouseclick(event); } } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void draw_background( ) { static bool first = true; static char buffer[BACKGROUND_BYTES]; if (first) { readimagefile("bg.bmp", 0, 0, WINDOW_WIDTH-1, WINDOW_HEIGHT-1); getimage(0, 0, WINDOW_WIDTH-1, WINDOW_HEIGHT-1, buffer); first = false; } else { putimage(0, 0, buffer, COPY_PUT); } setbkcolor(getpixel(0,0)); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void draw_stats( double vx, double vy, // Current velocities (m/s) double vr, // Current rotational velocitie (radians/s) double fuel, // Remaining fuel (kg) double t // Current time (s) ) { return; // Drawing all the stats crashes winbgim with the old (Fall 2005) // versiion of winbgim. bgiout << "vr (rad/s): " << vr << endl; bgiout << "Fuel (kg): " << static_cast(fuel) << endl; bgiout << "vx (m/s): " << static_cast(vx) << endl; bgiout << "vy (m/s): " << static_cast(vy) << endl; bgiout << "Time: " << static_cast(t) << endl; outstreamxy(10, 10); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void update_accelerations( double& ax, double& ay, double& ar, bool main, bool left, bool right, double mass, double r ) { double a_from_thruster; // Accelerations when no thrusters are on: ax = ar = 0; ay = GRAVITY; if (main) { // The x and y accelerations are modified by the main thruster: a_from_thruster = FORCE_MAIN_THRUSTER/mass; ax += a_from_thruster * cos(r); ay += a_from_thruster * sin(r); } if (left) ar -= FORCE_SIDE_THRUSTER/mass; if (right) ar += FORCE_SIDE_THRUSTER/mass; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// void update_fuel(double& fuel, bool& main, bool& left, bool& right) // Adjust the current amount of fuel based on which thrusters are on. // If the fuel drops to zero, then the thrusters are turned off. { if (main) fuel -= MAIN_FUEL_BURN_RATE * DELTA_T; if (left) fuel -= SIDE_FUEL_BURN_RATE * DELTA_T; if (right) fuel -= SIDE_FUEL_BURN_RATE * DELTA_T; if (fuel <= 0) { fuel = 0; main = left = right = false; } } ///////////////////////////////////////////////////////////////////////////////