c/c++语言开发共享贪吃蛇C/C++面向过程源码分析(双缓冲区 | 248行代码)

贪吃蛇会是C/C++选手学完做的一个小DIY,考察了游戏的设计逻辑和C/C++基础编程能力。由于贪吃蛇的设计过程相对简单,采用面向过程(函数式)编程来实现。一、游戏逻辑设计贪吃蛇的游戏逻辑可以归纳如下:蛇在一个区域中向上下左右四个方向移动吃到食物之后会变长(增长 1 格),在蛇尾巴添加一格蛇头的运动决定蛇的运动方向,只要蛇头不碰墙或者不撞到自己,游戏就能继续二、蛇活动区域设计:蛇的活动区域就是一个方格,本质就是一个字符二维矩阵。在这个矩阵中,有四种方格:墙(’#’)、蛇(’*’

贪吃蛇会是C/C++选手学完做的一个小DIY,考察了游戏的设计逻辑和C/C++基础编程能力。由于贪吃蛇的设计过程相对简单,采用面向过程(函数式)编程来实现。

一、游戏逻辑设计

贪吃蛇的游戏逻辑可以归纳如下:

  1. 蛇在一个区域中向上下左右四个方向移动

  2. 吃到食物之后会变长(增长 1 格),在蛇尾巴添加一格

  3. 蛇头的运动决定蛇的运动方向,只要蛇头不碰墙或者不撞到自己,游戏就能继续

二、蛇活动区域设计:

蛇的活动区域就是一个方格,本质就是一个字符二维矩阵。在这个矩阵中,有四种方格:墙(’#’)、蛇(’*’)、食物(‘O’)和空白(’ ‘)。蛇的运动会动态改变这些格子的状态

// 一开始蛇在中间,头朝右运动 // H = 20, W = 26 char mp[H][W] = {     "#########################",     "#                       #",     "#                       #",     "#                       #",     "#                       #",     "#                       #",     "#                       #",     "#                       #",     "#                       #",     "#          **           #",     "#                       #",     "#                       #",     "#                       #",     "#                       #",     "#                       #",     "#                       #",     "#                       #",     "#                       #",     "#                       #",     "#########################" }; 

三、蛇的表示设计:

一条蛇实际上就是“一条链”,在每个“节骨眼”(格子)都会有自己的状态,这些状态分别是上(UP),下(DOWN),左(LEFT),右(RIGHT),而这些状态就决定了蛇的运动方向,间接影响了区域中方格的状态。这四个状态可以使用枚举类型表示:

enum direction {     UP,     DOWN,     LEFT,     RIGHT,     null } ;  //紧接着定义 enum direction status[H][W],表示方格的状态(上下左右,仅对蛇身生效,若非蛇格,则为null),记得main刚开始要初始化: void init() {     for (int i = 1; i <= 18; ++i) {         for (int j = 1; j <= 23; ++j) {             status[i][j] = null;         }     }     status[9][12] = RIGHT;     status[9][11] = RIGHT;     while (true) { // 同时随机生成食物,注意食物不能在蛇身上和墙(外)         int rx = 1 + rand()%23;         int ry = 1 + rand()%18;         if (mp[rx][ry] == ' ') {             mp[rx][ry] = 'O';             return ;         }     } } 

四、蛇的运动设计

蛇的运动,根源来自于蛇头。所以可以从蛇头开始,循着status[][]的信息,进行DFS,一直更新到蛇尾状态结束。而如果蛇头刚好吃到食物,则在DFS开始之前,在蛇尾巴再加上一格。

// (hx,hy)是蛇头,从此DFS,线性更新蛇,模拟蛇的运动 void dfs(int hx,int hy) {     if (status[hx][hy] == UP) {         mp[hx][hy] = mp[hx+1][hy];         if (mp[hx][hy] == ' ') {             tail = {hx-1,hy}; // tail是尾巴标记,方便吃到食物增长             status[hx][hy] = null;             return ;         }         dfs(hx+1,hy);     }     else if (status[hx][hy] == DOWN) {         mp[hx][hy] = mp[hx-1][hy];         if (mp[hx][hy] == ' ') {             tail = {hx+1,hy};             status[hx][hy] = null;             return ;         }         dfs(hx-1,hy);     }     else if (status[hx][hy] == LEFT) {         mp[hx][hy] = mp[hx][hy+1];         if (mp[hx][hy] == ' ') {             tail = {hx,hy-1};             status[hx][hy] = null;             return ;         }         dfs(hx,hy+1);     }     else if (status[hx][hy] == RIGHT) {         mp[hx][hy] = mp[hx][hy-1];         if (mp[hx][hy] == ' ') {             tail = {hx,hy+1};             status[hx][hy] = null;             return ;         }         dfs(hx,hy-1);     } } 

贪吃蛇还要相应键盘输入,应该添加键盘相应事件,并更新蛇头状态,并调用DFS模块:

void operation() {     while (true) {         int hx = head.first; // 蛇头x坐标         int hy = head.second; // 蛇头y坐标         // 键盘相应事件         if (kbhit()) {             char ch = getch();             if (ch == 'w' && status[hx][hy] != DOWN) { // 往上                 --hx;                 status[hx][hy] = UP;             }             else if (ch == 's' && status[hx][hy] != UP) { // 往下                 ++hx;                 status[hx][hy] = DOWN;             }             else if (ch == 'a' && status[hx][hy] != RIGHT) { // 往左                 --hy;                 status[hx][hy] = LEFT;             }             else if (ch == 'd' && status[hx][hy] != LEFT) { // 往右                 ++hy;                 status[hx][hy] = RIGHT;             }             else {                 goto T1;             }         } else {             T1:             if (status[hx][hy] == UP) {                 --hx;                 status[hx][hy] = UP;             }             if (status[hx][hy] == DOWN) {                 ++hx;                 status[hx][hy] = DOWN;             }             if (status[hx][hy] == LEFT) {                 --hy;                 status[hx][hy] = LEFT;             }             if (status[hx][hy] == RIGHT) {                 ++hy;                 status[hx][hy] = RIGHT;             }         }         if (mp[hx][hy] == '#' || mp[hx][hy] == '*') {             printf("a");             printf("You lose! Get %d points",score);             exit(0);         }         // ----------------         if (mp[hx][hy] == 'O') {             int tx = tail.first;             int ty = tail.second;             if (status[tx][ty] == UP) {                 ++tx;                 status[tx][ty] = UP;                 mp[tx][ty] = '*';             }             if (status[tx][ty] == DOWN) {                 --tx;                 status[tx][ty] = DOWN;                 mp[tx][ty] = '*';             }             if (status[tx][ty] == LEFT) {                 ++ty;                 status[tx][ty] = LEFT;                 mp[tx][ty] = '*';             }             if (status[tx][ty] == RIGHT) {                 --ty;                 status[tx][ty] = RIGHT;                 mp[tx][ty] = '*';             }             tail = {tx,ty};             while (true) {                 int rx = 1 + rand()%23;                 int ry = 1 + rand()%18;                 if (mp[rx][ry] == ' ') {                     mp[rx][ry] = 'O';                     break;                 }             }             delay -= 10; // 运动加速,但不得少于100             delay = delay > 100 ? delay : 100;             score += 10; // 得分增加         }         dfs(hx,hy); // 新蛇头,进行蛇蠕动         head = {hx,hy};         display();     } } 

五、刷新控制台:

贪吃蛇游戏需要不断地刷新控制台,用System(“cls”)当然可以解决这个问题,但当蛇地活动区域较大时,由于输出缓冲的问题,看到的屏幕是闪烁的。良好的设计应该解决这个问题,这里我们可以使用双缓冲区解决这个问题。

HANDLE hOutput, hOutBuf;//控制台屏幕缓冲区句柄 HANDLE *houtpoint;//显示指针 COORD coord = { 0,0 }; //双缓冲处理显示 DWORD bytes = 0; bool showcircle = false; 

在main模块中引入下列定义:

//创建新的控制台缓冲区 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); 

展示活动区域的时候使用双缓冲区输出:

void display() {     showcircle = !showcircle;     if (showcircle) {         houtpoint = &hOutput;     }     else {         houtpoint = &hOutBuf;     }     for (int i = 0; i < H; ++i) {         coord.X = 0;         coord.Y = i;         WriteConsoleOutputCharacterA(*houtpoint, (char*)mp[i], W, coord, &bytes);     }     //设置新的缓冲区为活动显示缓冲     SetConsoleActiveScreenBuffer(*houtpoint);     Sleep(delay); } 

欢迎同步关注:ACMFans_Club微信公众号

c/c++开发分享贪吃蛇C/C++面向过程源码分析(双缓冲区 | 248行代码)地址:https://blog.csdn.net/weixin_44026604/article/details/107623402

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/c-cdevelopment/598448.html

(0)
上一篇 2021年5月8日
下一篇 2021年5月8日

精彩推荐