贪吃蛇游戏程序目前我写了两种。一种运用了二维数组记录全部数据,另一种用结构体记录蛇和食物的数据。
下面我详细介绍第一种方法:二维数组记录全部数据。
后面附上两种方法的完整代码
首先了解一个小知识。如果全局变量与局部变量同名,则在局部变量的作用域内访问的是局部变量,全局变量将被“屏蔽”。
写好游戏框架:
#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;
}