#include
C语言的图形库文件,其后下面函数在文件内:setfillstyle(),bar(),setcolor()
#include
C语言的标准输入输出库文件
#include
C语言的标准库文件,其后下面函数在文件内:int rand(void)
#include
C的标准库文件, 其后下面函数在文件内:kbhit()
#include
C语言的标准库文件,其后下面函数在文件内:bioskey()
语法点:文件包含P97
&& 函数P66
#define MAX 200
#define MAXX 30
#define MAXY 30
语法点:宏定义P95
#define UP
18432
#define DOWN
20480
#define LEFT
19200
#define RIGHT
19712
#define ESC
283
#define ENTER
7181
#define PAGEUP
18688
#define PAGEDOWN 20736
#define KEY_U
5749
#define KEY_K
9579
#define CTRL_P
6512
设定每个键在dos下的码,以便后面调用key
= bioskey(0)时用
如UP表示上方向键,它在系统的键码是18432,后面的键以此类推
#define TRUE 1
#define FALSE 0
C++中使用BOOL类型代替
#define GAMEINIT 1
游戏初始标志
#define GAMESTART 2
游戏开始标志
#define GAMEHAPPY 3
游戏成功标志
#define GAMEOVER 4
游戏结束标志
struct SPlace
{ int
x;
int y;
int st;
} place[MAX];
语法点:结构体P151
struct SPlace 用于记录蛇身体的一点的结构体,结构体内:x,
y为蛇身中某点在屏幕上的十字坐标,st为状态标志,值为1时则该结构体所存值为蛇身一点,需要显示;值为0时表示结构体不再存蛇身点数据的有效值,不显示. struct SPlace数组 place[MAX]用于记录组成蛇身体所有点的信息。开始时蛇身只有两点,每吃一个食物后,蛇身点数加1。当蛇移动时,组成蛇身的点的数据也会发生变化。
int speed;
控制游戏速度的变量,值越大,速度越快。
int count;
记录蛇吃食物数量
int score;
游戏得到的分值
int control;
游戏按键的控制,它的不同值表示按上下左右四种状态
int head;
蛇头点信息存在place[MAX]数组中的下标值
int tear;
蛇尾点信息存在place[MAX]数组中的下标值
int x, y;
int babyx, babyy;食物的在屏幕的X,Y坐标
int classa;
游戏的当前等级,等级越高,速度越快
int eat;
int game;
语法点:数据类型P10
&& 变量P12
int gamedelay[]={20000, 4000, 3000,
2000, 1000, 500, 250, 100};
int gamedelay2[]={1000, 1};
语法点:数组P104
&& 变量属性P84
延迟1
gamedelay[],用于控制速度的参数,延迟2 gamedelay2[],和延迟1一起来控制画面切换的speed:gamedelay[classa] +
gamedelay2[hit]
static int hitme = TRUE, hit =
TRUE;
语法点:变量的属性p84
void init(void);
初始化函数声明
void nextstatus(void);
下一状态计算函数声明
void draw(void);
绘制函数声明
语法点:函数P64
void init(void)
初始化函数,把游戏的所有用到的资源都初始化
{
int i;
for (i = 0; i < MAX; i++)
{
place[i].x
= 0;
蛇身体点数组place[i],记录某点在屏幕坐标
place[i].y
= 0;
place[i]的所有值赋0,这些点开始都不使用
place[i].st = FALSE;组成蛇的身体的点开始都不使用,也不用显示
}语法点:FOR语句P48 &&
结构体P151
place[0].st = TRUE;
开始的蛇身体只有2个点,这两个点要显示
place[1].st = TRUE;
place[1].x
= 1;
2个点:place[0]是蛇尾,place[1]是蛇头
speed = 9;游戏开始的速度是9,gamedelay[classa] + gamedelay2[hit]
count = 0;
游戏开始所吃食物数量为0
score = 0;
游戏开始分数为0,吃食物后分数增加
control = 4; control = 4时,游戏开始默认的前进方向是右方向
control = 3时,游戏开始默认的前进方向是左方向
control = 2时,游戏开始默认的前进方向是上方向
control = 1时,游戏开始默认的前进方向是下方向
head = 1;
place[tear]是蛇尾,place[head]是蛇头
tear = 0;
head和tear标志着蛇头蛇尾信息在place数组的位置
x = 1;
游戏开始蛇的坐标为(1,0)点,即也是蛇头的坐标
y = 0;
babyx = rand()%MAXX;
babyy = rand()%MAXY;
babyx和babyy是食物点的十字坐标,坐标由系统随机产生。函数 int rand(void)定义在下面文件中,#include
rand()会返回一随机数值,范围在0至2147483647。rand()%MAXX和babyy = rand()%MAXY使得食物的坐标最终会落入屏幕内。
eat = FALSE;
当前EAT状态量表示还没有吃到此刻的食物
game = GAMESTART;当前GAME状态量表示游戏开始
}
void nextstatus(void)
下一状态计算函数,用于计算出游戏的下一个画面的状态
{
int i;
int exit;
表示退出状态标志,它的值表示当前是否处理退出状态
int xx, yy;
xx, yy表示保持相同方向前进后,下一步蛇头可能的坐标
xx = x;
yy = y;
switch (control)
control表示当前所按下的方向键,定义于前的全局变量
{
case 1:
y--;
y表示在按下方向键后,此刻新的蛇头的y坐标
yy = y - 1;xx,
yy表示保持相同下方向前进后一步,下一步蛇头可能的坐标
这样做是为了检测蛇头就要碰墙这种状态。
break;
按方向键的下方向时,蛇头点的下一个坐标要在y方向下移一个单位,x方向坐标保持不变.
case 2:表示游戏状态中按上方向键,上方向时,蛇头点要整体y方向上移一个坐标,x方向坐标保持不变
y++;
y表示在按上方向键后,此刻新的蛇头的坐标
yy = y + 1;
xx, yy表示保持相同方向前进后,下一步蛇头可能的坐标
这样做是为了检测蛇头就要碰墙这种状态。
break;
按方向键的上方向时,蛇头点的下一个坐标要在y方向下移一个单位,x方向坐标保持不变.
case 3:
x--;
x, y表示在按左方向键后,此刻新的蛇头的坐标
xx = x - 1;xx,
yy表示保持相同方向前进后,下一步蛇头可能的坐标
break;
按方向键的左方向时,蛇头点的下一个坐标要在y方向下移一个单位,x方向坐标保持不变.
case 4:表示游戏状态中按右方向键,右方向时,蛇身要整体x方向右移一个坐标,y方向坐标保持不变
x++;
x, y表示在按右方向键后,此刻新的蛇头的坐标
xx = x + 1;
xx, yy表示保持相同方向前进后,下一步蛇头可能的坐标
break;
按下方向键的右方向时,蛇头点的下一个坐标要在y方向下移一个单位,x方向坐标保持不变
}
hit = TRUE;
hit用于标明一种状态,当蛇头下一步就要撞到墙或自身某点,但现在还没撞到的状态,此刻只要改变前进的方向,蛇就不会撞,我们要把画面切换的时间变慢,也就是要把游戏的速度变慢,以使游戏者有时间在这种状态改变运行方向,以免撞上。
if (((control == 1) ||(control ==2))
&&((y < 1) ||(y
>= MAXY - 1)) ||
(((control == 3) ||(control == 4))
&&((x < 1) ||(x
>= MAXX - 1))))
{
hit = FALSE;
}
上面的判断有没有出现下面2种情况:1蛇头出界了,这种情况后面的代码处理,2蛇此时没撞上,但即将撞上墙壁,上面说了x,y是当前蛇头的,xx,yy是当前蛇头在不改前进方向的情况下,蛇头下一步可能坐标,也就是下一状态就要撞上墙壁时,我们要把控制相邻两个画面切换的时间减小,以便有时间在马上要撞上后,改变方向。后面代码if (stchange > gamedelay[classa] +
gamedelay2[hit])就是控制这个时间延迟的,其中gamedelay2[hit](hit==false)>gamedelay2[hit](hit==true),以便尽快检测到具体情况的发生
if ((y < 0) ||(y >= MAXY)
|| (x
< 0) ||(x >= MAXX))
{
蛇出界发生的情况,游戏结束。蛇出界分水平和垂直出界
game = GAMEOVER;当前GAME状态量表示游戏结束
control = 0;
return;游戏结束,程序返回
}
for (i = 0; i < MAX; i++)
{
下面查看place数组中的所有点有出有出现下面两种情况
if ((place[i].st) && (x ==
place[i].x) && (y ==
place[i].y))
{
game = GAMEOVER;
control = 0;
return;
}
蛇已经撞到自身某点的情况,place[i].st说明这点是蛇身的某点,(x == place[i].x) && (y ==
place[i].y)说明蛇头当前点和蛇身某点place[i]的坐标值相同的情况,也就是两点相撞了,游戏结束。
if ((place[i].st)
&& (xx == place[i].x)
&& (yy ==
place[i].y))
{
蛇此时没撞上,但即将撞上蛇身,再过一个画面就要撞上蛇身时,我们要把控制画面切换的时间减小后面代码if (stchange > gamedelay[classa] +
gamedelay2[hit]),以便尽快检测到具体情况的发生
hit = FALSE;
goto OUT;
只要有一个点满足这种情况,其它点就不会满足这种情况,直接跳出这个for进入下一段
}
}
上面的循环结束了到此
OUT:
if ((x == babyx) &&(y ==
babyy))
{
吃到食物:蛇头坐标和食物坐标相同时就表示吃到食物
eat = TRUE;
count ++;
score +=(1 + classa) * 10;
eat = TRUE表示吃到食物的状态,此后,蛇的身体要加一个点。score表示当前游戏者所得分数的计数值,这是如何计分的呢?;count表示所吃食物的计数值;
}
head ++;
头部指针加1,这里的头部指针是指在place[MAX]数组中存蛇头的那个位置,用它标识的place[head]来存放当在前进一格后的蛇头。为什么这样呢,因为在head ++前那时的head是标识上一状态蛇头的,在蛇头前进了一下后,这一点就不是蛇头了,而是蛇头后的一点。大家可以画出相邻两个状态下,组成蛇所有点的变化图。
if (head >= MAX) head = 0;
假如蛇头点存在数组最后一个位置,那么从0,也就是从数组开头存起,
place[head].x = x;下一位置的点,即作新状态下的蛇头点
place[head].y = y;
place[head].st= TRUE;
新蛇头该点是要显示的
if (eat == FALSE)
没吃到食物的情况
{
place[tear].st = FALSE;
因为所有点向前进了一格,前一状态下的蛇尾的点坐标就变成新状态下蛇尾点坐标后一点的坐标,它不在蛇身体上,数组这个位置就不是有效位置,不要显示。如果吃到食物,蛇尾点在下一画面中仍然显示,这就是后面的处理了。
tear ++;
尾指针前进一个位置。
if (tear >= MAX)
tear = 0;
}
else
{吃到食物的情况是,原来整条蛇,包括原来的蛇头一起变成新蛇的蛇身,新蛇的蛇头是原来蛇头的下一点坐标
eat = FALSE; eat = FALSE重新变成初值,检测一下个食物有没有吃到状态
exit = TRUE;
while (exit) 假如随机设定的食物点和蛇身上某点重合,就重新设定食物坐标
{
babyx = rand()%MAXX; 用rand()%MAXX重新随机设定食物坐标
babyy = rand()%MAXY;
exit = FALSE;
for (i = 0; i < MAX; i++)
if ((place[i].st) &&(place[i].x ==
babyx) &&(place[i].y ==
babyy))
exit ++;
}
}
if (head == tear)
当存放蛇身点的place数组装满后,游戏成功
game = GAMEHAPPY;
}
void draw(void)把刚才计算出来的某一状态中,游戏中所有信息绘制出
{
char temp[50];
int i, j;
for (i = 0; i < MAX; i++)
{
setfillstyle(1, 9);
函数定义在#include
,是C语言图形库文件,设定当前的显示模式和颜色
if (place[i].st)
bar(place[i].x*15 + 1, place[i].y*10
+ 1, place[i].x*15 + 14, place[i].y*10 + 9);
}
画蛇的身体的各点,身体存在的点即place[i].st为1的点,蛇身体的一点,我们绘制成一个矩形调用 bar()函数绘制,此函数定义在#include
中.把蛇身体的一点绘制成矩形。
setfillstyle(1, 4);
设定当前的显示模式和颜色
bar(babyx*15 + 1, babyy*10 + 1, babyx*15 + 14, babyy*10 +
9);
用同样的方法画食物
setcolor(8);
此函数定义在#include
中,用不同的颜色标识蛇头区别蛇头和蛇身
setfillstyle(1, 8);
bar(place[head].x*15 + 1, place[head].y*10 + 1, place[head].x*15 +
14, place[head].y*10 + 9);
用同样的方法画蛇头,你想一下,place[head]是不是存的是蛇头的坐标信息
rectangle(0, 0, 15*MAXX, 10*MAXY);
此函数定义在#include
中,用来画矩形
sprintf(temp, "Count: %d", count);
此函数定义在#include
,用来把字串写入数组temp 中
settextstyle(1, 0, 2);
此函数定义在#include
中,用来设定文本风格
setcolor(8);
outtextxy(512, 142, temp);
此函数定义在#include
中,用来把字符数组TEMP输出到屏幕
setcolor(11);
outtextxy(510, 140, temp);
sprintf(temp, "1P: %d", score);
settextstyle(1, 0, 2);
setcolor(8);
outtextxy(512, 102, temp);
setcolor(12);
outtextxy(510, 100, temp);
sprintf(temp, "classa: %d", classa);
setcolor(8);
outtextxy(512, 182, temp);
setcolor(11);
outtextxy(510, 180, temp);
}
程序运行时右部有些提示信息,是用上面的几行实现的
int main()
{
int pause = 0;
程序开始时pause =
0表示程序不是暂停的
char temp[50];
int d, m;
d和m不用管它,它只是C语言画图的固定用法
int key;
int p;
static int keydown = FALSE;状态的表示:keydown = FALSE表示当前按键没有按下
int exit = FALSE;程序开始时exit = FALSE表示程序没有退出
int stchange = 0;表示后面的while循环执行1次*/
d = VGA;
m = VGAMED;d和m不用管它,它只是种固定的用法
initgraph(&d, &m,
"c:\\tc3\\bgi");
函数没有必要详细了解,是C语言画图时固定的格式。图形初始化函数,第一个参数传递
一个枚举常量,指定显示方式,第二个参数传递一个变量,获取当前显示模式第三个参数指定当前绘图系统的驱动库路径
setbkcolor(3);设定背景颜色
classa = 3;设定开始等级
为3,等级对应游戏的速度
init();
初始化所有状态信息
p = 1;
相邻的两个画面用p=0和p=1去标识
while (!exit)当按ESC时,EXIT变为TRUE,循环结束
{
if (kbhit()) 定义于conio.h中检查当前键有否按下
{
key = bioskey(0);
返回下一个在键盘键入的值,bioskey(0)定义于bios.h中,系统定义了键盘所有键的值。
switch (key) 根据KEY值判别当前按了上下左右等多个键
{
case UP:
if ((control != 2)&&
!keydown)
control = 1;
keydown = TRUE;
break;
当key的值等于UP说明我们按下方向键上,事先我们把宏UP定义为方向键上的键值;当游戏开始后,也就是keydown不是false时且刚才的按键不是下时,键才起作用
case DOWN:
if ((control != 1)&&
!keydown)
control = 2;
keydown = TRUE;
break;
当key的值等于DOWN说明我们按下方向键下,事先我们把宏DOWN定义为方向键下的键值;当游戏开始后,也就是keydown不是false时且刚才的按键不是上时,键才起作用
case LEFT:
if ((control != 4)&&
!keydown)
control = 3;
keydown = TRUE;
break;
当key的值等于LEFT说明我们按下方向键左,事先我们把宏LEFT定义为方向键左的键值;当游戏开始后,也就是keydown不是false时且刚才的按键不是右时,键才起作用
case RIGHT:
if ((control != 3)&&
!keydown)
control = 4;
keydown = TRUE;
break;
当key的值等于RIGHT说明我们按下方向键右,事先我们把宏RIGHT定义为方向键右的键值;当游戏开始后,也就是keydown不是false时且刚才的按键不是左时,键才起作用
case ESC:
exit = TRUE;
break;
当前按的是ESC就退出游戏
case ENTER:
init();
游戏开始初始化
break;
按ENTER键后,游戏才开始,其它的键才能控制通过keydown实现
case PAGEUP: 按下上翻页键表示减除等级,即减除延迟,游戏速度变快
classa --;
classa减小,游戏的延迟变小速度变快if (classa < 0)
classa = 0;
速度减小到为0
break;
case PAGEDOWN: 增加等级,即增加延迟,变慢
classa ++;
if (classa>7)
classa = 7;
break;
case KEY_U:
if (((control ==1) ||(control
==2))&& !keydown)
control = 3;
else if (((control == 3) ||(control ==
4))&& !keydown)
control = 1;
keydown = TRUE;
break;
改变前进状态,假如刚才是在垂直方向运动,就左转,水平方向运动就下转
case KEY_K:
if (((control ==1) ||(control
==2))&& !keydown)
control = 4;
else if (((control == 3) ||(control ==
4))&& !keydown)
control = 2;
keydown = TRUE;
break;
改变前进状态,假如刚才是在垂直方向运动,就右转,水平方向运动就上转
case CTRL_P:pause = 1 - pause;
break;
当按下暂停,时游戏停,pause
的值在0和1间切换,表示游戏暂停和继续。后面有这样的代码:if (!pause)
nextstatus();这就实现了游戏的暂停和继续
}
}
stchange ++;
前面的while
(!exit)执行一次stchange就加1,当while执行了gamedelay[classa] +
gamedelay2[hit]之和规定的次数时,画面就重新绘制一次,程序通过stchange和延迟数来控制游戏速度
putpixel(0, 0, 0);
在坐标0,0处画黑色,这里第三个0表示黑色,定义于graphics.h中
if (stchange > gamedelay[classa] +
gamedelay2[hit])
{
stchange从0开始加,循环每执行一次,就加上,一直加到大于gamedelay[classa] +
gamedelay2[hit]之和画面就重画一次,while循环执行的次数越多,时间就越长。gamedelay[classa] + gamedelay2[hit]之和用于控制stchange最大值,gamedelay[classa] +
gamedelay2[hit]越小游戏运行越快,越大运行越慢;classa值越大,表示等级越高,gamedelay[classa] 就越小,游戏运行就越快;hit可以取false和true,你想一下hit=false时表示蛇处于下一步就要撞上,而当前没有撞上的状态,这种情况下,把速度调慢,以使用户及时改变方向。
stchange = 0;
每当画面就重新绘制一次,stchange又变成0,用于控制下一次画面重新绘制的时间
keydown = FALSE;
p = 1 - p;
p一下取0,一下取1,以表示相隔的两个画面绘制状态
setactivepage(p);
C语言中画图的方法以:设P标识的页为活动页
cleardevice();
把上一个画面清除,准备绘制下一个画面,如果速度变化很快,所以你看到的游戏好像在动,其实它就是把一副画面绘制出来,然后清除图形屏幕,再把经过gamedelay[classa] +
gamedelay2[hit]次while后的画面再绘制出来,如此反复,所以你看起来游戏就在动了.
if (!pause)
nextstatus();
用pause控制游戏暂停和继续,如不在暂停状态,就进入下一状态信息的计算函数
else
显示暂停信息
{
settextstyle(1, 0, 4);
setcolor(12);设定文本绘制颜色
outtextxy(250, 100, "PAUSE");
}
draw();
把刚才计算出来的下一状态信息绘制出来,可以回到draw()中看是如何实现
if (game == GAMEOVER)
{
设定游戏结束状态信息,你想一下这3个函数
settextstyle(0, 0, 6)
setcolor(8)
outtextxy(101, 101, "GAME OVER")
settextstyle(0, 0, 6);
setcolor(8);
outtextxy(101, 101, "GAME OVER");
setcolor(15);
outtextxy(99, 99, "GAME OVER");
setcolor(12);
outtextxy(100, 100, "GAME OVER");
sprintf(temp, "Last Count: %d", count);
settextstyle(0, 0, 2);
outtextxy(200, 200, temp);
}
if (game == GAMEHAPPY)设定游戏成功状态信息
{
settextstyle(0, 0, 6);
setcolor(12);
outtextxy(100, 300, "YOU WIN");
sprintf(temp, "Last Count: %d", count);
settextstyle(0, 0, 2);
outtextxy(200, 200, temp);
}
setvisualpage(p);
c语言中画图的方式,把p标志的数字设置为当前图形输出活动页
}
closegraph();绘图结束,释放资源
return 0;
}