SnakeGame

From SWWorkshop

Jump to: navigation, search

Everyone knows snake game. It is very popular on phone. I try to develop on SWEngine. At first I design game

Game Design

  1. Game area consist of 20x20 grid.
  2. Snake consist of nodes. Every nodes size equals grid size.
  3. Snake could move East,West,South,North direction.
  4. Snake moves to selected direction with constant speed.
  5. Snake speed will increase when snake takes bonus.
  6. Snake control pad is keyboard's Left,Right,Up,Down keys.
  7. Bonus increase player score.
  8. Snake nodes will increase when snake takes bonus.
  9. After bonus taken a new bonus will be created on random position.
  10. Snake will be die if it intersect boundary of game area or it's tail.


Screenshot
Image:Snake.JPG

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);
   }
Personal tools