结果图:
先上全代码:
#include <stdio.h>
#include <Windows.h>//移动光标
#include <stdlib.h>
#include <time.h>//使用时间戳创建随机数种子
#include <conio.h>//监听键盘
#define WIDE 50//定义地图宽度
#define HIGH 28//定义地图高度
#define MAP_MODE "■"//地图与食物模型
#define SNAKE_HEAD "□"//蛇头模型
#define SNAKE_BODY "●"//蛇身模型
#define X 70//光标归位横坐标
#define Y 28//光标归位纵坐标
#define INITLEN 3//初始化蛇长
#define MAXN 100//最大值
int game = 1;//控制游戏是否退出
int initDirection = 'a';//原方向
int direction = 'a';//改变方向
struct Snake {
int size;//蛇长
int x[MAXN];//蛇头横坐标
int y[MAXN];//蛇头纵坐标
}snake;
struct Food {
int x;//食物横坐标
int y;//食物纵坐标
int flag;//此变量用于检测地图上是否需要食物
}food;
//声明部分
//打印地图的函数
void mapPrint();
//初始化蛇
void initSnake();
//打印蛇
void moveSnake();
//光标移动函数
void gotoxy(int x, int y);
//随机出现食物
void creatFood();
//判断游戏状态
int judge();
//结算界面
void gameOver();
//游戏生成
void gameStart();
//
int levelControl();
void main() {
system("title 贪吃蛇");//更改窗口名
system("mode con cols=100 lines=29");//更改窗口大小
printf("输入1开始游戏,输入0退出:");
scanf_s("%d", &game);
while (game) gameStart();
}
void gameStart() {
int k = 150;//此变量用于控制延迟(游戏速度)
k = levelControl();
system("cls");//清屏
mapPrint();
food.flag = 1;
initDirection = 'a';
direction = 'a';
while (game) {
Sleep(k);//延迟150ms
if (food.flag) creatFood();//生成食物
moveSnake();//控制移动
if (judege()) game = 0;//判断游戏状态
}
gameOver();//结算画面
}
int levelControl() {//延迟控制
int level = 0;
printf("请输入游戏速度(1-5)(默认为3):");
scanf_s("%d", &level);
switch (level) {
case 1:
return 250;
case 2:
return 200;
case 3:
return 150;
case 4:
return 100;
case 5:
return 50;
}
}
void gotoxy(int x,int y) {//此函数用于移动光标
HANDLE handle;
handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = { x,y };
SetConsoleCursorPosition(handle, pos);
}
void mapPrint() {
for (int i = 0;i < HIGH;i++) {//用i控制行
for (int j = 0;j < WIDE;j++) {//用j控制列
if (j == 0 || j == WIDE - 1) {//如果是第一列或最后一列
printf(MAP_MODE);
}
else if (i == 0 || i == HIGH - 1) {//如果是第一行或最后一行
printf(MAP_MODE);
}
else {
printf(" ");
}
}
printf("\n");
}
printf("使用wasd控制方向");//操作提示行
initSnake();
}
void creatFood() {
srand(time(NULL));//利用时间戳赋予一个种子
int isCreat = 1;//此变量用于确认食物位置与蛇是否重合
food.x = rand() % (WIDE * 2 - 4) + 2;//产生横坐标随机数,减4防止与地图重合
food.y = rand() % (HIGH - 2) + 1;//产生纵坐标随机数
if (food.x % 2 == 0) {//因蛇头的坐标为偶数,为使吃到食物,食物横坐标须为偶数
for (int i = 0;i < snake.size;i++) {
if (snake.x[i] == food.x && snake.y[i] == food.y) isCreat = 0;
}
if (isCreat) {
gotoxy(food.x, food.y);//光标跳跃
printf(MAP_MODE);//打印食物
gotoxy(X, Y);//光标归位
food.flag = 0;
}
}
}
//初始化一条蛇
void initSnake() {
snake.size = INITLEN;//初始化蛇长
snake.x[0] = WIDE;//定义蛇头在屏幕正中间
snake.y[0] = HIGH / 2;//蛇头纵坐标
gotoxy(snake.x[0], snake.y[0]);
printf(SNAKE_HEAD);//打印蛇头
for (int i = 1;i < snake.size;i++) {//打印蛇身
snake.x[i] = snake.x[i - 1] + 2;
snake.y[i] = snake.y[i - 1];
gotoxy(snake.x[i], snake.y[i]);
printf(SNAKE_BODY);
}
gotoxy(X, Y);//光标归位
}
//移动
void moveSnake() {
if (_kbhit()) {//监听键盘
fflush(stdin);//清空缓存区
int judgement = _getch();
if (judgement == 'w' || judgement == 'W' || judgement == 's' || judgement == 'S' || judgement == 'a' || judgement == 'A' || judgement == 'd' || judgement == 'D')
direction = judgement;//获得键盘输入的值,并赋给direction
}
gotoxy(snake.x[snake.size - 1], snake.y[snake.size - 1]);//移除蛇尾
printf(" ");//打印空格以移除蛇尾
for (int i = snake.size - 1;i > 0;i--) {//重新打印蛇身,蛇头使用direction控制
snake.x[i] = snake.x[i - 1];
snake.y[i] = snake.y[i - 1];
gotoxy(snake.x[i], snake.y[i]);
printf(SNAKE_BODY);
}
switch (direction) {//改变蛇头位置
case 'w':
case 'W':
if(initDirection != 's' && initDirection != 'S')
snake.y[0]--;
else{
direction = initDirection;
snake.y[0]++;
}
break;
case 's':
case 'S':
if (initDirection != 'w' && initDirection != 'W')
snake.y[0]++;
else {
direction = initDirection;
snake.y[0]--;
}
break;
case 'a':
case 'A':
if (initDirection != 'd' && initDirection != 'D')
snake.x[0] -= 2;
else {
direction = initDirection;
snake.x[0] += 2;
}
break;
case 'd':
case 'D':
if (initDirection != 'a' && initDirection != 'A')
snake.x[0] += 2;
else {
direction = initDirection;
snake.x[0] -= 2;
}
break;
}
initDirection = direction;
gotoxy(snake.x[0], snake.y[0]);//重新打印蛇头
printf(SNAKE_HEAD);
gotoxy(X, Y);//光标归位
if (snake.x[0] == food.x && snake.y[0] == food.y) {//若蛇头吃到食物
snake.size++;
food.flag = 1;
}
}
int judege() {//此函数用于判断游戏状态
if (snake.x[0] == 0 || snake.x[0] == WIDE * 2 - 2) return 1;//如果撞到左右墙
if (snake.y[0] == 0 || snake.y[0] == HIGH - 1) return 1;//如果撞到上下墙
for (int i = 1;i < snake.size;i++) {//如果吃到自己
if (snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i]) return 1;
}
return 0;
}
void gameOver() {
system("cls");//清屏
gotoxy(WIDE, HIGH / 2);//跳跃光标
printf("Game Over!\n");//打印结束语
printf("请输入0退出游戏,1重新游戏:\n");
scanf_s("%d", &game);
}
写在最前面,对于gotoxy函数的解释:
gotoxy函数用于移动光标,来控制在何处打印食物,蛇头或蛇身。
void gotoxy(int x,int y) {//此函数用于移动光标
HANDLE handle;
handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = { x,y };
SetConsoleCursorPosition(handle, pos);
}
HANDLE与COORD为系统定制好的一个结构体。
第三行代码获得标准输出句柄,并赋给handle。
COORD pos中的x,y代表光标移动的横坐标和纵坐标。
SetConsoleCursorPosition 函数是用来移动光标的。
贪吃蛇程序实现思路:
1.创建一个地图
2.创建一条蛇
3.随机生成食物
4.控制蛇移动,当蛇吃到食物,蛇变长
5.蛇头撞到墙或者自身,游戏结束
main函数前面就不解释了,从while开始:
gameStart游戏开始,使用levelControl函数控制返回延迟时间,来控制游戏速度
int levelControl() {//延迟控制
int level = 0;
printf("请输入游戏速度(1-5)(默认为3):");
scanf_s("%d", &level);
switch (level) {
case 1:
return 250;
case 2:
return 200;
case 3:
return 150;
case 4:
return 100;
case 5:
return 50;
}
}
然后开始创建地图,调用mapPrint函数:
我写代码习惯还不错,注释基本都写了,我感觉这个看注释就能懂了
void mapPrint() {
for (int i = 0;i < HIGH;i++) {//用i控制行
for (int j = 0;j < WIDE;j++) {//用j控制列
if (j == 0 || j == WIDE - 1) {//如果是第一列或最后一列
printf(MAP_MODE);
}
else if (i == 0 || i == HIGH - 1) {//如果是第一行或最后一行
printf(MAP_MODE);
}
else {
printf(" ");
}
}
printf("\n");
}
printf("使用wasd控制方向");//操作提示行
initSnake();
}
第一步完成,接下来玩蛇,首先用结构体定义一条蛇:
注释标的很清楚了,MAXN是蛇身最大长,在最开始写了define MAXN 100;
struct Snake {
int size;//蛇长
int x[MAXN];//蛇头横坐标
int y[MAXN];//蛇头纵坐标
}snake;
mapPrint函数的结尾调用的initSnake函数,初始化一条蛇
其中INITLEN,WIDE,HIGH分别为初始蛇长,宽度,高度,均已define。
须注意snake.x[0] = WIDE;是因为地图模型的■,每个占用两字节。
main函数中定义的宽度为WIDE * 2,所以地图的x轴正中间为WIDE
void initSnake() {
snake.size = INITLEN;//初始化蛇长
snake.x[0] = WIDE;//定义蛇头在屏幕正中间
snake.y[0] = HIGH / 2;//蛇头纵坐标
gotoxy(snake.x[0], snake.y[0]);
printf(SNAKE_HEAD);//打印蛇头
for (int i = 1;i < snake.size;i++) {//打印蛇身
snake.x[i] = snake.x[i - 1] + 2;
snake.y[i] = snake.y[i - 1];
gotoxy(snake.x[i], snake.y[i]);
printf(SNAKE_BODY);
}
gotoxy(X, Y);//光标归位
}
然后生成食物,首先创建一个食物结构体:
struct Food {
int x;//食物横坐标
int y;//食物纵坐标
int flag;//此变量用于检测地图上是否需要食物
}food;
for循环遍历蛇的各点坐标与食物坐标比较防止刷在蛇上面。
void creatFood() {
srand(time(NULL));//利用时间戳赋予一个种子
int isCreat = 1;//此变量用于确认食物位置与蛇是否重合
food.x = rand() % (WIDE * 2 - 4) + 2;//产生横坐标随机数,减4防止与地图重合
food.y = rand() % (HIGH - 2) + 1;//产生纵坐标随机数
if (food.x % 2 == 0) {//因蛇头的坐标为偶数,为使吃到食物,食物横坐标须为偶数
for (int i = 0;i < snake.size;i++) {
if (snake.x[i] == food.x && snake.y[i] == food.y) isCreat = 0;
}
if (isCreat) {
gotoxy(food.x, food.y);//光标跳跃
printf(MAP_MODE);//打印食物
gotoxy(X, Y);//光标归位
food.flag = 0;
}
}
}
接下来是moveSnake函数,首先定义两个全局变量用来控制方向:
int initDirection = 'a';//原方向
int direction = 'a';//改变方向
初始化方向为向左。
第二行if监听键盘是否输入,如果输入,获取输入值赋给judgement,判断是否为wasd,如果是,将其赋给direction。
移除蛇尾并重新打印蛇身,使用switch判断键盘输入的direction,其中if用于判断输入要移动的方向与原来移动的方向是否冲突,若冲突,仍按原来的方向进行移动。
结尾判断蛇头坐标与食物坐标是否重合,若重合蛇变长,食物消失,须重新打印
void moveSnake() {
if (_kbhit()) {//监听键盘
fflush(stdin);//清空缓存区
int judgement = _getch();
if (judgement == 'w' || judgement == 'W' || judgement == 's' || judgement == 'S' || judgement == 'a' || judgement == 'A' || judgement == 'd' || judgement == 'D')
direction = judgement;//获得键盘输入的值,并赋给direction
}
gotoxy(snake.x[snake.size - 1], snake.y[snake.size - 1]);//移除蛇尾
printf(" ");//打印空格以移除蛇尾
for (int i = snake.size - 1;i > 0;i--) {//重新打印蛇身,蛇头使用direction控制
snake.x[i] = snake.x[i - 1];
snake.y[i] = snake.y[i - 1];
gotoxy(snake.x[i], snake.y[i]);
printf(SNAKE_BODY);
}
switch (direction) {//改变蛇头位置
case 'w':
case 'W':
if(initDirection != 's' && initDirection != 'S')
snake.y[0]--;
else{
direction = initDirection;
snake.y[0]++;
}
break;
case 's':
case 'S':
if (initDirection != 'w' && initDirection != 'W')
snake.y[0]++;
else {
direction = initDirection;
snake.y[0]--;
}
break;
case 'a':
case 'A':
if (initDirection != 'd' && initDirection != 'D')
snake.x[0] -= 2;
else {
direction = initDirection;
snake.x[0] += 2;
}
break;
case 'd':
case 'D':
if (initDirection != 'a' && initDirection != 'A')
snake.x[0] += 2;
else {
direction = initDirection;
snake.x[0] -= 2;
}
break;
}
initDirection = direction;
gotoxy(snake.x[0], snake.y[0]);//重新打印蛇头
printf(SNAKE_HEAD);
gotoxy(X, Y);//光标归位
if (snake.x[0] == food.x && snake.y[0] == food.y) {//若蛇头吃到食物
snake.size++;
food.flag = 1;
}
}
最后判断蛇此刻的状态:
int judege() {//此函数用于判断游戏状态
if (snake.x[0] == 0 || snake.x[0] == WIDE * 2 - 2) return 1;//如果撞到左右墙
if (snake.y[0] == 0 || snake.y[0] == HIGH - 1) return 1;//如果撞到上下墙
for (int i = 1;i < snake.size;i++) {//如果吃到自己
if (snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i]) return 1;
}
return 0;
}
结尾添加结束界面:
void gameOver() {
system("cls");//清屏
gotoxy(WIDE, HIGH / 2);//跳跃光标
printf("Game Over!\n");//打印结束语
printf("请输入0退出游戏,1重新游戏:\n");
scanf_s("%d", &game);
}
结束