耗时三小时写了一个图形化的贪吃蛇(第一次使用easyx图形库)算是对学习的小小实战吧

简介: 熬夜连干三个小时写了这个小游戏,虽然很简单,我这次只写了无敌模式,你也可以自己继续优化,但是我收获了很多,就来分享一下我遇到的问题和逻辑。效果(gif没有背景音乐,实际上是有的):

文章目录


头文件


1.写出图形化界面


2.绘制🐍


3.移动🐍


4.绘制球球


5.🐍吃球球


6.加上音乐(灵魂)


7.主函数


8.完整代码


小结


20210709022856420.gif


20210709022856420.gif20210709022856420.gif

头文件



三个都是现学的,easyx图形库直接去网上搜下载安装到编译器就🆗,很容易上手,我习惯先去阅读官方文档,有没见过的查阅学习。


#include<stdio.h>//标准io
#include<conio.h>//控制键盘操作
#include<graphics.h>//easyx图形库,创建图形化窗口
#include<stdlib.h>//本次主要用来生成随机数
#include<mmstream.h>//最后这两个,就是花里胡哨,可以播放音乐,升华
#pragma comment(lib ,"winmm.lib")


1.写出图形化界面



注释都写好了


#include<stdio.h>
#include<graphics.h>//引入图形库
//需要知识:结构体,循环,函数,easyx图形库,结构体数组
int main()
{
  initgraph(640,480);//init graph 初始化  图形窗口
  while (1)//卡死
  {
  }
  return 0;
}


效果:


20210708173444582.png


好ok,就是这样,第一步就完成了。加个背景颜色优化一下


//设置背景颜色
  setbkcolor(RGB(82,143,22));
  //清空绘图设备
  cleardevice();


效果:


20210708174110842.png


🆗,完成


2.绘制🐍



说一下,后来背景颜色我要调了一下,配色白痴。。。。。。浪费了可长时间,然后细节代码里都写注释了。


1,好习惯

2,方便学习和增删改查


#include<stdio.h>
#include<graphics.h>//引入图形库
//需要知识:结构体,循环,函数,easyx图形库,结构体数组
//宏定义🐍的最大节数,宏定义建议大写,好的编码习惯
#define SNAKE_MAX 400
//结构体定义🐍的结构
struct Snake
{
  int size;//节数
  int speed;//速度
  int dir;//方向
  //坐标,定义好的结构体(x,y)
  POINT coor[SNAKE_MAX];//通过宏定义定义🐍的最大400节,想开挂,直接去修改宏定义
}snake;
//数据初始化,用函数模块化编程思想
void Gameinit()
{
  //init graph 初始化  图形窗口
  initgraph(640, 480);
  //初始化🐍,开始游戏有3节
  snake.size = 3;
  snake.speed = 10;
  snake.dir;
  //通过循环初始化出3段🐍
  for (int i = snake.size; i >= 0; i--)
  {
  snake.coor[i].x = 10*i+40;//x坐标慢慢调试,合适位置
  snake.coor[i].y = 30;
  }
}
//绘制,也单独写一个函数
void Gamedraw()
{
  //设置背景颜色
  setbkcolor(RGB( 193, 205, 193));
  cleardevice();
  //循环绘制3段🐍
  setfillcolor(GREEN);
  for (int i = 0; i < snake.size; i++)
  {
  solidcircle(snake.coor[i].x, snake.coor[i].y, 5);//三个参数,x坐标,y坐标,半径
  }
  EndBatchDraw();
}
int main()
{
  Gameinit();
  Gamedraw();
  while (1)//卡死
  {
  }
  return 0;
}


简简单单,无压力。最浪费我时间的就是逻辑判断和码字速度,需要多锻炼。😏

🆗看下效果:


20210708183750964.png


3.移动🐍


初步效果:


20210708191220644.gif


很简单,只需要循环让三节在x轴++就实现了,但是有很多问题。


//移动🐍
void snakeMove()
{
  for (int i = 0; i < snake.size; i++)
  {
  snake.coor[i].x++;
  }
}


用枚举加按键改变方向,这里要引入#include<conio.h>//控制键盘操作这个头文件,这里需要注意_getch()是一个阻塞函数,我们通过函数_kbhit()判断有没有按键来解决,有按键就返回真就移动,这里入坑了,🆗没问题


#include<stdio.h>
#include<conio.h>//控制键盘操作
#include<graphics.h>//引入图形界面的头文件
//需要知识:结构体,循环,函数,easyx图形库,结构体数组
//宏定义🐍的最大节数,宏定义建议大写,好的编码习惯
#define SNAKE_MAX 400
//🐍的方向通过枚举实现
enum DIR
{
  UP,
  DOWN,
  LEFT,
  RIGHT,  
};
//结构体定义🐍的结构
struct Snake
{
  int size;//节数
  int speed;//速度
  int dir;//方向
  //坐标,定义好的结构体(x,y)
  POINT coor[SNAKE_MAX];//通过宏定义定义🐍的最大400节,想开挂,直接去修改宏定义
}snake;
//数据初始化,用函数模块化编程思想
void Gameinit()
{
  //init graph 初始化  图形窗口
  initgraph(640, 480);
  //初始化🐍,开始游戏有3节
  snake.size = 3;
  snake.speed = 10;
  //默认往右走
  snake.dir=RIGHT;
  //通过循环初始化出3段🐍
  for (int i = snake.size; i >= 0; i--)
  {
  snake.coor[i].x = 10*i+40;//x坐标慢慢调试,合适位置
  snake.coor[i].y = 30;
  }
}
//绘制,也单独写一个函数
void Gamedraw()
{
  //设置背景颜色
  setbkcolor(RGB(193, 205, 193));
  cleardevice();
  //循环绘制3段🐍
  setfillcolor(GREEN);
  for (int i = 0; i < snake.size; i++)
  {
  solidcircle(snake.coor[i].x, snake.coor[i].y, 5);//三个参数,x坐标,y坐标,半径
  }
  EndBatchDraw();
}
//移动🐍
void snakeMove()
{
  for (int i = 0; i < snake.size; i++)
  {
  switch (snake.dir)
  {
  case UP:
    snake.coor[i].y-=snake.speed;
    break;
  case DOWN:
    snake.coor[i].y+=snake.speed;
    break;
  case LEFT:
    snake.coor[i].x-=snake.speed;
    break;
  case RIGHT:
    snake.coor[i].x+=snake.speed;
    break;
  }
  }
}
//通过按键改变蛇的方向(上下左右),需要头文件#include<conio.h>//控制键盘操作
//72  80  75  77上下左右键对应的数字,可以通过这些数字确定键
//_getch()是一个阻塞函数,我们通过函数_kbhit()判断有没有按键来解决,有按键就返回真,就移动
void keyControl()
{
  if (_kbhit())
  {
  switch (_getch())
  {
  case 'W':
  case'w':
  case 72:
  //移动上面已经写好了,在这就直接用
  snake.dir = UP;
  break;
  case 'S':
  case's':
  case 80:
  snake.dir = DOWN;
  break;
  case 'a':
  case'A':
  case 75:
  snake.dir = LEFT;
  break;
  case 'D':
  case'd':
  case 77:
  snake.dir = RIGHT;
  break;
  }
  }
}
int main()
{
  Gameinit();
  while (1)//卡死
  {
  Gamedraw();
  snakeMove();
  keyControl();
  Sleep(10);
  }
  return 0;
}


实现效果:


20210708194813309.gif


可以看到这……遇到Bug了。

分析了一下,你看它能左右能上下,什么怪物,所以,先让身体跟着头移动


//移动🐍
void snakeMove()
{
  //-1防止数组越界
  for (int i = snake.size-1; i > 0; i--)
  {
  //身体连接到头上
  snake.coor[i] = snake.coor[i - 1];
  }
  switch (snake.dir)
  {
  case UP:
    snake.coor[0].y-= snake.speed;
    break;
  case DOWN:
    snake.coor[0].y+= snake.speed;
    break;
  case LEFT:
    snake.coor[0].x-= snake.speed;
    break;
  case RIGHT:
    snake.coor[0].x+= snake.speed;
    break;
  } 
}

20210708214430589.gif


果然,现在还有问题,发现它是没有头的开了挂的🐍,向上直接能变成向下,左右也是

这时候再用 if 判断语句轻松实现上了不能下,左了不能右,试一下:


//如果不👇那就👆,乖乖听话
  if (snake.dir != DOWN)
  {
    snake.dir = UP;
  }
  break;
  case 'S':
  case's':
  case 80:
  //如果不👆那就👇,乖乖听话
  if (snake.dir != UP)
  { 
    snake.dir = DOWN;
  }
  break;
  case 'a':
  case'A':
  case 75:
  //如果不👉那就👈,乖乖听话
  if (snake.dir != RIGHT)
  {
    snake.dir = LEFT;
  }
  break;
  case 'D':
  case'd':
  case 77:
  //如果不👈那就👉,乖乖听话
  if (snake.dir != LEFT)
  {
    snake.dir = RIGHT;
  }
  break;
  }


好,🆗啊,简简单单。看下效果:


20210708215419302.gif


Good,好像有点样了

但是这个一出墙就不见了,我技术菜,我想让它无敌,上边进下边出,坐进又出。怎么实现?思考一下……


对移动部分的部分的函数做以下判断即可


普通模式: 如果蛇头在y轴坐标小于等于0,就说明已经碰到墙,可以直接结束游戏(不适合我)


开挂模式:如果撞墙把它坐标修改一下就实现了(适合我)


switch (snake.dir)
  {
  case UP:
    snake.coor[0].y-= snake.speed;
    //蛇头在y轴<等你0了,说明撞到墙了
    if (snake.coor[0].y -5 <= 0)
    {
    snake.coor[0].y = 480;
    }
    break;
  case DOWN:
    snake.coor[0].y+= snake.speed;
    if (snake.coor[0].y +5>= 480)
    {
    snake.coor[0].y = 0;
    }
    break;
  case LEFT:
    snake.coor[0].x-= snake.speed;
    if (snake.coor[0].x +5 <=0)
    {
    snake.coor[0].x = 640;
    }
    break;
  case RIGHT:
    snake.coor[0].x+= snake.speed;
    if (snake.coor[0].x-5>= 640)
    {
    snake.coor[0].x = 0;
    }
    break;
  }

20210708222051890.gif


4.绘制球球


首先定义食物的结构体


//结构体定义食物的结构
struct food
{
  int x;//食物坐标x
  int y;//食物坐标y
  int r;//食物半径r
  bool flag;  //布尔类型,1没有吃 0吃了
  DWORD color;  //color
}food;


然后进行初始化(随机数终于派上用场了😎)


//food初始化(用到随机函数)虚拟随机(如果没有随机数种子则产生的一样)
  //设置种子需要头文件stdlib.h
  food.x = rand() % 640;//初始化x
  food.y = rand() % 480;//初始化y
  food.color = RGB(rand() % 256, rand() % 256, rand() % 256);//分别初始化rgb的三个元素
  food.r = rand() % 10 + 2;//半径随机
  food.flag = true;//开局就生成食物


然后绘制,很简单,只需要判断是1就画


//绘制食物
  if (food.flag)
  {
  solidcircle(food.x, food.y, food.r);
  }


好,🆗


20210708230936771.png

20210708230957822.png


5.🐍吃球球


//判断吃食物
void eat()
{
  if (food.flag && snake.coor[0].x >= food.x - food.r && snake.coor[0].x <= food.x + food.r && snake.coor[0].y >= food.y - food.r && snake.coor[0].y <= food.y + food.r)
  {
  food.flag = false;
  snake.size++;
  }


吃完再生成


if (!food.flag)
  {
  food.x = rand() % 640;//随机生成一个整数,如果没有随机数种子,每次都是固定的。
  food.y = rand() % 480;//设置种子需要头文件stdlib.h
  food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
  food.r = rand() % 10 + 5;
  food.flag = true;
  }
}


6.加上音乐(灵魂)


头文件


#include<mmstream.h>
#pragma comment(lib ,"winmm.lib")//多媒体设备接口


这些都是固定的,是不是很简单呢


//播放贪吃蛇MP3,固定格式
  mciSendString("open 贪吃蛇.mp3 alias BEIJING", 0, 0, 0);
  mciSendString("play BEIJING repeat", 0, 0, 0);


7.主函数



int main()
{
  Gameinit();
  while (1) {
  Gamedraw();
  snakeMove();
  keyControl();
  eat();
  Sleep(60);
  }
return 0;


8.完整代码



/*
=====贪====吃=====🐍=======小=======游======戏======
@author:周棋洛 
@版本号:1.0 
@date:2021.7.9
*/
#include<stdio.h>
#include<conio.h>//控制键盘操作
#include<graphics.h>//引入图形界面的头文件
#include<stdlib.h>
#include<Windows.h>
#include<mmstream.h>
#pragma comment(lib ,"winmm.lib")//多媒体设备接口
//需要知识:结构体,循环,函数,easyx图形库,结构体数组
//宏定义🐍的最大节数,宏定义建议大写,好的编码习惯
#define SNAKE_MAX 400
//🐍的方向通过枚举实现
enum DIR
{
  UP,
  DOWN,
  LEFT,
  RIGHT,  
};
//结构体定义🐍的结构
struct Snake
{
  int size;//节数
  int speed;//速度
  int dir;//方向
  //坐标,定义好的结构体(x,y)
  POINT coor[SNAKE_MAX];//通过宏定义定义🐍的最大400节,想开挂,直接去修改宏定义
}snake;
//结构体定义食物的结构
struct food
{
  int x;//食物坐标x
  int y;//食物坐标y
  int r;//食物半径r
  bool flag;  //布尔类型,1没有吃 0吃了
  DWORD color;  //color
}food;
//数据初始化,用函数模块化编程思想
void Gameinit()
{
  //播放贪吃蛇MP3,固定格式
  mciSendString("open 贪吃蛇.mp3 alias BEIJING", 0, 0, 0);
  mciSendString("play BEIJING repeat", 0, 0, 0);
  //init graph 初始化  图形窗口
  initgraph(640, 480);
  //设置随机数种子(开机到目前的时间的毫秒)
  srand(GetTickCount());
  //初始化🐍,开始游戏有3节
  snake.size = 3;
  snake.speed = 10;
  //默认往右走
  snake.dir=RIGHT;
  //通过循环初始化出3段🐍
  for (int i = snake.size; i >= 0; i--)
  {
  snake.coor[i].x = 10*i+40;//x坐标慢慢调试,合适位置
  snake.coor[i].y = 30;
  }
  //food初始化(用到随机函数)虚拟随机(如果没有随机数种子则产生的一样)
  //设置种子需要头文件stdlib.h
  food.x = rand() % 600;//初始化x
  food.y = rand() % 440;//初始化y
  food.color = RGB(rand() % 256, rand() % 256, rand() % 256);//分别初始化rgb的三个元素
  food.r = rand() % 15 + 20;//半径随机
  food.flag = true;//开局就生成食物
}
//绘制,也单独写一个函数
void Gamedraw()
{
  //设置背景颜色
  setbkcolor(RGB(193, 205, 193));
  cleardevice();
  //循环绘制3段🐍
  setfillcolor(GREEN);
  for (int i = 0; i < snake.size; i++)
  {
  solidcircle(snake.coor[i].x, snake.coor[i].y, 5);//三个参数,x坐标,y坐标,半径
  }
  //绘制食物
  if (food.flag)
  {
  solidcircle(food.x, food.y, food.r);
  }
  EndBatchDraw();
}
//移动🐍
void snakeMove()
{
  //-1防止数组越界
  for (int i = snake.size-1; i > 0; i--)
  {
  //身体连接到头上
  snake.coor[i] = snake.coor[i - 1];
  }
  switch (snake.dir)
  {
  case UP:
    snake.coor[0].y-= snake.speed;
    //蛇头在y轴<等你0了,说明撞到墙了
    if (snake.coor[0].y -5 <= 0)
    {
    snake.coor[0].y = 480;
    }
    break;
  case DOWN:
    snake.coor[0].y+= snake.speed;
    if (snake.coor[0].y +5>= 480)
    {
    snake.coor[0].y = 0;
    }
    break;
  case LEFT:
    snake.coor[0].x-= snake.speed;
    if (snake.coor[0].x +5 <=0)
    {
    snake.coor[0].x = 640;
    }
    break;
  case RIGHT:
    snake.coor[0].x+= snake.speed;
    if (snake.coor[0].x-5>= 640)
    {
    snake.coor[0].x = 0;
    }
    break;
  }
}
//通过按键改变蛇的方向(上下左右),需要头文件#include<conio.h>//控制键盘操作
//72  80  75  77上下左右键对应的数字,可以通过这些数字确定键
//_getch()是一个阻塞函数,我们通过函数_kbhit()判断有没有按键来解决,有按键就返回真,就移动
void keyControl()
{
  if (_kbhit())
  {
  switch (_getch())
  {
  case 'W':
  case'w':
  case 72:
  //移动上面已经写好了,在这就直接用
  //如果不👇那就👆,乖乖听话
  if (snake.dir != DOWN)
  {
    snake.dir = UP;
  }
  break;
  case 'S':
  case's':
  case 80:
  //如果不👆那就👇,乖乖听话
  if (snake.dir != UP)
  { 
    snake.dir = DOWN;
  }
  break;
  case 'a':
  case'A':
  case 75:
  //如果不👉那就👈,乖乖听话
  if (snake.dir != RIGHT)
  {
    snake.dir = LEFT;
  }
  break;
  case 'D':
  case'd':
  case 77:
  //如果不👈那就👉,乖乖听话
  if (snake.dir != LEFT)
  {
    snake.dir = RIGHT;
  }
  break;
  //游戏暂停⏸
  case ' ':
  while (1)
  {
    if (_getch() == ' ')
    return;
  }
  break;
  }
  }
}
//判断吃食物
void eat()
{
  if (food.flag && snake.coor[0].x >= food.x - food.r && snake.coor[0].x <= food.x + food.r && snake.coor[0].y >= food.y - food.r && snake.coor[0].y <= food.y + food.r)
  {
  food.flag = false;
  snake.size++;
  }
  if (!food.flag)
  {
  food.x = rand() % 640;//随机生成一个整数,如果没有随机数种子,每次都是固定的。
  food.y = rand() % 480;//设置种子需要头文件stdlib.h
  food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
  food.r = rand() % 10 + 5;
  food.flag = true;
  }
}
int main()
{
  Gameinit();
  while (1) {
  Gamedraw();
  snakeMove();
  keyControl();
  eat();
  Sleep(60);
  }
  return 0;
}


好,🆗到这里呢,一个简单的小游戏就完成了。实践之后才发现原来你觉得很简单的东西也是一点一点实现的。还是那句话,不积小流无以成江海小流,不积跬步无以至千里。加油吧,总之这个实战的逻辑很简单。可以试着来自己体验一下,后续可以自己做优化。你会有很多感悟和收获。撒花★,°:.☆( ̄▽ ̄)/$:.°★ 。




小结



对与C又有了新的理解,封装函数,模块化设计,化繁为简,一步步实现简单的最后复杂问题就迎刃而解。


感觉C特别强,一个字,牛,虽然我现在学的浅,随着深入学习,我觉得C很棒,感觉你随便给一些合理的需求,C都可以满足你,呃~~,图形化啊,这个,这个,可以借助库嘛,哈哈


编写程序前,弄清你的需求,不盲目,有需求后,弄清逻辑关系,想到代码的封装以及重要的逻辑处理和编写,锻炼思维,秃头警告⚠


编码过程中,多多少少碰到Bug,这就是修炼之路,先自己想实践看看可不可以解决,不行借助外力干掉bug(我们既是bug的生产者,也是bug的终结者),对代码负责,对自己负责。哈哈


通过有趣的栗子激发自己,促进学习。生活很苦,但你很甜。

🆗,不bb了,打🐏( ̄o ̄) . z Z



相关文章
|
5月前
|
图形学
【制作100个unity游戏之28】花半天时间用unity复刻童年4399经典小游戏《黄金矿工》(附带项目源码)
【制作100个unity游戏之28】花半天时间用unity复刻童年4399经典小游戏《黄金矿工》(附带项目源码)
162 0
|
6月前
|
存储 开发者
彻底搞懂微信小游戏制作工具中的函数
彻底搞懂微信小游戏制作工具中的函数
154 0
|
存储 Java
Java实现贪吃蛇大作战小游戏(完整教程+源码)额外实现积分和变速功能(下)
文章目录 1 开发环境及游戏展示 1.1 游戏主界面 1.2 移动界面 1.3 奖励界面 1.4 F加速功能界面 1.5 死亡界面 2 需求分析 3 系统设计 3.1 系统总体功能设计 3.2 系统总体流程设计 4 功能设计 4.1 贪吃蛇移动及加速功能设计 4.2 贪吃蛇吃食物加速及死亡判定功能的设计 4.2.1 贪吃蛇吃食物加速功能的设计 4.2.2 贪吃蛇死亡判定功能的设计 4.3 贪吃蛇主动加速功能的设计 4.4 贪吃蛇奖励机制功能的设计 5 项目结构与项目实现 5.1 项目结构及类间关系 5.2 项目完整源码 5.2.1 Images类
|
图形学 数据安全/隐私保护
还在用Unity开发游戏?那你就out了,试试用Unity做一个答题系统吧
大家都支持Unity是用来做游戏开发,比如说做2D游戏、3D游戏,或者工业虚拟仿真软件的开发。 其他Unity可以做的有很多,比如答题系统。 本篇就介绍答题系统的开发 这个答题系统,可以从文本文档中提取题目和分数,然后绑定到UI上,在答题的过程中,自动判断分数,自动判断正确率。 目的是实现一个可快速导入到项目中使用的小模块。
|
存储 索引 容器
灰太狼系列—打地鼠(内含源码) inscode中的直观运行
灰太狼系列—打地鼠(内含源码) inscode中的直观运行
|
Python
python小游戏——贪吃蛇游戏2.0版本の得分功能实现
python小游戏——贪吃蛇游戏2.0版本の得分功能实现
184 0
|
XML 前端开发 JavaScript
没有一个顺手的流程绘制工具?好吧,自己动手,丰衣足食
没有一个顺手的流程绘制工具?好吧,自己动手,丰衣足食
|
Python
Python实现超级玛丽游戏系列教程02玛丽走跑
Python实现超级玛丽游戏系列教程02玛丽走跑
96 0