贪吃蛇游戏(代码篇)

简介: 贪吃蛇游戏(代码篇)

前言

 这是我自己做的第五个小项目---贪吃蛇游戏(代码篇)。后期我会继续制作其他小项目并开源至博客上。


 上一小项目是贪吃蛇游戏(必备知识篇),没看过的同学可以去看看:


有关贪吃蛇必备知识的小项目

https://blog.csdn.net/hsy1603914691/article/details/142455297?sharetype=blogdetail&sharerId=142455297&sharerefer=PC&sharesource=hsy1603914691&spm=1011.2480.3001.8118


实现代码

1. 下面代码直接复制即可运行。

2. 每个代码块都有简洁的总结和解释。

<snake.h>文件

#define _CRT_SECURE_NO_WARNINGS
#include <locale.h>
#include <stdio.h>
#include <windows.h>
#include <stdbool.h>
#include <stdlib.h>
#include <time.h>
 
#define KEY_PRESS(VK) ((GetAsyncKeyState(VK)&0x1) ? 1 : 0) //设置键值
#define POS_X 24 //蛇初始位置
#define POS_Y 5 //蛇初始位置
 
//节点类型
typedef struct SnakeNode
{
  //节点的坐标
  int x;
  int y;
  //指向下一个节点的指针
  struct SnakeNode* next;
}SnakeNode;
typedef struct SnakeNode*  pSnakeNode;
 
//贪吃蛇的信息
typedef struct Snake
{
  pSnakeNode _pSnake;//贪吃蛇的身体节点
  pSnakeNode _pFood;//食物节点
  enum Direction _dir;//贪吃蛇的方向
  enum Game_Statues _status;//贪吃蛇的状态
  int _food_weight;//一个食物的分数
  int _score;//总分数
  int _sleep_time;//休息时间,即贪吃蛇的速度
}Snake;
typedef struct Snake* pSnake;
 
//方向
enum Direction
{
  UP,
  DOWN,
  LEFT,
  RIGHT
};
 
//状态
enum Game_Status
{
  OK,
  KILL_BY_WALL,
  KILL_BY_SELF,
  END_NORMAL
};
 
//游戏开始
void GameStart(pSnake ps);
//欢迎函数
void WelcomeToGame();
//定位坐标
void SetPos(int x, int y);
//打印地图
void CreateMap();
//初始化贪吃蛇
void InitSnake(pSnake ps);
//创造食物
void CreateFood(pSnake ps);
//游戏运行
void GameRun(pSnake ps);
//打印帮助信息
void PrintHelpInfo();
//暂停设置
void Pause();
//实现贪吃蛇的移动
void SnakeMove(pSnake ps);
//判断是否吃到食物
int NextIsFood(pSnakeNode pn, pSnake ps);
//实现贪吃蛇吃食物并增长蛇身
void EatFood(pSnakeNode pn, pSnake ps);
//吃到食物后使食物消失
void NoFood(pSnakeNode pn, pSnake ps);
//被墙杀死
void KillByWall(pSnake ps);
//被自己杀死
void KillBySelf(pSnake ps);
//游戏结束
void GameEnd(pSnake ps);

<snake.c>文件

#include "snake.h"
//游戏开始
void GameStart(pSnake ps)
{
  //设置窗口
  system("mode con cols=100 lines=30");//调整CMD行与列
  system("title 贪吃蛇");//修改CMD的标题
 
  //获取标准输出的句柄,存放在houtput中。
  HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
  if (houtput == INVALID_HANDLE_VALUE) // 处理错误,例如输出错误信息
  {
    fprintf(stderr, "Failed to get standard output handle.\n");
    return;
  }
 
  //创建一个CONSOLE_CURSOR_INFO的结构体
  CONSOLE_CURSOR_INFO CursorInfo;
  if (!GetConsoleCursorInfo(houtput, &CursorInfo)) // 处理错误,例如输出错误信息
  {
    fprintf(stderr, "Failed to get console cursor info.\n");
    return;
  }
 
  //隐藏控制台光标
  CursorInfo.bVisible = false; 
  if (!SetConsoleCursorInfo(houtput, &CursorInfo)) // 处理错误,例如输出错误信息
  {
    fprintf(stderr, "Failed to set console cursor info.\n");
    return;
  }
  
  //欢迎函数
  WelcomeToGame();
  //打印地图
  CreateMap();
  //初始化贪吃蛇
  InitSnake(ps);
  //设置食物的位置
  CreateFood(ps);
}
 
//欢迎函数
void WelcomeToGame()
{
  SetPos(32, 13);
  printf("Welcome to the Classic Snake Game!");
  SetPos(39, 22);
  system("pause");//打印完一个界面后直接暂停,直到点击继续
  system("cls");//在清空界面,打印新的一个界面
  SetPos(30, 13);
  wprintf(L"Navigate the Snake using ↑ ↓ ← →.");
  SetPos(33, 15);
  wprintf(L"Accelerate to earn more points.");
  SetPos(38, 23);
  system("pause");
  system("cls");
}
 
//定位坐标
void SetPos(int x, int y)
{
  //获取标准输出的句柄,存放在houtput中
  HANDLE houtput = GetStdHandle(STD_OUTPUT_HANDLE);
  //设定我们想要定位的坐标
  COORD pos = { x,y };
  //将光标定位到pos2
  SetConsoleCursorPosition(houtput, pos);
}
 
//打印地图
void CreateMap()
{
  int i = 0;
  //打印上边界
  for (i = 0; i < 29; i++)
  {
    wprintf(L"□");
  }
  //打印下边界
  SetPos(0, 26);
  for (i=0; i < 29; i++)
  {
    wprintf(L"□");
  }
  //打印左边界
  for (i = 1; i <= 25; i++)
  {
    SetPos(0, i);
    wprintf(L"□");
  }
  //打印右边界
  for (i = 1; i <= 25; i++)
  {
    SetPos(56, i);
    wprintf(L"□");
  }
}
 
//初始化贪吃蛇
void InitSnake(pSnake ps)
{
  int i = 0;
  for (i = 0; i < 5; i++)//开始贪吃蛇一共设置五个长度
  {
    pSnakeNode  cur = (pSnakeNode)malloc(sizeof(SnakeNode));
    if (cur == NULL)
    {
      perror("InitSnake error");
      exit(1);
    }
    cur->next = NULL;
    cur->x = POS_X + 2 * i;
    cur->y = POS_Y;
    //头插法
    if (ps->_pSnake == NULL)
    {
      ps->_pSnake = cur;
    }
    else
    {
      cur->next = ps->_pSnake;
      ps->_pSnake = cur;
    }
  }
  pSnakeNode  cur = ps->_pSnake;
  while (cur != NULL)
  {
    SetPos(cur->x, cur->y);
    wprintf(L"●");
    cur = cur->next;
  }
  //设置贪吃蛇的属性
  ps->_dir = RIGHT;
  ps->_score = 0;
  ps->_food_weight = 10;
  ps->_sleep_time = 200;
  ps->_status = OK;
}
 
//设置食物的位置
void CreateFood(pSnake ps)
{
  int x;
  int y;
again:
  do
  {
    x = (rand()) % 53 + 2;
    y = (rand()) % 25 + 1;
  } while (x % 2 != 0);
  //不能与蛇身冲突
  pSnakeNode  cur = ps->_pSnake;
  while (cur != NULL)
  {
    if ((x == cur->x) && (y == cur->y))
    {
      goto again;
    }
    cur = cur->next;
  }
  //创建食物节点
  pSnakeNode  pFood = (pSnakeNode)malloc(sizeof(SnakeNode));
  if (pFood == NULL)
  {
    perror("CreateFood error");
    exit(1);
  }
  pFood->x = x;
  pFood->y = y;
  pFood->next = NULL;
  SetPos(x, y);
  wprintf(L"★");
  ps->_pFood = pFood;
}
 
//游戏运行
void GameRun(pSnake ps)
{
  //打印欢迎界面
  PrintHelpInfo();
  //游戏开始运行
  do
  {
    //显示分数
    SetPos(64, 7);
    printf("Current score: %d", ps->_score);
    SetPos(64, 8);
    printf("Current food score: %2d", ps->_food_weight);
    //判断玩家操作
    if (KEY_PRESS(VK_UP) && (ps->_dir != UP))
    {
      ps->_dir = UP;
    }
    else if (KEY_PRESS(VK_DOWN) && (ps->_dir != DOWN))
    {
      ps->_dir = DOWN;
    }
    else if (KEY_PRESS(VK_LEFT) && (ps->_dir != LEFT))
    {
      ps->_dir = LEFT;
    }
    else if (KEY_PRESS(VK_RIGHT) && (ps->_dir != RIGHT))
    {
      ps->_dir = RIGHT;
    }
    else if (KEY_PRESS(VK_SPACE))
    {
      Pause();//暂停设置
    }
    else if (KEY_PRESS(VK_ESCAPE))
    {
      ps->_status = END_NORMAL;
    }
    else if (KEY_PRESS(VK_F3))
    {
      if (ps->_sleep_time > 80)
      {
        ps->_sleep_time -= 30;
        ps->_food_weight += 2;
      }
    }
    else if (KEY_PRESS(VK_F4))
    {
      if (ps->_food_weight > 2)
      {
        ps->_sleep_time += 30;
        ps->_food_weight -= 2;
      }
    }
    //实现贪吃蛇的移动
    SnakeMove(ps);
    //通过短暂暂停来展现动态效果
    Sleep(ps->_sleep_time);
  } while (ps->_status == OK);
}
 
//打印欢迎界面
void PrintHelpInfo()
{
  SetPos(64, 10);
  wprintf(L"No wall passing. No self-biting.");
  SetPos(64, 12);
  wprintf(L"F3 to speed up. F4 to slow down.");
  SetPos(64, 14);
  wprintf(L"ESC to exit. Space to pause.");
  SetPos(74, 21);
  wprintf(L"Made by HSY,");
  SetPos(66, 22);
  wprintf(L"a uniquely independent pig.");
}
 
//暂停设置
void Pause()
{
  while (1)
  {
    Sleep(200);
    if (KEY_PRESS(VK_SPACE))
    {
      break;
    }
  }
}
 
//实现贪吃蛇的移动
void SnakeMove(pSnake ps)
{
  pSnakeNode  pNextNode = (pSnakeNode)malloc(sizeof(SnakeNode));
  if (pNextNode == NULL)
  {
    perror("SnakeMove error");
    exit(1);
  }
  switch (ps->_dir)
  {
  case UP:
    pNextNode->x = ps->_pSnake->x;
    pNextNode->y = ps->_pSnake->y - 1;
    break;
  case DOWN:
    pNextNode->x = ps->_pSnake->x;
    pNextNode->y = ps->_pSnake->y + 1;
    break;
  case LEFT:
    pNextNode->x = ps->_pSnake->x - 2;
    pNextNode->y = ps->_pSnake->y;
    break;
  case RIGHT:
    pNextNode->x = ps->_pSnake->x + 2;
    pNextNode->y = ps->_pSnake->y;
    break;
  }
  if (NextIsFood(pNextNode,ps))
  {
    EatFood(pNextNode, ps);
  }
  else
  {
    NoFood(pNextNode, ps);
  }
  KillByWall(ps);//被墙杀死
  KillBySelf(ps);//被自己杀死
}
 
//判断是否吃到食物
int NextIsFood(pSnakeNode pn, pSnake ps)
{
  return (ps->_pFood->x == pn->x && ps->_pFood->y == pn->y);
}
 
//实现贪吃蛇吃食物并增长蛇身
void EatFood(pSnakeNode pn, pSnake ps)
{
  ps->_pFood->next = ps->_pSnake;
  ps->_pSnake = ps->_pFood;
  free(pn);
  pn = NULL;
  pSnakeNode cur = ps->_pSnake;
  while (cur!=NULL)
  {
    SetPos(cur->x, cur->y);
    wprintf(L"●");
    cur = cur->next;
  }
  ps->_score += ps->_food_weight;
  CreateFood(ps);
}
 
//吃到食物后使食物消失
void NoFood(pSnakeNode pn, pSnake ps)
{
  pn->next = ps->_pSnake;
  ps->_pSnake = pn;
  pSnakeNode  cur = ps->_pSnake;
  while (cur->next->next != NULL)
  {
    SetPos(cur->x, cur->y);
    wprintf(L"●");
    cur = cur->next;
  }
  SetPos(cur->next->x, cur->next->y);
  printf("  ");
  free(cur->next);
  cur->next = NULL;
}
 
//被墙杀死
void KillByWall(pSnake ps)
{
  if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56 || ps->_pSnake->y == 0 || ps->_pSnake->y == 26)
  {
    ps->_status = KILL_BY_WALL;
  }
}
 
//被自己杀死
void KillBySelf(pSnake ps)
{
  pSnakeNode cur = ps->_pSnake->next;
  while (cur)
  {
    if (cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y)
    {
      ps->_status = KILL_BY_SELF;
      break;
    }
    cur = cur->next;
  }
}
 
//游戏结束
void GameEnd(pSnake ps)
{
  //判断哪种结束方式
  switch (ps->_status)
  {
  case END_NORMAL:
    SetPos(17, 12);
    printf("You have ended the game.");
    break;
  case KILL_BY_WALL:
    SetPos(10, 12);
    printf("You ended the game by hitting a wall.");
    break;
  case KILL_BY_SELF:
    SetPos(10, 12);
    printf("You ended the game by self-collision.");
    break;
  }
  //清除贪吃蛇
  pSnakeNode cur = ps->_pSnake;
  pSnakeNode prev = NULL;
  while (cur)
  {
    prev = cur;
    cur = cur->next;
    free(prev);
  }
 
}

<test.c>文件

#include "snake.h"
//游戏的主体进程
void test()
{
  char ch;
  do
  {
    system("cls");
    Snake snake = { 0 };
    GameStart(&snake);//游戏开始
    GameRun(&snake);//游戏运行
    GameEnd(&snake);//游戏结束
    SetPos(20, 15);//结束之后,询问是否再来一次
    printf("Play again? (Y/N)");
    ch = getchar();//用户输入一个字符并按回车后,实际上有两个字符进入了输入缓冲区:用户输入的字符和随后的换行符。第一个 getchar() 会读取用户输入的字符,而第二个 getchar() 则用来读取(并丢弃)换行符。
    getchar();
  } while (ch == 'Y'|| ch == 'y');
  SetPos(0, 28);//如果游戏结束,(为了美观)退出代码定位
}
 
//主函数
int main()
{
  //设置本地环境
  setlocale(LC_ALL, "");
  //生成随机值
  srand((unsigned int)time(NULL));
  //测试游戏
  test();
  return 0;
}

致谢

 感谢您花时间阅读这篇文章!如果您对本文有任何疑问、建议或是想要分享您的看法,请不要犹豫,在评论区留下您的宝贵意见。每一次互动都是我前进的动力,您的支持是我最大的鼓励。期待与您的交流,让我们共同成长,探索技术世界的无限可能!

相关文章
|
1月前
|
API C语言 C++
贪吃蛇游戏(必备知识篇)
贪吃蛇游戏(必备知识篇)
68 1
|
6月前
|
Linux C语言
|
6月前
|
程序员 API C语言
超详细实现【贪吃蛇】(1)
超详细实现【贪吃蛇】(1)
81 2
|
6月前
贪吃蛇小游戏
贪吃蛇小游戏
298 5
|
6月前
|
开发工具 索引 Python
python实现贪吃蛇游戏
贪吃蛇是我们小时候的一款经典游戏,毕竟在那个Nokia称霸的年代,这款游戏可是经典中的经典啊!而用Python(蛇)玩Snake(贪吃蛇),那再合适不过了。
111 1
python实现贪吃蛇游戏
|
6月前
|
Python
100行代码,使用 Pygame 制作一个贪吃蛇小游戏!
100行代码,使用 Pygame 制作一个贪吃蛇小游戏!
124 0
|
C语言
C项目(贪吃蛇)
C项目(贪吃蛇)
65 0
|
Python
项目猜拳游戏
用Python语言,编写出猜拳游戏。
65 0
|
移动开发
H5贪吃蛇小游戏
H5贪吃蛇小游戏
|
C语言 C++ Python
俄罗斯方块游戏代码
俄罗斯方块游戏代码
170 0
俄罗斯方块游戏代码