C语言——贪吃蛇

简介: C语言——贪吃蛇

一. 游戏效果

image.png


二. 游戏背景

贪吃蛇是久负盛名的游戏,它也和俄罗斯⽅块,扫雷等游戏位列经典游戏的⾏列。

贪吃蛇起源于1977年的投币式墙壁游戏《Blockade》,后移植到各种平台上。具体如下:

起源。1977年,投币式墙壁游戏生产商Gremlin推出了经典的街机游戏《Blockade》,两名玩家要分别控制角色移动,角色会不停长大,而且走过的地方会变成围栏,谁先碰到围栏或碰到自己的身体就算输。

贪吃蛇最初的版本是像素版本,后来又衍生出3D版本、多人对战版本等2 3。


三、游戏开发日志

13bd0a2af29b6a33b38af41e4b2713f3_2cd3bde588e040d79a76e296bc817475.png


四、知识准备

本次实现贪吃蛇会使⽤到的⼀些Win32 API知识,那么就学习⼀下


4.1 Win32 API

Windows 这个多作业系统除了协调应⽤程序的执⾏、分配内存、管理资源之外, 它同时也是⼀个很⼤的服务中⼼,调⽤这个服务中⼼的各种服务(每⼀种服务就是⼀个函数),可以帮应⽤程式达到开启 视窗、描绘图形、使⽤周边设备等⽬的,由于这些函数服务的对象是应⽤程序(Application), 所以便 称之为 Application Programming Interface, 简称 API 函数。 WIN32 API也就是Microsoft Windows 32位平台的应⽤程序编程接⼝。

4.2 控制台程序

平常我们运⾏起来的⿊框程序其实就是控制台程序。

我们可以使⽤cmd命令来设置控制台窗⼝的⻓宽,也可以通过命令设置控制台窗⼝的名字:

设置控制台窗⼝的⼤⼩,30⾏,100列,设置控制台窗⼝的名字。

//控制台窗口设置
system("mode con cols=100 lines=35");
system("title 贪吃蛇");

4.3  控制台屏幕上的坐标COORD

COORD 是Windows API中定义的⼀种结构,表⽰⼀个字符在控制台屏幕上的坐标


typedef struct _COORD {
SHORT X;
SHORT Y;
} COORD, *PCOORD;

4.4  GetStdHandle

GetStdHandle是⼀个Windows API函数。它⽤于从⼀个特定的标准设备(标准输⼊、标准输出或标准错误)中取得⼀个句柄(⽤来标识不同设备的数值),使⽤这个句柄可以操作设备。

HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);

4.5 GetConsoleCursorInfo

检索有关指定控制台屏幕缓冲区的光标⼤⼩和可⻅性的信息

HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息

4.5.1  CONSOLE_CURSOR_INFO

这个结构体,包含有关控制台游标的信息

typedef struct _CONSOLE_CURSOR_INFO {
DWORD dwSize;
BOOL bVisible;
} CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;

(1) dwSize,由光标填充的字符单元格的百分⽐。 此值介于1到100之间。 光标外观会变化,范围从完全填充单元格到单元底部的⽔平线条。

(2)  bVisible,游标的可⻅性。 如果光标可⻅,则此成员为 TRUE。

CursorInfo.bVisible = false; //隐藏控制台光标

5.6  SetConsoleCursorInfo

设置指定控制台屏幕缓冲区的光标的⼤⼩和可⻅性。


HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//影藏光标操作
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
CursorInfo.bVisible = false; //隐藏控制台光标
SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态

5.7  SetConsoleCursorPosition

设置指定控制台屏幕缓冲区中的光标位置,我们将想要设置的坐标信息放在COORD类型的pos中,调 ⽤SetConsoleCursorPosition函数将光标位置设置到指定的位置。

COORD pos = { 10, 5};
HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//设置标准输出上光标的位置为pos
SetConsoleCursorPosition(hOutput, pos);

温馨提示:

这里我们可以将SetPos:封装⼀个设置光标位置的函数


//设置光标的坐标
void SetPos(short x, short y)
{
COORD pos = { x, y };
HANDLE hOutput = NULL;
//获取标准输出的句柄(⽤来标识不同设备的数值)
hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//设置标准输出上光标的位置为pos
SetConsoleCursorPosition(hOutput, pos);
}

5.8 GetAsyncKeyState

获取按键情况,将键盘上每个键的虚拟键值传递给函数,函数通过返回值来分辨按键的状态。

返回值是short类型,在上⼀次调⽤ GetAsyncKeyState 函数后,如果 返回的16位的short数据中,最⾼位是1,说明按键的状态是按下,如果最⾼是0,说明按键的状态是抬起;如果最低位被置为1则说明,该按键被按过,否则为0。

如果我们要判断⼀个键是否被按过,可以检测GetAsyncKeyState返回值的最低值是否为1.

我们这里定义一个宏:

 #define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )

5.9 宽字符打印

在游戏地图上,我们打印墙体使⽤宽字符:□,打印蛇使⽤宽字符●,打印⻝物使⽤宽字符■

普通的字符是占⼀个字节的,这类宽字符是俩个字节。

5.9.1  setlocale函数

char* setlocale (int category, const char* locale);

setlocale 函数⽤于修改当前地区,可以针对⼀个类项修改,也可以针对所有类项。 setlocale 的第⼀个参数如果是LC_ALL,就会影响所有的类项。


C标准给第⼆个参数仅定义了2种可能取值:"C"和"  "。

在任意程序执⾏开始,都会隐藏式执⾏调⽤:

setlocale(LC_ALL, "C");

当地区设置为"C"时,库函数按正常⽅式执⾏.


当地区设置为"  "时,这种模式下程序会适应本地环境。⽐如:切换到我们的本地模式后就⽀

持宽字符(汉字)的输出等。

setlocale(LC_ALL, " ");//切换到本地环境

宽字符的打印如下:

#include <stdio.h>
#include<locale.h>
int main() {
setlocale(LC_ALL, "");
wchar_t ch1 = L'●';
wprintf(L"%c\n", ch1);
return 0;
}


五、游戏实现

5.1 游戏逻辑主体

#define _CRT_SECURE_NO_WARNINGS
#include"snake.h"
#include<locale.h>
#include<stdio.h>
int main()
{
  char ch;
  setlocale(LC_ALL, "");//切换到本地环境,支持宽字符
  srand((unsigned int)time(NULL));
  do
  {
    Snake snake = { 0 };
    GameInit(&snake);
    GameRun(&snake);
    GameOver(&snake);
    printf("小垃圾还要再来吗?是:Y,否:N\n");
      ch = getchar();
    getchar();
  } while (ch == 'y' || ch == 'Y');
  return 0;
}


5.2  游戏初始化实现

#define _CRT_SECURE_NO_WARNINGS
#include"snake.h"
#define  WALL  L'□'
#define  BODY  L'●'
#define  FOOD  L'█'
void SetPos(short x, short y)
{
  COORD pos = { x, y };
  HANDLE hOutput = NULL;
  //获取标准输出的句柄(⽤来标识不同设备的数值)
  hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  //设置标准输出上光标的位置为pos
  SetConsoleCursorPosition(hOutput, pos);
}
void  WelcomeToGame()
{
  SetPos(40, 13);
  printf("欢迎来到贪吃蛇小游戏");
    SetPos(42, 22);
    system("pause");//会显示按任意键继续的信息,按任意键后继续执行下一步
    system("cls");
    SetPos(25, 12);
    printf("↑ . ↓ . ← . → 分别控制蛇的移动, F3为加速,F4为减速\n");
    SetPos(42, 22);
    system("pause");//会显示按任意键继续的信息,按任意键后继续执行下一步
    system("cls");
    /*getchar();*/
}
void CreateMap()
{
  SetPos(0,0);
  int i = 0;
  for(i=0;i<58;i=i+2)
  {
    wprintf(L"%c", WALL);
  }
  SetPos(0, 26);
  for (i = 0; i < 58; i = i + 2)
  {
    wprintf(L"%c", WALL);
  }
  for (i = 1; i <26; i++)
  {
    SetPos(0, i);
    wprintf(L"%c", WALL);
  }
  for (i = 1; i < 26; i++)
  {
    SetPos(56, i);
    wprintf(L"%c", WALL);
  }
  /*getchar();*/
}
void InitSnake(Snake* snake)
{
  //初始化蛇身
  for (int i = 0; i < 5; i++)
  {
    SnakeNode* node = (SnakeNode*)malloc(sizeof(SnakeNode));
    node->x = 24 + 2 * i;
    node->y = 15;
    node->next = NULL;
    if (snake->pSnake== NULL)
    {
      snake->pSnake= node;
    }
    else
    {
      node->next = snake->pSnake;
      snake->pSnake = node;
    }
  }
  SnakeNode* cur = snake->pSnake;
  while (cur)
  {
    int x = cur->x;
    int y = cur->y;
    SetPos(x, y);
    wprintf(L"%c", BODY);
    cur = cur->next;
  }
  snake->status = RUN;
  snake->dir = RIGHT;
  snake->FoodWeight = 10;
  snake->pFood = NULL;
  snake->score = 0;
  snake->SleepTime = 200;
}
void CreateFood(Snake* snake)
{
  int x = 0;
  int y = 0;
again:
  do
  {
     x = rand() % 53 + 2;
     y = rand() % 24 + 1;
  } while (x % 2 != 0);
   SnakeNode* cur = snake->pSnake;
  do
  {
    if (x == cur->x && y == cur->y)
    {
      goto again;
    }
    cur = cur->next;
  } while (cur);
  SnakeNode* food = (SnakeNode*)malloc(sizeof(SnakeNode));
  food->x = x;
  food->y = y;
  snake->pFood = food;
  SetPos(x, y);
  wprintf(L"%c", FOOD);
  /*getchar();*/
}
void  GameInit(Snake* snake)
{
  //控制台窗口设置
  system("mode con cols=100 lines=35");
  system("title 贪吃蛇");
  //隐藏光标
   HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
     CONSOLE_CURSOR_INFO CursorInfo;
  GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
  CursorInfo.bVisible = false; //隐藏控制台光标
  SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态
  //打印欢迎界面
  WelcomeToGame();
  //创建地图
   CreateMap();
  //初始化贪吃蛇
     InitSnake(snake);
  //创建食物
      CreateFood(snake);
}


5.3 游戏运行实现

void PrintHelpInfo()
{
  SetPos(60, 13);
  printf("1. 不能穿墙不能咬到自己\n");
  SetPos(60, 14);
  printf("2.↑.↓.←.→分别控制蛇的移动\n");
  SetPos(60, 15);
    printf("3. F3为加速,F4为减速\n");
  SetPos(60, 16);
  printf("4. ESC-退出,空格-暂停\n");
}
void Pause()
{
  while (1)
  {
    Sleep(200);
    if (KEY_PRESS(VK_SPACE) == 1)
    {
      break;
    }
  }
}
void EatFood(Snake* snake, SnakeNode* snakenext)
{
  snakenext->next = snake->pSnake;
  snake->pSnake = snakenext;
  SnakeNode* cur = snake->pSnake;
  while (cur)
  {
    SetPos(cur->x, cur->y);
    wprintf(L"%lc", BODY);
    cur = cur->next;
  }
  free(snake->pFood);
  snake->pFood = NULL;
  CreateFood(snake);
}
void NoEatFood(Snake* snake, SnakeNode* snakenext)
{
  snakenext->next = snake->pSnake;
  snake->pSnake = snakenext;
  SnakeNode* cur = snake->pSnake;
  while (cur->next->next)
  {
    SetPos(cur->x, cur->y);
    wprintf(L"%lc", BODY);
    cur = cur->next;
  }
  SetPos(cur->x, cur->y);
  wprintf(L"%lc", BODY);
  SetPos(cur->next->x, cur->next->y);
  printf("  ");
  free(cur->next);
  cur->next = NULL;
}
void SnakeMove(Snake* snake)
{
  SnakeNode* snakenext = (SnakeNode*)malloc(sizeof(SnakeNode));
  if (snake->dir == UP)
  {
    snakenext->x = snake->pSnake->x;
    snakenext->y = snake->pSnake->y -1;
    snakenext->next = NULL;
  }
  else if (snake->dir == DOWN)
  {
    snakenext->x = snake->pSnake->x;
    snakenext->y = snake->pSnake->y +1;
    snakenext->next = NULL;
  }
  if (snake->dir == LEFT)
  {
    snakenext->x = snake->pSnake->x-2;
    snakenext->y = snake->pSnake->y ;
    snakenext->next = NULL;
  }
  if (snake->dir == RIGHT)
  {
    snakenext->x = snake->pSnake->x + 2;
    snakenext->y = snake->pSnake->y;
    snakenext->next = NULL;
  }
  //判断下一个节点是否为食物
  if (snakenext->x == snake->pFood->x && snakenext->y == snake->pFood->y)
  {
    EatFood(snake,snakenext);//吃食物
    snake->score += snake->FoodWeight;
  }
  else
  {
    NoEatFood(snake,snakenext);//不吃食物
  }
  //碰撞检测
  if (snake->pSnake->x == 0 || snake->pSnake->x == 56 || snake->pSnake->y == 0 || snake->pSnake->y == 26)
  {
    snake->status = KILL_BY_WALL;
  }
  SnakeNode* cur = snake->pSnake->next;
  while (cur)
  {
    if (cur->x == snake->pSnake->x && cur->y == snake->pSnake->y)
    {
      snake->status = KILL_BY_SELF;
    }
    cur = cur->next;
  }
}
void GameRun(Snake* snake)
{
  //打印帮助信息
  PrintHelpInfo();
  do
  {
    SetPos(60, 8);
    printf("游戏总得分:%5d ", snake->score);
    SetPos(60, 9);
    printf("每个食物的分数:%2d ", snake->FoodWeight);
    if (KEY_PRESS(VK_UP) == 1&&snake->dir!=DOWN)
    {
      snake->dir = UP;
    }
    else if (KEY_PRESS(VK_DOWN) == 1 && snake->dir != UP)
    {
      snake->dir = DOWN;
    }
    else if (KEY_PRESS(VK_RIGHT) == 1 && snake->dir != LEFT)
    {
      snake->dir = RIGHT;
    }
    else if (KEY_PRESS(VK_LEFT) == 1 && snake->dir != RIGHT)
    {
      snake->dir =LEFT;
    }
    else if (KEY_PRESS(VK_F3) == 1)
    {
      if (snake->SleepTime>=80)
      {
        snake->SleepTime -= 30;
        snake->FoodWeight += 2;
      }
    }
    else if (KEY_PRESS(VK_F4) == 1)
    {
      if (snake->SleepTime<=320)
      {
        snake->SleepTime += 30;
        snake->FoodWeight -= 2;
      }
    }
    else if (KEY_PRESS(VK_ESCAPE) == 1)
    {
      snake->status = OVER;
      break;
    }
    else if (KEY_PRESS(VK_SPACE) == 1)
    {
      Pause();
    }
    SnakeMove(snake);
    Sleep(snake->SleepTime);//每一帧停0.2s
  } while (snake->status==RUN);
}


5.4 游戏结束实现

void GameOver(Snake* snake)
{
  switch (snake->status)
  {
  case KILL_BY_WALL:
    SetPos(20, 15);
    printf("你已经撞墙了,小垃圾!\n");
      break;
  case KILL_BY_SELF:
    SetPos(20, 15);
    printf("你已经自杀了,小垃圾!\n");
             break;
  case  OVER:
    SetPos(20, 15);
    printf("你退出游戏了,小垃圾!\n");
              break;
  }
  SetPos(0, 29);
}


六、 完整代码

6.1 snake.h

#define _CRT_SECURE_NO_WARNINGS
#include<stdbool.h>
#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
#include<time.h>
#define KEY_PRESS(VK) ( (GetAsyncKeyState(VK) & 0x1) ? 1 : 0 )
enum direction{UP = 1,  DOWN, LEFT, RIGHT};//蛇的方向
enum game_status{RUN,KILL_BY_WALL,KILL_BY_SELF,OVER};
typedef struct  SnakeNode
{
  int x;
  int y;
  struct SnakeNode* next;
}SnakeNode;
typedef struct Snake
{
  SnakeNode* pSnake;//指向贪吃蛇头节点
  SnakeNode* pFood;//指向食物的节点
  int  score;
  int FoodWeight;
  int SleepTime;
  enum direction dir;//蛇的方向
  enum  game_status status;//游戏状态
}Snake;
void  GameInit(Snake*snake);
void  WelcomeToGame();
void CreateMap();
void InitSnake(Snake* snake);
void CreateFood(Snake* snake);
void GameRun(Snake* snake);
void PrintHelpInfo();
void Pause();
void SnakeMove(Snake* snake);
void EatFood(Snake* snake,SnakeNode*snakenext);
void NoEatFood(Snake* snake, SnakeNode* snakenext);
void GameOver(Snake* snake);

6.2 snake.c

#define _CRT_SECURE_NO_WARNINGS
#include"snake.h"
#define  WALL  L'□'
#define  BODY  L'●'
#define  FOOD  L'█'
void SetPos(short x, short y)
{
  COORD pos = { x, y };
  HANDLE hOutput = NULL;
  //获取标准输出的句柄(⽤来标识不同设备的数值)
  hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  //设置标准输出上光标的位置为pos
  SetConsoleCursorPosition(hOutput, pos);
}
void  WelcomeToGame()
{
  SetPos(40, 13);
  printf("欢迎来到贪吃蛇小游戏");
    SetPos(42, 22);
    system("pause");//会显示按任意键继续的信息,按任意键后继续执行下一步
    system("cls");
    SetPos(25, 12);
    printf("↑ . ↓ . ← . → 分别控制蛇的移动, F3为加速,F4为减速\n");
    SetPos(42, 22);
    system("pause");//会显示按任意键继续的信息,按任意键后继续执行下一步
    system("cls");
    /*getchar();*/
}
void CreateMap()
{
  SetPos(0,0);
  int i = 0;
  for(i=0;i<58;i=i+2)
  {
    wprintf(L"%c", WALL);
  }
  SetPos(0, 26);
  for (i = 0; i < 58; i = i + 2)
  {
    wprintf(L"%c", WALL);
  }
  for (i = 1; i <26; i++)
  {
    SetPos(0, i);
    wprintf(L"%c", WALL);
  }
  for (i = 1; i < 26; i++)
  {
    SetPos(56, i);
    wprintf(L"%c", WALL);
  }
  /*getchar();*/
}
void InitSnake(Snake* snake)
{
  //初始化蛇身
  for (int i = 0; i < 5; i++)
  {
    SnakeNode* node = (SnakeNode*)malloc(sizeof(SnakeNode));
    node->x = 24 + 2 * i;
    node->y = 15;
    node->next = NULL;
    if (snake->pSnake== NULL)
    {
      snake->pSnake= node;
    }
    else
    {
      node->next = snake->pSnake;
      snake->pSnake = node;
    }
  }
  SnakeNode* cur = snake->pSnake;
  while (cur)
  {
    int x = cur->x;
    int y = cur->y;
    SetPos(x, y);
    wprintf(L"%c", BODY);
    cur = cur->next;
  }
  snake->status = RUN;
  snake->dir = RIGHT;
  snake->FoodWeight = 10;
  snake->pFood = NULL;
  snake->score = 0;
  snake->SleepTime = 200;
}
void CreateFood(Snake* snake)
{
  int x = 0;
  int y = 0;
again:
  do
  {
     x = rand() % 53 + 2;
     y = rand() % 24 + 1;
  } while (x % 2 != 0);
   SnakeNode* cur = snake->pSnake;
  do
  {
    if (x == cur->x && y == cur->y)
    {
      goto again;
    }
    cur = cur->next;
  } while (cur);
  SnakeNode* food = (SnakeNode*)malloc(sizeof(SnakeNode));
  food->x = x;
  food->y = y;
  snake->pFood = food;
  SetPos(x, y);
  wprintf(L"%c", FOOD);
  /*getchar();*/
}
void  GameInit(Snake* snake)
{
  //控制台窗口设置
  system("mode con cols=100 lines=35");
  system("title 贪吃蛇");
  //隐藏光标
   HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
     CONSOLE_CURSOR_INFO CursorInfo;
  GetConsoleCursorInfo(hOutput, &CursorInfo);//获取控制台光标信息
  CursorInfo.bVisible = false; //隐藏控制台光标
  SetConsoleCursorInfo(hOutput, &CursorInfo);//设置控制台光标状态
  //打印欢迎界面
  WelcomeToGame();
  //创建地图
   CreateMap();
  //初始化贪吃蛇
     InitSnake(snake);
  //创建食物
      CreateFood(snake);
}
void PrintHelpInfo()
{
  SetPos(60, 13);
  printf("1. 不能穿墙不能咬到自己\n");
  SetPos(60, 14);
  printf("2.↑.↓.←.→分别控制蛇的移动\n");
  SetPos(60, 15);
    printf("3. F3为加速,F4为减速\n");
  SetPos(60, 16);
  printf("4. ESC-退出,空格-暂停\n");
}
void Pause()
{
  while (1)
  {
    Sleep(200);
    if (KEY_PRESS(VK_SPACE) == 1)
    {
      break;
    }
  }
}
void EatFood(Snake* snake, SnakeNode* snakenext)
{
  snakenext->next = snake->pSnake;
  snake->pSnake = snakenext;
  SnakeNode* cur = snake->pSnake;
  while (cur)
  {
    SetPos(cur->x, cur->y);
    wprintf(L"%lc", BODY);
    cur = cur->next;
  }
  free(snake->pFood);
  snake->pFood = NULL;
  CreateFood(snake);
}
void NoEatFood(Snake* snake, SnakeNode* snakenext)
{
  snakenext->next = snake->pSnake;
  snake->pSnake = snakenext;
  SnakeNode* cur = snake->pSnake;
  while (cur->next->next)
  {
    SetPos(cur->x, cur->y);
    wprintf(L"%lc", BODY);
    cur = cur->next;
  }
  SetPos(cur->x, cur->y);
  wprintf(L"%lc", BODY);
  SetPos(cur->next->x, cur->next->y);
  printf("  ");
  free(cur->next);
  cur->next = NULL;
}
void SnakeMove(Snake* snake)
{
  SnakeNode* snakenext = (SnakeNode*)malloc(sizeof(SnakeNode));
  if (snake->dir == UP)
  {
    snakenext->x = snake->pSnake->x;
    snakenext->y = snake->pSnake->y -1;
    snakenext->next = NULL;
  }
  else if (snake->dir == DOWN)
  {
    snakenext->x = snake->pSnake->x;
    snakenext->y = snake->pSnake->y +1;
    snakenext->next = NULL;
  }
  if (snake->dir == LEFT)
  {
    snakenext->x = snake->pSnake->x-2;
    snakenext->y = snake->pSnake->y ;
    snakenext->next = NULL;
  }
  if (snake->dir == RIGHT)
  {
    snakenext->x = snake->pSnake->x + 2;
    snakenext->y = snake->pSnake->y;
    snakenext->next = NULL;
  }
  //判断下一个节点是否为食物
  if (snakenext->x == snake->pFood->x && snakenext->y == snake->pFood->y)
  {
    EatFood(snake,snakenext);//吃食物
    snake->score += snake->FoodWeight;
  }
  else
  {
    NoEatFood(snake,snakenext);//不吃食物
  }
  //碰撞检测
  if (snake->pSnake->x == 0 || snake->pSnake->x == 56 || snake->pSnake->y == 0 || snake->pSnake->y == 26)
  {
    snake->status = KILL_BY_WALL;
  }
  SnakeNode* cur = snake->pSnake->next;
  while (cur)
  {
    if (cur->x == snake->pSnake->x && cur->y == snake->pSnake->y)
    {
      snake->status = KILL_BY_SELF;
    }
    cur = cur->next;
  }
}
void GameRun(Snake* snake)
{
  //打印帮助信息
  PrintHelpInfo();
  do
  {
    SetPos(60, 8);
    printf("游戏总得分:%5d ", snake->score);
    SetPos(60, 9);
    printf("每个食物的分数:%2d ", snake->FoodWeight);
    if (KEY_PRESS(VK_UP) == 1&&snake->dir!=DOWN)
    {
      snake->dir = UP;
    }
    else if (KEY_PRESS(VK_DOWN) == 1 && snake->dir != UP)
    {
      snake->dir = DOWN;
    }
    else if (KEY_PRESS(VK_RIGHT) == 1 && snake->dir != LEFT)
    {
      snake->dir = RIGHT;
    }
    else if (KEY_PRESS(VK_LEFT) == 1 && snake->dir != RIGHT)
    {
      snake->dir =LEFT;
    }
    else if (KEY_PRESS(VK_F3) == 1)
    {
      if (snake->SleepTime>=80)
      {
        snake->SleepTime -= 30;
        snake->FoodWeight += 2;
      }
    }
    else if (KEY_PRESS(VK_F4) == 1)
    {
      if (snake->SleepTime<=320)
      {
        snake->SleepTime += 30;
        snake->FoodWeight -= 2;
      }
    }
    else if (KEY_PRESS(VK_ESCAPE) == 1)
    {
      snake->status = OVER;
      break;
    }
    else if (KEY_PRESS(VK_SPACE) == 1)
    {
      Pause();
    }
    SnakeMove(snake);
    Sleep(snake->SleepTime);//每一帧停0.2s
  } while (snake->status==RUN);
}
void GameOver(Snake* snake)
{
  switch (snake->status)
  {
  case KILL_BY_WALL:
    SetPos(20, 15);
    printf("你已经撞墙了,小垃圾!\n");
      break;
  case KILL_BY_SELF:
    SetPos(20, 15);
    printf("你已经自杀了,小垃圾!\n");
             break;
  case  OVER:
    SetPos(20, 15);
    printf("你退出游戏了,小垃圾!\n");
              break;
  }
  SetPos(0, 29);
}


6.3 text.c

#define _CRT_SECURE_NO_WARNINGS
#include"snake.h"
#include<locale.h>
#include<stdio.h>
int main()
{
  char ch;
  setlocale(LC_ALL, "");//切换到本地环境,支持宽字符
  srand((unsigned int)time(NULL));
  do
  {
    Snake snake = { 0 };
    GameInit(&snake);
    GameRun(&snake);
    GameOver(&snake);
    printf("小垃圾还要再来吗?是:Y,否:N\n");
      ch = getchar();
    getchar();
  } while (ch == 'y' || ch == 'Y');
  return 0;
}


七、穿墙版(进阶版)

穿墙也非常简单,只是将撞墙检测去除,改为


1)判断snakenext->y ==0,则snakenext->y =25


2)判断snakenext->y ==26,则snakenext->y =1


3)判断snakenext->x==0,则snakenext->x =54


4)   判断snakenext->x==56,则snakenext->x =2


其余状态为正常吃食物状态


只在snake.c中的void SnakeMove(Snake* snake)中略有改动,其余不变

void SnakeMove(Snake* snake)
{
  SnakeNode* snakenext = (SnakeNode*)malloc(sizeof(SnakeNode));
  if (snake->dir == UP)
  {
    snakenext->x = snake->pSnake->x;
    snakenext->y = snake->pSnake->y -1;
    snakenext->next = NULL;
  }
  else if (snake->dir == DOWN)
  {
    snakenext->x = snake->pSnake->x;
    snakenext->y = snake->pSnake->y +1;
    snakenext->next = NULL;
  }
  if (snake->dir == LEFT)
  {
    snakenext->x = snake->pSnake->x-2;
    snakenext->y = snake->pSnake->y ;
    snakenext->next = NULL;
  }
  if (snake->dir == RIGHT)
  {
    snakenext->x = snake->pSnake->x + 2;
    snakenext->y = snake->pSnake->y;
    snakenext->next = NULL;
  }
  //判断下一个节点是否为食物
  if (snakenext->x == snake->pFood->x && snakenext->y == snake->pFood->y)
  {
    EatFood(snake,snakenext);//吃食物
    snake->score += snake->FoodWeight;
  }
  else
  { 
    if (snakenext->y == 0)
    {
      snakenext->y = 25;
      snakenext->next = snake->pSnake;
      snake->pSnake = snakenext;
      SnakeNode* cur = snake->pSnake;
      while (cur->next->next)
      {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc", BODY);
        cur = cur->next;
      }
      SetPos(cur->x, cur->y);
      wprintf(L"%lc", BODY);
      SetPos(cur->next->x, cur->next->y);
      printf("  ");
      free(cur->next);
      cur->next = NULL;
    }
    else if (snakenext->y == 26)
    {
      snakenext->y =1;
      snakenext->next = snake->pSnake;
      snake->pSnake = snakenext;
      SnakeNode* cur = snake->pSnake;
      while (cur->next->next)
      {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc", BODY);
        cur = cur->next;
      }
      SetPos(cur->x, cur->y);
      wprintf(L"%lc", BODY);
      SetPos(cur->next->x, cur->next->y);
      printf("  ");
      free(cur->next);
      cur->next = NULL;
    }
    else if (snakenext->x == 0)
    {
      snakenext->x = 54;
      snakenext->next = snake->pSnake;
      snake->pSnake = snakenext;
      SnakeNode* cur = snake->pSnake;
      while (cur->next->next)
      {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc", BODY);
        cur = cur->next;
      }
      SetPos(cur->x, cur->y);
      wprintf(L"%lc", BODY);
      SetPos(cur->next->x, cur->next->y);
      printf("  ");
      free(cur->next);
      cur->next = NULL;
    }
    else if (snakenext->x == 56)
    {
      snakenext->x =2;
      snakenext->next = snake->pSnake;
      snake->pSnake = snakenext;
      SnakeNode* cur = snake->pSnake;
      while (cur->next->next)
      {
        SetPos(cur->x, cur->y);
        wprintf(L"%lc", BODY);
        cur = cur->next;
      }
      SetPos(cur->x, cur->y);
      wprintf(L"%lc", BODY);
      SetPos(cur->next->x, cur->next->y);
      printf("  ");
      free(cur->next);
      cur->next = NULL;
    }
    else
    {
      NoEatFood(snake, snakenext);//不吃食物
    }
  }
  //穿墙检测
  SnakeNode* cur = snake->pSnake->next;
  while (cur)
  {
    if (cur->x == snake->pSnake->x && cur->y == snake->pSnake->y)
    {
      snake->status = KILL_BY_SELF;
    }
    cur = cur->next;
  }
}
相关文章
|
8天前
|
API 定位技术 C语言
贪吃蛇---C语言---详解
贪吃蛇---C语言---详解
|
8天前
|
API 定位技术 C语言
C语言项目实战——贪吃蛇
贪吃蛇是久负盛名的游戏,它也和俄罗斯方块,扫雷等游戏位列经典游戏的行列。 在编程语言的学习中,我将以贪吃蛇为例,从设计到代码来展示一个C语言项目实战,进而再一步提升读者对C语言的理解和认知。
92 0
|
8天前
|
存储 定位技术 C语言
【c语言】简单贪吃蛇的实现
【c语言】简单贪吃蛇的实现
|
8天前
|
C语言
C语言之实现贪吃蛇小游戏篇(2)
C语言之实现贪吃蛇小游戏篇(2)
27 1
|
7月前
|
C语言 C++
【c语言】贪吃蛇
【c语言】贪吃蛇
69 0
|
8天前
|
C语言
|
8天前
|
C语言 计算机视觉
C语言贪吃蛇(有详细注释)
C语言贪吃蛇(有详细注释)
12 0
|
8天前
|
存储 定位技术 API
C语言实现贪吃蛇【完整版】
C语言实现贪吃蛇【完整版】
|
8天前
|
C语言
C语言实战演练之贪吃蛇游戏
C语言实战演练之贪吃蛇游戏
|
8天前
|
存储 定位技术 API
贪吃蛇-c语言版本
贪吃蛇-c语言版本