C语言 贪吃蛇实现(不闪屏)

  不费话,直接上代码。VC++ 6.0可以完美运行,VS2015等版本需要做点修改,请按提示将“scanf”这些函数改成“_scanf”这样的形式即可。

  不闪屏的原理部分请参考这篇文章

  代码只有两百多行,很多地方都加上了注释,有疑问可以留言

/*
* File : myRetroSnake.cpp
* Author : weixinhum
* Date : 2017.5.16
* Function : Snake game of overcoming the splash screen
*/

#include "stdio.h"
#include "stdlib.h"
#include 
#include 
#include   //键盘按下判断相关头文件
#define LENGTH 15

int i;
HANDLE hOutput, hOutBuf;//控制台屏幕缓冲区句柄
HANDLE *houtpoint;//显示指针
COORD coord = { 0,0 };
//双缓冲处理显示
DWORD bytes = 0;
unsigned char data[LENGTH + 2][LENGTH + 2];//最后的+2为墙符号
bool showcircle = false;
char direction = 'd';//蛇头方向标志位,'d'为向右,'s'为向下,'a'为向左,'w'为向上
int headx = 2, heady = 0;//蛇头坐标
int foodx = 0, foody = 0;//食物坐标
bool running = true;//游戏结束标志位
char level[LENGTH];
int gamelevel = 1;//游戏等级
int eatcount = 0;
struct SnakeBody//蛇身结构体
{
    int x[LENGTH*LENGTH];
    int y[LENGTH*LENGTH];
}snake;
int snakelength = 3;//蛇身长度

void show()//显示要绘制的游戏界面
{
    showcircle = !showcircle;
    if (showcircle)
    {
        houtpoint = &hOutput;
    }
    else
    {
        houtpoint = &hOutBuf;
    }
    //游戏等级打印
    coord.X = 50;//以(45,5)为起点,不用贴着左上角
    coord.Y = 3;
    sprintf(level, "level:%2d", gamelevel);
    WriteConsoleOutputCharacterA(*houtpoint, level, strlen(level), coord, &bytes);
    //游戏内容打印
    for (i = 0; i < LENGTH + 2; i++)
    {
        coord.X = 44;//以(45,5)为起点,不用贴着左上角
        coord.Y = i + 5;
        WriteConsoleOutputCharacterA(*houtpoint, (char*)data[i], (LENGTH + 2), coord, &bytes);
    }
    //设置新的缓冲区为活动显示缓冲
    SetConsoleActiveScreenBuffer(*houtpoint);
    Sleep(200 / gamelevel);
}

void showover()//显示游戏结束
{
    showcircle = !showcircle;
    if (showcircle)
    {
        houtpoint = &hOutput;
    }
    else
    {
        houtpoint = &hOutBuf;
    }
    memset(data, 255, (LENGTH + 2)*(LENGTH + 2));//清零
    for (i = 0; i < LENGTH + 2; i++)
    {
        coord.X = 44;//以(45,5)为起点,不用贴着左上角
        coord.Y = i + 5;
        WriteConsoleOutputCharacterA(*houtpoint, (char*)data[i], (LENGTH + 2), coord, &bytes);
    }
    coord.X = 44 + LENGTH / 2 - 3;
    coord.Y = 5;
    WriteConsoleOutputCharacterA(*houtpoint, "GAME OVER", 9, coord, &bytes);
    //设置新的缓冲区为活动显示缓冲
    SetConsoleActiveScreenBuffer(*houtpoint);
}

void draw()//绘制游戏界面
{
    //游戏界面绘制,里面用到了扩展的ASCII码表对应的字符数字值
    memset(data, 255, (LENGTH + 2)*(LENGTH + 2));//清零
                                                 //绘制边界
    for (i = 0; i < (LENGTH + 2); i++)
    {
        data[0][i] = 205;                       //  ═
        data[i][0] = 186;                       //  ║
        data[LENGTH + 1][i] = 205;              //  ═
        data[i][LENGTH + 1] = 186;              //  ║
    }
    data[0][0] = 201;                           //  ╔
    data[LENGTH + 1][0] = 200;                  //  ╚
    data[0][(LENGTH + 2) - 1] = 187;                //  ╗
    data[LENGTH + 1][(LENGTH + 2) - 1] = 188;   //  ╝
                                                //画蛇
    for (i = 0; i < snakelength; i++)
    {
        data[snake.y[i] + 1][(snake.x[i] + 1)] = 233;
    }
    //画食物
    data[foody + 1][(foodx + 1)] = 236;
}

void generatefood()//食物生成
{
    bool dogenerate = true;
    while (dogenerate)
    {
        srand((unsigned)time(NULL));       //初始化随机数
        foodx = rand() % LENGTH;
        foody = rand() % LENGTH;
        dogenerate = false;
        for (i = 0; i < snakelength; i++)
        {
            if (snake.y[i] == foody && foodx == snake.x[i])
            {
                dogenerate = true;
            }
        }
    }
}

bool control()//控制信息处理
{
    //键盘按下判断
    char getkeych;
    if (kbhit())
    {/* 判断是否按下键盘,如果按下,ch接收键盘输入 */
        char key = _getch();
        switch (key)
        {
        case 'd':
        case 's':
        case 'a':
        case 'w':direction = key; break;
        }
    }
    switch (direction)
    {
    case 'd':headx++; break;
    case 's':heady++; break;
    case 'a':headx--; break;
    case 'w':heady--; break;
    }
    //越界穿墙
    if (headx < 0)
    {
        headx = LENGTH - 1;
    }
    else if (headx > LENGTH - 1)
    {
        headx = 0;
    }
    if (heady < 0)
    {
        heady = LENGTH - 1;
    }
    else if (heady > LENGTH - 1)
    {
        heady = 0;
    }
    //游戏结束判断
    for (i = 0; i < snakelength; i++)
    {
        if (snake.x[i] == headx && snake.y[i] == heady)
        {
            return false;
        }
    }
    //吃到食物判断
    if (foodx == headx && foody == heady)
    {
        snakelength++;
        snake.x[snakelength - 1] = headx;
        snake.y[snakelength - 1] = heady;
        generatefood();
        eatcount++;
        gamelevel = eatcount / 10 + 1;
    }
    //蛇身数据更新
    for (i = 0; i < snakelength - 1; i++)
    {
        snake.x[i] = snake.x[i + 1];
        snake.y[i] = snake.y[i + 1];
    }
    snake.x[snakelength - 1] = headx;
    snake.y[snakelength - 1] = heady;

    return true;
}

void main()
{
    SetConsoleOutputCP(437);//改成标准英文输出标志,这样可以使用扩展ASCII码表,以方便后面游戏界面的制作
                            //创建新的控制台缓冲区
    hOutBuf = CreateConsoleScreenBuffer(
        GENERIC_WRITE,//定义进程可以往缓冲区写数据
        FILE_SHARE_WRITE,//定义缓冲区可共享写权限
        NULL,
        CONSOLE_TEXTMODE_BUFFER,
        NULL
    );
    hOutput = CreateConsoleScreenBuffer(
        GENERIC_WRITE,//定义进程可以往缓冲区写数据
        FILE_SHARE_WRITE,//定义缓冲区可共享写权限
        NULL,
        CONSOLE_TEXTMODE_BUFFER,
        NULL
    );
    //隐藏两个缓冲区的光标
    CONSOLE_CURSOR_INFO cci;
    cci.bVisible = 0;
    cci.dwSize = 1;
    SetConsoleCursorInfo(hOutput, &cci);
    SetConsoleCursorInfo(hOutBuf, &cci);

    //蛇身初设数据初始化
    snake.x[2] = 2; snake.y[2] = 0;
    snake.x[1] = 1; snake.y[1] = 0;
    snake.x[0] = 0; snake.y[0] = 0;

    generatefood();//生成食物
    while (running)
    {
        if (!control())
        {
            showover();
            break;
        }
        draw();
        show();
    }
    scanf("%d", &i);//等待退出
}

你可能感兴趣的:(c语言)