2020博客之星年度总评选进行中:请为74号的狗子投上宝贵的一票!

我的投票地址:点击为我投票

花狗C语言彩色贪吃蛇(完整代码)_贪吃蛇


写在前面:
大家好,我是 花狗Fdog ,来自内蒙古的一个小城市,目前在泰州读书。
很感谢能有这样一个平台让我能够在这里分享所学所感。
我喜欢编程,喜欢代码,喜欢去做一个程序员。
努力学习,争取多年后,给亲人更好的生活。



文章目录

  • 0.前言
  • 1.游戏效果
  • 2.相关变量
  • 3.定义链表
  • 4.食物坐标
  • 5.绘制初始界面和游戏地图
  • 6.初始化蛇身
  • 7.产生食物
  • 8.游戏刷新和暂停 ,按回车可暂停游戏。
  • 9.显示分数和难度,更新分数和难度。
  • 10.移动光标
  • 11.检测判断
  • 12.游戏循环
  • 13.释放内存
  • 14.主函数内容
  • 15.完整代码奉上,也可自行下载。



0.前言

这是我在博客写的第一篇文章,如果哪里有问题还请多多指教!!以前我写的贪吃蛇是全图刷新的,导致在窗口运行时,眼睛都快闪瞎了!今天为大家带来了不闪的贪吃蛇!!!废话不多说,上图,上代码!!

1.游戏效果

花狗C语言彩色贪吃蛇(完整代码)_彩色_02

2.相关变量

int JudgeSum = 0;            //判断是否加快
int Pause = 200000000;       //暂停速度(移动速度)
int * PJ = &JudgeDirection;  //用指针传值判断移动方向
nakebody *end = NULL;        //尾节点

3.定义链表

贪吃蛇的身体如何保存是游戏的核心,所以我们需要用到链表来保存蛇的身体,这样就可以随时知道蛇身数据。

typedef struct Snakebody
{
 int x, y;  		//蛇身的坐标
 struct Snakebody *next;//保存下一个蛇身的地址
}Snakebody;   		//通过typedef将 Snakebody 替代 struct Snakebody

4.食物坐标

typedef struct Snakexy
{
 int x;
 int y;
}Snakexy; //记录食物坐标

5.绘制初始界面和游戏地图

#include<Windows.h>
#define HEIGHT  20  //设置地图高度
#define WIDTH   40  //设置地图宽度
#define PRINTF  printf("■");
#define LINE    printf("\n");
#define EMPTY   printf("  "); //因为这三个语句经常用,所以我就定义成了宏
void Front();       //绘制初始界面
void DeawMap();     //绘制地图

void Front()
{
 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//设置红色
 MoveCursor(18, 11);
 printf(" 贪 吃 蛇 ");
 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);//设置绿色
 MoveCursor(18, 12);
 printf("作者:花狗");
 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_BLUE);//设置蓝色
 MoveCursor(18, 13);
 printf("QQ:2506897252");
 MoveCursor(18, 14);
 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);//设置红色和蓝色相加
 MoveCursor(18, 15);
 printf("请等待......");
 for (int i = 0; i <= 3000000000; i++){}
 system("cls");//清屏处理
}
void DeawMap()
{
 for (int i = 0; i < WIDTH; i++)PRINTF LINE  //打印上边框
    for (int i = 1; i < HEIGHT - 1; i++)          //打印左右边框
    {
        for (int j = 0; j < WIDTH; j++)
        {
           if (j == 0 || j == WIDTH - 1 || j == WIDTH - 10)
           {
              PRINTF
                if (j == WIDTH - 1)LINE
           }
           else EMPTY
        }
    }
    for (int i = 0; i < WIDTH; i++)PRINTF LINE  //打印下边框
}

SetConsoleTextAttribute()函数是一个API设置字体颜色和背景色的函数。参数表中使用两个属性(属性之间用,隔开),不同于system(),SetConsoleTextAttribute()可以改变界面多种颜色,而system()只能修改为一种!。

6.初始化蛇身

刚开始蛇不应该只要一个头,所以我们必须创建几个身体。

Snakebody *Phead = NULL;    //存储着整个蛇身 不可更改
Snakebody *Phead_1 = NULL;  //指向蛇身
Snakebody *Pbady = NULL;    //创建节点
void ISnake();     	    //初始化蛇身
void ISnake()
{
   for (int i = 0; i < 5; i++)//初始化蛇身拥有五个长度
   {
   	Pbady = (Snakebody*)malloc(sizeof(Snakebody));//创建节点
  	Pbady->x = 5 - i;
  	Pbady->y = 5;
  	   if (Phead == NULL)
  	   {
  	      Phead = Pbady;
  	   }
  	   else
  	   {
  	      end->next = Pbady;
  	   }
        Pbady->next = NULL;
        end = Pbady;
    }
    Phead_1 = Phead;
    while (Phead_1->next != NULL)//打印蛇身
   {
     MoveCursor(Phead_1->x, Phead_1->y);
     PRINTF
     Phead_1 = Phead_1->next;
   }
}

7.产生食物

随机产生食物,如果和蛇身体重合则再次随机产生食物。

#include<time.h>
int sum = 0;     //计算得分
Snakexy * Food = NULL;          //保存食物位置
void FoodRand();    //生成食物
void FoodRand()
{
 srand((int)time(0));
 int x = rand() % 27 + 2;//生成随机数
 int y = rand() % 17 + 2;
 Phead_1 = Phead;
 for (int i = 0; i <= 200; i++)
 {
    if (Phead_1->x == x&&Phead_1->y == y)
    {
      x = rand() % 27 + 2; 
      y = rand() % 17 + 2; 
    }
    else
    {
      Phead_1 = Phead_1->next;
    }
    if (Phead_1->next == NULL)
    {
      break;
    }
 }
 MoveCursor(x, y);
 PRINTF
 Food = (Snakexy*)malloc(sizeof(Snakexy));
 Food->x = x;
 Food->y = y;
 MoveCursor(33, 5);
 printf("  ");
 Showf();
 sum++;
 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);// 蓝
}

rand函数功能为获取一个伪随机数,如要产生[m,n]范围内的随机数num,可用int num=rand()%(n-m+1)+m;

8.游戏刷新和暂停 ,按回车可暂停游戏。

int JudgeDirection = 4;   //判断方向
void ControlMove();    //控制移动和暂停
void ControlMove()
{
   if (GetAsyncKeyState(VK_UP) && 0x8000)
   {
      if (JudgeDirection == 2)
      {
      }
      else
      {
        JudgeDirection = 1;
      }
   }
   if (GetAsyncKeyState(VK_DOWN) && 0x8000)
   {
  	 if (JudgeDirection == 1)
  	 {
  	 }
         else
         {
            JudgeDirection = 2;
         }
   }
  if (GetAsyncKeyState(VK_RIGHT) && 0x8000)
  {
        if (JudgeDirection == 3)
        {
        }
        else
        {
          JudgeDirection = 4;
        }
 }
  if (GetAsyncKeyState(VK_LEFT) && 0x8000)
  {
        if (JudgeDirection == 4)
        {
        }
        else
        {
        JudgeDirection = 3;
        }
  }
  if (GetAsyncKeyState(VK_RETURN) && 0x0D)//判断回车
  {
     while (1)
    {
      if (GetAsyncKeyState(VK_RETURN) && 0x0D)//再次回车退出死循环
      {
    break;
      }
    }
  }
}

GetAsyncKeyState()确定用户当前是否按下了键盘上的一个键

9.显示分数和难度,更新分数和难度。

int sum = 0;     //计算得分
int Hard = 0;     //计算难度
void Showf();                   //显分数以及难度
void Showf()
{
 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE);// 蓝
 MoveCursor(33, 5);
 printf("得分:%d", sum);
 MoveCursor(33, 6);
 printf("难度:%d", Hard);
}

10.移动光标

游戏不闪的原因就是我们只绘制一次地图 然后用光标定点刷新目标点。

void MoveCursor(int x, int y); //移动光标
void MoveCursor(int x, int y)//设置光标位置(就是输出显示的开始位置)
{
 COORD pos = { x * 2,y };
 HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);//获得标准输出的句柄   
 SetConsoleCursorPosition(output, pos); //设置光标位置
}

COORD是Windows API中定义的一种结构体

11.检测判断

检测是否吃到食物,是否撞墙,是否撞到自己。

void Jfood();     //检测是否吃到食物
void Jwall();     //检测蛇头是否撞墙
void Jsnake();     //检测蛇头是否撞到蛇身
void Jfood()
{
 Phead_1 = Phead;
 if (Phead_1->x == Food->x&&Phead_1->y == Food->y)
 {
     FoodRand();
     JudgeSum += 1;
  if (JudgeSum == 5)
  {
     JudgeSum = 0;//如果JudgeSum等于5则从新判断
     Hard += 1;
     Pause -= 20000000;//每成立一次循环减少20000000
  }
  while (Phead_1->next != NULL)
  {
     Phead_1 = Phead_1->next;
  }
  Snakebody *S = (Snakebody*)malloc(sizeof(Snakebody));
  S->x = Food->x;
  S->y = Food->y;
  S->next = NULL;
  Phead_1->next = S;
  ControlMove();
  MoveCursor(Phead_1->x, Phead_1->y);
  PRINTF
 }
 //获取食物的坐标和蛇头做对比
}
void Jwall()
{
 if (Phead->x == 0 || Phead->x == 29 || Phead->y == 0 || Phead->y == 19)
 {
    MoveCursor(10, 20);
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//设置红色
    printf("抱歉,你撞到了自己,游戏结束!              ");
    system("pause>nul");
    exit(0);
 }
}
void Jsnake()
{
 Phead_1 = Phead->next;
 while (Phead_1->next != NULL)
 {
     if ((Phead->x == Phead_1->x) && (Phead->y == Phead_1->y))
     {
   	MoveCursor(10, 20);
   	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//设置红色
   	printf("抱歉,你撞到了自己,游戏结束!          ");
   	system("pause>nul");
   	exit(0);
  	}
  Phead_1 = Phead_1->next;
 }
}

12.游戏循环

void Move();     //游戏运行
void Move()
{
  while (1)
  {
     Phead_1 = Phead;
     while (Phead_1->next->next != NULL)
     {
        Phead_1 = Phead_1->next;
     }
     Phead_1->next = NULL;
     for (int i = 0; i < Pause; i++) {}
     ControlMove();
     MoveCursor(Phead_1->x, Phead_1->y);
     EMPTY
     //上面为消除尾部
     Snakebody *Phead_2 = (Snakebody*)malloc(sizeof(Snakebody));
     if (*PJ == 1)
     {
   	   Phead_2->x = Phead->x;
   	   Phead_2->y = Phead->y - 1;
  	}
     if (*PJ == 2)
  	{
   	   Phead_2->x = Phead->x;
   	   Phead_2->y = Phead->y + 1;
     }
     if (*PJ == 3)
     {
           Phead_2->x = Phead->x - 1;
           Phead_2->y = Phead->y;
     }
     if (*PJ == 4)
     {
   	   Phead_2->x = Phead->x + 1;
           Phead_2->y = Phead->y;
     }
  Phead_2->next = Phead;
  Phead = Phead_2;
  MoveCursor(Phead_2->x, Phead_2->y);
  PRINTF
  Jfood();
  Jwall();
  Jsnake();
  MoveCursor(40, 20);
   }
}

13.释放内存

void Free();                    //释放内存
void Free()
{
 while (Phead->next != NULL)
 {
  Phead=Phead->next;
  free(Phead);
 }
 free(Phead);
}

14.主函数内容

int main()
{
 Front();
 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN);//绿
 DeawMap();
 Showf();
 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);// 暗白
 MoveCursor(34, 10);
 printf("↑");
 MoveCursor(31, 11);
 printf("使用←↓→来控制");
 MoveCursor(31, 12);
 printf("蛇的移动,撞墙游");
 MoveCursor(31, 13);
 printf("戏结束,每5分增 ");
 MoveCursor(31, 14);
 printf("一个难度(速度)");
 ISnake();
 FoodRand();
 MoveCursor(40, 20);
 Move();
 Free();
 return 0;
}

15.完整代码奉上,也可自行下载。

#include<stdio.h>
#include<time.h>
#include<Windows.h>
#define HEIGHT  20  //设置地图高度
#define WIDTH   40  //设置地图宽度
#define PRINTF  printf("■");
#define LINE    printf("\n");
#define EMPTY   printf("  ");
typedef struct Snakebody
{
 int x, y;//身体的坐标
 struct Snakebody *next;//结构指针
}Snakebody;//先来创建保持身体的链表,贪吃蛇的核心代码就是该如何保存蛇的身体
typedef struct Snakexy
{
 int x;
 int y;
}Snakexy; //记录食物坐标
int sum = 0;     //计算得分
int JudgeSum = 0;    //判断是否加快
int Hard = 0;     //计算难度
int Pause = 200000000;   //暂停速度(移动速度)
int JudgeDirection = 4;   //判断方向
int * PJ = &JudgeDirection;  //用指针传值判断移动方向
Snakebody *Phead = NULL;  //存储着整个蛇身 不可更改
Snakebody *Phead_1 = NULL;  //指向蛇身
Snakebody *Pbady = NULL;  //创建节点
Snakebody *end = NULL;   //尾节点
Snakexy * Food = NULL;          //保存食物位置
void Front();                   //游戏开始页面1
void Jfood();     //检测是否吃到食物1
void Jwall();     //检测蛇头是否撞墙1
void Jsnake();     //检测蛇头是否撞到蛇身1
void ISnake();     //初始化蛇身1
void DeawMap();     //绘制地图1
void FoodRand();    //生成食物1
void ControlMove();    //控制移动和暂停1
void MoveCursor(int x, int y); //移动光标1
void Move();     //游戏运行1
void Showf();                   //显分数以及难度1
void Free();                    //释放内存
int main()
{
 Front();
 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN);//绿
 DeawMap();
 Showf();
 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);// 暗白
 MoveCursor(34, 10);
 printf("↑");
 MoveCursor(31, 11);
 printf("使用←↓→来控制");
 MoveCursor(31, 12);
 printf("蛇的移动,撞墙游");
 MoveCursor(31, 13);
 printf("戏结束,每5分增 ");
 MoveCursor(31, 14);
 printf("一个难度(速度)");
 ISnake();
 FoodRand();
 MoveCursor(40, 20);
 Move();
 return 0;
}
void Front()
{
 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//设置红色
 MoveCursor(18, 11);
 printf(" 贪 吃 蛇 ");
 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);//设置绿色
 MoveCursor(18, 12);
 printf("作者:花狗");
 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_BLUE);//设置蓝色
 MoveCursor(18, 13);
 printf("QQ:2506897252");
 MoveCursor(18, 14);
 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_BLUE);//设置红色和蓝色相加
 MoveCursor(18, 15);
 printf("请等待......");
 for (int i = 0; i <= 3000000000; i++){}
 system("cls");
}
void DeawMap()
{
 for (int i = 0; i < WIDTH; i++)PRINTF LINE  //上边框
  for (int i = 1; i < HEIGHT - 1; i++)          //打印左右边框
  {
   for (int j = 0; j < WIDTH; j++)
   {
    if (j == 0 || j == WIDTH - 1 || j == WIDTH - 10)
    {
     PRINTF
      if (j == WIDTH - 1)LINE
    }
    else EMPTY
   }
  }
  for (int i = 0; i < WIDTH; i++)PRINTF LINE  //下边框
}
void MoveCursor(int x, int y)//设置光标位置(就是输出显示的开始位置)
{
 /*  COORD是Windows API中定义的一种结构体
 * typedef struct _COORD
 * {
 * SHORT X;
 * SHORT Y;
 * } COORD;
 *  */
 COORD pos = { x * 2,y };
 HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);//获得 标准输出的句柄   
 SetConsoleCursorPosition(output, pos); //设置控制台光标位置
}
void FoodRand()
{
 srand((int)time(0));
 int x = rand() % 27 + 2;
 int y = rand() % 17 + 2;
 Phead_1 = Phead;
 for (int i = 0; i <= 200; i++)
 {
  if (Phead_1->x == x&&Phead_1->y == y)
  {
   x = rand() % 27 + 2;
   y = rand() % 17 + 2;
  }
  else
  {
   Phead_1 = Phead_1->next;
  }
  if (Phead_1->next == NULL)
  {
   break;
  }
 }
 MoveCursor(x, y);
 PRINTF
  Food = (Snakexy*)malloc(sizeof(Snakexy));
 Food->x = x;
 Food->y = y;
 MoveCursor(33, 5);
 printf("  ");
 Showf();
 sum++;
 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);// 蓝
}
void ControlMove()
{
 if (GetAsyncKeyState(VK_UP) && 0x8000)
 {
  if (JudgeDirection == 2)
  {
  }
  else
  {
   JudgeDirection = 1;
  }
 }
 if (GetAsyncKeyState(VK_DOWN) && 0x8000)
 {
  if (JudgeDirection == 1)
  {
  }
  else
  {
   JudgeDirection = 2;
  }
 }
if (GetAsyncKeyState(VK_RIGHT) && 0x8000)
 {
  if (JudgeDirection == 3)
  {
  }
  else
  {
   JudgeDirection = 4;
  }
 }
 if (GetAsyncKeyState(VK_LEFT) && 0x8000)
 {
  if (JudgeDirection == 4)
  {
  }
  else
  {
   JudgeDirection = 3;
  }
 }
 if (GetAsyncKeyState(VK_RETURN) && 0x0D)
 {
  while (1)
  {
   	if (GetAsyncKeyState(VK_RETURN) && 0x0D)
   	{
    		break;
   	}
  }
 }
}
void ISnake()
{
 for (int i = 0; i < 5; i++)
 {
  Pbady = (Snakebody*)malloc(sizeof(Snakebody));
  Pbady->x = 5 - i;
  Pbady->y = 5;
  if (Phead == NULL)
  {
   Phead = Pbady;
  }
  else
  {
   end->next = Pbady;
  }
  Pbady->next = NULL;
  end = Pbady;
 }
 Phead_1 = Phead;
  while (Phead_1->next != NULL)
 {
   MoveCursor(Phead_1->x, Phead_1->y);
   PRINTF
   Phead_1 = Phead_1->next;
 }
 }
 void Move()
{
 while (1)
 {
  Phead_1 = Phead;
    while (Phead_1->next->next != NULL)
    {
   	Phead_1 = Phead_1->next;
    }
  Phead_1->next = NULL;
  for (int i = 0; i < Pause; i++) {}
  ControlMove();
  MoveCursor(Phead_1->x, Phead_1->y);
  EMPTY
  //上面为消除尾部
   Snakebody *Phead_2 = (Snakebody*)malloc(sizeof(Snakebody));
   if (*PJ == 1)
  {
   Phead_2->x = Phead->x;
   Phead_2->y = Phead->y - 1;
  }
  if (*PJ == 2)
  {
   Phead_2->x = Phead->x;
   Phead_2->y = Phead->y + 1;
  }
  if (*PJ == 3)
  {
   Phead_2->x = Phead->x - 1;
   Phead_2->y = Phead->y;
  }
  if (*PJ == 4)
  {
   Phead_2->x = Phead->x + 1;
   Phead_2->y = Phead->y;
  }
  Phead_2->next = Phead;
  Phead = Phead_2;
  MoveCursor(Phead_2->x, Phead_2->y);
  PRINTF
  Jfood();
  Jwall();
  Jsnake();
  MoveCursor(40, 20);
 }
}
void Jfood()
{
 Phead_1 = Phead;
 if (Phead_1->x == Food->x&&Phead_1->y == Food->y)
 {
  FoodRand();
  JudgeSum += 1;
  if (JudgeSum == 5)
  {
   	JudgeSum = 0;
   	Hard += 1;
   	Pause -= 20000000;
  }
  while (Phead_1->next != NULL)
  {
   	Phead_1 = Phead_1->next;
  }
  Snakebody *S = (Snakebody*)malloc(sizeof(Snakebody));
  S->x = Food->x;
  S->y = Food->y;
  S->next = NULL;
  Phead_1->next = S;
  ControlMove();
  MoveCursor(Phead_1->x, Phead_1->y);
  PRINTF
 }
 //获取食物的坐标和蛇头做对比
}
void Jwall()
{
  if (Phead->x == 0 || Phead->x == 29 || Phead->y == 0 || Phead->y == 19)
  {
  	MoveCursor(10, 20);
  	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//设置红色
  	printf("抱歉,你撞到了自己,游戏结束!              ");
  	system("pause>nul");
  	exit(0);
  }
}
void Jsnake()
{
 Phead_1 = Phead->next;
 while (Phead_1->next != NULL)
 {
  	if ((Phead->x == Phead_1->x) && (Phead->y == Phead_1->y))
  	{
  	 	MoveCursor(10, 20);
   		SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);//设置红色
   		printf("抱歉,你撞到了自己,游戏结束!          ");
   		system("pause>nul");
   		exit(0);
  	}
  Phead_1 = Phead_1->next;
 }
}
void Showf()
{
 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE);// 蓝
 MoveCursor(33, 5);
 printf("得分:%d", sum);
 MoveCursor(33, 6);
 printf("难度:%d", Hard);
}
void Free()
{
 while (Phead->next != NULL)
 {
  Phead=Phead->next;
  free(Phead);
 }
 free(Phead);
}

如有不对,欢迎指出,期待我的下一篇文章。
每文一句:梦是心灵的思想,是我们的秘密真情。