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() {
foreach (DeviceInstance di in Manager.GetDevices(
DeviceClass.GameControl,
EnumDevicesFlags.AttachedOnly))
{
joystick = new Device(di.InstanceGuid);
break;
}
if (joystick == null)
{
}
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);
joystick.Acquire();
state = joystick.CurrentJoystickState;
}
}
public static void UpdateJoystick() {
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) else if(joysticks.state.X < -40)
if(joysticks.state.Y > -40) else if(joysticks.state.Y < -40)
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.
public void Load_Graphic() {
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) {
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) {
graph.FillRectangle(back_color,x*node_length,
y*node_length,node_length,node_length);
}
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++;
}
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;
}
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;
}
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;
}
public bool Did_Snake_Eat_The_Bait()
{
if((node_x == bait.X) && (node_y==bait.Y))
return true;
else
return false;
}
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;
}
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++;
}
}
}
public void Eated_Sound()
{
if(sound_control == "on" && File.Exists(sound_eated.SoundLocation) )
sound_eated.Play();
}
public void Main_Menu_Sound()
{
if(sound_control == "on" && File.Exists(sound_eated.SoundLocation))
sound_menu.Play();
}
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:
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)
{
Clear_Level();
return true;
}
if(mygame.Did_Snake_Crash_Byself(mygame.snake_route)) {
MyGame_timer.Enabled = false;
wait_timer.Enabled = true;
mygame.wait_time = 20;
return true;
}
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();
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);
}
}
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