小游戏-贪吃蛇

本文介绍了两种实现贪吃蛇游戏的方法:一是使用二维数组记录游戏数据,二是利用结构体存储蛇和食物信息。详细阐述了数组方法中的坐标理解、蛇的移动、时间控制、游戏失败判断等关键步骤,以及结构体方法中的坐标更新、方向控制和食物生成。同时,代码示例展示了两种实现方式的细节。
摘要由CSDN通过智能技术生成

贪吃蛇游戏程序目前我写了两种。一种运用了二维数组记录全部数据,另一种用结构体记录蛇和食物的数据。

下面我详细介绍第一种方法:二维数组记录全部数据。

后面附上两种方法的完整代码

首先了解一个小知识。如果全局变量与局部变量同名,则在局部变量的作用域内访问的是局部变量,全局变量将被“屏蔽”。

写好游戏框架:

#include<conio.h>
#include<stdio.h>
#include<graphics.h>
#define BLOCK_SIZE 20;
#define HEIGH 30;
#define WIDTH 40;

void startup()    //初始化函数
{
}

void show()   //绘制函数
{
}

void updateWithoutInput()         //与输入无关的更新
{
}

void updateWithInput()          //与输入有关的更新
{
}

int main()
{
	startup();      //初始化函数
	while (1)
	{
		show();  //进行绘制
		updateWithoutInput();  //输入无关的更新
		updateWithInput();      //输入有关的更新
	}
	return 0;
}

数组记录游戏数据

下面是数组记录数据的简单例子

原理:把数据全部放进二维数组中储存,通过改变数组元素的数据来达到控制贪吃蛇。

这个方法的运用在于理解数组定义的坐标

 贪吃蛇的绘制是在定义的坐标元素下赋予颜色来达到显示蛇的效果

 贪吃蛇的移动

       1.获取键盘

_kbhit()函数是判断键盘是否按下

_getch()函数是获取按下的键盘信息

#include<iostream>
#include<string>
#include<fstream>
#include<conio.h>
int main()
{    
	cout << "你好" << endl;
	while (1)
	{
		if (_kbhit())//当按键时
		{
			char input = _getch();
			if (input == ('s')) //当按下s
			{
				cout << "你好" << endl;
			}
		}
	}
	

	system("pause");
	return 0;
}

2.控制蛇的运动:

假设小蛇初始元素值为54321,其中1为蛇头、5432为蛇身、最大值5为蛇尾。首先将二维数组中所有大于0的元素加1,得到65432;然后将最大值6变为0,即去除了原来的蛇尾;最后将2右边的元素由0变成1,即实现了小蛇向右移动。

 代码:

 

 3.时间控制

Sleep()函数运行时,整个程序都会暂停,包括用户输入模块。用户会感觉到卡顿,明明按了键,小蛇却没有反应。

为了解决这个问题,我用了静态变量。

static修饰的变量称为静态变量,即程序开始运行时就为其分配内存空间,直到程序运行完收回。

动态变量如果不赋初值,其初值为随机数;静态变量如果不赋初值,其初值为0

 4.游戏失败的判断

当小蛇碰到画面边界时,则认为游戏失败。由于每次只有蛇头是新生成的位置,所以在moveSnake()函数中只需判断蛇头是否越过边界:

 

 

当最后运行游戏时会出现一个bug ,当运行代码时,输入法发生了转换导致键盘消息无法获取。此时可以快速转换输入法就可以获取键盘消息了

 

    

第一种方法完整代码:

#include <graphics.h> 
#include <conio.h>
#include <stdio.h>
#define BLOCK_SIZE 20 // 每个小格子的长宽大小 
#define HEIGHT 30 // 高度上一共30个小格子
#define WIDTH 40 // 宽度上一共40个小格子 

// 全局变量定义
int Blocks[HEIGHT][WIDTH] = { 0 }; // 二维数组,用于记录所有的游戏数据
 char moveDirection; // 小蛇移动方向
 int food_i, food_j; // 食物的位置
 int isFailure = 0; // 是否游戏失败
 int speed = 40;

 void moveSnake() // 移动小蛇及相关处理函数
 {
      int i, j;
      for (i = 0; i < HEIGHT; i++) // 对行遍历 
        for (j = 0; j < WIDTH; j++) // 对列遍历
            if (Blocks[i][j] > 0) // 大于0的为小蛇元素 
                Blocks[i][j]++; // 让其+1
      int oldTail_i, oldTail_j, oldHead_i, oldHead_j; // 定义变量,存储旧蛇尾、旧蛇头坐标 
      int max = 0; // 用于记录最大值 
      for (i = 0; i < HEIGHT; i++) // 对行列遍历
          {
              for (j = 0; j < WIDTH; j++)
             {
                   if (max < Blocks[i][j]) // 如果当前元素值比max大
                       {
                                   max = Blocks[i][j]; // 更新max的值
                                   oldTail_i = i; // 记录最大值的坐标,就是旧蛇尾的位置
                                   oldTail_j = j;
                        }
                    if (Blocks[i][j] == 2) // 找到数值为2 
                        {
                            oldHead_i = i; // 数值为2恰好是旧蛇头的位置
                            oldHead_j = j;
                        }
              }
          }
   int newHead_i = oldHead_i; // 设定变量存储新蛇头的位置
   int newHead_j = oldHead_j;
   
       // 根据用户按键,设定新蛇头的位置
   switch (moveDirection)
   {
   case 'w':
   case 'W':
   case  72:
       newHead_i = oldHead_i - 1;
       break;

   case 'S':
   case 's':
       newHead_i = oldHead_i + 1;
       break;

   case 'D':
   case 'd':
       newHead_j = oldHead_j + 1;
       break;

   case 'A':
   case 'a':
       newHead_j = oldHead_j - 1;
       break;
   }
     
   
       // 如果蛇头超出边界,或者蛇头碰到蛇身,游戏失败
       if (newHead_i >= HEIGHT || newHead_i < 0 || newHead_j >= WIDTH || newHead_j < 0|| Blocks[newHead_i][newHead_j]>0)
         {
             isFailure = 1; // 游戏失败
             return; // 函数返回
         }
 
     Blocks[newHead_i][newHead_j] = 1; // 新蛇头位置数值为1   

 if (newHead_i == food_i && newHead_j == food_j) // 如果新蛇头正好碰到食物
     {
         food_i = rand() % (HEIGHT - 5) + 2; // 食物重新随机位置
         food_j = rand() % (WIDTH - 5) + 2;
         // 不对旧蛇尾处理,相当于蛇的长度+1
     }
       else // 新蛇头没有碰到食物
               Blocks[oldTail_i][oldTail_j] = 0; // 旧蛇尾变成空白,不吃食物时 蛇的长度保持不变


          }

    void startup() // 初始化函数
    {
        //画蛇
       int i;
       Blocks[HEIGHT / 2][WIDTH / 2] = 1; // 画面中间画蛇头,数字为1
       for (i = 1; i <= 4; i++) // 向左依次4个蛇身,数值依次为2、3、4、5
               Blocks[HEIGHT / 2][WIDTH / 2 - i] = i + 1;
       moveDirection = 'd';     // 初始向右移动
       food_i = rand() % (HEIGHT - 5) + 2; // 初始化随机食物位置
       food_j = rand() % (WIDTH - 5) + 2;
       
       initgraph(WIDTH * BLOCK_SIZE, HEIGHT * BLOCK_SIZE); // 新开画面
       setlinecolor(RGB(200, 200, 200)); // 设置线条颜色
       BeginBatchDraw(); // 开始批量绘制
      
    }
    
       void show() // 绘制函数
       {
           cleardevice(); // 清屏
           int i, j;
           for (i = 0; i < HEIGHT; i++) // 对二维数组所有元素遍历
              {
          for (j = 0; j < WIDTH; j++)
              {
                  if (Blocks[i][j] > 0) // 元素大于0表示是蛇,
                      setfillcolor(RGB(0, 128, 255));
                  else
                      setfillcolor(RGB(34,177,76)); // 元素为0表示为空,颜色为灰色
                  // 在对应位置处,以对应颜色绘制小方格
                      fillrectangle(j * BLOCK_SIZE, i * BLOCK_SIZE,
                                      (j + 1) * BLOCK_SIZE, (i + 1) * BLOCK_SIZE);
               }
           }
       setfillcolor(RGB(0, 255, 0)); // 食物颜色为绿色
       // 绘制食物小方块
           fillrectangle(food_j * BLOCK_SIZE, food_i * BLOCK_SIZE,
                           (food_j + 1) * BLOCK_SIZE, (food_i + 1) * BLOCK_SIZE);
         if (isFailure) // 如果游戏失败
             {
                 setbkmode(TRANSPARENT); // 文字字体透明 
                 settextcolor(RGB(255, 0, 0));// 设定文字颜色
                 settextstyle(80, 0, _T("宋体")); // 设定文字大小、样式
                 outtextxy(240, 220, _T("游戏失败")); // 输出文字内容
             }
         FlushBatchDraw(); // 批量绘制
   }
   
   void updateWithoutInput() // 与输入无关的更新函数
   {
       if (isFailure) // 如果游戏失败,函数返回
            return;
       static int waitIndex = 1; // 静态局部变量,初始化时为1
       waitIndex++; // 每一帧+1
       if (waitIndex == 10) // 如果等于10才执行,这样小蛇每隔10帧移动一次
           {
               moveSnake(); // 调用小蛇移动函数
               waitIndex = 1; // 再变成1
           }
    }
    
    void updateWithInput() // 和输入有关的更新函数
    {
        if (_kbhit()) // 如果有按键输入,并且不失败
        {
            if (isFailure == 0)
            {
                char input = _getch(); //获得按键输入               
                 moveDirection = input; // 设定移动方向
                 moveSnake(); // 调用小蛇移动函数
                

            }
          
        }
       
    }
   
   int main() // 主函数
   {
       startup(); // 初始化函数,仅执行一次    
       while (1) // 一直循环
           {
               show(); // 进行绘制
               updateWithoutInput(); // 和输入无关的更新 
               updateWithInput(); // 和输入有关的更新
           }
       return 0;
       
       
      /* system("pause");
       return 0;
       */
   }
  

 第二种方法完整代码:

#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<graphics.h>


#define  SNAKE_NUM  500//蛇的最大节数
enum DIR
{
	up,
	down,
	LEFt,
	RIGHT,

};
struct snake 
{
	int size;//蛇的节数
	int dir;//蛇的方向
	int speed;//蛇的速度
	POINT coor[SNAKE_NUM];//蛇的坐标
}snake;
//数据的初始化
struct food  //定义食物
{
	int x;
	int y;
	int r;//食物半径
	bool flay;//判断食物是否被吃
	DWORD collor;//食物的颜色

}food;
void GameInit()
{
	srand(GetTickCount());
	//做界面,创建一个窗口
	//init 初始化   graph 图片
	initgraph(640, 480);
	//初始化食物,以食物为随机数组的种子。
	//GetTickCount  获取系统开机到现在所经过的时间,以毫秒为单位。
	//初始化蛇,一开始有三节
	snake.size = 3;
	snake.speed= 10;
	snake.dir = RIGHT;
	for (int i =0; i <snake.size; i++)
	{
		//snake.coor[i].x = 10 * i + 20;//蛇的坐标在改变
		//snake.coor[i].y = 10;    蛇头的方向定义错了,让蛇尾在移动了。
		snake.coor[i].x = 40 - 10*i;
		snake.coor[i].y = 10;             //数学逻辑。设置蛇的坐标,让蛇头能移动,(定义蛇头)


	}
	food.x = rand() % 640;//rand()随机数组,随机数要头文件<stdlib.h>
	food.y = rand() % 480;
	food.collor = RGB(rand() % 256, rand() % 256, rand() % 256);
	food.r = rand() % 10 + 5;
	food.flay = true;
	
}
void GameDraw()
{	//双缓冲绘图
	BeginBatchDraw();
	//设置背景颜色。
	setbkcolor(RGB(28, 115, 119));
	cleardevice();
	//绘制蛇  定义蛇的坐标
	setfillcolor(GREEN);
	for (int i = 0; i < snake.size; i++)
	{
		solidcircle(snake.coor[i].x ,snake.coor[i].y, 5);//solidcirle  绘制圆。

	}
	EndBatchDraw();
	if (food.flay)
	{
		solidcircle(food.x, food.y, food.r);

	}
}

void  snakemove()
{
	//改变蛇的坐标,用蛇头移动
	for (int i =snake.size-1;i>0;i--)
	{
		snake.coor[i] = snake.coor[i - 1];
	}
		//snake.coor[i].x++;
		switch (snake.dir)//蛇移动时,方向改变坐标。
		{
		case up:
			snake.coor[0].y-=snake.speed;
			if (snake.coor[0].y + 10 <= 0)
			{
				snake.coor[0].y = 640;
			}
			break;
		case down:
			snake.coor[0].y+=snake.speed;
			if (snake.coor[0].y - 10 >= 640)
			{
				snake.coor[0].y = 0;
			}
			break;
		case LEFt:
			snake.coor[0].x-=snake.speed;
			if (snake.coor[0].x + 10 <= 0)
			{
				snake.coor[0].x =640;
			}
			break;
		case RIGHT:
			snake.coor[0].x+=snake.speed;
			if (snake.coor[0].x - 10 >=640)
			{
				snake.coor[0].x = 0;
			}
			break;
		}
	


}

void  keycontrol()
{
	if (_kbhit())//判断有没有按键   键盘。函数

	{//72  80  75  77  上下左右的按键数值。   W S A D 
		switch (_getch())//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;
		}


	}
}
void eatfood()
{
	if (food.flay&&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.flay = false;
		snake.size++;
	}
	if (!food.flay)
	{
		food.x = rand() % 640;//rand()随机数组,随机数要头文件<stdlib.h>
		food.y = rand() % 480;
		food.collor = RGB(rand() % 256, rand() % 256, rand() % 256);
		food.r = rand() % 10 + 5;
		food.flay = true;


	}
}
int main()
{
	GameInit();
	GameDraw();
    while(1)
	{
		eatfood();
		Sleep(10);
		GameDraw();
		snakemove();
		keycontrol();
		Sleep(100);


	}


	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值