jzzc 发表于 2020-3-14 15:04:52

学完第一季C写了个很简单的贪吃蛇嘿嘿嘿

学了小甲鱼的C第一季,写了一个贪吃蛇嘿嘿{:10_254:} ,下面是游戏效果图:

写出来就图一乐,为了纪念学完了C的第一季,其实代码语法很简单,逻辑也不难,感觉大概学完数组就可以写了;我的代码有点长,windows环境下运行,复制粘贴就可以玩,希望我们一起加油学,一起奋斗{:10_254:},怀挺!!!{:10_311:}
代码附上,注释都写了,哪有不懂得或者有啥建议,欢迎大家讨论{:10_310:}
/**************************************************/
/*作者:JZZC
/*日期:2020.3.14
/*可借鉴,可参考,祝君开心
/**************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>      //光标设置的API,构建gotoxy()函数有使用
#include <time.h>      //食物随机
#include <conio.h>      //按键监控,构建directionJudge()函数使用

#define         MAPWIDTH         52      //地图宽
#define         MAPHEIGHT         22      //地图高
#defineBOUNDRY         78      //界面右边界
#define         MAXLENGTH       522      //蛇最大身长
#defineINITDIRECTION   'w'      //初始化移动方向
#defineBUMP               1      //碰撞
#defineNOBUMP               0      //未碰撞

//变量声明
struct      Food
{
      int x;
      int y;
}food;                                        //食物的结构体

struct      Snake
{
      int length;
      int speed;
      int x;
      int y;
}snake;                                        //蛇的结构体

unsigned int unused = (srand((unsigned int)time(NULL)), 0);//撒随机种子
char moveDirection = INITDIRECTION;      //初始化移动方向
int changeSnake = 0;                        //蛇身长度变化标志位
int bumpFlag = 0;                        //碰撞标志位
int score = 0;                              //得分

//函数声明
void initWall(void);                        //初始化围墙
void gameExplain(void);                        //打印游戏说明
void initSnake(void);                         //初始化一条蛇
void creatFood(void);                        //生成食物
void initGame(void);                        //游戏初始化(调用以上函数)

void gotoxy(int x, int y);                //控制光标的函数,会用即可,都是库函数

void directionJudge(void);                //接收按键键值,判断蛇移动方向
int stateJudge(void);                        //计算出新的蛇坐标后,调用此函数判断是否发生碰撞
void printSnake(void);                        //打印蛇,达到动态效果

void gameOver(void);                        //打印结局

//函数定义
void gotoxy(int x, int y)
{
      //1.找到控制台窗口
      HANDLE      handle = GetStdHandle(STD_OUTPUT_HANDLE);

      //2.声明光标结构体
      COORD coord;

      //3.设置坐标
      coord.X = x;
      coord.Y = y;

      //4.同步到控制台
      SetConsoleCursorPosition(handle, coord);
}

void initWall(void)
{
      int i;
      //1.打印竖起来的围墙
      for (i = 0; i <= MAPHEIGHT; i++)
      {
                gotoxy(0, i);
                printf("■");

                gotoxy(MAPWIDTH, i);
                printf("■");

                gotoxy(BOUNDRY, i);
                printf("■");
      }
      //2.打印上下两排围墙,注意 '■'占两个位置,所以要加2,后面不再强调
      for (i = 0; i <= BOUNDRY; i += 2)
      {
                gotoxy(i, 0);
                printf("■");
                gotoxy(i, MAPHEIGHT);
                printf("■");
      }
}

void gameExplain(void)
{
      //打印游戏说明
      int a = 0;
      gotoxy(MAPWIDTH + 4, 2);
      printf("游戏说明:W键向上,");
      gotoxy(MAPWIDTH + 4, 3);
      printf("A键向左,S键向下,");
      gotoxy(MAPWIDTH + 4, 4);
      printf("D键向右(不区分大小写)");

      gotoxy(MAPWIDTH + 8, MAPHEIGHT / 2);
      printf("当前分数:%03d", score);

      gotoxy(MAPWIDTH + 8, MAPHEIGHT - 4);
      printf("游戏作者:JZZC");
}

void initSnake(void)
{
      int i;
      //画蛇
      //1 初始化蛇的属性
      snake.length = 3;
      snake.speed = 180;

      //2 画蛇头
      snake.x = MAPWIDTH / 2;
      snake.y = MAPHEIGHT / 2;
      gotoxy(snake.x, snake.y);
      printf("⊙");

      //3 画蛇身
      for (i = 1; i < snake.length; i++)
      {
                snake.x = snake.x + 2;
                snake.y = snake.y;
                gotoxy(snake.x, snake.y);
                printf("●");
      }
}

void creatFood(void)
{
      int foodOnsnake;
      int i = 0;
      //注意产生的食物不能在蛇身上,横坐标也不能是奇数开头,
      //读者可以想一下为什么要这样规定,想不懂欢迎追问^_^
      do
      {
                foodOnsnake = 0;
                food.x = rand() % (MAPWIDTH - 4) + 2;
                if (food.x % 2)
                {
                        food.x -= 1;
                }
                food.y = rand() % (MAPHEIGHT - 2) + 1;

                for (i = 0; i < snake.length; i++)
                {
                        if (food.x == snake.x && food.y == snake.y)
                        {
                              foodOnsnake = 1;
                              break;
                        }
                }
      } while (foodOnsnake);

      gotoxy(food.x, food.y);
      printf("◆");
}

void initGame(void)
{
      //1初始化围墙
      initWall();
      //2游戏说明
      gameExplain();
      //3初始化蛇
      initSnake();
      //4画出食物
      creatFood();
}

void gameOver(void)
{
      //1.打印框高
      for (int i = MAPHEIGHT / 4; i <= MAPHEIGHT / 4 * 3; i++)
      {
                gotoxy(MAPWIDTH / 4, i);
                printf("■");
                gotoxy(MAPWIDTH / 4 * 3, i);
                printf("■");
      }
      //2.打印框宽
      for (int i = MAPWIDTH / 4; i <= MAPWIDTH / 4 * 3; i += 2)
      {
                gotoxy(i, MAPHEIGHT / 4);
                printf("■");
                gotoxy(i, MAPHEIGHT / 4 * 3);
                printf("■");
      }
      
      //3.框内像素点清除(用两个空格清除)
      for (int i = MAPHEIGHT / 4 + 1; i < MAPHEIGHT / 4 * 3; i++)
      {
                for (int j = MAPWIDTH / 4 + 2; j < MAPWIDTH / 4 * 3; j += 2)
                {
                        gotoxy(j, i);
                        printf("");
                }                              
      }
      
      //4.打印Game Over!
      gotoxy(MAPWIDTH / 2 - 4, MAPHEIGHT / 2 - 2);
      printf("Game Over!");
}

void directionJudge(void)
{
      char ch;
      char lastkey = moveDirection;      //记录上次移动方向,防止蛇头往里缩(形象比喻)
      //接收按键值
      if (_kbhit())
      {
                //清除标准输入流缓存
                fflush(stdin);
               
                if ((ch = _getch()) == 'w' || ch == 'W' || ch == 'a' || ch == 'A' || ch == 's'
                        || ch == 'S' || ch == 'd' || ch == 'D')
                {
                        switch (ch)
                        {
                              case 'w':
                              case 'W':
                                        if (lastkey == 's' || lastkey == 'S')
                                        {
                                                moveDirection = 's';
                                                break;
                                        }
                                        else
                                        {
                                                moveDirection = 'w';
                                                break;
                                        }
                              case 'a':
                              case 'A':
                                        if (lastkey == 'd' || lastkey == 'D')
                                        {
                                                moveDirection = 'd';
                                                break;
                                        }
                                        else
                                        {
                                                moveDirection = 'a';
                                                break;
                                        }
                              case 's':
                              case 'S':
                                        if (lastkey == 'w' || lastkey == 'W')
                                        {
                                                moveDirection = 'w';
                                                break;
                                        }
                                        else
                                        {
                                                moveDirection = 's';
                                                break;
                                        }
                              case 'd':
                              case 'D':
                                        if (lastkey == 'a' || lastkey == 'A')
                                        {
                                                moveDirection = 'a';
                                                break;
                                        }
                                        else
                                        {
                                                moveDirection = 'd';
                                                break;
                                        }
                        }
                }
      }
}

int stateJudge(void)
{
      int i;
      
      //1.判断是否撞到墙壁
      if (snake.x == 0 || snake.x == MAPWIDTH || snake.y == 0 || snake.y == MAPHEIGHT)
      {
                return BUMP;
      }
      
      //2.判断是否撞到自己
      for (i = 1; i < snake.length; i++)
      {
                if (snake.x == snake.x && snake.y == snake.y)
                {
                        return BUMP;
                }
      }

      return NOBUMP;
}

void printSnake(void)
{
      //未算出蛇身新坐标前,先记录下当前的蛇尾坐标。
      int blockx = snake.x;
      int blocky = snake.y;
      static char lastkey;
      
      lastkey = moveDirection;
      
      //1.判断移动方向
      directionJudge();
      
      //2.计算蛇身坐标(不包含蛇头)
      for (int i = snake.length - 1; i > 0; i--)
      {
                snake.x = snake.x;
                snake.y = snake.y;
      }
      //3.计算蛇头坐标
      switch (moveDirection)
      {
                case 'w':
                case 'W':
                         snake.y -= 1;
                         break;
                case 'a':
                case 'A':
                         snake.x -= 2;
                         break;
                case 's':
                case 'S':
                         snake.y += 1;
                         break;
                case 'd':
                case 'D':
                         snake.x += 2;
                         break;
                default: break;
      }
      
      //4.判断是否发生碰撞,若产生碰撞会在main函数中直接跳出while(1),防止蛇“吃”墙
      if (stateJudge() == 1)
      {
                bumpFlag = 1;
                return;
      }
      
      //5.未发生碰撞,打印蛇
      gotoxy(snake.x, snake.y);
      printf("⊙");
      for (int i = 1; i < snake.length; i++)
      {
                gotoxy(snake.x, snake.y);
                printf("●");
      }
      
      //6.判断蛇头是否吃到食物      
      if (snake.x == food.x && snake.y == food.y)
      {
                snake.length += 1;
                changeSnake = 1;
      }
      else
      {
                changeSnake = 0;
      }
      
      //7.根据是否吃到食物判断蛇尾是保留还是用空格替代,已达到蛇身增长和蛇移动的效果
      if (changeSnake == 0)
      {
                gotoxy(blockx, blocky);
                printf("");
      }
      else
      {
                snake.x = blockx;
                snake.y = blocky;
                score += 1;
                gotoxy(MAPWIDTH + 18, MAPHEIGHT / 2);
                printf("%03d", score);
                creatFood();
      }
}

int main(void)
{
      initGame();
      
      while (1)
      {
                //1.打印蛇
                printSnake();
               
                //检测是否发生碰撞
                if (bumpFlag == 1)
                {
                        break;
                }
               
                //短暂停顿
                Sleep(snake.speed);
      }
      
      gameOver();
      getchar();
      gotoxy(0, MAPHEIGHT +1);
      return 0;
}

鱼忘七秒 发表于 2020-3-15 00:12:06

赞一个!

正在学习的新手 发表于 2020-3-15 06:32:04

厉害厉害!大佬

tciswangbo 发表于 2020-3-15 22:22:48

严重性        代码        说明        项目        文件        行        禁止显示状态
错误        LNK2019        无法解析的外部符号 _WinMain@16,该符号在函数 "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ) 中被引用        Snake        c:\Users\tciswangbo\documents\visual studio 2015\Projects\Snake\Snake\MSVCRTD.lib(exe_winmain.obj)        1       
这个是什么问题啊

tciswangbo 发表于 2020-3-15 22:23:28

unsigned int unused = (srand((unsigned int)time(NULL)), 0);//撒随机种子 ,,这一行显示错误常量不能使用函数调用

jzzc 发表于 2020-3-16 08:44:22

鱼忘七秒 发表于 2020-3-15 00:12
赞一个!

嘻嘻{:10_287:}

jzzc 发表于 2020-3-16 08:45:01

正在学习的新手 发表于 2020-3-15 06:32
厉害厉害!大佬

你也可以的!加油{:10_279:}

jzzc 发表于 2020-3-16 08:54:50

tciswangbo 发表于 2020-3-15 22:23
unsigned int unused = (srand((unsigned int)time(NULL)), 0);//撒随机种子 ,,这一行显示错误常量不能使 ...

那把这一行剪切到main函数的第一行,就行了{:10_254:}

jzzc 发表于 2020-3-16 08:55:49

tciswangbo 发表于 2020-3-15 22:22
严重性        代码        说明        项目        文件        行        禁止显示状态
错误        LNK2019        无法解析的外部符号 _WinMain@16,该符号在函 ...

这个问题我也不知道诶,我的电脑上DEVC++和Visual Studio都可以用诶

最后的魁拔 发表于 2020-3-17 10:20:51

只能说天赋太强了

jzzc 发表于 2020-3-17 13:56:19

最后的魁拔 发表于 2020-3-17 10:20
只能说天赋太强了

靴靴夸奖,嘻嘻{:10_297:}

最后的魁拔 发表于 2020-3-17 14:04:10

jzzc 发表于 2020-3-17 13:56
靴靴夸奖,嘻嘻

楼主大几了{:10_299:}

ba21 发表于 2020-3-17 14:10:54

代码值得参考。
不过
1这可不是学完第一季C能写的代码。
2没有编程功底不可能写出这样的代码。
3从你的回复中 了解到你用编译器DEVC++和Visual Studio;所以你有一定编程功底的。

jzzc 发表于 2020-3-17 14:22:07

最后的魁拔 发表于 2020-3-17 14:04
楼主大几了

刚毕业,工作是嵌入式行业的研发岗,要用到c语言,但大学全荒废了,趁着春节跟着小甲鱼学了一遍C,学完想做点东西,就拿贪吃蛇练手了{:10_298:}

最后的魁拔 发表于 2020-3-17 14:28:30

jzzc 发表于 2020-3-17 14:22
刚毕业,工作是嵌入式行业的研发岗,要用到c语言,但大学全荒废了,趁着春节跟着小甲鱼学了一遍C,学完想 ...

请问楼主考研了吗?

jzzc 发表于 2020-3-17 14:33:31

ba21 发表于 2020-3-17 14:10
代码值得参考。
不过
1这可不是学完第一季C能写的代码。


1.如果说全靠自己想的话,学完第一季写出这个贪吃蛇确实难,但是如果有参考的话就会有醍醐灌顶的感觉,就比如说自己封装一个gotoxy(); 这个函数内容倒是简单,重要的是这个思想;还有这个程序里面甚至都没有用到指针,最最新的可能也就是结构体了,感觉语法确实不难啊{:10_291:};
2.编程功底的话,自己还是太菜了,在一些单片机小项目里全是分支结构和for循环,可能逻辑能力会强一些,但是在这之前复杂的语法没有学过;{:10_266:}
3.visual studio 是搜相关问题的时候看大佬们都在用,感觉界面很炫酷,就搜了一下是怎么下载的哈哈哈,(确实很炫酷)。

jzzc 发表于 2020-3-17 14:34:22

最后的魁拔 发表于 2020-3-17 14:28
请问楼主考研了吗?

考研失利了,当时没有打算二战,就趁着春招找了个工作

风吹来了 发表于 2020-3-17 15:10:09

狠人

zgh15618511320 发表于 2020-3-17 15:14:07

看了楼主写的不错 程序跑下来也能运行,好好消化 谢谢分享

jzzc 发表于 2020-3-17 15:40:18

zgh15618511320 发表于 2020-3-17 15:14
看了楼主写的不错 程序跑下来也能运行,好好消化 谢谢分享

嗯嗯 哪里不懂的话可以在这里留言{:10_298:}
页: [1] 2 3
查看完整版本: 学完第一季C写了个很简单的贪吃蛇嘿嘿嘿