Adding game states and menu

* Implement a simple state machine to simulate different game stats
 * Create a generic menu to display different menus
 * Update the Makefile for TTF support
 * Draw a simple diagram to show the stats
This commit is contained in:
Aaron Mueller 2009-12-16 15:37:24 +01:00
parent e13cc36bee
commit 01147c0d35
13 changed files with 302 additions and 51 deletions

View file

@ -4,7 +4,7 @@ CC= gcc
# Compiler flags
CFLAGS= -Wall -std=c99 -ggdb
# Libraries
CLIBS= `sdl-config --cflags` -lSDL -lGL
CLIBS= `sdl-config --cflags` -lSDL -lSDL_ttf -lGL
####### Commands
RM= rm -r
MKDIR= mkdir -p

21
snippets/state_graph.dot Normal file
View file

@ -0,0 +1,21 @@
// dot -Tpng -Gcharset=latin1 state_graph.dot -o states.png
digraph StateMachine {
node [style=rounded, shape=box, fontname="Helvetica"];
edge [fontsize=9, fontname="Helvetica"];
Start [shape=plaintext];
Ende [shape=plaintext];
Start -> "Hauptmenü";
"Hauptmenü" -> Ende [label="Spiel beenden"];
"Hauptmenü" -> Spielerwahl [label="Neues Spiel",labelfontsize=4.0];
"Hauptmenü" -> Credits [label="Credits"];
Credits -> "Hauptmenü" [label="Zurück"];
Spielerwahl -> "Hauptmenü" [label="Zurück"];
Spielerwahl -> "Spiel läuft" [label="1/2/3 Spieler"];
"Spiel läuft" -> "Spiel wirklich beenden?" [label="ESC, F10"];
"Spiel wirklich beenden?" -> "Hauptmenü" [label="Ja"];
"Spiel wirklich beenden?" -> "Spiel läuft" [label="Nein"];
}

View file

@ -6,6 +6,10 @@
// Initialization for the global variables
SDL_Surface *screen = NULL;
SDL_Event event;
TTF_Font *menuFont;
bool gameRunning = true;
int *terrain = NULL;
enum states currentState;
int currentMenuActionState = 0;

View file

@ -1,19 +1,36 @@
#include <stdbool.h>
#include "SDL.h"
#include "SDL_ttf.h"
// In this file we define all global variables for the whole project. All
// variables definied here can be accessed from all other source files.
// Global variables and definitions
extern SDL_Surface *screen; // Screen to paint on
extern SDL_Event event; // Event storage
extern bool gameRunning; // Set this variable to false to shutdown the game
extern int *terrain; // Game terrain
// Constants
#ifndef CONFIG_H
#define CONFIG_H
enum states {
STATE_MAINMENU,
STATE_NUMPLAYERS,
STATE_CREDITS,
STATE_RUNNINGGAME,
STATE_RLYQUIT,
STATE_EXIT,
MAX_STATES
};
// Screen size
static const int SCREEN_WIDTH = 800;
static const int SCREEN_HEIGHT = 600;
#endif
// Global variables and definitions
extern SDL_Surface *screen; // Screen to paint on
extern SDL_Event event; // Event storage
extern TTF_Font *menuFont; // The font for the menu
extern int *terrain; // Game terrain
extern enum states currentState; // The current game state
extern int currentMenuActionState; // The current selected action state in a menu

44
src/display.c Normal file
View file

@ -0,0 +1,44 @@
#include <stdlib.h>
#include <stdio.h>
#include "SDL.h"
#include "SDL_ttf.h"
#include "config.h"
#include "states.h"
#include "draw.h"
#include "menu.h"
void displayMainmenu() {
drawMenu(mainMenu, 3);
}
void displayNumplayers() {
drawMenu(numplayersMenu, 4);
}
void displayCredits() {
drawMenu(creditsMenu, 1);
}
void displayRunninggame() {
if (terrain == NULL) terrain = generateTerrain(150.0, 180.0);
Uint32 sky = SDL_MapRGB(screen->format, 186, 215, 217);
Uint32 green = SDL_MapRGB(screen->format, 101, 200, 21);
Uint32 darkGreen = SDL_MapRGB(screen->format, 52, 99, 14);
Uint32 ground = SDL_MapRGB(screen->format, 124, 88, 10);
for (int x=0; x<SCREEN_WIDTH; x++) {
int y = terrain[x];
drawLine(x, 0, x, y, sky);
if (y <= SCREEN_HEIGHT-15) drawLine(x, y, x, y+15, green);
if (y <= SCREEN_HEIGHT-20) drawLine(x, y+15, x, y+20, darkGreen);
if (y <= SCREEN_HEIGHT-21) drawLine(x, y+20, x, SCREEN_HEIGHT, ground);
}
}
void displayRlyquit() {
drawMenu(rlyquitMenu, 2);
}

View file

@ -97,19 +97,3 @@ int* generateTerrain(float peakheight, float flatness) {
return &(yvals[0]);
}
void drawScreen() {
Uint32 sky = SDL_MapRGB(screen->format, 186, 215, 217);
Uint32 green = SDL_MapRGB(screen->format, 101, 200, 21);
Uint32 darkGreen = SDL_MapRGB(screen->format, 52, 99, 14);
Uint32 ground = SDL_MapRGB(screen->format, 124, 88, 10);
for (int x=0; x<SCREEN_WIDTH; x++) {
int y = terrain[x];
drawLine(x, 0, x, y, sky);
if (y <= SCREEN_HEIGHT-15) drawLine(x, y, x, y+15, green);
if (y <= SCREEN_HEIGHT-20) drawLine(x, y+15, x, y+20, darkGreen);
if (y <= SCREEN_HEIGHT-21) drawLine(x, y+20, x, SCREEN_HEIGHT, ground);
}
}

View file

@ -14,14 +14,9 @@ void drawLine(int x1, int y1, int x2, int y2, int color);
void drawRect(int x1, int y1, int x2, int y2, int color, bool filled);
void drawCircle(int x, int y, int radius, int color, bool filled);
/* Terrain generator
*/
int* generateTerrain(float peakheight, float flatness);
/*
* This function will be called for every frame. Here you can actual draw things
* on the screen.
*/
void drawScreen();
#endif

42
src/events.c Normal file
View file

@ -0,0 +1,42 @@
#include <stdio.h>
#include <stdlib.h>
#include "config.h"
#include "states.h"
#include "menu.h"
#include "draw.h"
void eventsMainmenu() {
handleMenuEvent(mainMenu, 3, STATE_EXIT);
}
void eventsNumplayers() {
handleMenuEvent(numplayersMenu, 4, STATE_MAINMENU);
}
void eventsCredits() {
handleMenuEvent(creditsMenu, 1, STATE_MAINMENU);
}
void eventsRunninggame() {
if (event.type == SDL_KEYDOWN) {
switch (event.key.keysym.sym) {
case SDLK_RETURN:
free(terrain);
terrain = generateTerrain(150.0, 180.0);
printf("New terrain generated.\n");
break;
case SDLK_ESCAPE:
case SDLK_F10:
currentState = STATE_RLYQUIT;
break;
default:
break;
}
}
}
void eventsRlyquit() {
handleMenuEvent(rlyquitMenu, 2, STATE_RUNNINGGAME);
}

View file

@ -2,47 +2,45 @@
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>
#include "SDL.h"
#include "SDL_ttf.h"
#include "config.h"
#include "draw.h"
#include "states.h"
#include "menu.h"
int main(int argc, char **argv) {
// Initialize SDL and open up a screen
SDL_Init(SDL_INIT_VIDEO);
TTF_Init();
screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);
menuFont = TTF_OpenFont("/usr/share/fonts/corefonts/verdana.ttf", 50);
currentState = STATE_MAINMENU;
// Initialize internal game state
srand(time(NULL));
terrain = generateTerrain(150.0, 180.0);
// Choose right state
stateTable[currentState].drawFun();
stateTable[currentState].handleEventsFun();
// the main game loop
while (gameRunning == true) {
while (currentState != STATE_EXIT) {
// Check for events
if (SDL_PollEvent(&event)) {
// Make it possible to close the game window
if (event.type == SDL_QUIT) gameRunning = false;
// Keyboard interaction
if (event.type == SDL_KEYDOWN) {
switch (event.key.keysym.sym) {
case SDLK_RETURN:
free(terrain);
terrain = generateTerrain(150.0, 180.0);
printf("New terrain generated.\n");
break;
case SDLK_ESCAPE:
case SDLK_F10:
gameRunning = false;
break;
default:
break;
}
}
if (event.type == SDL_QUIT) currentState = STATE_EXIT;
stateTable[currentState].handleEventsFun();
}
// Draw the stuff on the screen and "flip" th the next frame
SDL_FillRect(screen, NULL, 0x000000);
drawScreen();
stateTable[currentState].drawFun();
SDL_Flip(screen);
}

72
src/menu.c Normal file
View file

@ -0,0 +1,72 @@
#include "SDL.h"
#include "states.h"
#include "menu.h"
struct menuItem mainMenu[] = {
{"Neues Spiel", STATE_NUMPLAYERS},
{"Credits", STATE_CREDITS},
{"Spiel beenden", STATE_EXIT}
};
struct menuItem numplayersMenu[] = {
{"Einspieler", STATE_RUNNINGGAME},
{"Zweispieler", STATE_RUNNINGGAME},
{"Dreispieller", STATE_RUNNINGGAME},
{"Zurueck", STATE_MAINMENU}
};
struct menuItem creditsMenu[] = {
{"Zurueck", STATE_MAINMENU}
};
struct menuItem rlyquitMenu[] = {
{"Beenden", STATE_MAINMENU},
{"Weiterspielen", STATE_RUNNINGGAME}
};
void handleMenuEvent(struct menuItem items[], int numItems, enum states prevState) {
if (event.type == SDL_KEYDOWN) {
switch (event.key.keysym.sym) {
case SDLK_DOWN:
case SDLK_j:
currentMenuActionState++;
if (currentMenuActionState > numItems-1) currentMenuActionState = 0;
break;
case SDLK_UP:
case SDLK_k:
currentMenuActionState--;
if (currentMenuActionState < 0) currentMenuActionState = numItems-1;
break;
case SDLK_RETURN:
case SDLK_SPACE:
currentState = items[currentMenuActionState].targetState;
currentMenuActionState = 0;
break;
case SDLK_ESCAPE:
case SDLK_BACKSPACE:
currentMenuActionState = 0;
currentState = prevState;
break;
default:
break;
}
}
}
void drawMenu(struct menuItem items[], int numItems) {
SDL_Surface *text;
SDL_Rect targetPos;
SDL_Color colorSelected = {0, 255, 66};
SDL_Color colorNormal = {59, 71, 62};
for (int i=0; i<numItems; i++) {
targetPos.x = 200;
targetPos.y = i*60+200;
targetPos.w = SCREEN_WIDTH-200;
targetPos.h = 50;
text = TTF_RenderText_Solid(menuFont, items[i].buttonDescription,
currentMenuActionState == i ? colorSelected : colorNormal);
SDL_BlitSurface(text, NULL, screen, &targetPos);
}
}

23
src/menu.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef MENU_H
#define MENU_H
#include "states.h"
#include "config.h"
struct menuItem {
char buttonDescription[30];
enum states targetState;
};
struct menuItem mainMenu[3];
struct menuItem numplayersMenu[4];
struct menuItem creditsMenu[1];
struct menuItem rlyquitMenu[2];
void handleMenuEvent(struct menuItem items[], int numItems, enum states prevState);
void drawMenu(struct menuItem items[], int numItems);
#endif

17
src/states.c Normal file
View file

@ -0,0 +1,17 @@
#include "states.h"
struct gameState stateTable[] = {
{*displayMainmenu, *eventsMainmenu}, /* STATE_MAINMENU */
{*displayNumplayers, *eventsNumplayers}, /* STATE_NUMPLAYERS */
{*displayCredits, *eventsCredits}, /* STATE_CREDITS */
{*displayRunninggame, *eventsRunninggame}, /* STATE_RUNNINGGAME */
{*displayRlyquit, *eventsRlyquit}, /* STATE_RLYQUIT */
{*exitGame, *exitGame} /* STATE_EXIT */
};
void exitGame() {
currentState = STATE_EXIT;
}

34
src/states.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef STATES_H
#define STATES_H
#include "config.h"
// All possible game states are defined in config.h
// A single game state
struct gameState {
void(*drawFun)();
void(*handleEventsFun)();
};
struct gameState stateTable[MAX_STATES];
// State Functions prototypes
void displayMainmenu();
void displayNumplayers();
void displayCredits();
void displayRunninggame();
void displayRlyquit();
void eventsMainmenu();
void eventsNumplayers();
void eventsCredits();
void eventsRunninggame();
void eventsRlyquit();
void exitGame();
#endif