Skip to content

Instantly share code, notes, and snippets.

@dlivingstone
Created August 4, 2012 13:37
Show Gist options
  • Save dlivingstone/3257815 to your computer and use it in GitHub Desktop.
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.
// 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