【编程日常NO.00001】DOS贪吃蛇小程序的编写by arttnba3

前言

作为一名C/C++刚刚入门的小萌新,在刚刚学会敲代码后不久,便不自量力地着手开始尝试编写各种小程序

由于贪吃蛇看着好写,于是咱便“磨刀霍霍向IDE”…

事实证明萌新的代码力还是不够(趴)

在经历了一个下午与一个晚上的“奋战”后,才最终实现了一个贪吃蛇小游戏的基本功能_(XD)∠)_

由于DOS刷新机制十分蛋疼的缘故,游戏效果也十分蛋疼,等有时间再弄上图形化窗口(无限期咕咕咕)

基本原理

一、地图刷新与小蛋糕生成机制

(1)多维数组:设置多维度地图

要定义一个基础的游戏地图,我们很容易便能想到利用多维数组表示多维坐标
这一次我们所编写的是二维平面内的贪吃蛇,因此简单的设置一个二维数组便可:

char map[20][90]={0};//由于dos的布局十分蛋疼,因此我们定义一个长90高20的地图,利用C特性使其内的值全都被初始化为0,即空格字符
 /*
   x x x x x
y
y
y
y
y
y
*/

(2)system()函数:清空屏幕上所有字符,进行屏幕刷新

system()函数包含于stdlib.h头文件当中,接受一个字符串参数,在Windows系统下执行DOS命令,在Linux/UNIX系统下执行shell命令
我们已知cls命令在DOS下是清空屏幕字符,那么用此函数我们便可以在每一次贪吃蛇爬行结束后清空屏幕再重新输出,以实现屏幕刷新机制

#include<cstdlib>//C++特有形式,当然若是写C当中的stdlib.h也是完全没问题
...
system("cls");//清空屏幕内容

(3)srand()&rand()&time():随机生成小蛋糕位置

rand()函数为C当中用以生成伪随机数的函数,同样包含于stdlib.h当中,不接收任何参数,返回值为一遵循某一特定规律的整数
srand()函数为rand()函数的初始化函数,接收一个整型参数作为rand()函数的“种子”,即rand()以这个“种子”为初始参数生成的随机数数列
time()函数包含于time.h接受一个time_t类型的指针作为参数,将当前自UNIX初始纪元(1970/1/1 00:00:00)起所经过的秒数赋予所输入指针所指向的地址并将秒数作为返回值
因为时间值是在不断变化着且不会重复的,将time()的返回值作为“种子”输入srand()当中,我们就可以利用rand()生成完全随机的小蛋糕的坐标:

	time_t t;
	srand(time(t));
	cake_x=rand()%90;//控制x坐标在[0,89]以内
	cake_y=rand()%20;//控制y坐标在[0,19]以内
	int i;
	//检查蛋糕是否意外生成在?体内
	for(i=0;i<length_snake;i++)
		if(cake_x==snake_x[i])
		{
			i=0;
			cake_x=rand()%90;
		}
	for(i=0;i<length_snake;i++)
		if(cake_y==snake_y[i])
		{
			i=0;
			cake_y=rand()%20;
		}

二、 贪吃蛇爬行机制

(1)kbhit()函数:监听键盘

监听键盘,顾名思义,kbhit()函数在键盘没有输入时返回0,在监听到键盘有输入时便会返回非零值,但是它只会检测输入流当中是否有数据,而不会读取
与getch()这个无缓冲输入下读取字符的函数不同,在尚未读取到输入时,kbhit()函数就会直接返回0并使程序从下一个语句开始运行,而不会像getch()那样在无输入情况下便一直等待,因此配合着这两个函数,我们很容易便能写出一个简单的贪吃蛇移动控制模块的框架:

#include<conio.h>
...
if(kbhit())//若未监听到输入,则kbhit()会直接返回0,C/C++中0默认为false值
{
	ch=getch();//无缓冲读取输入,以实现实时操纵
	move_direction_change(ch);//在有输入的情况下,通过此函数改变移动方向
}

kbhit()函数虽然并非C标准库当中的函数,但是其定义所包含在的conio.h头文件在大部分的win32平台上的C编译器都是提供的,Linux和Unix平台虽然不提供,但是我们可以在网上下载该头文件
getch()函数同样包含在conio.h当中

(2)Sleep()函数:控制游戏速度

Sleep()函数接收一个整形输入,并将应用挂起相应的时间,通过使用这个函数与while循环结合,我们就可以控制贪吃蛇的爬行速度,并真正让我们的贪吃蛇跑起来!
Windows当中,Sleep函数沉睡的时间以毫秒记,而在UNIX当中则是以秒记,这点是需要我们当心的
那么我们很容易便能写出一个基础的游戏框架:

//以下除库内函数外皆需自行编写
while(true)
	{
		if(kbhit())//监听键盘是否传入操作
		{
			ch=getch();
			direction_move(ch);//不仅仅可以判断移动方向,还可以判断其他指令:存档、暂停、退出......
			if(stop_game){//全局变量,若接收到退出游戏的指令则打破循环
				stop_game=false;
				break;
			}
		}
		if(moving())//每次循环利用此函数使贪吃蛇前进一格,包含游戏结束判定模块,若游戏结束返回真值,否则返回假
		{
			gamefail();//输出游戏结束信息,跳出循环
			break;
		}
		Sleep(game_speed_set[game_speed]);//因为while循环的判定一直为true,因此通过控制程序挂起的时间便可以控制速度
		showmap();//每次程序挂起结束后便重新输出一次地图
	}

(3)爬行时蛇身的接续与死亡判定

初始化这条贪吃蛇我们有两种方式:

1.一维数组:蛇身接续

由于蛇是由一个个“小节点”构成的,因此我们很容易就能想到:可以利用多个一维数组表示每个节点在不同维度的坐标
本次我们编写的是二维平面内的贪吃蛇,因此使用两个一维数组即可:

int snake_x[2000]={0};//地图大小20*90=1800,因此我们设一个长度为2000的数组完全够用,同样利用C特性将其内全部初始化为0
int snake_y[2000]={0};

每次蛇进行移动时,只需要把每个节点自己的坐标给下一个节点即可,最后第一个节点再根据移动方向的设置改变坐标值:

int temp_x[2],temp_y[2];//用以临时储存坐标值
...
void linking(void)
{
	int i;
	bool pd=true;
	for(i=1;i<length_snake;i++)
	{
		if(pd)
		{
			temp_x[1]=snake_x[i];
			temp_y[1]=snake_y[i];
			snake_x[i]=temp_x[0];
			snake_y[i]=temp_y[0];
			pd=false;
		}
		else
		{
			temp_x[0]=snake_x[i];
			temp_y[0]=snake_y[i];
			snake_x[i]=temp_x[1];
			snake_y[i]=temp_y[1];
			pd=true;
		}
	}
}
2.死亡判定

而贪吃蛇的死亡判断分为两种:吃到自己或是撞到边界,这也是很好处理的:

由于一直在移动的是蛇头,撞墙时首当其冲的也是蛇头,因此我们只需要在每次运动完之后检测蛇头的坐标是否达到地图边界即可:

  if(snake_x[0]<0||snake_x[0]>90||snake_y[0]<0||snake_y[0]>20)//检查移动后是否撞墙
    {
    	failkind=0;//记录游戏失败原因
    	return 1;
    }

利用这个思想,对于贪吃蛇是否咬到自己,我们只需要检测蛇头移动后的位置是否是蛇身所在的位置即可:

int checkeaten(void)
{
	if(length_snake<4)
		return 0;
	for(int i=1;i<length_snake+1;i++)
	{
		if((snake_y[0]==snake_y[i])&&(snake_x[0]==snake_x[i]))
			return 1;//you've eaten yourself!
	}
	return 0;	
}

最后整合入移动模块主体:

int moving(void)
{
	temp_y[0]=snake_y[0];//储存第一个节点的值
	temp_x[0]=snake_x[0];
	switch(moving_direction)//根据移动方向设置进行第一个节点的坐标改变
	{
		case 0:
				snake_y[0]--;
				break;
		case 1:
				snake_y[0]++;
				break;
		case 2:
				snake_x[0]--;
				break;
		case 3:
				snake_x[0]++;
				break;
	}
	if(snake_x[0]==cake_x&&snake_y[0]==cake_y)//检查是否吃到小蛋糕
	{
		length_snake++;
		gam
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值