SnakeGame
From SWWorkshop
Everyone knows snake game. It is very popular on phone. I try to develop on SWEngine. At first I design game
Game Design
- Game area consist of 20x20 grid.
- Snake consist of nodes. Every nodes size equals grid size.
- Snake could move East,West,South,North direction.
- Snake moves to selected direction with constant speed.
- Snake speed will increase when snake takes bonus.
- Snake control pad is keyboard's Left,Right,Up,Down keys.
- Bonus increase player score.
- Snake nodes will increase when snake takes bonus.
- After bonus taken a new bonus will be created on random position.
- Snake will be die if it intersect boundary of game area or it's tail.
Game and Code
SWEngineSDK
Code Details
We define header and lib files
#include "../../include/SWEngine.h" #pragma comment (lib,"../../lib/SWUtil.lib") #pragma comment (lib,"../../lib/SWTypes.lib") #pragma comment (lib,"../../lib/SWCore.lib") #pragma comment (lib,"../../lib/SWEngine.lib")
Game main...
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd) { //Application Settings snakeApp.hInstance=hInstance; snakeApp.fullScreen=false; snakeApp.cursor=true; snakeApp.width=800; snakeApp.height=600; snakeApp.title="Snake"; snakeApp.path="\\rsc\\Snake\\"; snakeApp.appRun=GameLoop; //Application Execution swEngineInit(&snakeApp); //Init My Application initApp(); swEngineRun(); return 0; }
First we define swApplication object. We set application settings 800x600 WindowedMode, ResourcePath=\\Rsc\\Snake\\ and etc... Then we call swEngineInit func with snakeApp. This call startup all SWEngine services(Timer,Input,OpenGL,FMod, etc.. ) Now we use SWEngine functions.
swApplication snakeApp; int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd){ //Application Settings snakeApp.hInstance=hInstance; snakeApp.fullScreen=false; snakeApp.cursor=true; snakeApp.width=800; snakeApp.height=600; snakeApp.title="Snake"; snakeApp.path="\\rsc\\Snake\\"; snakeApp.appRun=GameLoop; //Application Execution swEngineInit(&snakeApp);
After SWEngine initialization, we init our application settings. We create a snake,timer and font. Font for scores.., Timer for snake motion.., Snake is an actor.
void initApp(){ //Define Snake snake.nodeListID=swLinkedListCreate(); snake.direction=NORTH; addSnakeNode(10,10); addSnakeNode(10,9); addSnakeNode(10,8); //Define Timer for Snake Movement timerID=swTimerCreate(0.2,NULL,moveSnake); //FontCreate fontID=swGraphicsCreateFont("Font.tga"); }
We define snake structure which can hold it's nodes and defined direction.
enum Direction{ NORTH, SOUTH, EASTH, WEST }; struct Snake{ Direction direction; int nodeListID; }; Snake snake;
In initApp function. We create a swLinkedList for snake nodes. And add default three node N1(10,10), N2(10,9), N3(10,8).
snake.nodeListID=swLinkedListCreate(); addSnakeNode(10,10); addSnakeNode(10,9); addSnakeNode(10,8); snake.direction=NORTH;
This function add nodes to snake. Function create a swPoint object which hold nodes position. After that add this to nodeList. We will also use this function when snake take bonus.
void addSnakeNode(float x,float y){ swPoint *node=swPointCreate(x,y); swLinkedListAdd(snake.nodeListID,node); }
We set direction. Default snake moves NORTH.
snake.direction=NORTH;Create swTimer object for snake move. Timer will trigger callback function recusively with 0.4sec delay. And in this function we move snake one grid to defined direction.
timerID=swTimerCreate(0.4,NULL,moveSnake);
We must create Font object for Score rendering.
fontID=swGraphicsCreateFont("Font.tga");
We add first bonus (3,3) coordinate. After bonus will be created random when snake takes bonus.
swPoint bonus={3,3};
After that swEngineRun function will be called in WinMain. The gameLoop function will be triggered every screen refresh and moveSnake will be called by Timer.
swEngineRun()
GameLoop function is responsible with user interaction and rendering process.
void GameLoop(){ //Input Handling swInputListenKeyboard(&keybState); if(keybState.keyESCAPE){ swEngineExit(); } if(keybState.keyLEFT) snake.direction=WEST; else if(keybState.keyRIGHT) snake.direction=EASTH; else if(keybState.keyUP) snake.direction=NORTH; else if(keybState.keyDOWN) snake.direction=SOUTH; //Display Scene swGraphicsBeginScene(); swGraphicsSetBgColor0(0,0,0.0); //Display Grids swGraphicsSetColor0(1,0,0,1); swGraphicsSetColor0(1,1,1,1); for(int i=0;i<GRID_HOLE_COUNT+1;i++){ //i=X axis float x=GRID_X+i*GRID_HOLE_SIZE; float y0=GRID_Y; float y1=GRID_Y+GRID_HOLE_SIZE*GRID_HOLE_COUNT; swGraphicsRenderLine0(x,y0,x,y1,2); }//X axis for(int i=0;i<GRID_HOLE_COUNT+1;i++){ //Y axis float y=GRID_Y+i*GRID_HOLE_SIZE; float x0=GRID_X; float x1=GRID_X+GRID_HOLE_COUNT*GRID_HOLE_SIZE; swGraphicsRenderLine0(x0,y,x1,y,2); } //Display Bonus swGraphicsSetColor0(0,1,0,0); displayNode(&bonus); //Display Snake swPoint *node=(swPoint*)swLinkedListGetFirst(snake.nodeListID); swGraphicsSetColor0(1,0,0,0); while(node!=NULL){ displayNode(node); node=(swPoint*)swLinkedListGetNext(snake.nodeListID); } //DisplayScore swGraphicsSetBlendingMode(SW_BLENDING_MODE_ADDITIVE); swGraphicsSetColor0(1,1,1,0); swGraphicsRenderText(fontID,1,20,550,250,0,"Score=%i",score); swGraphicsEndScene(); }
Player will interact game with keyboard. And we listen keyboard with swInput api. And define snake direction.
swKeyboardState keybState; //Input Handling swInputListenKeyboard(&keybState); if(keybState.keyESCAPE){ swEngineExit(); } if(keybState.keyLEFT) snake.direction=WEST; else if(keybState.keyRIGHT) snake.direction=EASTH; else if(keybState.keyUP) snake.direction=NORTH; else if(keybState.keyDOWN) snake.direction=SOUTH;
If you want to render something with SWEngine you must call below display block. And you must do all rendering operations between these two function.
Scene Control
swGraphicsBeginScene(); //execute drawing operations swGraphicsEndScene();
Set background color Colouring functions
swGraphicsSetBgColor0(0,0,0.0);
Render grid. Position 100,100 , GridCount 20x20 and GridSize=16px.
#define GRID_X 100 #define GRID_Y 100 #define GRID_HOLE_COUNT 20 #define GRID_HOLE_SIZE 16
Draw grid line. Line Functions
swGraphicsSetColor0(1,0,0,1); swGraphicsSetColor0(1,1,1,1); for(int i=0;i<GRID_HOLE_COUNT+1;i++){ //i=X axis float x=GRID_X+i*GRID_HOLE_SIZE; float y0=GRID_Y; float y1=GRID_Y+GRID_HOLE_SIZE*GRID_HOLE_COUNT; swGraphicsRenderLine0(x,y0,x,y1,2); }//X axis for(int i=0;i<GRID_HOLE_COUNT+1;i++){ //Y axis float y=GRID_Y+i*GRID_HOLE_SIZE; float x0=GRID_X; float x1=GRID_X+GRID_HOLE_COUNT*GRID_HOLE_SIZE; swGraphicsRenderLine0(x0,y,x1,y,2); }
Render bonus object
swGraphicsSetColor0(0,1,0,0); displayNode(&bonus);
Render snake object.
swPoint *node=(swPoint*)swLinkedListGetFirst(snake.nodeListID); swGraphicsSetColor0(1,0,0,0); while(node!=NULL){ displayNode(node); node=(swPoint*)swLinkedListGetNext(snake.nodeListID); }
Render score.
swGraphicsSetBlendingMode(SW_BLENDING_MODE_ADDITIVE); swGraphicsSetColor0(1,1,1,0); swGraphicsRenderText(fontID,1,20,550,250,0,"Score=%i",score);
moveSnake function call by Timer with 0.4sec interval. All game logic executes in this function. Move snake, check collision, check bonus take, and reflect the result of collision(Increase speed,score or gameover)
void moveSnake(void *obj){ //Process Request Head Location swPoint requestLoc; processRequestLoc(&requestLoc); if(isRequestLocCollideSnake(requestLoc)||isRequestLocCollideWall(requestLoc)){ GameOver(); return; } if(isRequestLocCollideBonus(requestLoc)){ addSnakeNode(0,0); score+=1000; speed-=0.02; swTimerUpdateDelaySeconds(timerID,speed); bonus.x=(int)swMathRandom(0,20); bonus.y=(int)swMathRandom(0,20); } //Move All Node To RequestLoc swPoint *node=(swPoint*)swLinkedListGetFirst(snake.nodeListID); swPoint temp; while(node!=NULL){ swPointCopy(&temp,node); swPointCopy(node,&requestLoc); swPointCopy(&requestLoc,&temp); node=(swPoint*)swLinkedListGetNext(snake.nodeListID); } }
This function calculates next position of snake head. Current Pos + Direction = Next Pos. But we don't set next position directly. We only calculate next position
void processRequestLoc(swPoint *requestLoc){ swPoint *head=(swPoint*)swLinkedListGetFirst(snake.nodeListID); switch(snake.direction){ case NORTH: requestLoc->x=head->x; requestLoc->y=head->y+1; break; case SOUTH: requestLoc->x=head->x; requestLoc->y=head->y-1; break; case WEST: requestLoc->x=head->x-1; requestLoc->y=head->y; break; case EASTH: requestLoc->x=head->x+1; requestLoc->y=head->y; break; default: break; } }
We find next pos. Now we check collision with tail or wall. If it collide then finished game. Call GameOver function.
if(isRequestLocCollideSnake(requestLoc)||isRequestLocCollideWall(requestLoc)){ GameOver(); return; }
Checks snake head is collide with it's tail nodes.
boolean isRequestLocCollideSnake(swPoint requestLoc){ swPoint *node=(swPoint*)swLinkedListGetFirst(snake.nodeListID); while(node!=NULL){ if(swPointEquals(node,&requestLoc)){ return true; } node=(swPoint*)swLinkedListGetNext(snake.nodeListID); } return false; }
Checks snake head is collide with grid wall.
boolean isRequestLocCollideWall(swPoint requestLoc){ if(requestLoc.x==-1||requestLoc.y==-1||requestLoc.x==GRID_HOLE_COUNT|| requestLoc.y==GRID_HOLE_COUNT){ return true; } return false; }
GameOver function stopswTimer'ı and ends snake's motion.
void GameOver(){ swTimerStop(timerID); }
If snake takes bonus. Add new node to snake, Increament score and speed. Create new bonus.
if(isRequestLocCollideBonus(requestLoc)){ addSnakeNode(0,0); score+=1000; speed-=0.02; swTimerUpdateDelaySeconds(timerID,speed); bonus.x=(int)swMathRandom(0,20); bonus.y=(int)swMathRandom(0,20); }
Check Bonus Collsion.
boolean isRequestLocCollideBonus(swPoint requestLoc){ if(swPointEquals(&bonus,&requestLoc)){ return true; } return false; }
At last. If snake head's new position is valid to move then all nodes move to one front. Fist move head, after second, third ..
swPoint *node=(swPoint*)swLinkedListGetFirst(snake.nodeListID); swPoint temp; while(node!=NULL){ swPointCopy(&temp,node); swPointCopy(node,&requestLoc); swPointCopy(&requestLoc,&temp); node=(swPoint*)swLinkedListGetNext(snake.nodeListID); }

