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;
}