Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A Simple Snake Game: Engerek

0.00/5 (No votes)
9 Feb 2011 2  
A simple snake game controllable both by joystick and keyboard, graphics using C#, joystick control using Directx
img1.jpg img2.jpg

Introduction

Yes, again a snake game if you aren't bored yet. You must have seen a lot of snake games created in different languages. But have you ever seen a snake game that is controllable both by keyboard and joystick? Yes, this is the difference between snake games and Engerek.

  • Game control by joystick - Microsoft.Directx.DirectInput
  • Game graphics - System.Drawing*
  • Game sounds - System.Media

Before you say anything, I have to indicate that this is my first project. Maybe you will find my coding bad, I accept all of your comments.

Ok, now we are looking at the code. The files and paths of project are as follows:

MyGame.cs This is our main form.
Game.cs This is the main game class. It includes all snake's properties.
Joystick.cs This one includes joystick class (Directx.DirectInput).
LinkedList.cs Includes linked list class that is used for storing snake nodes.
/sounds Includes sound files *
/fonts Includes font files
/pictures Includes bitmap files

* If you want to enable sounds, you have to locate "menu.wav", "eated.wav" and "gameover.wav" sound files to "/sounds/" directory. the source code doesn't have sounds file because of file upload limit (6 MB).

Joystick.cs

In here, we create a joystick class. You can see the most simple Directx code in here.

class joysticks
{
	public static Device joystick;
	public static JoystickState state;
		
	public static void InitDevices() //Function of initialize device
	{
		//create joystick device.
		foreach (DeviceInstance di in Manager.GetDevices(
			DeviceClass.GameControl,
			EnumDevicesFlags.AttachedOnly))
		{
			joystick = new Device(di.InstanceGuid);
			break;
		}
			
		if (joystick == null)
		{
			//Throw exception if joystick not found.
		}
			
		//Set joystick axis ranges.
		else {
			foreach (DeviceObjectInstance doi in joystick.Objects)
			{
				if ((doi.ObjectId & 
					(int)DeviceObjectTypeFlags.Axis) != 0)
				{
					joystick.Properties.SetRange(
						ParameterHow.ById,
						doi.ObjectId,
						new InputRange(-5000, 5000));
				}					
			}
				
			joystick.Properties.AxisModeAbsolute = true;
			joystick.SetCooperativeLevel
				(null,CooperativeLevelFlags.NonExclusive | 
					CooperativeLevelFlags.Background);
			//Acquire devices for capturing.
			joystick.Acquire();
			state = joystick.CurrentJoystickState;
		}
	}
		
	public static void UpdateJoystick()   // Capturing from device joystick
	{
		//Get Joystick State.
		if(joystick!=null)
			state = joystick.CurrentJoystickState;
	}		
}

That's all. As you see, we set InputRange as -5000 and 5000, this means, axis button's value can be max 5000 and min -5000. While axis button is not pressed, the value average value is -40. So how can we access joystick with this class?

Accessing axis buttons is very simple:

if(joysticks.state.X > -40) //pressing x+ (right button)
else if(joysticks.state.X < -40) //pressing x- (left button)

if(joysticks.state.Y > -40) //pressing y+ (up button)
else if(joysticks.state.Y < -40) //pressing x+ (down button)

If you still didn't understand, you can look at these articles:

Game.cs

This file has our main game class. All snake properties are implemented in here.

//I won't write all variables here, you can understand what's it's purpose. 
// as we use you will understand also. I will write about function

public void Load_Graphic() //creating a bmp that have a black background.
{
	bmp = new Bitmap(game_width,game_height);
	graph = Graphics.FromImage(bmp);
	graph.FillRectangle(back_color,0,0,game_width,game_height);
}
		
public void Add_Snake_Node(int x,int y) //add node to snake when it eat a bait.
{
	graph.FillRectangle(snake_skin_color,x*node_length,
			y*node_length,node_length,node_length);
	graph.FillRectangle(snake_body_color,x*node_length,
	y*node_length,node_length-node_border_length,node_length-node_border_length);
}		
		
public void Delete_Snake_Node(int x, int y)  //delete node by coordinate.
{
	graph.FillRectangle(back_color,x*node_length,
			y*node_length,node_length,node_length);
}
		
//assign direction of snake
		
public void Move_Snake_Left()
{
	node_x--;
}
		
public void Move_Snake_Right()
{
	node_x++;
}
		
public void Move_Snake_Up()
{
	node_y--;
}
		
public void Move_Snake_Down()
{
	node_y++;
}
		
//control to bait if it's on the same coordinate with wall or snake body.
public bool Control_Bait_Coordinate()
{
	Node temp_x = snake_area_x.first;
	Node temp_y = snake_area_y.first;
		
	for(int i= snake_area_x.length;i>0;i--)
	{
		if((bait.X == temp_x.value) && (bait.Y == temp_y.value))
			return true;
		else
		{
			temp_x = temp_x.next;
			temp_y = temp_y.next;			
		}
	}
			
	temp_x = wall_area_x.first;
	temp_y = wall_area_y.first;
			
	for(int j= wall_area_x.length;j>0;j--)
	{
		if((bait.X == temp_x.value) && (bait.Y == temp_y.value))
			return true;
		else
		{
			temp_x = temp_x.next;
			temp_y = temp_y.next;
		}
	}
		
	return false;
}
		
// getting a random coordinate for bait.
	
public void Find_Coordinate_For_Bait()
{
	Random rnd = new Random();
	bait.X = rnd.Next(1,game_width/node_length-1);
			
	bait.Y = rnd.Next(1,game_height/node_length-1);
	bait_bool = true;
}
		
// put a bait by coordinate
		
public void Add_New_Bait(int x, int y)
{
	if(!Control_Bait_Coordinate())
	{
		graph.FillRectangle(bait_skin_color,x*node_length,y*node_length,
		node_length,node_length);
		graph.FillRectangle(bait_body_color,x*node_length,y*node_length,
		node_length-node_border_length,node_length-node_border_length);
	}
			
	else bait_bool=false;
}
		
//control for "did snake eat the bait"
public bool Did_Snake_Eat_The_Bait()
{
	if((node_x == bait.X) && (node_y==bait.Y))
		return true;
	else
		return false;
}
		
//control for "did snake crash byself"
public bool Did_Snake_Crash_Byself(int temp_route)
{
	int x = node_x*node_length;
	int y = node_y*node_length;
	try
	{
		if (temp_route == 4) {
			if (bmp.GetPixel(x + node_length/2, y + node_length/2) == 
			bmp.GetPixel(x + node_length/2 - node_length, y + node_length/2))
			{
				return true;
			}
					
			if(bmp.GetPixel(x + node_length/2 - node_length, y + 
			node_length/2) == new Pen(wall_body_color).Color)
			{
				return true;
			}
		}

		if (temp_route == 2) {
			if (bmp.GetPixel(x + node_length/2, y + node_length/2) == 
			bmp.GetPixel(x + node_length/2 + node_length , 
					y + node_length/2))
			{
				return true;
			}
					
			if(bmp.GetPixel(x + node_length/2 + node_length , y + 
			node_length/2) == new Pen(wall_body_color).Color)
			{
				return true;
			}
		}

		if (temp_route == 1) {
			if (bmp.GetPixel(x + node_length/2, y + node_length/2) == 
			bmp.GetPixel(x + node_length/2, y + 
				node_length/2 - node_length))
			{
				return true;
			}
					
			if(bmp.GetPixel(x + node_length/2, y + node_length/2 - 
			node_length) == new Pen(wall_body_color).Color)
			{
				return true;
			}
		}

		if (temp_route == 3) {
			if (bmp.GetPixel(x + node_length/2, y + node_length/2) == 
			bmp.GetPixel(x + node_length/2, y + 
					node_length/2 + node_length))
			{
				return true;
			}
				
			if(bmp.GetPixel(x + node_length/2, y + node_length/2 + 
			node_length) == new Pen(wall_body_color).Color)
			{
				return true;
			}
		}				
	}
	catch (Exception e) { string str = e.Message.ToString(); return false; }

	return false;
}
		
// putting wall node by coordinate
public void Add_Wall_Node(int x1,int y1,int x2,int y2)
{
	if(x1==x2)
	{
		while(y1<=y2)
		{
			graph.FillRectangle(wall_skin_color,x1*node_length,
			y1*node_length,node_length,node_length);
			graph.FillRectangle(wall_body_color,x1*node_length,
			y1*node_length,node_length-node_border_length,
					node_length-node_border_length);
			wall_area_x.Add_Node(x1);
			wall_area_y.Add_Node(y1);
			y1++;
		}
	}
			
	else if(y1==y2)
	{
		while(x1<=x2)
		{
			graph.FillRectangle(wall_skin_color,x1*node_length,
			y1*node_length,node_length,node_length);
			graph.FillRectangle(wall_body_color,x1*node_length,
			y1*node_length,node_length-node_border_length,
					node_length-node_border_length);
			wall_area_x.Add_Node(x1);
			wall_area_y.Add_Node(y1);
			x1++;
		}
	}
}
		
// play sound when snake eat a bait.
public void Eated_Sound()
{
	if(sound_control == "on" && File.Exists(sound_eated.SoundLocation) )
		sound_eated.Play();
}
		
// play sound while user on menu.
public void Main_Menu_Sound()
{
	if(sound_control == "on" && File.Exists(sound_eated.SoundLocation))
		sound_menu.Play();		
}
		
// play sound when snake eat a bait.
public void Gameover_Sound()
{
	if(sound_control == "on" && File.Exists(sound_eated.SoundLocation))
		sound_gameover.Play();
}	

You will see more code about menu design when you look at Game.cs, but that's not an important part of the game. I won't explain it.

Mygame.cs

Simply, we have a form. The form has a picturebox and timers. This is the most important element in the game. Our main timer, MyGame_timer, is looping the game. In every tick, it calls the function named Game_Playing(). Let's look at that function:

/*********************** THE MAIN GAME FUNCTION ***********************/
// All controls and snake movements are implemented in here.
public bool Game_Playing()
{
	lb_level.Text = "LEVEL-" + mygame.game_level.ToString();
	lb_point.Text = mygame.point.ToString() + "/" + mygame.level_point.ToString();
			
	if(mygame.point >=  mygame.level_point)
	{
		//for loading the next level
		Clear_Level();
		return true;
	}
			
	if(mygame.Did_Snake_Crash_Byself(mygame.snake_route)) // control if snake 
						// crashed to byself or wall
	{
		MyGame_timer.Enabled = false;
		wait_timer.Enabled = true;
		mygame.wait_time = 20;
		return true;		
	}
			
	/*Snake route is setting in here  */
	if(mygame.snake_route == 1) mygame.Move_Snake_Up();
	else if(mygame.snake_route == 2) mygame.Move_Snake_Right();
	else if(mygame.snake_route == 3) mygame.Move_Snake_Down();
	else if(mygame.snake_route == 4) mygame.Move_Snake_Left();
			
	/*Adding new node to snake*/
	mygame.Add_Snake_Node(mygame.node_x,mygame.node_y);
	mygame.snake_area_x.Add_Node(mygame.node_x);
	mygame.snake_area_y.Add_Node(mygame.node_y);
	mygame.snake_route_temp= mygame.snake_route;
			
	if(!mygame.eated) {
		mygame.Delete_Snake_Node(mygame.snake_area_x.first.value,
				mygame.snake_area_y.first.value);
		mygame.snake_area_x.Delete_First_Node();
		mygame.snake_area_y.Delete_First_Node();
	}
	else mygame.eated = false;

	if(!mygame.bait_bool) {
		mygame.Find_Coordinate_For_Bait();
		if(!mygame.Control_Bait_Coordinate())
		{
			mygame.Add_New_Bait(mygame.bait.X,mygame.bait.Y);
		}
	}
			
	/* control if snake eated the bait */
	if(mygame.Did_Snake_Eat_The_Bait())
	{
		mygame.Eated_Sound();
		mygame.Find_Coordinate_For_Bait();
				
		if(!mygame.Control_Bait_Coordinate())
		{
			mygame.Add_New_Bait(mygame.bait.X,mygame.bait.Y);
		}
				
		else mygame.bait_bool=false;
				
		mygame.eated = true;
		mygame.point +=10;
		if(MyGame_timer.Interval > 40)
			MyGame_timer.Interval--;			
	}
			
	pictureBox1.Image = mygame.bmp;
	pictureBox1.Refresh();
			
	if((mygame.node_x > (mygame.game_width/mygame.node_length-1)) && 
			mygame.snake_route==2)
	{
		mygame.node_x = 0;
		mygame.Add_Snake_Node(mygame.node_x,mygame.node_y);
		mygame.snake_area_x.Add_Node(mygame.node_x);
		mygame.snake_area_y.Add_Node(mygame.node_y);
		if(!mygame.eated)
		{
			mygame.Delete_Snake_Node(mygame.snake_area_x.first.value,
				mygame.snake_area_y.first.value);
			mygame.snake_area_x.Delete_First_Node();
			mygame.snake_area_y.Delete_First_Node();
		}
		else mygame.eated = false;				
	}
	else if(mygame.node_x < 0 && mygame.snake_route==4)
	{
		mygame.node_x = (mygame.game_width/mygame.node_length-1);
		mygame.Add_Snake_Node(mygame.node_x,mygame.node_y);
		mygame.snake_area_x.Add_Node(mygame.node_x);
		mygame.snake_area_y.Add_Node(mygame.node_y);
		if(!mygame.eated)
		{
			mygame.Delete_Snake_Node(mygame.snake_area_x.first.value,
				mygame.snake_area_y.first.value);
			mygame.snake_area_x.Delete_First_Node();
			mygame.snake_area_y.Delete_First_Node();
		}
		else mygame.eated = false;
	}
	if((mygame.node_y > (mygame.game_height/mygame.node_length-1)) && 
			mygame.snake_route==3)
	{
		mygame.node_y = 0;
		mygame.Add_Snake_Node(mygame.node_x,mygame.node_y);
		mygame.snake_area_x.Add_Node(mygame.node_x);
		mygame.snake_area_y.Add_Node(mygame.node_y);
		if(!mygame.eated)
		{
			mygame.Delete_Snake_Node(mygame.snake_area_x.first.value,
				mygame.snake_area_y.first.value);
			mygame.snake_area_x.Delete_First_Node();
			mygame.snake_area_y.Delete_First_Node();
		}
		else mygame.eated = false;
	}
	else if(mygame.node_y < 0 && mygame.snake_route==1)
	{
		mygame.node_y = (mygame.game_height/mygame.node_length-1);
		mygame.Add_Snake_Node(mygame.node_x,mygame.node_y);
		mygame.snake_area_x.Add_Node(mygame.node_x);
		mygame.snake_area_y.Add_Node(mygame.node_y);
		if(!mygame.eated)
		{
			mygame.Delete_Snake_Node(mygame.snake_area_x.first.value,
				mygame.snake_area_y.first.value);
			mygame.snake_area_x.Delete_First_Node();
			mygame.snake_area_y.Delete_First_Node();
		}
		else mygame.eated = false;	
	}
	return true;			
}

That's all. I hope this will help you to understand about Directx.DirectInput and System.Draw libraries. Thanks for reading now.

History

  • 9th February, 2011: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here