目录

前言

拓展知识

介绍system("pause")

First:制作可控移动的小蛇

Second:添加食物

Third:如何Game Over

完整代码


 

前言

声明:本游戏参考《c语言课程设计与游戏开发实践教程》。

 

这个小游戏尤其可以加深对整体循环、二维数组的理解。

我将用循序渐进的步骤制作。

如果想直接学习完整代码,目录最后一条可直接查看完整代码(带解析)。 

先来放一个游戏效果视频 


贪吃蛇


  • 开局小蛇在画面中央,会自动向右移动。用户通过键盘按键'w''s''a''d'控制小蛇上下左右移动。
  • 小蛇吃到食物后会增加一节长度
  • 小蛇撞到边界,或者咬到自己就会game over!

涉及到的知识点:

printf和scanf,宏定义常量,运算符,定义全局变量,for循环,while循环,if语句,二维数组,自定义函数。

(可能的)拓展知识:

system("pause")函数,rand函数,Sleep函数,自定义清屏函数gotoxy,自定义隐藏光标函数,getch函数,kbhit函数。(如果不认识不要害怕,这些拓展知识都会在文章中有详细的说明介绍)

拓展知识

  • kbhit函数,getch函数,rand函数,gotoxy函数,隐藏光标函数:见(在该文章目录中查找)
  • Sleep函数:见(在该文章目录中查找)

介绍system("pause")

  • 位于<stdlib.h>函数库,头文件加上#include<stdlib.h>。
  • 作用:使程序暂停
  • 使用:system("pause");

First:制作可控移动的小蛇

  •  画面描述:小蛇开局在画面中央,会自动向右移动。用户可以通过按键'w''s''a''d'控制小蛇上下左右移动。
  • 原理描述:我们用二维数组制作整个画面。二维数组a[y][x],这个yx就相当于坐标(x,y),数组不同位置被赋予不同的值。空白画面被赋值为0,边界被赋值为-1,蛇头被赋值为1,蛇身被依次赋值为2、3、4、5...(初始身体有4节)。然后控制相应数值的地方显示不同的画面。小蛇每向前移动一步,原来蛇身和蛇头数值都被+1,而原蛇头前方对应的数组的值被赋值为1(新的蛇头),尾巴被赋值为0。

下方见代码,附有保姆级解析

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>

//宏定义画面大小
#define high 20
#define width 30

int a[high][width]={0};//画面储存在这个二维数组中
int direction;//小蛇移动的方向

void start()//初始化
{
	int i,n; 
	//初始化边框,边框对应的数组值为-1
	for(i=0;i<high;i++)
	{
		a[i][0]=-1;//第一列 
		a[i][width-1]=-1;//最后一列 
	} 
	for(n=1;n<width;n++)
	{
		a[0][n]=-1;//第一行 
		a[high-1][n]=-1;//最后一行 
	}//重复赋值是没问题的 
	

	a[high/2][width/2]=1;//蛇头赋值为1
	

	//蛇身,四节分别赋值2345
	 for(i=1;i<=4;i++)
	{
	 	a[high/2][width/2-i]=i+1;
	} 
	
    //初始蛇向右移动,4代表向右
	direction=4;
}


int movesnake()//小蛇移动时给数组重新赋值
{
	int i,n;
	//先给蛇头和蛇身对应的数组的值都+1
	for(i=1;i<high-1;i++)
	{
		for(n=1;n<width-1;n++)
		{
			if(a[i][n]>0)
			a[i][n]++;
		}
	}

	int oldtail_y,oldtail_x,oldhead_y,oldhead_x;//变量可以在用的地方随时定义
	int max=0;
	//找出原先的蛇尾和蛇头,并且赋值给oldtail和oldhead
	for(i=1;i<high;i++)
	{
		for(n=1;n<width;n++)
		{
			if(a[i][n]>0)
			{
				if(max<a[i][n])
				{
					max=a[i][n];
					oldtail_y=i;
					oldtail_x=n;
				}
				if(a[i][n]==2)
				{
					oldhead_y=i;
					oldhead_x=n;
				}
			}
	    }
	}
	
	a[oldtail_y][oldtail_x]=0;//把蛇尾赋值为0
	
	//蛇头移动方向:1上,2下,3左,4右 
    //对蛇头坐标进行+-实现不同方向移动
	if(direction==1)
	a[oldhead_y-1][oldhead_x]=1;
	if(direction==2)
	a[oldhead_y+1][oldhead_x]=1;
	if(direction==3)
	a[oldhead_y][oldhead_x-1]=1;
	if(direction==4)
	a[oldhead_y][oldhead_x+1]=1;
}



void gotoxy(int x,int y)//清屏函数。原理是每循环一次让光标回归到指定坐标
{
	HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos;
	pos.X=x;
	pos.Y=y;
	SetConsoleCursorPosition(handle,pos);
}

void HideCursor()//隐藏光标函数
{
	CONSOLE_CURSOR_INFO cursor_info={1,0};
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}

void show()//根据数组不同的值输出不同画面
{
	gotoxy(0,0);//每次循环都将光标移动到(0,0)位置,相当于清屏
	int i,n;
	for(i=0;i<high;i++)
	{
		for(n=0;n<width;n++)
		{
			if(a[i][n]==0)
			printf(" ");
			else if(a[i][n]==-1)
			printf("+");          //边界
			else if(a[i][n]==1)
			printf("0");          //蛇头(0当作字符就好)
			else if(a[i][n]>1)
			printf("o");          //蛇身
		}
		printf("\n");
	}
	Sleep(100);//                 //减慢蛇的移动速度,自由调整
}

void put()                        //用户通过键盘控制小蛇移动方向
{
	char move;
	if(kbhit())
	{
		move=getch();
		if(move=='a')
		{
			direction=3;
			movesnake(); 
	    }
	    if(move=='d')
		{
			direction=4;
			movesnake(); 
	    }
	    if(move=='w')
		{
			direction=1;
			movesnake(); 
	    }
	    if(move=='s')
		{
			direction=2;
			movesnake(); 
	    }
	}
}

int main()                           //主函数
{
	start();                         //数据初始化要放在外边

	while(1)
	{
		show();
		movesnake();
		put();
		HideCursor();
	}
}

Second:添加食物

  • 画面描述:小蛇吃到食物后身体长度+1,食物随机重现。
  • 原理描述:我们已经知道画面的显示依靠数组不同的值。食物对应数组的值为-2,小蛇吃到食物后,把原本食物位置的数组值从-2变为1,原本蛇头和身体对应数组值全部+1,这样一来食物被吃掉后相当于变成新的蛇头,蛇尾不再赋值为0,不对蛇尾进行操作。

见代码

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>

#define high 20
#define width 30

int a[high][width]={0};
int direction;//蛇头移动方向
int food_y,food_x;
//0为空格,-1为边框,-2为食物,1是蛇头,>1是蛇身 

int movesnake()
{
	int i,n;
//扫描所有位置,找到蛇并全部+1 
	for(i=1;i<high-1;i++)
		for(n=1;n<width-1;n++)
			if(a[i][n]>0)
			a[i][n]++;
		
	int oldtail_y,oldtail_x,oldhead_y,oldhead_x;
	int max=0;
	 
//定位此时的头尾 
	for(i=1;i<high-1;i++)
	
		for(n=1;n<width-1;n++)
		
			if(a[i][n]>0)
			{
				if(max<a[i][n])
				{//定位尾巴
					max=a[i][n];
					oldtail_y=i;
					oldtail_x=n;
				}
				if(a[i][n]==2)
				{//全部+1之后的2就是原来的头 
					oldhead_y=i;
					oldhead_x=n;
				}
			}
	
	int newhead_y,newhead_x;
//上下左右移动 
	if(direction==1)
	{
		newhead_y=oldhead_y-1;
		newhead_x=oldhead_x;
	}
	if(direction==2)
	{
		newhead_y=oldhead_y+1;
		newhead_x=oldhead_x;
	}
	if(direction==3)
	{
		newhead_y=oldhead_y;
		newhead_x=oldhead_x-1;
	}
	if(direction==4)
	{
		newhead_y=oldhead_y;
		newhead_x=oldhead_x+1;
	}
	//如果蛇头吃到食物
	if(a[newhead_y][newhead_x]==-2)
	{
		a[food_y][food_x]=0;
		food_x=rand()%(width-5)+2;
		food_y=rand()%(high-5)+2;
		a[food_y][food_x]=-2;
	  //原来的蛇尾被+1后自动保留了 
	} 
	else
	a[oldtail_y][oldtail_x]=0;
	
    //更新蛇头坐标
	a[newhead_y][newhead_x]=1;
	
    //不让蛇游得太快
	Sleep(100); 
}



void gotoxy(int x,int y)
{
	HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos;
	pos.X=x;
	pos.Y=y;
	SetConsoleCursorPosition(handle,pos);
}

void HideCursor()
{
	CONSOLE_CURSOR_INFO cursor_info={1,0};
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}

void start()
{
	int i,n; 
	//初始化边框 
	for(i=0;i<high;i++)
	{
		a[i][0]=-1;//第一列 
		a[i][width-1]=-1;//最后一列 
	} 
	for(n=1;n<width;n++)
	{
		a[0][n]=-1;//第一行 
		a[high-1][n]=-1;//最后一行 
	}//重复赋值是没问题的 
	
	//蛇头
	a[high/2][width/2]=1;
	
	//蛇身,四节分别赋值2345
	 for(i=1;i<=4;i++)
	{
	 	a[high/2][width/2-i]=i+1;
	} 
	
	//初始蛇向右移动
	direction=4;
	
	food_y=rand()%(high-5)+2;
	food_x=rand()%(width-5)+2;
	a[food_y][food_x]=-2; 
}

void show()
{
	gotoxy(0,0);
	int i,n;
	for(i=0;i<high;i++)
	{
		for(n=0;n<width;n++)
		{
			if(a[i][n]==0)
			printf(" ");
			else if(a[i][n]==-1)
			printf("+");
			else if(a[i][n]==1)
			printf("0");
			else if(a[i][n]>1)
			printf("o");
			else if(a[i][n]==-2)
			printf("*"); 
		}
		printf("\n");
	}
	Sleep(100);
}


void put()                  //用户使用键盘控制小蛇
{
	char move;
	if(kbhit())
	{
		move=getch();
		if(move=='a')
		{
			direction=3;
			movesnake(); 
	    }
	    if(move=='d')
		{
			direction=4;
			movesnake(); 
	    }
	    if(move=='w')
		{
			direction=1;
			movesnake(); 
	    }
	    if(move=='s')
		{
			direction=2;
			movesnake(); 
	    }
	}
}

int main()                    //主函数
{
	start();
	while(1)
	{
		show();
		movesnake();
		put();
		HideCursor();
	}
	return 0;
}

Third:如何Game Over

  • 画面效果:小蛇咬到自己或者撞墙就会game over!
  • 原理:蛇头坐标和身体/墙重合的时候输出game over

完整代码

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>

//宏定义画面大小
#define high 20
#define width 30

int a[high][width]={0};//画面储存在这个二维数组中
int direction;//小蛇移动的方向
int food_y,food_x;//食物坐标

void start()//初始化
{
	int i,n; 
	//初始化边框,边框对应的数组值为-1
	for(i=0;i<high;i++)
	{
		a[i][0]=-1;//第一列 
		a[i][width-1]=-1;//最后一列 
	} 
	for(n=1;n<width;n++)
	{
		a[0][n]=-1;//第一行 
		a[high-1][n]=-1;//最后一行 
	}//重复赋值是没问题的 
	

	a[high/2][width/2]=1;//蛇头赋值为1
	

	//蛇身,四节分别赋值2345
	 for(i=1;i<=4;i++)
	{
	 	a[high/2][width/2-i]=i+1;
	} 
	
    //初始蛇向右移动,4代表向右
	direction=4;
}


int movesnake()//小蛇移动时给数组重新赋值
{
	int i,n;
	//先给蛇头和蛇身对应的数组的值都+1
	for(i=1;i<high-1;i++)
	{
		for(n=1;n<width-1;n++)
		{
			if(a[i][n]>0)
			a[i][n]++;
		}
	}

	int oldtail_y,oldtail_x,oldhead_y,oldhead_x;//变量可以在用的地方随时定义
	int max=0;
	//找出原先的蛇尾和蛇头,并且赋值给oldtail和oldhead
	for(i=1;i<high;i++)
	{
		for(n=1;n<width;n++)
		{
			if(a[i][n]>0)
			{
				if(max<a[i][n])
				{//定位尾巴
					max=a[i][n];
					oldtail_y=i;
					oldtail_x=n;
				}
				if(a[i][n]==2)
				{//定位蛇头
					oldhead_y=i;
					oldhead_x=n;
				}
			}
	    }
	}
	
	int newhead_y,newhead_x;
	//蛇头移动方向:1上,2下,3左,4右 
    //对蛇头坐标进行+-实现不同方向移动
		if(direction==1)
	{
		newhead_y=oldhead_y-1;
		newhead_x=oldhead_x;
	}
	if(direction==2)
	{
		newhead_y=oldhead_y+1;
		newhead_x=oldhead_x;
	}
	if(direction==3)
	{
		newhead_y=oldhead_y;
		newhead_x=oldhead_x-1;
	}
	if(direction==4)
	{
		newhead_y=oldhead_y;
		newhead_x=oldhead_x+1;
	}
    
     //如果蛇头吃到食物
	if(a[newhead_y][newhead_x]==-2)
	{
		a[food_y][food_x]=0;
		food_x=rand()%(width-5)+2;
		food_y=rand()%(high-5)+2;
		a[food_y][food_x]=-2;
	  //原来的蛇尾被+1后自动保留了 
	} 
	else
	a[oldtail_y][oldtail_x]=0;
	 
	
	//蛇碰壁或者咬到自己就game over!
	if((a[newhead_y][newhead_x]>0)||(a[newhead_y][newhead_x]==-1))
	{
		printf("Game Over!\n");
		system("pause");
		exit(0);
	} 
	else
	{
		a[newhead_y][newhead_x]=1;
	}
	Sleep(100); 

}



void gotoxy(int x,int y)//清屏函数。原理是每循环一次让光标回归到指定坐标
{
	HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
	COORD pos;
	pos.X=x;
	pos.Y=y;
	SetConsoleCursorPosition(handle,pos);
}

void HideCursor()//隐藏光标函数
{
	CONSOLE_CURSOR_INFO cursor_info={1,0};
	SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
}

void show()//根据数组不同的值输出不同画面
{
	gotoxy(0,0);//每次循环都将光标移动到(0,0)位置,相当于清屏
	int i,n;
	for(i=0;i<high;i++)
	{
		for(n=0;n<width;n++)
		{
			if(a[i][n]==0)
			printf(" ");
			else if(a[i][n]==-1)
			printf("+");          //边界
			else if(a[i][n]==1)
			printf("0");          //蛇头(0当作字符就好)
			else if(a[i][n]>1)
			printf("o");          //蛇身
		}
		printf("\n");
	}
	Sleep(100);//                 //减慢蛇的移动速度,自由调整
}

void put()                        //用户通过键盘控制小蛇移动方向
{
	char move;
	if(kbhit())
	{
		move=getch();
		if(move=='a')
		{
			direction=3;
			movesnake(); 
	    }
	    if(move=='d')
		{
			direction=4;
			movesnake(); 
	    }
	    if(move=='w')
		{
			direction=1;
			movesnake(); 
	    }
	    if(move=='s')
		{
			direction=2;
			movesnake(); 
	    }
	}
}

int main()                           //主函数
{
	start();                         //数据初始化要放在外边

	while(1)
	{
		show();
		movesnake();
		put();
		HideCursor();
	}
}

如果有不懂的可以随时问我
就酱,拜拜~