emmm,昨天沒有更新,,這個嘛。還是因爲STM32的原因,,,昨天弄它的串口通信弄了一下午加半個晚上,,再加上降溫了。。手冷,,昨天就沒寫。。今天因爲妹妹開網課了,,我又給她把家裏以前的臺式拼起來,,所以耽誤的不少時間。。
好了,話不多說,我們來開始今天的主題,這是最後一章了,我會把剩餘的函數都講了它。大家看完後可以自己嘗試着也寫一個,或者找啥子像素鳥啊,五子棋啊啥子試試,原理都是通的。這些項目除了可以激發興趣外,還可以加深你對c語言的理解。
moveSnake函數
這個函數可以說是整個遊戲的核心函數了,我們一定要弄明白蛇在整個遊戲中的移動的邏輯。
首先,蛇在地圖初始化的時候已經被我們打印出來了,但是在遊戲中,即使我們不操作,蛇也是會自動移動的,這個移動的方向初始化時是向上,其他時候就是我們上次操作時的方向。
怎麼樣顯示蛇“移動”的這個效果嘞?嘿嘿,通過座標運算,把蛇的身子的其他值往前一個移動就可以了。
for (k = snake.len - 1; k>0; k--)//蛇的身子一格一格的移動
{
snake.x[k] = snake.x[k - 1];
snake.y[k] = snake.y[k - 1];
}
並且蛇在移動時,如果沒有喫到過果子,是沒有長大的。所以我們渲染蛇向前移動時,最後一格的蛇尾“#”也要變成“ ”。
同時,使用鍵盤控制蛇的移動時,如果蛇是向上走的,則你輸入“S”向下是無效的。即需要考慮操作的合理性。
這個函數的完整代碼:
void moveSnake()
{
if (!grow)//如果沒有長大,則上次的最後一個點,即尾巴,的地方顯示空.這裏的grow是在更新食物時我們標記的
{
gotoxy(snake.x[snake.len - 1], snake.y[snake.len - 1]);
printf(" ");//用空格覆蓋之前的蛇尾巴
}
for (k = snake.len - 1; k>0; k--)//蛇的身子一格一格的移動
{
snake.x[k] = snake.x[k - 1];
snake.y[k] = snake.y[k - 1];
}
if (kbhit())//檢查當前是否有鍵盤輸入
{
ch = getch();
if (ch == ' ') //如果輸入空格表示暫停,使用while函數一直卡着。
while (getch() != ' '){};
if (ch != UP && ch != DOWN && ch != LEFT && ch != RIGHT) //如果輸入的時其他的鍵,則使用默認方向。這裏的UP啊啥的都是之前定義好的
{
ch = Direction;
goto sport;//無條件跳轉
}
if (Direction != ch)//如果輸入的不默認方向
{
if ((Direction == UP &&ch != DOWN) || (Direction == DOWN &&ch != UP) || (Direction == LEFT && ch != RIGHT) || (Direction == RIGHT && ch != LEFT)) //Prevent reverse movement
{//確保輸入的方向與當前方向邏輯上不違背,即確保蛇不能倒退
Direction = ch;
goto sport;
}
else
{//如果違背了。則保持當前
ch = Direction;
goto sport;
}
}
else
{
goto sport;
}
}
else
{
sport:
switch (ch) //移動蛇的頭部
{
case UP: snake.x[0]--; break;//注意,在窗口中,座標與我們常用的座標是倒過來的
case DOWN: snake.x[0]++; break;
case LEFT: snake.y[0]--; break;
case RIGHT: snake.y[0]++; break;
default: break;
}
}
gotoxy(snake.x[0], snake.y[0]);
printf("@"); //輸出蛇的頭
grow = 0;//把grow標記回0
gotoxy(FRAME_HEIGHT, 0);//把光標始終弄在結尾處
}
emm,爲了方便大家理解,我在它原本的基礎上加了不少註釋,方便大家理解。
isAlive函數
這個函數時用來判斷蛇是否存活。主要的邏輯是判斷蛇是否撞到邊界和撞到自己。
這個函數很簡單,就是一個判斷函數
int isAlive(void)
{//判斷蛇是否還活着,即判斷蛇有沒有撞上自己的身體,有沒有碰到邊界
if (snake.x[0] == 0 || snake.x[0] == FRAME_HEIGHT - 1 || snake.y[0] == FRAME_WIDTH - 1 || snake.y[0] == 0) //When you touch a wall or eat your own body, you die
return 0;
for (k = 1; k<snake.len; k++)
if (snake.x[k] == snake.x[0] && snake.y[k] == snake.y[0])
return 0;
return 1;
}
getSpeed函數
這個函數是用來根據蛇的長度判斷蛇的速度的。但是我們主要的速度邏輯並不是在這裏實現的,所以這個函數並不複雜。
void getSpeed(void)//根據長度修改速度
{
if (snake.len <= 6)
snake.speed = 200;
else if (snake.len <= 10)
snake.speed = 175;
else if (snake.len <= 20)
snake.speed = 150;
else if (snake.len <= 30)
snake.speed = 100;
else snake.speed = 50;
if (snake.len == 40)
finish = 0;//長度40爲通關
}
main主函數
嘿嘿,關於snake.h和它的函數終於寫完了。接下來我們就可以來完成最後一步了。之前說了,我們只要在開頭把snake.h的頭文件包含進來,就可以直接用了。具體邏輯我就不詳細講解了,但是註釋我還是給大家打的挺詳細的。
#include "snake.h"
#include <windows.h>
#include <math.h>
//Transposed structure object
extern struct Food food;//全局變量申明
extern struct Snake snake;
//main function
int main()
{
ret = menu();//顯示初始化菜單,並且判斷用戶是否想玩遊戲
if (ret == 1)//輸入1,表示是
{
while (finish)
{
initMap(); //初始化地圖
while (TRUE)
{
updataFood(); //updata food
getSpeed(); //speed
moveSnake(); //move snake
Sleep(snake.speed); //是計算機處理的程序的函數。。功能是暫停,單位是毫秒,它就是用來控制蛇的快慢的,嘿嘿。其實就是通過這個函數讓系統進程變慢,從而控制蛇的快慢
printFont(FONT_WINDOW_STARTX, FONT_WINDOW_STARTY, snake.speed); //顯示遊戲信息
if (!(isAlive()))//如果沒有存活,則結束
break;
}
if (finish)//如果沒有標記通關,則顯示你這次的長度 //Death or customs clearance
{
system("cls");//清屏函數
gotoxy(22, 5);
printf("您的遊戲結束,您操作的貪喫蛇長度爲%d,請再接再厲",snake.len);
gotoxy(24, 5);
printf("按(1)鍵退出遊戲,按(2)鍵重玩");
char ch;
while((ch=getch(),ch)!= 50)
{
if(ch==49) return 0;
if(ch==50) break;
}
system("cls");
}
else//如果通關了
{
system("cls");
gotoxy(22, 5);
printf("恭喜你通關了");
gotoxy(24, 5);
printf("按(1)鍵退出遊戲,按(2)鍵重玩");
char ch;
while((ch=getch(),ch)!= 50)
{
if(ch==49) return 0;//注意這裏的49沒有引號,是數字,因爲“1”的ASIIC碼值就是49,所以可以這樣判斷
if(ch==50) break;
}
system("cls");
}
}
}
}
關於貪喫蛇這個小項目的講解就到這裏了,大家可以自己去嘗試下,完善下。其實在實際的寫課設的過程中,寫的順序並不是我這個講解的順序,我只是爲了方便大家理解。寫過課設的童鞋都知道,大多都是自己先定一個大概的框架,然後再逐步完善的而不是像這個一樣一口氣寫出來。
最後,大家如果有不明白的地方也歡迎在評論區問我。也可以來技術菌團的qq羣一起討論