对于新手而言,代码似乎是令人难以忍受的事情。但实际上,随着难题一个个解决,不论是谁,都会由衷地感到激动。
先从一个经典的贪吃蛇游戏开始。我们做的是贪吃蛇的极为简化的版本,使用命令行的、按一次走一次的那种。
首先,我们要用自顶向下、逐步求精的思想来逐步构建框架。构建框架的最好的方式,当然是通过头文件了。
我们要思考的是,这个贪吃蛇里面需要完成哪些动作?
最开始,游戏需要初始化,因此我们在头文件里面写上:
void Initialize();
然后,我们考虑到蛇是需要运动的,因此:
void MoveSnake();
但是这个函数似乎需要一点参数,因为要确定蛇的方向。因此我们改动一下这个函数的原型:
void MoveSnakeTowards(enum direction);
这里需要指出的是,“enum direction
”是一个整体,它是一个数据类型。我们要在这个头文件的顶部添加它的定义:
enum direction
{
RIGHT, DOWN, LEFT, UP
};
然后,我们考虑到贪吃蛇里除了蛇之外还有食物,因此我们需要一个函数:
void PutFood();
这个函数是否需要什么参数(比如说指定x, y)呢?我们考虑到食物放置的位置是随机的,因此不需要什么参数,只要随机取点,判断那个点是否是空的就可以了。
我们考虑到蛇头可能会碰到别的东西,可能是墙,也可能是食物,因此,如果蛇头进入了非空的地方我们需要判断一下:
void CheckScore();
这个函数的参数似乎不够,因为判断一个具体的点才有意义。因此改为:
void CheckScore(int, int);
如果判断发现玩家被淘汰了,我们需要:
void EndGame();
这样看起来代码的框架就做出来了。但是我们往深层次考虑,蛇身移动的时候应该怎么处理?我们需要一个队列:
struct queue
{
int n[200];
int count;
int frontIndex;
int backIndex;
};
再定义一下几个函数原型:
void queue_Push(struct queue*, int);
void queue_Pop(struct queue*);
void queue_Clear(struct queue*);
int queue_Front(struct queue*);
int queue_Back(struct queue*);
void queue_GetNextIndex(struct queue*, int*);
这样,代码的框架基本就构造完了。然后我们在main
函数中加上这些代码:
int main()
{
Initialize();
srand(time(NULL));
while (canGameContinue)
{
Output();
do
{
scanf("%c", &operation);
} while (operation == ' ' || operation == '\n' || operation == '\t');
switch (operation)
{
case 'A':
case 'a':
MoveSnakeTowards(LEFT);
break;
case 'D':
case 'd':
MoveSnakeTowards(RIGHT);
break;
case 'W':
case 'w':
MoveSnakeTowards(UP);
break;
case 'S':
case 's':
MoveSnakeTowards(DOWN);
break;
default:
break;
}
}
return 0;
}
这样自顶向下的工作就完成得差不多了。接下来就需要逐步求精,把函数的定义补全。考虑到很多读者看到这里已经昏昏欲睡,作者的写博客的热情也不是很高。这里就不详细讲了。不过需要注意的是,“自顶向下,逐步求精”和“面向对象”的方法并不是互斥的。例如,上面有关queue
的函数,实际上是用了“面向对象”的方法。在编程的过程中,经常会同时使用多种方法,读者应当灵活选用。