Created
August 4, 2012 13:37
-
-
Save dlivingstone/3257815 to your computer and use it in GitHub Desktop.
GED: SDL base code. Requires glew, SDL and SDL_ttf. This base project code is in need of substantial re-writing and improvement - see comments for more details. The MavenPro font required is a free Google Webfont.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Game Engine Design - SDL base code | |
// This sample project has a complete lack of OO design and makes use of | |
// a very large number of global variables. What it does do, is demonstrate | |
// use of SDL for window/event management with OpenGL for graphics rendering | |
// This project requires the following 3rd party libraries: | |
// - glew (GL extension wrangler, for access to OpenGL past 1.1 on Windows) | |
// - SDL (base libraries) | |
// - SDL_ttf (to render text to a surface/texture for display in OpenGL) | |
// | |
// There are MANY ways to improve this code, some of which are identified in | |
// comments, and these are left as an exercise to the reader | |
// | |
// Copyright (C) 2012 Daniel Livingstone | |
// | |
// This software is provided 'as-is', without any express or implied | |
// warranty. In no event will the authors be held liable for any damages | |
// arising from the use of this software. | |
// | |
// Permission is granted to anyone to use this software for any purpose, | |
// including commercial applications, and to alter it and redistribute it | |
// freely, subject to the following restrictions: | |
// | |
// 1. The origin of this software must not be misrepresented; you must not | |
// claim that you wrote the original software. If you use this software | |
// in a product, an acknowledgment in the product documentation would be | |
// appreciated but is not required. | |
// 2. Altered source versions must be plainly marked as such, and must not be | |
// misrepresented as being the original software. | |
// 3. This notice may not be removed or altered from any source distribution. | |
#include <iostream> | |
#include <SDL.h> | |
#include <SDL_ttf.h> | |
#include <GL/glew.h> | |
// C stdlib and C time libraries for rand and time functions | |
#include <cstdlib> | |
#include <ctime> | |
// iostream for cin and cout | |
#include <iostream> | |
// stringstream and string | |
#include <sstream> | |
#include <string> | |
// Lots of global variables: not nice at all | |
// this will need to be resolved by refactoring the project quickly | |
float xpos = 0.0f; | |
float ypos = 0.0f; | |
float xsize = 0.15f; | |
float ysize = 0.15f; | |
float targetXPos = 0.0f; | |
float targetYPos = 0.0f; | |
float targetXSize = 0.1f; | |
float targetYSize = 0.1f; | |
int score = 0; | |
clock_t lastTime; // clock_t is an integer type | |
clock_t currentTime; // use this to track time between frames | |
TTF_Font* textFont; // SDL type for True-Type font rendering | |
// SDL projects do not automatically work with the console window. | |
// On windows with visual studio, the following line is required to use console output | |
#if _DEBUG | |
#pragma comment(linker, "/subsystem:\"console\" /entry:\"WinMainCRTStartup\"") | |
#endif | |
// We should be able to detect when errors occur with SDL if there are | |
// unrecoverable errors, then we need to print an error message and quit the program | |
void exitFatalError(char *message) | |
{ | |
std::cout << message << " " << SDL_GetError(); | |
SDL_Quit(); | |
exit(1); | |
} | |
// Set up rendering context | |
// Sets values for, and creates an OpenGL context for use with SDL | |
void setupRC(SDL_WindowID &window, SDL_GLContext &context) | |
{ | |
if (SDL_Init(SDL_INIT_VIDEO) < 0) // Initialize video | |
exitFatalError("Unable to initialize SDL"); | |
// Request an OpenGL 2.1 context. | |
// If you request a context not supported by your drivers, no OpenGL context will be created | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); | |
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); | |
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); // double buffering on | |
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); // 8 bit alpha buffering | |
// Optional: Turn on x4 multisampling anti-aliasing (MSAA) | |
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); | |
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); | |
// Create 800x600 window | |
window = SDL_CreateWindow("SDL OpenGL Demo for GED", | |
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, | |
800, 600, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN ); | |
if (!window) // Check window was created OK | |
exitFatalError("Unable to create window"); | |
context = SDL_GL_CreateContext(window); // Create opengl context and attach to window | |
SDL_GL_SetSwapInterval(1); // set swap buffers to sync with monitor's vertical refresh rate | |
// set up TrueType / SDL_ttf | |
if (TTF_Init()== -1) | |
exitFatalError("TTF failed to initialise."); | |
textFont = TTF_OpenFont("MavenPro-Regular.ttf", 24); | |
if (textFont == NULL) | |
exitFatalError("Failed to open font."); | |
} | |
// Initialise OpenGL values and game related values and variables | |
void glInit(void) | |
{ | |
glClearColor(0.0, 0.0, 0.0, 0.0); // set background colour | |
std::srand( std::time(NULL) ); | |
targetXPos = (float)rand()/RAND_MAX - 0.75f; | |
targetYPos = (float)rand()/RAND_MAX - 0.75f; | |
lastTime = clock(); | |
} | |
// This function assumes that strings are dynamic: | |
// creating and deleting textures for the string | |
// Strings that remain throughout the game should only be generated once | |
// or should be generated at compile time and loaded as fixed textures | |
// Generating textures during init at run time can make it easier to change | |
// text, while using artist generated textures can allow for a much more | |
// professional quality finish on the graphics | |
void displayString(float x, float y, const char * str) | |
{ | |
SDL_Surface *stringImage; | |
SDL_Color colour = { 255, 255, 0 }; | |
stringImage = TTF_RenderText_Blended(textFont,str,colour); | |
if (stringImage == NULL) | |
exitFatalError("String surface not created."); | |
GLuint colors = stringImage->format->BytesPerPixel; | |
GLuint format; | |
if (colors == 4) { // alpha | |
if (stringImage->format->Rmask == 0x000000ff) | |
format = GL_RGBA; | |
else | |
format = GL_BGRA; | |
} else { // no alpha | |
if (stringImage->format->Rmask == 0x000000ff) | |
format = GL_RGB; | |
else | |
format = GL_BGR; | |
} | |
GLuint texture; | |
glGenTextures(1, &texture); | |
glBindTexture(GL_TEXTURE_2D, texture); | |
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP); | |
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR); | |
glTexImage2D(GL_TEXTURE_2D, 0, colors, stringImage->w, stringImage->h, 0, | |
format, GL_UNSIGNED_BYTE, stringImage->pixels); | |
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE); | |
// Draw texture here | |
glEnable(GL_TEXTURE_2D); | |
glEnable(GL_BLEND); | |
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); | |
glBegin(GL_QUADS); | |
glTexCoord2d(0,1); // Texture has origin at top not bottom | |
glVertex3f (x,y, 0.0); // first corner | |
glTexCoord2d(1,1); | |
glVertex3f (x+0.002f*stringImage->w, y, 0.0); // second corner | |
glTexCoord2d(1,0); | |
glVertex3f (x+0.002f*stringImage->w, y+0.002f*stringImage->h, 0.0); // third corner | |
glTexCoord2d(0,0); | |
glVertex3f (x, y+0.002f*stringImage->h, 0.0); // fourth corner | |
glEnd(); | |
glDisable(GL_TEXTURE_2D); | |
glDeleteTextures(1, &texture); | |
} | |
// The main rendering function | |
// In principle, this function should never perform updates to the game | |
// ONLY render the current state. Reacting to events should be taken care | |
// of in a seperate update function | |
void draw(const SDL_WindowID &window) | |
{ | |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear window | |
// draw player | |
glColor3f(1.0,1.0,1.0); | |
glBegin(GL_POLYGON); | |
glVertex3f (xpos, ypos, 0.0); // first corner | |
glVertex3f (xpos+xsize, ypos, 0.0); // second corner | |
glVertex3f (xpos+xsize, ypos+ysize, 0.0); // third corner | |
glVertex3f (xpos, ypos+ysize, 0.0); // fourth corner | |
glEnd(); | |
displayString(xpos+(xsize/2.0f), ypos+ysize, "Player"); | |
// draw target | |
glColor3f(1.0,0.0,0.0); | |
glBegin(GL_POLYGON); | |
glVertex3f (targetXPos, targetYPos, 0.0); // first corner | |
glVertex3f (targetXPos+targetXSize, targetYPos, 0.0); // second corner | |
glVertex3f (targetXPos+targetXSize, targetYPos+targetYSize, 0.0); // third corner | |
glVertex3f (targetXPos, targetYPos+targetYSize, 0.0); // fourth corner | |
glEnd(); | |
displayString(targetXPos+(targetXSize/2.0f), targetYPos+targetYSize, "Target"); | |
if ( (targetXPos >= xpos) && (targetXPos+targetXSize <= xpos+xsize) // cursor surrounds target in x | |
&& (targetYPos >= ypos) && (targetYPos+targetYSize <= ypos+ysize) ) // cursor surrounds target in y | |
{ | |
score += 100; // congrats, player has scored! | |
// randomize the new target position | |
targetXPos = (float)rand()/RAND_MAX - 0.75f; | |
targetYPos = (float)rand()/RAND_MAX - 0.75f; | |
} | |
// Calculate ms/frame | |
// Some OpenGL drivers will limit the frames to 60fps (16.66 ms/frame) | |
// If so, expect to see the time to rapidly switch between 16 and 17... | |
glColor3f(1.0,1.0,1.0); | |
currentTime = clock(); | |
// On some systems, CLOCKS_PER_SECOND is 1000, which makes the arithmetic below redundant | |
// - but this is not necessarily the case on all systems | |
float milliSecondsPerFrame = ((currentTime - lastTime)/(float)CLOCKS_PER_SEC*1000); | |
// Print out the score and frame time information | |
std::stringstream strStream; | |
strStream << "Score:" << score; | |
strStream << " ms/frame: " << milliSecondsPerFrame; | |
displayString(-0.9,0.9, strStream.str().c_str()); | |
lastTime = clock(); | |
SDL_GL_SwapWindow(window); // swap buffers | |
} | |
// The event handling function | |
// In principle, this function should never perform updates to the game | |
// ONLY detect what events have taken place. Reacting to the events should | |
// be taken care of in a seperate update function | |
// This would allow e.g. diagonal movement when two keys are pressed together | |
// (which is not possible with this implementation) | |
void handleSDLEvent(SDL_Event const &sdlEvent) | |
{ | |
if (sdlEvent.type == SDL_KEYDOWN) | |
{ | |
//std::cout << "Scancode: " << sdlEvent.key.keysym.scancode ; | |
//std::cout << ", Name: " << SDL_GetKeyName( sdlEvent.key.keysym.sym ) << std::endl; | |
switch( sdlEvent.key.keysym.sym ) | |
{ | |
case SDLK_UP: | |
case 'w': case 'W': | |
ypos += 0.05f; | |
break; | |
case SDLK_DOWN: | |
case 's': case 'S': | |
ypos -= 0.05f; | |
break; | |
case SDLK_LEFT: | |
case 'a': case 'A': | |
xpos -= 0.05f; | |
break; | |
case SDLK_RIGHT: | |
case 'd': case 'D': | |
xpos += 0.05f; | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
// Program entry point | |
// SDL manages the actual WinMain entry point for us | |
int main(int argc, char *argv[]) | |
{ | |
SDL_WindowID hWindow; // window handle | |
SDL_GLContext glContext; // OpenGL context handle | |
setupRC(hWindow, glContext); // Create window and render context | |
glInit(); // initialise the OpenGL and game variables | |
bool running = true; // set running to true | |
SDL_Event sdlEvent; // variable to detect SDL events | |
std::cout << "Progress: About to enter main loop" << std::endl; | |
// unlike GLUT, SDL requires you to write your own event loop | |
// This puts much more power in the hands of the programmer | |
// This simple loop only responds to the window being closed. | |
while (running) // the event loop | |
{ | |
while (SDL_PollEvent(&sdlEvent)) | |
{ | |
if (sdlEvent.type == SDL_QUIT) | |
running = false; | |
else | |
handleSDLEvent(sdlEvent); | |
} | |
//update(); // this is the place to put a call to the game update function | |
draw(hWindow); // call the draw function | |
} | |
TTF_CloseFont(textFont); | |
SDL_DestroyWindow(hWindow); | |
SDL_Quit(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment