// PhilVaz Ship Demo // Moving and Rotating Ship around the screen // Full Screen GDI demo // May 3, 2003 // INCLUDES /////////////////////////////////////////////// #define WIN32_LEAN_AND_MEAN #include // include all the windows headers #include // include useful macros #include // for rand functions #include #include // DEFINES //////////////////////////////////////////////// // defines for windows #define WINDOW_CLASS_NAME "WINCLASS1" #define WINDOW_WIDTH 800 // size of game window #define WINDOW_HEIGHT 600 #define GAME_SPEED 30 // speed of game (increase to go slower) // MACROS ///////////////////////////////////////////////// #define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0) #define KEYUP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1) // GLOBALS //////////////////////////////////////////////// HWND game_window = NULL; // global game window handle HINSTANCE game_instance = NULL; // global game instance handle HDC game_dc = NULL; // global device context (for GDI) handle DEVMODE game_screen; // global for full screen mode // global pen and brush HPEN white_pen = CreatePen(PS_SOLID, 1, RGB(255,255,255)); HBRUSH white_brush = CreateSolidBrush(RGB(255,255,255)); HPEN black_pen = CreatePen(PS_SOLID, 1, RGB(0,0,0)); HBRUSH black_brush = CreateSolidBrush(RGB(0,0,0)); POINT points[4]; // for temp erase/display of ship points float THRUST_SPEED = .75f; // experiment with this thrust value float THRUST_FRICTION = .1f; // experiment with this friction value // // X and Y ship shape (4 points, 32 angle positions) // relative to center point (24 x 24 square total) // int xship[4][32] = { {0, 2, 4, 6, 8, 9, 10, 11, 11, 11, 10, 9, 8, 6, 4, 2, 0, -2, -4, -6, -8, -9, -10, -11, -11, -11, -10, -9, -8, -6, -4, -2}, {-7, -9, -11, -12, -13, -14, -14, -13, -12, -10, -8, -6, -4, -1, 2, 5, 7, 9, 11, 12, 13, 14, 14, 13, 12, 10, 8, 6, 4, 1, -2, -5}, {0, -2, -3, -5, -6, -7, -8, -9, -9, -9, -8, -7, -6, -5, -3, -2, 0, 2, 3, 5, 6, 7, 8, 9, 9, 9, 8, 7, 6, 5, 3, 2}, {8, 6, 3, 0, -3, -6, -8, -10, -12, -13, -14, -14, -14, -13, -12, -10, -8, -6, -3, 0, 3, 6, 8, 10, 12, 13, 14, 14, 14, 13, 12, 10} }; int yship[4][32] = { {-11, -11, -10, -9, -8, -6, -4, -2, 0, 2, 4, 6, 8, 9, 10, 11, 11, 11, 10, 9, 8, 6, 4, 2, 0, -2, -4, -6, -8, -9, -10, -11}, {12, 10, 8, 6, 4, 1, -2, -5, -7, -9, -11, -12, -13, -14, -14, -13, -12, -10, -8, -6, -4, -1, 2, 5, 7, 9, 11, 12, 13, 14, 14, 13}, {9, 9, 8, 7, 6, 5, 3, 2, 0, -2, -3, -5, -6, -7, -8, -9, -9, -9, -8, -7, -6, -5, -3, -2, 0, 2, 3, 5, 6, 7, 8, 9}, {12, 13, 14, 14, 14, 13, 12, 10, 8, 6, 3, 0, -3, -6, -8, -10, -12, -13, -14, -14, -14, -13, -12, -10, -8, -6, -3, 0, 3, 6, 8, 10} }; // // THRUST (move) and BULLET (shoot) direction X (sin) and Y (-cos) pre-calculated // double xdir[32] = {0, 0.19509032201613, 0.38268343236509, 0.5555702330196, 0.70710678118655, 0.83146961230255, 0.92387953251129, 0.98078528040323, 1, 0.98078528040323, 0.92387953251129, 0.83146961230255, 0.70710678118655, 0.5555702330196, 0.38268343236509, 0.19509032201613, 0, -0.19509032201613, -0.38268343236509, -0.5555702330196, -0.70710678118655, -0.83146961230255, -0.92387953251129, -0.98078528040323, -1, -0.98078528040323, -0.92387953251129, -0.83146961230255, -0.70710678118655, -0.5555702330196, -0.38268343236509, -0.19509032201613}; double ydir[32] = {-1, -0.98078528040323, -0.92387953251129, -0.83146961230255, -0.70710678118655, -0.5555702330196, -0.38268343236509, -0.19509032201613, 0, 0.19509032201613, 0.38268343236509, 0.5555702330196, 0.70710678118655, 0.83146961230255, 0.92387953251129, 0.98078528040323, 1, 0.98078528040323, 0.92387953251129, 0.83146961230255, 0.70710678118655, 0.5555702330196, 0.38268343236509, 0.19509032201613, 0, -0.19509032201613, -0.38268343236509, -0.5555702330196, -0.70710678118655, -0.83146961230255, -0.92387953251129, -0.98078528040323}; typedef struct { float xcenter; // x center of ship float ycenter; // y center of ship float xmove; // x movement direction (x vector) float ymove; // y movement direction (y vector) int angle; // angle rotate position (0 to 31) } SHIP_STRUCT; SHIP_STRUCT Ship; // declare ship struct char text[80]; // for display text output // FUNCTIONS //////////////////////////////////////////////////////////// void GameInit(); void GameMain(); void GameQuit(); void SetPoints(int); // set the points for polygon erase/draw void RotateShip(int); // rotate ship polygon int number of degrees void MoveShip(); // move (update) ship once, check for thrust void DisplayPos(); // diplay position and angle of ship // WINPROC ///////////////////////////////////////////////////////////// LRESULT CALLBACK WinProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { // this is the main message handler of the system HDC hdc; // handle to a device context PAINTSTRUCT ps; // used in WM_PAINT switch(msg) // what is the message { case WM_CREATE: { // do initialization stuff here return(0); // return success } break; case WM_PAINT: { hdc = BeginPaint(hwnd, &ps); // validate the window EndPaint(hwnd, &ps); return(0); // return success } break; case WM_DESTROY: { PostQuitMessage(0); // kill the application, sends a WM_QUIT message return(0); // return success } break; default:break; } // end switch // process default messages return (DefWindowProc(hwnd, msg, wparam, lparam)); } // end WinProc // WINMAIN //////////////////////////////////////////////// int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow) { WNDCLASSEX winclass; // this will hold the class we create HWND hwnd; // generic window handle MSG msg; // generic message // first fill in the window class structure winclass.cbSize = sizeof(WNDCLASSEX); winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW; winclass.lpfnWndProc = WinProc; winclass.cbClsExtra = 0; winclass.cbWndExtra = 0; winclass.hInstance = hinstance; winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); winclass.hCursor = LoadCursor(NULL, IDC_ARROW); winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); winclass.lpszMenuName = NULL; winclass.lpszClassName = WINDOW_CLASS_NAME; // save the game instance handle game_instance = hinstance; // register the window class if (!RegisterClassEx(&winclass)) return(0); // create the window if (!(hwnd = CreateWindowEx(NULL, // extended style WINDOW_CLASS_NAME, // class "Ship Demo", // title WS_POPUP | WS_VISIBLE, // use POPUP for full screen 0,0, // initial game window x,y WINDOW_WIDTH, // initial game width WINDOW_HEIGHT, // initial game height NULL, // handle to parent NULL, // handle to menu hinstance, // instance of this application NULL))) // extra creation parms return(0); // save the game window handle game_window = hwnd; GameInit(); // game initialization function called here // enter main event loop using PeekMessage() to retrieve messages while(TRUE) { // get initial tick count to keep game speed constant DWORD start_tick = GetTickCount(); // is there a message in queue, if so get it if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { // test if this is a quit if (msg.message == WM_QUIT) break; // translate any accelerator keys TranslateMessage(&msg); // send the message to WinProc DispatchMessage(&msg); } // end if GameMain(); // game main processing function called here // check for key and send quit game if (KEYDOWN(VK_ESCAPE)) SendMessage (hwnd, WM_CLOSE, 0, 0); // wait until we hit correct game speed frame rate while ((GetTickCount() - start_tick) < GAME_SPEED); } // end while GameQuit(); // game quit function and clean up before exit called here return(msg.wParam); // return to Windows } // end WinMain // BEGIN GAME CODE //////////////////////////////////////// /////////////////////////////////////////////////////////// // // GAME INITIALIZATION // /////////////////////////////////////////////////////////// void GameInit() { srand(GetTickCount()); // seed the random numbers // temporary change to full screen mode ZeroMemory(&game_screen, sizeof(game_screen)); // clear out size of DEVMODE struct game_screen.dmSize = sizeof(game_screen); game_screen.dmPelsWidth = WINDOW_WIDTH; game_screen.dmPelsHeight = WINDOW_HEIGHT; game_screen.dmBitsPerPel = 16; game_screen.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL; ChangeDisplaySettings(&game_screen, CDS_FULLSCREEN); game_dc = GetDC(game_window); // get the GDI device context // set initial position and speed of ship Ship.xcenter = WINDOW_WIDTH / 2.0; // set x center Ship.ycenter = WINDOW_HEIGHT / 2.0; // set y center Ship.xmove = 0.0; // initial x movement is 0 Ship.ymove = 0.0; // initial y movement is 0 Ship.angle = 0; // point up to start SelectObject(game_dc, white_pen); // white outline pen SelectObject(game_dc, black_brush); // black fill brush SetPoints(Ship.angle); Polygon(game_dc, points, 4); DisplayPos(); } // END OF GameInit /////////////////////////////////////////////////////////// // // GAME MAIN LOOP AND PROCESSING // /////////////////////////////////////////////////////////// void GameMain() { // check for rotate LEFT or RIGHT if (KEYDOWN(VK_RIGHT)) RotateShip(1); if (KEYDOWN(VK_LEFT)) RotateShip(-1); MoveShip(); // update ship movement once } // END OF GameMain /////////////////////////////////////////////////////////// // // GAME QUIT AND CLEAN UP // /////////////////////////////////////////////////////////// void GameQuit() { // delete the pens and brushes DeleteObject(white_pen); DeleteObject(white_brush); DeleteObject(black_pen); DeleteObject(black_brush); // release the device context (for GDI) from the game window ReleaseDC(game_window, game_dc); // return to original display settings ChangeDisplaySettings(NULL,NULL); } // END OF GameQuit // // set the points for draw/erase polygon ship // void SetPoints(int deg) { int i; // for count loop for (i = 0; i < 4; i++) { points[i].x = (int)Ship.xcenter + xship[i][deg]; points[i].y = (int)Ship.ycenter + yship[i][deg]; } } //////////////////////////////////////////////////////////////////// // // Rotate Ship specified number of degrees from original position // //////////////////////////////////////////////////////////////////// void RotateShip(int deg) { int old_angle; old_angle = Ship.angle; // save current ship angle Ship.angle += deg; // increment/decrement new angle by given degrees // check if out of range if (Ship.angle > 31) Ship.angle -= 32; if (Ship.angle < 0) Ship.angle += 32; DisplayPos(); // erase the old ship angle position SelectObject(game_dc, black_pen); // black outline pen SetPoints(old_angle); // set points of ship with old angle Polygon(game_dc, points, 4); // erase the old ship // draw the new ship angle position SelectObject(game_dc, white_pen); // white outline pen SetPoints(Ship.angle); // set points of ship with new angle Polygon(game_dc, points, 4); // draw the new ship } // END OF RotateShip /////////////////////////////////////////////////////// // // Move Ship once and check for thrust // if no thrust, slow ship down /////////////////////////////////////////////////////// void MoveShip() { if (Ship.xmove != 0.0 || Ship.ymove != 0.0) { // the ship is moving, so erase and redraw // erase the old ship position SelectObject(game_dc, black_pen); // black outline pen SetPoints(Ship.angle); // set points of ship at ship angle Polygon(game_dc, points, 4); // erase the old ship Ship.xcenter += Ship.xmove; Ship.ycenter += Ship.ymove; // check if out of range on x or y axis, if so wrap around if (Ship.xcenter < 50 || Ship.xcenter > (WINDOW_WIDTH - 50)) Ship.xcenter = WINDOW_WIDTH - Ship.xcenter; if (Ship.ycenter < 50 || Ship.ycenter > (WINDOW_HEIGHT - 50)) Ship.ycenter = WINDOW_HEIGHT - Ship.ycenter; // draw the new ship position SelectObject(game_dc, white_pen); // white outline pen SetPoints(Ship.angle); // set points of ship at ship angle Polygon(game_dc, points, 4); // draw the new ship DisplayPos(); // redisplay position } if (KEYDOWN(VK_UP)) // thrust is pressed -- speed ship up { Ship.xmove += (float)xdir[Ship.angle] * THRUST_SPEED; Ship.ymove += (float)ydir[Ship.angle] * THRUST_SPEED; if (Ship.xmove > 20.0) Ship.xmove = 20.0; if (Ship.xmove < -20.0) Ship.xmove = -20.0; if (Ship.ymove > 20.0) Ship.ymove = 20.0; if (Ship.ymove < -20.0) Ship.ymove = -20.0; } else // no thrust -- use friction to slow ship down { if (Ship.xmove > 0) Ship.xmove -= THRUST_FRICTION; if (Ship.xmove < 0) Ship.xmove += THRUST_FRICTION; if (Ship.ymove > 0) Ship.ymove -= THRUST_FRICTION; if (Ship.ymove < 0) Ship.ymove += THRUST_FRICTION; if (Ship.xmove < .1 && Ship.xmove > -.1) { Ship.xmove = 0.0; DisplayPos(); } if (Ship.ymove < .1 && Ship.ymove > -.1) { Ship.ymove = 0.0; DisplayPos(); } } } // END OF MoveShip void DisplayPos() { SetTextColor(game_dc, RGB(255,255,255)); // white text color SetBkColor(game_dc, RGB(0,0,0)); // black background sprintf(text,"Angle Pos = %d ", Ship.angle); TextOut(game_dc, 1, 580, text, strlen(text)); sprintf(text,"Pos X = %g ", Ship.xcenter); TextOut(game_dc, 110, 580, text, strlen(text)); sprintf(text,"Pos Y = %g ", Ship.ycenter); TextOut(game_dc, 220, 580, text, strlen(text)); sprintf(text,"Speed X = %g ", Ship.xmove); TextOut(game_dc, 330, 580, text, strlen(text)); sprintf(text,"Speed Y = %g ", Ship.ymove); TextOut(game_dc, 440, 580, text, strlen(text)); sprintf(text,"Left/Right = Rotate, Up = Thrust"); TextOut(game_dc, 550, 580, text, strlen(text)); } // END OF DisplayPos // END GAME CODE //////////////////////////////////////////