1.功能需求分析

1.1主要功能

i.游戏欢迎界面

ii.游戏执行功能,包括计算得分

iii.游戏结束界面

1.2游戏基本规则

游戏开始时蛇的长度是4个单位,并且按照当前方向不停地移动。移动范围是COLUMNS*ROWS个格子。食物随机出现在屏幕上,但不能紧靠边缘,保存屏幕上有3个事物。如果蛇碰到边缘或自己,则游戏结束。游戏中可以暂停以及恢复游戏。

基本操作

i.游戏进行中玩家可以按键盘上的上、下、左、右键改变蛇的当前行进方向。蛇是根据当前方向自动移动的。

ii.游戏中按下回车键,暂停游戏。再次按下回车键则继续游戏。

2.总体设计

游戏以windows窗口的形式运行。窗口的左边作为游戏的桌面,右边是计分等提示信息显示区域,桌面的大小是COLUMNS*ROWS个单位。蛇出现的位置是桌面的中心,蛇的颜色为绿色,长度是4个单位。开始以后蛇向上移动。

食物为随机出现的原形图案,分为三种类型,用红、蓝和黄三种颜色区分,吃到红色食物的1分,蓝色食物得2分,黄色食物得3分。在桌面的右上方显示得分。得分下面显示帮助信息。当蛇的头部(蛇身体的第一个单位)碰到食物时,碰到的食物变为蛇的身体的一部分。如果蛇的头部碰到窗口的边缘或自己的身体时,结束游戏。当键盘的方向键按下时改变蛇的当前运动方向为方向键所指向的方向,但不能回头。例如,假设蛇当前的移动方向是向左移动,这个时候按下右方向键不起作用。

3.处理流程

贪吃蛇游戏开始以后首先创建连续的四个坐标作为贪吃蛇的身体,然后随机在桌面上的三个坐标点生成食物;默认设置贪吃蛇的移动方向为向上移动;贪吃蛇移动时根据方向计算蛇头部移动方向的坐标,将头部坐标设置为新的坐标,依次改变坐标为其前一节的坐标。移动之前要检查是否碰到边缘、食物或自己。主要的处理流程如图:

4.详细设计与系统实现

作为游戏编程,绘图是程序主要实现的功能,玩家的操作改变数据的值以及程序中自动改变的数据的值通过每隔一小段时间重新绘制,体现到界面上就构成了游戏的主体。由此可见,数据是游戏的核心部分。下面首先设计游戏的主要数据结构和变量,然后设计如何将数据绘制到界面上以及如何操作这些数据。

数据结构

1.游戏桌面数据结构和食物设计

根据总体设计,在游戏窗口的左边部分,用ROWS*COLUMNS的单位大小表现一个表格,作为蛇移动的空间。那么需要定义一个整型二维数组:

table[ROWS][COLUMNS]

这个二维数组的第一维ROWS表示行即y轴坐标,第二维COLUMNS表示列即x轴坐标。每个坐标点值的含义是:0表示空白,1到3表示分值分别为1到3的食物。如图:

      图1.游戏桌面数据table的示例

2.贪吃蛇数据结构的设计

贪吃蛇身体的每一部分用其在桌面上的坐标表示,用单链表的形式将每部分联系起来。设计全局变量head存储头节点的指针。身体每部分的数据结构如下:

  1. typedef struct _snake_body
  1. {
  1. int x;
  1. int y;
  1. struct _snake_body* next;
  1. }snake_body;

所以蛇在桌面上的表示形式以及每一节之间的联系,如图:

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

3.系统实现

首先打开vs,新建Win32 Application, 选择Empty Proj, 单击Finished 完成SnakeWin项目创建,然后在Header Files 和Source Files 中分别新建Snake.h和Snake.c文件。首先打开Snake.h文件把系统要用到的函数声明到这个文件中,具体声明如下:

  1. //Header Info
  1. #ifndef WIN_H_H
  1. #define WIN_H_H
  1. #include <Windows.h>
  1. #endif
  1.  
  1. //Function Definitions
  1. void clear_snake(); //清空贪吃蛇链表
  1. int out_of_table(int x ,int y); //检查某一个坐标点(x,y)是否在游戏桌面外
  1. int eat_self(int x , int y); //检查坐标点(x,y)是否碰到贪吃蛇的身体部分
  1. void eat_food(int x , int y); //处理贪吃蛇吃掉(x,y)坐标点食物的函数
  1. //贪吃蛇头部移动到(x,y)坐标点的函数,头部移动到(x,y)点,
  1. //其它部分依次移动到前一个节点的坐标
  1. void move(int x , int y);
  1. void create_food(); //在游戏桌面上创建食物
  1. int random(int seed); //取得一个随机数
  1. void new_game(); //创建一个新游戏
  1. void run_game(); //执行游戏的处理
  1. void draw_table(); //绘制游戏桌面
  1. void paint(); //WM_PAINT消息调用的函数,将内存位图绘制到窗口上
  1. void key_down(WPARAM wParam); //按键处理函数
  1. void resize(); //位图随窗口大小改变而改变
  1. void intialize(); //初始化游戏
  1. void finalize(); //释放初始化游戏时创建的资源
  1. //响应windows消息的回调函数,用来处理windows消息
  1. LRESULT CALLBACK WndProc(HWND hwnd, UINT message,WPARAM wParam,LPARAM lParam);

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

在本系统中要用到的函数大概就是上面这些了, 那么在Snake.h中的函数声明也就结束了,下面回到Snake.c中来实现这些功能模块,打开Snake.c, 首先包含相关的头文件,具体如下:

  1. //header Info
  1. #include <time.h>
  1. #include <stdio.h>
  1. #include "Snake.h"

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

游戏中要设定游戏桌面的长宽,食物数量以及食物颜色等诸多常量,为了便于维护我们决定把它们声明为常量宏,这样需求有变,只需修改这些常量宏即可了。相关常量定义如下:

  1. //constant definition
  1. #define APP_NAME "SNAKE" //游戏窗口标题
  1. #define APP_TITLE "Snake Game" //游戏开始标题
  1. #define GAMEOVER "GAME OVER" //游戏结束标题
  1. #define COLUMS 40 //游戏桌面可分成COLUMS列
  1. #define ROWS 40 //游戏桌面可分成ROWS行
  1.  
  1. #define FOOD_COUNT 3 //游戏食物数量
  1. //以下是游戏中可能要用到的颜色变量定义
  1. #define RED RGB(255,0,0)
  1. #define GREEN RGB(0,255,0)
  1. #define BLUE RGB(0,0,255)
  1. #define YELLOW RGB(255,255,0)
  1. #define GRAY RGB(128,128,128)
  1. #define BLACK RGB(0,0,0)
  1. #define WHITE RGB(255,255,255)
  1. #define STONE RGB(192,192,192)
  1. #define CHARS_IN_LINE 14 //显示分数字符串的长度
  1. #define SCORE "SCORE %4d" //显示分数的格式化字符串

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }为了实现系统功能,还需要定义相应的数据类型,包括一些结构体和枚举类型

1.定义一个贪吃蛇节点链表的结构体类型snake_body,用来表示蛇,其中x,y用来表示蛇身体的相对坐标,next表示指向下一个节点的指针,即蛇身体的下一块。具体定义如下:

  1. typedef struct _snake_body
  1. {
  1. int x;
  1. int y;
  1. struct _snake_body* next;
  1. }snake_body;

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }2.蛇移动的时候需要一个方向,而方向就只有上下左右这么四种值可选,没有别,所以合适定义为一个枚举类型direction,用来表示贪吃蛇的移动方向,0表示向上,1表示向下,2表示向左,3表示向右。具体定义如下:

  1. typedef enum _direction //蛇移动方向枚举类型
  1. {
  1. UP = 0,
  1. DOWN,
  1. LEFT,
  1. RIGHT
  1. }direction;

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }3.游戏中还需要一个变量来表示游戏当前的状态, 而游戏的状态也只有四种情况,即游戏开始、运行、暂停或结束。所以合适定义为一个枚举类型state, 来表示游戏的当前状态,具体实现如下:

  1. enum game_state //游戏状态
  1. {
  1. game_start,
  1. game_run,
  1. game_pause,
  1. game_over
  1. }state = game_start;

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

接下来还需要声明一些变量,这些变量是跟随系统的生命周期而存在的,所以需要声明为全局变量才行,它们的具体声明如下:

  1. //global variable Definition
  1. COLORREF food_color[] = // 食物颜色
  1. {
  1. RED,
  1. BLUE,
  1. YELLOW
  1. };
  1. snake_body* head = NULL; //定义一个蛇头节点,将其初始化为NULL
  1. direction dirt; //贪吃蛇当前移动的方向
  1. char score_char[CHARS_IN_LINE] = {0}; //显示分数的字符串
  1.  
  1. char* press_enter = "Press Enter key..."; //游戏结束时的提示信息
  1.  
  1. char* help[] = //字符串数组help中存储的是帮助信息
  1. {
  1. "Press direction key to change snake's direction.",
  1. "Press enter key to pause/resume game.",
  1. "Enjoy it. :-)",
  1. 0
  1. };
  1.  
  1. int score = 0; //得分记录的变量
  1. //int table[ROWS][COLUMS] = {0};
  1. int table[ROWS][COLUMS] = {0}; //游戏桌面
  1. clock_t start = 0; //游戏每一帧开始时间
  1. clock_t finish = 0; //游戏每一帧结束时间
以上是游戏操作时需要的变量,还有一类变量需要定义为全局变量,就是游戏的表示,游戏的表示图形需要通过WindowsGDI的一些函数和变量来实现,而它们同样是随着系统的生命周期而存在的,所以同样需要定义为全局变量才行,具体定义如下:
  1. //用于绘图的GDI全局变量
  1. HWND gameWND; //Window窗口句柄
  1. HBITMAP memBM; //内存位图
  1. HBITMAP memBMOld; //内存原始位图
  1. HDC memDC; //内存DC
  1. RECT clientRC; //游戏界面显示区域
  1. HBRUSH blackBrush; //黑色画笔
  1. HBRUSH snakeBrush; //贪吃蛇画笔
  1. HBRUSH foodBrush[FOOD_COUNT]; //食物画笔数组,三种食物,每种一个
  1. HPEN grayPen; //灰色画笔,用来绘制帮助信息
  1. HFONT bigFont; //大字体,用来显示游戏名字和字符串"GAME OVER"
  1. HFONT smallFont; //小字体,用来显示帮助信息等字体

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

至此,已经把系统中需要使用到的数据结构即变量等都声明定义完毕,下面来实现相应的功能模块,具体实现如下:

1.清空链表

函数名称:clear_snake

函数功能:清空贪吃蛇链表。具体实现如下:

  1. //Functions
  1. void clear_snake()
  1. {
  1. snake_body* p = head; //定义一个蛇链表节点p 指向头节点
  1. while(head != NULL)
  1. {//从头节点开始往后清空链表
  1. p = head->next;
  1. free(head);
  1. head = p;
  1. }
  1. }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }2.检查坐标越界

函数名称:out_of_table

函数功能:检查某一个坐标点(x,y)是否在游戏桌面外。具体实现如下:

  1. int out_of_table(int x ,int y)
  1. {
  1. //如果坐标点不在游戏桌面范围内,则返回真否则返回假
  1. if(x < 0 || y < 0 || x >= COLUMS || y >= ROWS)
  1. {
  1. return 1;
  1. }
  1. return 0;
  1. }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }3.检测坐标是否碰到蛇身

函数名称:eat_self

函数功能:检查坐标点(x,y)是否碰到贪吃蛇的身体部分,用来判断贪吃蛇在运行过程中,头部是否碰到了自己的身体。具体实现如下:

  1. int eat_self(int x , int y)
  1. {
  1. snake_body* p = head;
  1. while(p != NULL)
  1. { //将当前坐标值与蛇身上的每个坐标值进行对比
  1. //如果有坐标值相等的情况则返回1,表示碰到蛇身了
  1. //否则返回0
  1. if(x == p->x && y == p->y)
  1. {
  1. return 1;
  1. }
  1. p = p->next;
  1. }
  1. return 0;
  1. }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

4.蛇吃食物

函数名称:eat_food

函数功能:蛇吃到食物,身体变长

处理流程:处理贪吃蛇吃掉(x,y)坐标点食物的函数,蛇在顺利吃食物时,用食物所在的坐标值创建一个新的节点,作为蛇的头部坐标,将新节点的next指向蛇的原头部节点。所以蛇的身体变长,当移动方向不变。具体实现如下:

  1. void eat_food(int x , int y)
  1. {
  1. snake_body* p = head;//定义一个蛇链表指针变量,指向头节点
  1. //为head重新分配内存
  1. head = (snake_body*)malloc(sizeof(snake_body));
  1. //将当前食物坐标点赋值给头节点head,然后将p赋值给head的next域
  1. head->x = x;
  1. head->y = y;
  1. head->next = p;
  1. }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

5.蛇身移动

函数名称:move

函数功能:蛇移动的处理

处理流程:蛇的移动主要靠头部坐标的移动,函数的功能是将贪吃蛇头部移动到(x,y)坐标点,其它部分依次移动到前一个节点的坐标位置。具体实现如下:

  1. void move(int x , int y)
  1. {
  1. int tmpx, tmpy;
  1. snake_body* p = head;
  1. while(p != NULL)
  1. {
  1. //tmpx tmpy 记录当前p节点的坐标值
  1. //将当前传入的x,y赋值给p节点
  1. //然后将tmpx,tmpy记录的之前的p节点的值赋值给变量x,y
  1. //然后循环将所有值都向前移动一个单位位置。
  1. tmpx = p->x;
  1. tmpy = p->y;
  1. p->x = x;
  1. p->y = y;
  1. x = tmpx;
  1. y = tmpy;
  1. p = p->next;
  1. }
  1. }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }6.创建食物

函数名称:create_food

函数功能:在游戏桌面上创建食物,调用create_food()函数在桌面上随机创建3个不同颜色的食物。具体实现如下:

  1. void create_food()
  1. {
  1. //声明x,y变量获取食物生成的坐标值
  1. int x ,y;
  1. x = random(COLUMS - 2);
  1. y = random(ROWS - 2);
  1.  
  1. //如果随机生成的x,y构成的坐标点的值不是0,就表示上已经有食物了
  1. //或者说这个点是蛇身的一节,则
  1. //重新调用create_food()生成新的食物坐标点
  1. if(table[y+1][x+1] > 0)
  1. {
  1. create_food();
  1. Sleep(1);
  1. return ;
  1. }else{
  1. //否则,为该坐标点赋值
  1. //FOOD_COUNT 为常量3 通过random(FOOD_COUNT)+1 最终为选定的
  1. //食物坐标点赋值1,2或3 代表该食物的得分。
  1. table[y+1][x+1] = random(FOOD_COUNT) + 1;
  1. }
  1. }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }7.取随机数

函数名称:random

函数功能:取得一个随机数。具体实现如下:

  1. int random(int seed)
  1. {
  1. if(seed == 0)
  1. {
  1. return 0;
  1. }
  1. //注意这里返回的rand()%seed值 肯定是 0,1,……,到seed-1之间的自然数
  1. return (rand() % seed);
  1. }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }8.创建游戏

函数名称:new_game

函数功能:创建一个新游戏

处理流程:创建新游戏的过程是首先创建贪吃蛇,然后调用create_food()函数随机创建3个食物。具体实现如下:

  1. void new_game()
  1. {
  1. int i = 0;
  1. snake_body* p; //声明一个蛇链表节点
  1. //因为要用到rand()函数来为随机生成食物坐标点
  1. //但是在使用rand()函数之前,需要先调用srand()函数来生成相应的
  1. //种子才行,反正就是说要调用rand()函数,就要先调用srand()函数,
  1. //但是srand()函数只要调用一次就可以了
  1. srand((unsigned)time(NULL));
  1. //将表格所有值置0,即去除所有食物
  1. //因为不是0就代表那里是个食物的坐标点
  1. memset(table, 0, sizeof(int)*COLUMS*ROWS);
  1. //将上一局中的贪吃蛇链表清空
  1. clear_snake();
  1. //蛇头节点是全局变量,下面是定义一个蛇头节点,赋值给p
  1. head = (snake_body*)malloc(sizeof(snake_body));
  1. head->x = COLUMS /2;
  1. head->y = (ROWS - 4) / 2;
  1. head->next = NULL;
  1. p = head;
  1.  
  1. //蛇身初始化为4个节点
  1. for( i = 1; i <4; i++)
  1. {
  1. p->next = (snake_body*)malloc(sizeof(snake_body));
  1. p->next->x = head->x;
  1. p->next->y = head->y + i;
  1. p->next->next = NULL;
  1. p = p->next;
  1. }
  1. //随机创建3个食物
  1. for(i = 0; i < 3; i++)
  1. {
  1. create_food();
  1. }
  1. start = clock(); //初始化当前帧的开始时钟
  1. score = 0; //分数清零处理
  1. }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }9.运行游戏

函数名称:run_game

函数功能:运行游戏的处理,首先移动蛇的坐标,移动过程中要判断蛇头坐标是否出界,是否碰到蛇的身体,是否吃到食物等状态,如果蛇头出界或碰到蛇的身体,则结束游戏,如果碰到食物,调用eat_food()函数完成蛇吃食物的操作。具体实现如下:

  1. void run_game()
  1. {
  1. int x , y;
  1. finish = clock();
  1. //clock()函数返回从"开启这个程序进程"到
  1. //"程序中调用clock()函数"时之间的CPU时钟计时单元数目
  1. //linux下1秒钟大概有1000000个CPU时钟计时单元
  1. //如果从开始到结束用时超过300个CPU计时单元,
  1. //则响应一下蛇身的移动情况
  1. if((finish - start) > 300)
  1. {
  1. //取得贪吃蛇的头节点将要移动到的坐标点
  1. x = head->x;
  1. y = head->y;
  1. //方向键位判定
  1. switch(dirt)
  1. {
  1. case UP:
  1. y--;
  1. break;
  1. case DOWN:
  1. y++;
  1. break;
  1. case LEFT:
  1. x--;
  1. break;
  1. case RIGHT:
  1. x++;
  1. break;
  1. }
  1. //判断是否出界
  1. if(out_of_table(x,y))
  1. {
  1. state = game_over;
  1. }else if(eat_self(x,y)) //判断是否碰到贪吃蛇
  1. {
  1. state = game_over;
  1. }else if(table[y][x]) //判断是否碰到食物
  1. {
  1. score += table[y][x]; //加分
  1. table[y][x] = 0; //清除当前食物
  1. create_food();//生成新食物
  1. eat_food(x,y);//吃掉食物
  1. }else //正常移动贪吃蛇
  1. {
  1. move(x,y);
  1. }
  1. //下面的start可以控制蛇移动的速度
  1. start = clock();
  1. //重绘窗口区域
  1. InvalidateRect(gameWND,NULL,TRUE);
  1. }
  1. }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }10.绘制游戏桌面函数

函数名称:draw_table

函数功能:绘制游戏桌面。为了能看到蛇身体上的每一方块,绘制桌面时要按照先绘制蛇,再绘制食物,最后绘制表格的顺序来进行。这里还要根据蛇吃食物的情况绘制得分及相应的帮助信息。具体实现如下:

  1. void draw_table()
  1. {
  1. //声明三个变量hBrushOld
  1. //hPenOld, hFontOld记录系统原有的画刷,画笔,字体属性
  1. HBRUSH hBrushOld;
  1. HPEN hPenOld;
  1. HFONT hFontOld;
  1. //声明一个RECT结构的rc变量,记录客户区
  1. RECT rc;
  1. //声明变量x0,y0表示记录游戏桌面左上角的起始坐标点
  1. //w表示游戏桌面块的大小
  1. int x0, y0, w;
  1. //x,y表示坐标点的坐标值,i,j表示循环中需要用的变量
  1. int x ,y , i ,j;
  1. //字符指针变量str用来接收相应的说明字符串
  1. char* str;
  1. // int x1 ;
  1. // int y1 ;
  1. // int row ;
  1. // int column ;
  1. snake_body* snake = NULL; //定义蛇身节点,初始化为NULL
  1.  
  1.  
  1. w = clientRC.bottom / (ROWS + 2);//游戏桌面块的大小
  1. //w = clientRC.right /(ROWS*2);
  1. //w = 8;
  1. x0 = y0 = w;//赋值,游戏桌面的左上角坐标值
  1.  
  1. //用黑色绘制背景
  1. FillRect(memDC,&clientRC,blackBrush);
  1. //绘制开始和结束画面
  1. if(state == game_start || state == game_over)
  1. {
  1. memcpy(&rc,&clientRC,sizeof(RECT));
  1. rc.bottom = rc.bottom /2;
  1. hFontOld = (HFONT)SelectObject(memDC,bigFont);//选择字体
  1. SetBkColor(memDC,BLACK);//设置背景颜色
  1.  
  1. if(state == game_start)
  1. {
  1. str = APP_TITLE;
  1. SetTextColor(memDC,YELLOW);
  1. }else{
  1. str = GAMEOVER;
  1. SetTextColor(memDC,RED);
  1. }
  1.  
  1. DrawText(memDC,str,strlen(str),&rc,DT_SINGLELINE | DT_CENTER | DT_BOTTOM);
  1. SelectObject(memDC,hFontOld);//恢复系统原有的字体属性
  1.  
  1. //重新选择字体属性
  1. hFontOld = (HFONT)SelectObject(memDC,smallFont);
  1. rc.top = rc.bottom;
  1. rc.bottom = rc.bottom*2;
  1. //如果是游戏结束,则需要显示最终得分
  1. if(state == game_over)
  1. {
  1. SetTextColor(memDC,YELLOW);
  1. //将score以SCORE格式赋值给score_char
  1. sprintf(score_char, SCORE,score);
  1. //绘制最终得分
  1. DrawText(memDC,score_char, strlen(score_char),&rc,DT_SINGLELINE|DT_CENTER|DT_TOP);
  1. }
  1.  
  1. SetTextColor(memDC,STONE);
  1. DrawText(memDC,press_enter,strlen(press_enter),&rc,DT_SINGLELINE|DT_CENTER|DT_VCENTER);
  1. SelectObject(memDC,hFontOld);
  1.  
  1. return ;
  1. }
  1.  
  1. //绘制食物,扫描整个表格,只要
  1. //坐标值不是0,则表示有食物,则需要
  1. //根据坐标值来绘制相应颜色的食物
  1. for(i = 0; i < ROWS ; i++)
  1. {
  1. for(j = 0; j <COLUMS; j++)
  1. {
  1. if(table[i][j] > 0)
  1. {
  1. x = x0 +j*w;
  1. y = y0 + i*w;
  1. //根据坐标值来选择相应的颜色
  1. hBrushOld = (HBRUSH)SelectObject(memDC,foodBrush[table[i][j]-1]);
  1. //绘制椭圆
  1. Ellipse(memDC,x,y,x+w+1,y+w+1);
  1. }
  1. }
  1. }
  1. //选入新的画刷,准备绘制贪吃蛇
  1. //将贪吃蛇链表中的所有点所在的方块都绘制成带颜色的矩形
  1. //整体表示一条蛇
  1. hBrushOld = (HBRUSH)SelectObject(memDC,snakeBrush);
  1. snake = head;
  1. while(snake != NULL)
  1. {
  1. x = x0 + snake->x*w;
  1. y = y0 + snake->y*w;
  1. Rectangle(memDC,x,y,x+w+1,y+w+1);
  1. snake = snake->next;
  1. }
  1. //选择新的画笔
  1. hPenOld = (HPEN)SelectObject(memDC,grayPen);
  1. //下面绘制表格,此表格指定了绘制的行列数,且其中每一块
  1. //会随着窗口的缩放而改变大小
  1. for(i = 0; i <= ROWS; i++)
  1. //for(i = 0; i <= clientRC.bottom; i++)
  1. {
  1. MoveToEx(memDC,x0,y0+i*w,NULL);
  1. LineTo(memDC,x0+COLUMS*w,y0+i*w);
  1. }
  1. for(i = 0; i<= COLUMS; i++)
  1. //for(i = 0; i<= clientRC.right; i++)
  1. {
  1. MoveToEx(memDC,x0+i*w,y0,NULL);
  1. LineTo(memDC,x0+i*w,y0+ROWS*w);
  1. }
  1.  
  1. //下面这种绘制表格的方法是随着窗口的大小改变其绘制的行列数目,但是块的大小不改变
  1. //Start /////第二种绘制表格的方法///////////////////////////////////////
  1.  
  1. //w = 16;
  1. // x1 = w;
  1. // y1 = w;
  1. // column = clientRC.bottom/w -w;
  1. // row = clientRC.right/w*2;
  1. ////先绘制行
  1. //for(i = 0; i<= row;i++)
  1. //{
  1. // MoveToEx(memDC,x1,y1+i*w,NULL);
  1. // LineTo(memDC,x1+column*w,y1+i*w);
  1. //}
  1. ////绘制列
  1. //for(i = 0; i<= column;i++)
  1. //{
  1. // MoveToEx(memDC,x1+i*w,y1,NULL);
  1. // LineTo(memDC,x1+i*w,y1+row*w);
  1. //}
  1.  
  1. //End /////第二种绘制表格的方法///////////////////////////////////////
  1. SelectObject(memDC,hPenOld);
  1. //重新定位左上角的坐标点,用于绘制得分情况
  1. x0 = x0 + COLUMS*w + 3*w;
  1. y0 = y0 + w;
  1. hFontOld = (HFONT)SelectObject(memDC,smallFont);
  1. SetTextColor(memDC,YELLOW);
  1. sprintf(score_char, SCORE,score);
  1. //绘制得分情况
  1. TextOut(memDC,x0,y0,score_char,strlen(score_char));
  1. //重新定位左上角的坐标点,用于绘制提示信息
  1. x0 = (COLUMS + 2)*w;
  1. y0 += 4*w;
  1. SetTextColor(memDC,GRAY);
  1. i = 0;
  1. while(help[i])
  1. {
  1. TextOut(memDC,x0,y0,help[i],strlen(help[i]));
  1. y0 += clientRC.bottom / (ROWS + 2);
  1. i++;
  1. }
  1. SelectObject(memDC,hFontOld);
  1. }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }11.绘图

函数名称:paint

函数功能:WM_PAINT消息调用的函数,在窗口上绘制内存位图。具体实现如下:

  1. void paint()
  1. {
  1. //声明一个绘图结构信息变量ps
  1. PAINTSTRUCT ps;
  1. HDC hdc;
  1. draw_table();
  1. //在调用BeginPaint函数时,会把要绘制的图像信息自动填入到
  1. //ps中的各个字段中
  1. //如果BeginPaint执行成功,则返回指向特定显示设备的句柄
  1. //否则返回NULL
  1. hdc = BeginPaint(gameWND,&ps);
  1. //将内存DC位图贴到显示器上
  1. BitBlt(hdc,clientRC.left,clientRC.top,clientRC.right,clientRC.bottom,memDC,0,0,SRCCOPY);
  1. EndPaint(gameWND, &ps);
  1. }

 

接下来的实现部分请参看:C项目实践--贪吃蛇(2)

C项目实践--贪吃蛇(1)的更多相关文章

  1. C项目实践--贪吃蛇(2)

    12.按键处理 函数名称:key_down 函数功能:按键处理函数,主要包括:1.刚开始或结束时的按键处理,游戏开始时,按任意键进入游戏,游戏运行过程中按回车键是游戏的暂停或开始的切换键:2.游戏运行 ...

  2. 【C语言项目】贪吃蛇游戏(上)

    目录 00. 目录 01. 开发背景 02. 功能介绍 03. 欢迎界面设计 3.1 常用终端控制函数 3.2 设置文本颜色函数 3.3 设置光标位置函数 3.4 绘制字符画(蛇) 3.5 欢迎界面函 ...

  3. 【C语言项目】贪吃蛇游戏(下)

    目录 00. 目录 07. 游戏逻辑 7.5 按下ESC键结束游戏 7.6 判断是否撞到墙 7.7 判断是否咬到自己 08. 游戏失败界面设计 8.1 游戏失败界面边框设计 8.2 撞墙失败界面 8. ...

  4. 小项目特供 贪吃蛇游戏(基于C语言)

    C语言写贪吃蛇本来是打算去年暑假写的,结果因为ACM集训给耽搁了,因此借寒假的两天功夫写了这个贪吃蛇小项目,顺带把C语言重温了一次. 是发表博客的前一天开始写的,一共写了三个版本,第一天写了第一版,第 ...

  5. Android快乐贪吃蛇游戏实战项目开发教程-01项目概述与目录

    一.项目简介 贪吃蛇是一个很经典的游戏,也很适合用来学习.本教程将和大家一起做一个Android版的贪吃蛇游戏. 我已经将做好的案例上传到了应用宝,无病毒.无广告,大家可以放心下载下来把玩一下.应用宝 ...

  6. C语言实现贪吃蛇

    日期:2018.9.11 用时:150min 项目:贪吃蛇(C语言--数组   结构体实现) 开发工具:vs2013 关键知识:数组,结构体,图形库,键位操作 源代码: #include<std ...

  7. Unity3D游戏贪吃蛇大作战源码休闲益智手机小游戏完整项目

    <贪吃蛇大作战>一款休闲竞技游戏,不仅比拼手速,更考验玩家的策略. 视频演示: http://player.youku.com/player.php/sid/XMzc5ODA2Njg1Ng ...

  8. JavaScript实践-简单的贪吃蛇小游戏

    实现逻辑: //获取Html中的格子(行,列) //建立数组存储所有格子(x,y) //建立数组用于存储蛇身(x,y) //生成随机坐标(x,y)的函数 //随机创建蛇身并存储到蛇身数组 //创建食物 ...

  9. 初级练手项目——用Python一步一步实现“智能”贪吃蛇!

    贪吃蛇作为一款经典的小游戏,想必大家一定并不陌生,今天我们就来用Python来设计与实现贪吃蛇游戏,回味一下童年的快乐.快跟着小编来看看吧! ​ 基本环境配置 ●版本:Python3 ●系统:Wind ...

随机推荐

  1. linux 文件三大特殊权限(SUID SGID SBIT)

    SGID(这个应该是文件共享里面最常用权限管理手段) 作用于目录或可执行程序,作用于目录代表在此目录创建的文件或目录,默认的属组继承此目录的属组.例如 我这个testgroup 没有设置SGID .我 ...

  2. lr函数之lr_eval_string()函数的使用学习

    lr_eval_string() 函数的主要作用:返回脚本中的一个参数当前的值(从参数中取得对应的值,并且转换为一个字符串). 格式:lr_eval_string("{参数名}") ...

  3. NYOJ 203 三国志

    三国志 时间限制:3000 ms  |  内存限制:65535 KB 难度:5   描述 <三国志>是一款很经典的经营策略类游戏.我们的小白同学是这款游戏的忠实玩家.现在他把游戏简化一下, ...

  4. PTA 01-复杂度1 最大子列和问题 (20分)

    题目地址 https://pta.patest.cn/pta/test/15/exam/4/question/709 5-1 最大子列和问题   (20分) 给定KK个整数组成的序列{ N_1N​1​ ...

  5. BZOJ 1924: [Sdoi2010]所驼门王的宝藏 【tarjan】

    Description 在宽广的非洲荒漠中,生活着一群勤劳勇敢的羊驼家族.被族人恭称为“先 知”的Alpaca L. Sotomon 是这个家族的领袖,外人也称其为“所驼门王”.所 驼门王毕生致力于维 ...

  6. 51nod1184 第N个质数

    如题.$n \leq 1e9$. 方法零:二分,然后洲阁筛.要魔改一下的洲阁筛.跑得慢.卡卡能过.没意思. //#include<iostream> #include<cstring ...

  7. msp430项目编程13

    msp430中项目---温湿度检测系统 1.dht11工作原理 2.电路原理说明 3.代码(显示部分) 4.代码(功能实现) 5.项目总结 msp430项目编程 msp430入门学习

  8. vagrant的学习之Git学习

    vagrant的学习之Git学习 参考学习网址: http://www.runoob.com/git/git-install-setup.html. http://www.bootcss.com/p/ ...

  9. Swift--错误集:couldn’t be opened because you don’t have permission to view it

    bug复现过程  把snapkit拉入代码中时,也把里面的info.plist文件拖到项目中,运行时,提示“couldn’t be opened because you don’t have perm ...

  10. vijos 2035 奇数偶数与绚丽多彩的数

    描述 Q先生是一个热爱学习的男孩子. 他认为一个 n 位的正整数 x 若能被称作是绚丽多彩的,一定要满足对于{1,3,5,7,9} 中任意一个奇数或者没有在 x 中出现,或者在 x 中出现了恰好奇数次 ...