C语言版贪吃蛇:第四部分
“马上就要结束了!”
本章学习:
- 判断是否吃到食物并增长身体
- 判断是否撞墙或吃到自己
判断是否存活
- 定义一个变量 life 表示是否活着
- life=1表示活着,life=0表示挂掉
- 记得在 init( ) 里给 life 初始值1
- 主函数的while条件里要加life
- 判断撞墙
- 判断蛇头座标与墙是否重合
- 判断吃到自己
- 依次判断蛇头座标是否与每节蛇身重合
- 记得要在move()函数的最后加上check_life( )
- 每移动一次都要判断是否活着
参考代码如下
- 每移动一次都要判断是否活着
// 判断蛇是否还活着
void check_life()
{
// 判断是否撞墙,只需判断蛇头座标是否与墙重合即可
if (Snake[head].x == 1 || Snake[head].x == 70 || Snake[head].y == 1 || Snake[head].y == 20)
Game_over();
// 判断是否吃到自己
// 方法:依次判断蛇头是否与蛇身重合
int i, j = Snake[head].next;
for (i = 1; i < lenth; i++)
{
// 重合就结束游戏
if (Snake[j].x == Snake[head].x && Snake[j].y == Snake[head].y)
Game_over();
j = Snake[j].next;
}
}
增长身体
- 直接判断蛇头座标与食物是否重合
- 若重合,则增长
- 增长分为水平和竖直增长
- 具体可参考代码
参考代码如下
// 判断是否吃到食物
void check_food()
{
// 检验蛇头是否与食物重合
// 重合则增长身体(在蛇尾后增加一节),并重新放置一个食物
if (Snake[head].x == food_x && Snake[head].y == food_y)
{
// 增长身体,要注意水平增长还是竖直增长
// 通过最后两节来判断
// 水平座标相同则水平增长
// 竖直座标相同则竖直增长
lenth++;
// 水平增长
if (Snake[nail].x == Snake[Snake[nail].pre].x)
{
// 水平座标 x 不变
Snake[lenth].x = Snake[nail].x;
// 竖直座标向蛇尾方向加 1 或 减 1
// 为了省去判断,这里用蛇尾竖直座标减蛇尾前一个竖直座标表示
Snake[lenth].y = Snake[nail].y + (Snake[nail].y - Snake[Snake[nail].pre].y);
// 将新的一节接上去
Snake[nail].next = lenth;
Snake[lenth].pre = nail;
// 设置新的蛇尾
nail = lenth;
}
// 竖直增长,将上面的 x 变成 y 即可
if (Snake[nail].y == Snake[Snake[nail].pre].y)
{
Snake[lenth].y = Snake[nail].y;
Snake[lenth].x = Snake[nail].x + (Snake[nail].x - Snake[Snake[nail].pre].x);
Snake[nail].next = lenth;
Snake[lenth].pre = nail;
nail = lenth;
}
// 放置新的食物
printfood();
}
}
完整源代码
/*
这里是贪吃蛇源代码
chapter 1:
解释下头文件:
time.h 生成随机数要用到
windows.h 要用到里面的函数 gotoxy
---------
1.画围墙
===========================
chapter 2:
目标:绘制蛇,食物
蛇的存储结构:简单的链表(数组实现功能)
食物:随机数的生成
===========================
chapter 3:
目标: 实现蛇的运动
判断键盘输入
改变蛇的座标
重绘图像
===========================
chapter 4:
目标: 判断是否吃到食物
增长身体
判断是否撞墙,是否吃到自己
===========================
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <windows.h>
#include <conio.h>
// 定义一个结构体用来存储蛇的每节身体的座标
struct snake
{
int x, y;
int next; //保存当前节点的下一个节点的位置(数组下标)
int pre; //保存上一个节点的位置
} Snake[100];
// 定义三个变量存 蛇长,蛇头,蛇尾
int lenth, head, nail;
// 食物位置
int food_x, food_y;
// 储存蛇的当前,和上一次的移动方向
int direation, direation_pre;
// 存储蛇是否还活着,1活着
int life;
// 光标移动函数
void gotoxy(int x, int y)
{
COORD coord = {x, y};
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
// 打印蛇
void printsnake()
{
int i, j = head;
for (i = 1; i <= lenth; i++)
{
// 注意 里面的参数是j,j从蛇头移动到蛇尾
gotoxy(Snake[j].x, Snake[j].y);
printf("*");
j = Snake[j].next;
}
}
// 清除蛇
void clear()
{
int i, j = head;
for (i = 1; i <= lenth; i++)
{
// 注意 里面的参数是j,j从蛇头移动到蛇尾
gotoxy(Snake[j].x, Snake[j].y);
printf(" ");
j = Snake[j].next;
}
}
// 游戏结束
void Game_over()
{
// 将 life 设置为0,表示游戏结束,用来结束主函数中的while循环
life = 0;
// 清屏
system("cls");
// 打印提示信息
gotoxy(10, 10);
printf("GAME OVER");
}
// 判断食物是否合法
int ok_food()
{
// 判断是否和墙重合
// 横座标不能等于1,或70 ; 纵座标不能等于1,或20
if (food_x <= 1 || food_x >= 70)
return 0;
if (food_y <= 1 || food_y >= 20)
return 0;
// 判断是否和蛇重合
int j = head;
for (int i = 1; i <= lenth; i++)
{
if (food_x == Snake[j].x && food_y == Snake[j].y)
return 0;
j = Snake[j].next;
}
// 都没有,则返回1
return 1;
}
// 打印食物
void printfood()
{
// 不断产生随机数,知道座标符合要求
do
{
srand((unsigned long)time(0));
food_x = rand() % 70;
food_y = rand() % 20;
} while (!ok_food());
// ok_food()为判断食物是否合法的函数,合法返回1,不合法返回0
gotoxy(food_x, food_y);
printf("@");
}
// 判断是否吃到食物
void check_food()
{
// 检验蛇头是否与食物重合
// 重合则增长身体(在蛇尾后增加一节),并重新放置一个食物
if (Snake[head].x == food_x && Snake[head].y == food_y)
{
// 增长身体,要注意水平增长还是竖直增长
// 通过最后两节来判断
// 水平座标相同则水平增长
// 竖直座标相同则竖直增长
lenth++;
// 水平增长
if (Snake[nail].x == Snake[Snake[nail].pre].x)
{
// 水平座标 x 不变
Snake[lenth].x = Snake[nail].x;
// 竖直座标向蛇尾方向加 1 或 减 1
// 为了省去判断,这里用蛇尾竖直座标减蛇尾前一个竖直座标表示
Snake[lenth].y = Snake[nail].y + (Snake[nail].y - Snake[Snake[nail].pre].y);
// 将新的一节接上去
Snake[nail].next = lenth;
Snake[lenth].pre = nail;
// 设置新的蛇尾
nail = lenth;
}
// 竖直增长,将上面的 x 变成 y 即可
if (Snake[nail].y == Snake[Snake[nail].pre].y)
{
Snake[lenth].y = Snake[nail].y;
Snake[lenth].x = Snake[nail].x + (Snake[nail].x - Snake[Snake[nail].pre].x);
Snake[nail].next = lenth;
Snake[lenth].pre = nail;
nail = lenth;
}
// 放置新的食物
printfood();
}
}
// 判断蛇是否还活着
void check_life()
{
// 判断是否撞墙,只需判断蛇头座标是否与墙重合即可
if (Snake[head].x == 1 || Snake[head].x == 70 || Snake[head].y == 1 || Snake[head].y == 20)
Game_over();
// 判断是否吃到自己
// 方法:依次判断蛇头是否与蛇身重合
int i, j = Snake[head].next;
for (i = 1; i < lenth; i++)
{
// 重合就结束游戏
if (Snake[j].x == Snake[head].x && Snake[j].y == Snake[head].y)
Game_over();
j = Snake[j].next;
}
}
// 实现蛇的座标改变
void move()
{
// 擦掉原来的蛇
clear();
// 根据 direation 选择运动方向
switch (direation)
{
case 'w':
{
// 蛇尾变到蛇头前面,向上运动,则纵座标减一(回忆之前介绍的座标轴)
Snake[nail].x = Snake[head].x;
Snake[nail].y = Snake[head].y - 1;
// 将新蛇头的下一个位置指向旧蛇头
Snake[nail].next = head;
// 将旧蛇头的前一个位置指向新蛇头
Snake[head].pre = nail;
// 新蛇头是原来的蛇尾
head = nail;
// 新蛇尾是原来蛇尾的前一节
nail = Snake[nail].pre;
break;
}
case 's':
{
Snake[nail].x = Snake[head].x;
Snake[nail].y = Snake[head].y + 1;
Snake[nail].next = head;
Snake[head].pre = nail;
head = nail;
nail = Snake[nail].pre;
break;
}
case 'a':
{
Snake[nail].x = Snake[head].x - 1;
Snake[nail].y = Snake[head].y;
Snake[nail].next = head;
Snake[head].pre = nail;
head = nail;
nail = Snake[nail].pre;
break;
}
case 'd':
{
Snake[nail].x = Snake[head].x + 1;
Snake[nail].y = Snake[head].y;
Snake[nail].next = head;
Snake[head].pre = nail;
head = nail;
nail = Snake[nail].pre;
break;
}
}
printsnake();
check_food();
check_life();
}
// 绘制围墙
void printwall()
{
/*
chapter 1 绘制围墙部分
围墙大小 70*20, 长70,宽20
*/
// 绘制水平围墙,
for (int i = 1; i <= 70; i++)
{
gotoxy(i, 1);
printf("#");
gotoxy(i, 20);
printf("#");
}
// 绘制竖直围墙
for (int i = 1; i <= 20; i++)
{
gotoxy(1, i);
printf("#");
gotoxy(70, i);
printf("#");
}
}
void init()
{
printwall();
// 初始化蛇
Snake[1].x = 4;
Snake[1].y = 4;
Snake[2].x = 4;
Snake[2].y = 5;
head = 1;
nail = 2;
lenth = 2;
Snake[head].next = nail;
Snake[nail].pre = head;
direation = 's';
direation_pre = 's';
life=1;
// 第一次打印蛇
printsnake();
// 第一次打印食物
printfood();
}
int main()
{
init(); // 初始化函数,绘制围墙
char c;
c=getch();
while (1)
{
// 没有读入则按照之前的方向一直运动
while (!kbhit() && life)
{
move();
Sleep(150);
}
// 有读入则停下来改变方向
// 先记下原来的方向
direation_pre = direation;
// 读入要改变的方向
direation = getch();
// 判断方向是否合法,如果和上次相反,则保持原来方向
if (direation == 'w' && direation_pre == 's' || direation == 's' && direation_pre == 'w')
direation = direation_pre;
if (direation == 'a' && direation_pre == 'd' || direation == 'd' && direation_pre == 'a')
direation = direation_pre;
}
system("pause");
return 0;
}