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:
parent
e13cc36bee
commit
01147c0d35
13 changed files with 302 additions and 51 deletions
2
Makefile
2
Makefile
|
@ -4,7 +4,7 @@ CC= gcc
|
||||||
# Compiler flags
|
# Compiler flags
|
||||||
CFLAGS= -Wall -std=c99 -ggdb
|
CFLAGS= -Wall -std=c99 -ggdb
|
||||||
# Libraries
|
# Libraries
|
||||||
CLIBS= `sdl-config --cflags` -lSDL -lGL
|
CLIBS= `sdl-config --cflags` -lSDL -lSDL_ttf -lGL
|
||||||
####### Commands
|
####### Commands
|
||||||
RM= rm -r
|
RM= rm -r
|
||||||
MKDIR= mkdir -p
|
MKDIR= mkdir -p
|
||||||
|
|
21
snippets/state_graph.dot
Normal file
21
snippets/state_graph.dot
Normal 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"];
|
||||||
|
}
|
|
@ -6,6 +6,10 @@
|
||||||
// Initialization for the global variables
|
// Initialization for the global variables
|
||||||
SDL_Surface *screen = NULL;
|
SDL_Surface *screen = NULL;
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
|
TTF_Font *menuFont;
|
||||||
bool gameRunning = true;
|
bool gameRunning = true;
|
||||||
int *terrain = NULL;
|
int *terrain = NULL;
|
||||||
|
|
||||||
|
enum states currentState;
|
||||||
|
int currentMenuActionState = 0;
|
||||||
|
|
||||||
|
|
29
src/config.h
29
src/config.h
|
@ -1,19 +1,36 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
|
#include "SDL_ttf.h"
|
||||||
|
|
||||||
// In this file we define all global variables for the whole project. All
|
// In this file we define all global variables for the whole project. All
|
||||||
// variables definied here can be accessed from all other source files.
|
// 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
|
// 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
|
// Screen size
|
||||||
static const int SCREEN_WIDTH = 800;
|
static const int SCREEN_WIDTH = 800;
|
||||||
static const int SCREEN_HEIGHT = 600;
|
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
44
src/display.c
Normal 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);
|
||||||
|
}
|
||||||
|
|
16
src/draw.c
16
src/draw.c
|
@ -97,19 +97,3 @@ int* generateTerrain(float peakheight, float flatness) {
|
||||||
return &(yvals[0]);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -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 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);
|
void drawCircle(int x, int y, int radius, int color, bool filled);
|
||||||
|
|
||||||
|
/* Terrain generator
|
||||||
|
*/
|
||||||
int* generateTerrain(float peakheight, float flatness);
|
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
|
#endif
|
||||||
|
|
||||||
|
|
42
src/events.c
Normal file
42
src/events.c
Normal 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);
|
||||||
|
}
|
||||||
|
|
40
src/main.c
40
src/main.c
|
@ -2,47 +2,45 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
#include "SDL.h"
|
#include "SDL.h"
|
||||||
|
#include "SDL_ttf.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
|
#include "states.h"
|
||||||
|
#include "menu.h"
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
// Initialize SDL and open up a screen
|
// Initialize SDL and open up a screen
|
||||||
SDL_Init(SDL_INIT_VIDEO);
|
SDL_Init(SDL_INIT_VIDEO);
|
||||||
|
TTF_Init();
|
||||||
screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);
|
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
|
// Initialize internal game state
|
||||||
srand(time(NULL));
|
srand(time(NULL));
|
||||||
terrain = generateTerrain(150.0, 180.0);
|
terrain = generateTerrain(150.0, 180.0);
|
||||||
|
|
||||||
|
|
||||||
|
// Choose right state
|
||||||
|
stateTable[currentState].drawFun();
|
||||||
|
stateTable[currentState].handleEventsFun();
|
||||||
|
|
||||||
// the main game loop
|
// the main game loop
|
||||||
while (gameRunning == true) {
|
while (currentState != STATE_EXIT) {
|
||||||
// Check for events
|
// Check for events
|
||||||
if (SDL_PollEvent(&event)) {
|
if (SDL_PollEvent(&event)) {
|
||||||
// Make it possible to close the game window
|
// Make it possible to close the game window
|
||||||
if (event.type == SDL_QUIT) gameRunning = false;
|
if (event.type == SDL_QUIT) currentState = STATE_EXIT;
|
||||||
|
stateTable[currentState].handleEventsFun();
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the stuff on the screen and "flip" th the next frame
|
// Draw the stuff on the screen and "flip" th the next frame
|
||||||
SDL_FillRect(screen, NULL, 0x000000);
|
SDL_FillRect(screen, NULL, 0x000000);
|
||||||
drawScreen();
|
stateTable[currentState].drawFun();
|
||||||
SDL_Flip(screen);
|
SDL_Flip(screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
72
src/menu.c
Normal file
72
src/menu.c
Normal 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
23
src/menu.h
Normal 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
17
src/states.c
Normal 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
34
src/states.h
Normal 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
|
||||||
|
|
Loading…
Reference in a new issue