文章目錄
- 1.前言
- 2.基于控制臺實作,
- 3.為貪吃蛇添加更多創新,
- (1)輸出一個比較漂亮的影像作為墻體
- (2)初始化改變顏色,以及視窗大小,
- (3)更具想象力的貪吃蛇功能
- (4)實作簡單的互動界面以及功能
- 4.總結
1.前言
上一個blog我們講了講貪吃蛇的核心思想,其實主要就是整個貪吃蛇游戲最核心的部分,有了那些核心思想完成一個簡陋的貪吃蛇不成問題,那么如何讓簡陋的貪吃蛇不再簡陋呢?這一篇我們就主要來講一下如何實作更加完美的貪吃蛇,
2.基于控制臺實作,
實驗主要的要求有:實作貪吃蛇游戲基本功能,螢屏上隨機出現一個“食物”,稱為豆子,上下左右控制“蛇”的移動,吃到“豆子”以后“蛇”的身體加長一點,“蛇”碰到邊界或蛇頭與蛇身相撞,蛇死亡,游戲結束,為游戲設計友好的互動界面;例如歡迎界面,游戲界面,游戲結束界面,要有開始鍵、暫停鍵和停止退出的選項,對蛇吃到豆子進行分值計算,可以設定游戲速度,游戲音樂等拓展元素,
實驗要求是需要用到一些互動界面開發軟體的,開始的時候自己也是想過要用MFC來實作整個貪吃蛇的,百度了一下相關教程,我所知道的就是B站上只有一篇是講述MFC實作貪吃蛇的視頻,B站上講解的視頻確實講了很重點的東西,但如果沒有任何MFC基礎去看那個的話,有點小自閉,很多按鍵以及添加的頭檔案和函式都是最大的難題,如果有興趣的話覺得自己MFC的知識還足夠豐富的話,推薦可以去看一下,
我就比較菜了,學了大概一整天,一個多小時的視頻看了好幾遍,學是沒學到多少,整個人是真的自閉了,所以我果斷放棄了MFC,就用控制臺了,界面丑一點就丑一點了,功能多一點就好了,
3.為貪吃蛇添加更多創新,
(1)輸出一個比較漂亮的影像作為墻體
一般來說貪吃蛇的墻都是很普通的“@"或者“#“,就是一個比較短的符號,我們可以在C++里看一下生成地圖的效果,
```cpp
cout << " ";
for (int i=1;i<=n;i++)
cout << "-";
cout << endl;
for (int j=0;j<=m-1;j++)
{
cout << "|";
for (int i=1;i<=n;i++) cout << " ";
cout << "|" << endl;
}
cout << " ";
for (int i=1;i<=n;i++)
cout << "-"
大概是這樣的:
還是比較丑的,這時候我們能不能用特殊符號來當作墻呢,輸出的話應該就比較漂亮一點了,就比如▲或◆或●這樣的,但是這時候如果我們還是像上次那樣定位然后輸出的話可以試下效果
cout << " ";
for (int i=1;i<=n;i++)
cout << "■";
cout << endl;
for (int j=0;j<=m-1;j++)
{
cout << "■";
for (int i=1;i<=n;i++) cout << " ";
cout << "■" << endl;
}
cout << " ";
for (int i=1;i<=n;i++)
cout << "■";

有點奇怪,為什么呢,這就要說明一下了,我們#$%這種符號都占一個位元組,而在螢屏上你會發現螢屏的寬兩個符號之間距離特別大,具體應該是寬是長的兩倍,寬就占了大概兩個位元組,所以我們用特殊符號其實輸出的不是一個位元組的數,而是一個兩位元組的符號,我們剛剛輸出的空格其實算是一個位元組的,這時候只需要輸出兩個空格就好了,
這樣看起來就舒服多了,但其實還是有問題的,我們所用到的蛇還是只占一個位元組的,所以我們還必須改一下蛇占陣列長度的值,把列數從1改為2.
(2)初始化改變顏色,以及視窗大小,
對于想讓用戶自定義顏色的問題,需要用到的函式其實也是包含在頭檔案下,就是windows.h這個頭檔案,實作改變顏色的話,我們需要用到system函式分別進行幾個操作,system(”cls“),system(”pause“),system(“color. XX”),實作清屏功能,暫停功能以及改變前景色和背景色的功能,基本思想就是用戶輸入兩個字符,然后判斷這兩個字符是不是十六進制中的一個(0~A),如果是的話就可以呼叫system函式,然后判斷用戶按鍵輸入的字符是什么,然后呼叫System(“color XX”)函式,XX就可以賦值為用戶所輸入的那個值,實作起來的話,不嫌麻煩的話可以直接switch嵌套switc case,然后得到第一個十六進制數,再得到另一個十六進制的數字,
#include<Windows.h>
void color(char a, char b)
{
switch (a)
{
case '0':
switch (b) {
case'0':
system("color 00"); break;
case'1':
system("color 01"); break;
case'2':
system("color 02"); break;
case'3':
system("color 03"); break;
case'4':
system("color 04"); break;
case'5':
system("color 05"); break;
case'6':
system("color 06"); break;
case'7':
system("color 07"); break;
case'8':
system("color 08"); break;
case'9':
system("color 09"); break;
case'A':
system("color 0A"); break;
case'B':
system("color 0B"); break;
case'C':
system("color 0C"); break;
case'D':
system("color 0D"); break;
case'E':
system("color 0E"); break;
case'F':
system("color 0F"); break;
}
break;
case '1':
switch (b) {
case'0':
system("color 10"); break;
case'1':
system("color 11"); break;
case'2':
system("color 12"); break;
case'3':
system("color 13"); break;
case'4':
system("color 14"); break;
case'5':
system("color 15"); break;
case'6':
system("color 16"); break;
case'7':
system("color 17"); break;
case'8':
system("color 18"); break;
case'9':
system("color 19"); break;
case'A':
system("color 1A"); break;
case'B':
system("color 1B"); break;
case'C':
system("color 1C"); break;
case'D':
system("color 1D"); break;
case'E':
system("color 1E"); break;
case'F':
system("color 1F"); break;
}
break;
case '2':
switch (b) {
case'0':
system("color 20"); break;
case'1':
system("color 21"); break;
case'2':
system("color 22"); break;
case'3':
system("color 23"); break;
case'4':
system("color 24"); break;
case'5':
system("color 25"); break;
case'6':
system("color 26"); break;
case'7':
system("color 27"); break;
case'8':
system("color 28"); break;
case'9':
system("color 29"); break;
case'A':
system("color 2A"); break;
case'B':
system("color 2B"); break;
case'C':
system("color 2C"); break;
case'D':
system("color 2D"); break;
case'E':
system("color 2E"); break;
case'F':
system("color 2F"); break;
}
break;
case '3':
switch (b) {
case'0':
system("color 30"); break;
case'1':
system("color 31"); break;
case'2':
system("color 32"); break;
case'3':
system("color 33"); break;
case'4':
system("color 34"); break;
case'5':
system("color 35"); break;
case'6':
system("color 36"); break;
case'7':
system("color 37"); break;
case'8':
system("color 38"); break;
case'9':
system("color 39"); break;
case'A':
system("color 3A"); break;
case'B':
system("color 3B"); break;
case'C':
system("color 3C"); break;
case'D':
system("color 3D"); break;
case'E':
system("color 3E"); break;
case'F':
system("color 3F"); break;
}
break;
case '4':
switch (b) {
case'0':
system("color 40"); break;
case'1':
system("color 41"); break;
case'2':
system("color 42"); break;
case'3':
system("color 43"); break;
case'4':
system("color 44"); break;
case'5':
system("color 45"); break;
case'6':
system("color 46"); break;
case'7':
system("color 47"); break;
case'8':
system("color 48"); break;
case'9':
system("color 49"); break;
case'A':
system("color 4A"); break;
case'B':
system("color 4B"); break;
case'C':
system("color 4C"); break;
case'D':
system("color 4D"); break;
case'E':
system("color 4E"); break;
case'F':
system("color 4F"); break;
}
break;
case '5':
switch (b) {
case'0':
system("color 50"); break;
case'1':
system("color 51"); break;
case'2':
system("color 52"); break;
case'3':
system("color 53"); break;
case'4':
system("color 54"); break;
case'5':
system("color 55"); break;
case'6':
system("color 56"); break;
case'7':
system("color 57"); break;
case'8':
system("color 58"); break;
case'9':
system("color 59"); break;
case'A':
system("color 5A"); break;
case'B':
system("color 5B"); break;
case'C':
system("color 5C"); break;
case'D':
system("color 5D"); break;
case'E':
system("color 5E"); break;
case'F':
system("color 5F"); break;
}
break;
case '6':
switch (b) {
case'0':
system("color 60"); break;
case'1':
system("color 61"); break;
case'2':
system("color 62"); break;
case'3':
system("color 63"); break;
case'4':
system("color 64"); break;
case'5':
system("color 65"); break;
case'6':
system("color 66"); break;
case'7':
system("color 67"); break;
case'8':
system("color 68"); break;
case'9':
system("color 69"); break;
case'A':
system("color 6A"); break;
case'B':
system("color 6B"); break;
case'C':
system("color 6C"); break;
case'D':
system("color 6D"); break;
case'E':
system("color 6E"); break;
case'F':
system("color 6F"); break;
}
break;
case '7':
switch (b) {
case'0':
system("color 70"); break;
case'1':
system("color 71"); break;
case'2':
system("color 72"); break;
case'3':
system("color 73"); break;
case'4':
system("color 74"); break;
case'5':
system("color 75"); break;
case'6':
system("color 76"); break;
case'7':
system("color 77"); break;
case'8':
system("color 78"); break;
case'9':
system("color 79"); break;
case'A':
system("color 7A"); break;
case'B':
system("color 7B"); break;
case'C':
system("color 7C"); break;
case'D':
system("color 7D"); break;
case'E':
system("color 7E"); break;
case'F':
system("color 7F"); break;
}
break;
case '8':
switch (b) {
case'0':
system("color 80"); break;
case'1':
system("color 81"); break;
case'2':
system("color 82"); break;
case'3':
system("color 83"); break;
case'4':
system("color 84"); break;
case'5':
system("color 85"); break;
case'6':
system("color 86"); break;
case'7':
system("color 87"); break;
case'8':
system("color 88"); break;
case'9':
syqostem("color 89"); break;
case'A':
system("color 8A"); break;
case'B':
system("color 8B"); break;
case'C':
system("color 8C"); break;
case'D':
system("color 8D"); break;
case'E':
system("color 8E"); break;
case'F':
system("color 8F"); break;
}
break;
case '9':
switch (b) {
case'0':
system("color 90"); break;
case'1':
system("color 91"); break;
case'2':
system("color 92"); break;
case'3':
system("color 93"); break;
case'4':
system("color 94"); break;
case'5':
system("color 95"); break;
case'6':
system("color 96"); break;
case'7':
system("color 97"); break;
case'8':
system("color 98"); break;
case'9':
system("color 99"); break;
case'A':
system("color 9A"); break;
case'B':
system("color 9B"); break;
case'C':
system("color 9C"); break;
case'D':
system("color 9D"); break;
case'E':
system("color 9E"); break;
case'F':
system("color 9F"); break;
}
break;
case 'A':
switch (b) {
case'0':
system("color A0"); break;
case'1':
system("color A1"); break;
case'2':
system("color A2"); break;
case'3':
system("color A3"); break;
case'4':
system("color A4"); break;
case'5':
system("color A5"); break;
case'6':
system("color A6"); break;
case'7':
system("color A7"); break;
case'8':
system("color A8"); break;
case'9':
system("color A9"); break;
case'A':
system("color AA"); break;
case'B':
system("color AB"); break;
case'C':
system("color AC"); break;
case'D':
system("color AD"); break;
case'E':
system("color AE"); break;
case'F':
system("color AF"); break;
}
break;
case 'B':
switch (b) {
case'0':
system("color B0"); break;
case'1':
system("color B1"); break;
case'2':
system("color B2"); break;
case'3':
system("color B3"); break;
case'4':
system("color B4"); break;
case'5':
system("color B5"); break;
case'6':
system("color B6"); break;
case'7':
system("color B7"); break;
case'8':
system("color B8"); break;
case'9':
system("color B9"); break;
case'A':
system("color BA"); break;
case'B':
system("color BB"); break;
case'C':
system("color BC"); break;
case'D':
system("color BD"); break;
case'E':
system("color BE"); break;
case'F':
system("color BF"); break;
}
break;
case 'C':
switch (b) {
case'0':
system("color C0"); break;
case'1':
system("color C1"); break;
case'2':
system("color C2"); break;
case'3':
system("color C3"); break;
case'4':
system("color C4"); break;
case'5':
system("color C5"); break;
case'6':
system("color C6"); break;
case'7':
system("color C7"); break;
case'8':
system("color C8"); break;
case'9':
system("color C9"); break;
case'A':
system("color CA"); break;
case'B':
system("color CB"); break;
case'C':
system("color CC"); break;
case'D':
system("color CD"); break;
case'E':
system("color CE"); break;
case'F':
system("color CF"); break;
}
break;
case 'D':
switch (b) {
case'0':
system("color D0"); break;
case'1':
system("color D1"); break;
case'2':
system("color D2"); break;
case'3':
system("color D3"); break;
case'4':
system("color D4"); break;
case'5':
system("color D5"); break;
case'6':
system("color D6"); break;
case'7':
system("color D7"); break;
case'8':
system("color D8"); break;
case'9':
system("color D9"); break;
case'A':
system("color DA"); break;
case'B':
system("color DB"); break;
case'C':
system("color DC"); break;
case'D':
system("color DD"); break;
case'E':
system("color DE"); break;
case'F':
system("color DF"); break;
}
break;
case 'E':
switch (b) {
case'0':
system("color E0"); break;
case'1':
system("color E1"); break;
case'2':
system("color E2"); break;
case'3':
system("color E3"); break;
case'4':
system("color E4"); break;
case'5':
system("color E5"); break;
case'6':
system("color E6"); break;
case'7':
system("color E7"); break;
case'8':
system("color E8"); break;
case'9':
system("color E9"); break;
case'A':
system("color EA"); break;
case'B':
system("color EB"); break;
case'C':
system("color EC"); break;
case'D':
system("color ED"); break;
case'E':
system("color EE"); break;
case'F':
system("color EF"); break;
}
break;
case'F':
switch (b) {
case'0':
system("color F0"); break;
case'1':
system("color F1"); break;
case'2':
system("color F2"); break;
case'3':
system("color F3"); break;
case'4':
system("color F4"); break;
case'5':
system("color F5"); break;
case'6':
system("color F6"); break;
case'7':
system("color F7"); break;
case'8':
system("color F8"); break;
case'9':
system("color F9"); break;
case'A':
system("color FA"); break;
case'B':
system("color FB"); break;
case'C':
system("color FC"); break;
case'D':
system("color FD"); break;
case'E':
system("color FE"); break;
case'F':
system("color FF"); break;
}
break;
}
}
的確是比較多,也比較笨吧,但至少可以寫出來,
關于視窗大小的陳述句,其實也是為了讓地圖可以設定的更大一點,不用每一次都麻煩地去調一下最大化,在這個程序之中很可能會出現BUG這類的,所以就直接視窗最大化吧,相關的視窗設定大小百度應該有具體教程,講解地比較詳細,我這里就說一下大概,就是讀取電腦螢屏的大小用像素記錄一下,然后用設定視窗大小的函式來設定一下程式輸出有多大,
void full_screen()
{
HWND hwnd = GetForegroundWindow();
int cx = GetSystemMetrics(SM_CXSCREEN); /* 螢屏寬度 像素 */
int cy = GetSystemMetrics(SM_CYSCREEN); /* 螢屏高度 像素 */
LONG l_WinStyle = GetWindowLong(hwnd, GWL_STYLE); /* 獲取視窗資訊 */
/* 設定視窗資訊 最大化 取消標題欄及邊框 */
SetWindowLong(hwnd, GWL_STYLE, (l_WinStyle | WS_POPUP | WS_MAXIMIZE) & ~WS_CAPTION & ~WS_THICKFRAME & ~WS_BORDER);
SetWindowPos(hwnd, HWND_TOP, 0, 0, cx, cy, 0);
}
(3)更具想象力的貪吃蛇功能
關于這點,可以實作好多,我們可以吃不同的食物增加不同的長度,也可以吃一次食物改變一次顏色,也可以在地圖上隨機生成障礙物,蛇碰到會死亡,還有好多具有特色的功能可以去實作,
關于生成不同分數種類的食物,我所想到的方法是先生成一個食物坐標點,先把這個點確定了再說具體生成的型別,具體生成型別的函式也可以用一個隨時間變化的函式來判斷,假定T=1,2,3,1生成一種食物,2生成另一種,3生成下一種,
bool print_food()
{
srand((unsigned)time(0));//srand是種子函式,需要unsinged型別的輸入,unsigned函式是為了將系統的時間time型別轉換為unsigned型別,time函式賦值為0,是獲取標準時間函式
bool e;
while (1)//回圈恒成立,除非break跳出回圈結構,
{
e = true;//將bool e賦值為真,
int i = (int)(random((double)H+1, (double)m + H));
int j=(int)(random((double)L+1, (double)n + L ));//隨機生成食物的x,y坐標,
food.x = i; food.y = j;
for (int k = 0; k <= snake_length - 1; k++)//判斷食物生成的位置是不是位于蛇的身體上,如果不是跳出回圈,并且將食物的標志e設定為真,反之設定為假,
{
if (snake[k].x == food.x && snake[k].y == food.y)
{
e = false; break;
}
}
if (e) break; //判斷食物生成的位置不在蛇的身體上就跳出回圈,
}
locate(food.x, food.y);//在指定的位置上生成食物,
srand((unsigned)time(NULL));
int T;
T = rand() % 3 + 1;
if (T == 1) {
cout << "¥";
food1 = food;
return false;
}//食物1的標志為¥,吃到這食物蛇的長度增加1,
else {
if (T == 2) {
cout << "▲";//食物2的標志位▲,吃到這個食物的長度增加2,
food2 = food;
return false;
}
else {
if (T == 3) {
cout << "★";//食物3的標志為★,吃到這個食物蛇的長度增加3,
food3 = food;
return false;
}
}
}
return true;
}
這上面只是一個簡單的例子,吃到食物長度增加多少,甚至還可以減少,都是自己去定義的,反正原理是一樣的,
關于變色的實作,就更為簡單了,我們可以判定分數達到一定的值會變色,吃到特定的食物會變色,隨著時間的推移會自動變色等等之類的,或者是蛇身體可以有不同的花色,這些都是可以實作的,也是舉個簡單的例子,比如達到多少分會變色,
int score(int a) {//計算得分的函式,
a = a - 3;//先把初始的長度減去,
int b = 0; //定義這個函式的回傳值為b,
if (a != 0) {
if (a > 0 && a <= 10) b = a * 5;
else {
if (a > 50) b = 450 + a * 50;//得分計算的方式
else b = 50 + a * 10;
}
}
if (b >= 100&&b<=200) system("color 4C");
if (b > 200 && b <= 300)system("color 3A");
if (b > 300 && b <= 400)system("color 2F");
if (b > 400)system("color 8B");
return b;
}
然后講解一下有關其他功能實作的大概思路,比如實作穿墻,我們不能簡單地判斷蛇撞墻不會死亡,這樣的程式不能用,實作穿墻必須蛇從下面的墻進去,然后從上面的相同位置按照相同長度穿出來,這個在我的程式里是用到了一個定位函式,可以判定蛇頭碰到墻體然后對應碰到上墻體或者下墻體的話,它的列數是不變的當行數需要從上(下)墻體轉變為下(上)墻體的坐標,相應的,左右是行數不變,列數改變,根據上一篇講解的,我的程式是dir控制方向的全域變數,這個方向也不變,用定位函式確定了第一個蛇體的位置之后,還是按照原則,根據蛇鏈表長度,把蛇列印出來就好了,相當于一次初始化,
還有一個比較重要的點,隨著蛇身體長度的增加,應該游戲是越來越難的,并且初始化的時候還可以選擇難度,這是需要的函式其實是計時函式clock,只需要控制兩個計時器之間時間的差值,然后經這一段時間之后,蛇可以前進,因為clock的時間單位是ms,所以我們一定要慎重判斷這個引數的設定,我對于這些引數沒什么概念,最后還是參照了一些有關資料吧,
while (1)
{
/*難度隨長度增加而提高*/
hard_len = (double)snake_length / (double)(double(m) * double(n));
/*調節時間,單位是ms*/
a = clock();
while (1)//回圈恒成立,
{
b = clock();
if ((b - a )>= (int)((400 - 30 * hard) * (1 - sqrt(hard_len)))) break;
}
講解一下是什么意思,計算方法中的30hard就是我們剛開始輸入的難度,sqrt是開方,hard_len其實就是蛇的長度除以地圖的大小,畢竟不同地圖的不可能用到同一個引數,比如難度我們設定了1~10中的5,400-530就等于250,比如地圖大小為10乘以10,蛇的長度為25,此時另一個值就為25/100=1/4,然后開根,就為1/2,兩個數字相乘,1/2乘以250=125ms,所以每一次相隔125ms蛇就會移動一次,
如果還想有更高級的功能的話,就需要用到進階版的AI貪吃蛇,需要用到下一篇博客講解的A*演算法,那就下一節再次講解,
(4)實作簡單的互動界面以及功能
要有一個比較完善的互動界面和初始化功能,比如失敗了,還可以重新開始,或者剛開始輸入引數的話,我們可以用一步一步的引導來讓用戶輸入引數,此時最重要的就是背景顏色和文字在地圖中的位置了,關于這點還是用到了最為關鍵的定位函式,
/*下面這一部分是控制臺里的陳述句,需要我們自己去學習一些相關知識, */
/*游標讀取*/
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);//獲得標準輸入輸出的句柄,這個函式是在windows.h頭檔案下的屬于控制臺函式,
COORD coord;//這是獲取坐標的結構體的名稱,
void locate(int x, int y)
{
COORD coord;
coord.X = y * 2;
coord.Y = x;
//獲取控制臺緩沖區句柄
HANDLE ConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
//設定游標位置
SetConsoleCursorPosition(ConsoleHandle, coord);
}
上面的定位函式需要解釋的就兩點,其他的話都是一些基本的控制臺操作,相關知識需要自己學習,第一點為什么X得到是y,Y得到的是x,這個是因為我的坐標系選取的是以螢屏的左上角為坐標原點,橫向為x軸,縱向為y軸,與電腦函式中的行和列正好是坐標互換過了的,為什么*2其實上面解釋過了,一般顯示的話我們可以明顯看出來橫向顯示的話,是挨得比較緊的,而縱向顯示的話是比較稀疏的,這就是因為行數所占寬度是列數的兩倍,我們要用兩倍所占的列數來顯示特殊符號,他所占的字符就為兩個字符,要確保它為正方形,
那就簡單展示一下我設計的圖形吧!


死亡之后可以重新開始的話,可以之直接在死亡之后加一個按鍵輸入,輸入“y”重新開始,輸入“n”退出,重新開始的話判斷輸入y直接呼叫初始化函式就可以了,
if (_kbhit())//kbhit()檢測是否有按鍵按下,有按下回傳鍵值,
{
ch = _getch();
if (ch == 'y' || ch == 'Y')
{
system("cls");
initall();
break;
}
if (ch == 'N' || ch == 'n')
{
locate(31, 50);
cout << "游戲結束!!!" << endl;
break;
}
system("pause");
}
還有一點暫停功能的補充吧,就是空格暫停,然后再次按下控制就繼續,其實也就是在按鍵那里加一個判定吧,就是如果按下空格鍵的話,只有按下下一個空格的話才可以繼續讀取按鍵,
if (_kbhit())//kbhit()檢測是否有按鍵按下,有按下回傳鍵值,
{
ch = _getch();
if (ch == ' ')//暫停的功能實作
{
while (_getch() != ' ') { //再次按下空格繼續,
ch = _getch();
}
}
加上上篇博客的所有,我們就大概實作了幾乎整個功能吧,也讓很簡陋的貪吃蛇有了一點自己的特色功能吧,雖然還是很簡陋,
下面附一下所有程式的代碼吧!color函式放在頭檔案里了,include一下就好了,
#include<bits/stdc++.h>
#include<ctime>
#include<conio.h>
#include<windows.h>
#include"color.h"
using namespace std;
const int L = 10;// 生成蛇的列位置為居中
const int H = 2;//生成蛇的位置行為居中
/*定義地圖的長寬,蛇的坐標,長度,方向,食物的位置,*/
int m, n, end1;
struct node//定義節點的結構體,主要是為了蛇身體的表示和隨機生成食物,
{
int x, y;
}
snake[1000];
int snake_length, dir;//定義蛇的長度以及判斷方向的引數,
node food, food1, food2, food3;//定義食物
int direct[4][2] = { {-1,0},{1,0},{0,-1},{0,1} };//定義移動方向所對應的陣列的操作,四行兩列的陣列,
/*資料初始化,包括蛇的長度,蛇的位置,方向*/
clock_t a, b;//判斷難度的引數,
char ch;//存放鍵盤輸入的按鍵,
double hard_len;//設定難度,
void full_screen();
int hard;
int map1[5000][5000];
char A, B;
using namespace std;
void locate(int, int);
void hide();
double random(double, double);
int score(int);
void print_snake();
bool is_correct();
bool print_food();
bool go_ahead();
void print_wall(int, int);
void initall();
/*下面這一部分是控制臺里的陳述句,需要我們自己去學習一些相關知識, */
/*游標讀取*/
HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);//獲得標準輸入輸出的句柄,這個函式是在windows.h頭檔案下的屬于控制臺函式,
COORD coord;//這是獲取坐標的結構體的名稱,
void locate(int x, int y)
{
COORD coord;
coord.X = y * 2; //第3列
coord.Y = x; //第3行
//獲取控制臺緩沖區句柄
HANDLE ConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);
//設定游標位置
SetConsoleCursorPosition(ConsoleHandle, coord);
}
/*隱藏游標*/
void hide() {
CONSOLE_CURSOR_INFO cursor_info = { 1,0 };//函式結構體,賦值為0,游標被隱藏,
/*CONSOLE_CURSOR_INFO結構體定義如下:
typedef struct
{ DWORD dwSize;
BOOL bVisible; //為0時游標不可見
CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;
}
*/
SetConsoleCursorInfo(hout, &cursor_info);//函式 SetConsoleCursorInfo 設定游標的大小和可見度,需向其傳遞指向結構 CONSOLE_CURSOR_INFO 的指標:
}
/*生成亂數*/
double random(double start, double end)
{
return start + (end - start) * rand() / (RAND_MAX + 1.0);//主要是為了確保生成的亂數在固定的范圍內,并且要確保亂數生成的隨機性,
}
// -1,0 代表向上移動過,1,0為向下移動,0,-1為向左移動,0,1為向右移動,
int score(int a) {//計算得分的函式,
a = a - 3;//先把初始的長度減去,
int b = 0; //定義這個函式的回傳值為b,
if (a != 0) {
if (a > 0 && a <= 10) b = a * 5;
else {
if (a > 50) b = 450 + a * 50;//得分計算的方式
else b = 50 + a * 10;
}
}
if (b >= 100&&b<=200) system("color 4C");
if (b > 200 && b <= 300)system("color 3A");
if (b > 300 && b <= 400)system("color 2F");
if (b > 400)system("color 8B");
return b;
}
/*首次輸出蛇頭,snake[0]代表蛇頭*/
void print_snake()
{
locate(snake[0].x, snake[0].y);//確定蛇頭在陣列里的位置,
printf("●");//蛇頭的形狀為●,
for (int i = 1; i <= snake_length - 1; i++)//判斷蛇的身體有多長,
{
locate(snake[i].x, snake[i].y);//確定每一節蛇身的位置,
printf("◆");//列印蛇身的形狀為◆,
}
}
/*判斷蛇頭是否撞到墻壁或者撞到自己的身體*/
bool is_correct()//回傳值為假的時候說明撞到了蛇身或者墻壁,為真說明沒有撞到 ,
{
if (snake[0].x == H || snake[0].y == L || snake[0].x == m + H || snake[0].y == n + L) return false;//判斷是否撞墻,
for (int i = 1; i <= snake_length - 1; i++)
{
if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) return false;//判斷是否撞到了蛇身,
}
return true; //如果都沒有撞到,回傳值為真,
}
/*運用種子函式來隨機生成并輸出食物位置*/
bool print_food()
{
srand((unsigned)time(0));//srand是種子函式,需要unsinged型別的輸入,unsigned函式是為了將系統的時間time型別轉換為unsigned型別,time函式賦值為0,是獲取標準時間函式
bool e;
while (1)//回圈恒成立,除非break跳出回圈結構,
{
e = true;//將bool e賦值為真,
int i = (int)(random((double)H+1, (double)m + H));
int j=(int)(random((double)L+1, (double)n + L ));//隨機生成食物的x,y坐標,
food.x = i; food.y = j;
for (int k = 0; k <= snake_length - 1; k++)//判斷食物生成的位置是不是位于蛇的身體上,如果不是跳出回圈,并且將食物的標志e設定為真,反之設定為假,
{
if (snake[k].x == food.x && snake[k].y == food.y)
{
e = false; break;
}
}
if (e) break; //判斷食物生成的位置不在蛇的身體上就跳出回圈,
}
locate(food.x, food.y);//在指定的位置上生成食物,
srand((unsigned)time(NULL));
int T;
T = rand() % 3 + 1;
if (T == 1) {
cout << "¥";
food1 = food;
return false;
}//食物1的標志為¥,吃到這食物蛇的長度增加1,
else {
if (T == 2) {
cout << "▲";//食物2的標志位▲,吃到這個食物的長度增加2,
food2 = food;
return false;
}
else {
if (T == 3) {
cout << "★";//食物3的標志為★,吃到這個食物蛇的長度增加3,
food3 = food;
return false;
}
}
}
return true;
}
/*蛇的前進*/
bool go_ahead()
{
node temp;//定義蛇身的節點當作中間變數,
bool e = false;
temp = snake[snake_length - 1];//把包括蛇頭的完整蛇的長度賦值給temp,
for (int i = snake_length - 1; i >= 1; i--)
snake[i] = snake[i - 1];//把蛇頭開始的數值全部賦值給蛇身,確定所有點的值,這些值存在的地方就是要列印蛇體的地方,
snake[0].x += direct[dir][0];//蛇的橫向移動,
snake[0].y += direct[dir][1];//蛇的縱向移動,
locate(snake[1].x, snake[1].y);//確定蛇身所占的全部陣列,
printf("◆");//輸出所有的蛇身
if (snake[0].x == food1.x && snake[0].y == food1.y)//蛇吃到了食物,
{
snake_length++;//蛇身的長度加一,
e = true;//將食物的標志位設定為真,
snake[snake_length - 1] = temp;//把原來蛇總體長度temp的值賦給現在吃到食物之后的蛇身陣列,
}
if (snake[0].x == food2.x && snake[0].y == food2.y)//蛇吃到了食物,
{
snake_length += 2;//蛇身的長度加二,
e = true;//將食物的標志位設定為真,
snake[snake_length - 1] = temp;//把原來蛇總體長度temp的值賦給現在吃到食物之后的蛇身陣列,
}
if (snake[0].x == food3.x && snake[0].y == food3.y)//蛇吃到了食物,
{
snake_length += 3;//蛇身的長度加三,
e = true;//將食物的標志位設定為真,
snake[snake_length - 1] = temp;//把原來蛇總體長度temp的值賦給現在吃到食物之后的蛇身陣列,
}
if (!e)//沒吃到食物標志位依舊為假,非假為真,
{
locate(temp.x, temp.y);//確定蛇身最后一個陣列的點,將值賦為空,如果吃到了食物,長度加一,所以末尾不需要發生變化,
cout << " ";
}
else
print_food();//蛇吃到了食物之后,食物才會繼續生成,
locate(snake[0].x, snake[0].y);//最后確定蛇頭的位置,輸出蛇頭,
printf("●");
if (!is_correct())//這個函式前面設定了內容,為真說明什么都沒撞到,為假執行下面的陳述句,
{
system("cls");
system("color F4");
locate(30, 50);
if (snake_length == end1) {
cout << "干得漂亮,你成功了";
locate(31, 50);
cout << "你最后的長度為:" << snake_length;
locate(32, 50);
cout << "你的得分為:" << score(snake_length);
locate(33, 50);
cout << "輸入y繼續,輸入n退出" << endl;
}
else {
cout << "你失敗了!";
locate(31, 50);
cout << "你最后的長度為:" << snake_length;
locate(32, 50);
cout << "你的得分為:" << score(snake_length);
locate(33, 50);
cout << "輸入y繼續,輸入n退出" << endl;
}
return false;
}
return true;
}
void print_wall(int m, int n) {//用陣列來輸出整個墻
int a, b;
a = m + H;
b = n + L;
for (int i = 0; i < m+H; i++) {
for (int j = 0; j < n+L; j++) {
map1[i][j] = 0;
}
}
for (int i = H; i <= a; i++) {
map1[i][L] = 1;
map1[i][b] = 1;//第一列和最后一列都賦值為1.
}
for (int i = L; i <= b; i++) {
map1[H][i] = 1;
map1[a][i] = 1;
}
for (int i = 0; i <= a; i++) {
for (int j = 0; j <= b; j++) {
if (map1[i][j] == 0)printf(" ");
if (map1[i][j] == 1)printf("■");
}
printf("\n");
}
}
void full_screen()
{
HWND hwnd = GetForegroundWindow();
int cx = GetSystemMetrics(SM_CXSCREEN); /* 螢屏寬度 像素 */
int cy = GetSystemMetrics(SM_CYSCREEN); /* 螢屏高度 像素 */
LONG l_WinStyle = GetWindowLong(hwnd, GWL_STYLE); /* 獲取視窗資訊 */
/* 設定視窗資訊 最大化 取消標題欄及邊框 */
SetWindowLong(hwnd, GWL_STYLE, (l_WinStyle | WS_POPUP | WS_MAXIMIZE) & ~WS_CAPTION & ~WS_THICKFRAME & ~WS_BORDER);
SetWindowPos(hwnd, HWND_TOP, 0, 0, cx, cy, 0);
}
void initall()
{
system("cls");//重繪螢屏,
hide();//隱藏游標,
color(A, B);
print_wall(m, n);//列印墻,
print_food();//列印食物,
for (int i = 0; i <= 2; i++)
{
snake[i].x = H + 2;//初始化蛇陣列的位置為第H+2行,
snake[i].y = L + 5 - i;//蛇陣列的位置為第二至第四列,
}
dir = 3;
/*初始化地圖并且輸出,蛇與食物*/
snake_length = 3;//蛇的初始化長度為3.
print_snake();//列印蛇,
locate(1, L /2+ n /2);
cout << "貪吃蛇大作戰";
locate(m + 2 + H, L);
cout << "現在的長度: ";//輸出現在的長度,
locate(m + 2 + H, 20 + L);//在地圖的下兩行輸出,
cout << "現在的得分: ";//輸出現在的得分,
locate(m + 3 + H, L);
cout << "提示:↑,↓,←,→控制蛇的運動,空格:暫停/繼續,吃到¥長度加1,▲加2,★加3.";
/*開始游戲*/
while (1)
{
/*難度隨長度增加而提高*/
hard_len = (double)snake_length / (double)(double(m) * n);
/*調節時間,單位是ms*/
a = clock();
while (1)//回圈恒成立,
{
b = clock();
if ((b - a )>= (int)((400 - 30 * hard) * (1 - sqrt(hard_len)))) break;
}
/*接受鍵盤輸入的上下左右,并以此改變方向*/
if (_kbhit())//kbhit()檢測是否有按鍵按下,有按下回傳鍵值,
{
ch = _getch();
if (ch == ' ')//暫停的功能實作
{
while (_getch() != ' ') { //再次按下空格繼續,
ch = _getch();
}
}
if (ch == -32)
{
ch = _getch();
switch (ch)
{
case 72:
if (dir == 2 || dir == 3)/*蛇頭在輸入一個向上移動的指令,必須要確定方向原本是向左或者向右,
如果原本蛇頭向下移動輸入一個向上的指令是無法實作的,原本蛇頭向上移動,輸入向上指令也不發生改變,*/
dir = 0;
break;
case 80:
if (dir == 2 || dir == 3)//同理,
dir = 1;
break;
case 75:
if (dir == 0 || dir == 1)//同理,
dir = 2;
break;
case 77:
if (dir == 0 || dir == 1)//同理,
dir = 3;
break;
}
}
}
if (!go_ahead())
{//蛇的前進,如果不前進跳出回圈,
break;
}
/*在最后輸出此時長度與分數*/
locate(m + 2 + H, 8 + L);//在界面的下兩行以及固定位置輸出長度,
cout << snake_length;//輸出實時長度,
locate(m + 2 + H, 28 + L);//在界面的相對固定位置輸出分數,
cout << score(snake_length);//輸出實時得分,
}
system("pause");
while(1){
if (_kbhit())//kbhit()檢測是否有按鍵按下,有按下回傳鍵值,
{
ch = _getch();
if (ch == 'y' || ch == 'Y')
{
system("cls");
initall();
break;
}
if (ch == 'N' || ch == 'n')
{
locate(31, 50);
cout << "游戲結束!!!" << endl;
break;
}
system("pause");
}
}
}
int main(){
system("cls");
full_screen();
system("color E9");
locate(2, 20);
cout << "--------------------------------------------------------------------貪吃蛇游戲-----------------------------------------------------------------------" << endl;
locate(4, 20);
cout << "#####################################################################################################################################################" << endl;
locate(6, 20);
cout << "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%" << endl;
locate(21, 45);
cout << "請先輸入兩個數,表示地圖大小.要求長寬均不小于10." << endl;
locate(23, 45);
cout << "請注意視窗大小,以免發生錯位.建議將視窗調為最大." << endl;
locate(25, 45);
cout << "再選擇難度.請在1-10中輸入1個數,1最簡單,10則最難" << endl;
locate(27, 45);
cout << " 再輸入兩個數字代表顏色,第一背景色,第二字體色," << endl;
locate(29, 45);
cout << "輸入兩個0到F的數字(區分大小寫),輸入非法則默認." << endl;
locate(31, 45);
cout << "0=黑色 1=藍色 2=綠色3=湖藍色4=紅色5=紫色6=黃色." << endl;
locate(33, 45);
cout << "7=白色8=灰色 9=淡藍色A=淡綠色 B=淺綠色C=淡紅色." << endl;
locate(35, 45);
cout << "D=淡紫 E=淡黃色 F=亮白色 系統默認顏色為黑白色." << endl;
locate(37, 45);
cout << "然后進入游戲畫面,以方向鍵控制方向.祝你游戲愉快!" << endl;
locate(58, 20);
cout << "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" << endl;
locate(60, 20);
cout << "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" << endl;
locate(62, 20);
cout << "-----------------------------------------------------------------------------------------------------------------------------------------------------" << endl;
end1 = (m - 1) * (n - 1);//判斷蛇的長度是不是達到最大,
locate(39, 45);
cin >> m;
locate(39, 50);
cin >> n;
if (m < 10 || n < 10 || m>60 || n>100)//地圖大小設定,最小不超過10,最大不能超過本來的視窗,
{
system("cls");
system("color F4");
locate(30, 50);
cout << "INPUT ERROR!!!" << endl;//輸入不合法就會暫停,重新輸入
system("pause");
return 0;
}
locate(39, 55);
cin >> hard;//設定難度與等級
if (hard <= 0 || hard > 10)//難度的區間在1到10之間
{
system("cls");
system("color F4");
locate(30, 50);
cout << "INPUT ERROR!!!" << endl;
system("pause");
return 0;
}
locate(39, 60);
cin >> A;
locate(39, 65);
cin >> B;
initall();
return 0;
}
4.總結
算算開始做貪吃蛇然后到現在完全實作貪吃蛇,包括一些優化吧,幾乎就弄了一周多,畢竟以前也從未接觸過吧,想用MFC真的是心有余而力不足吧,還是太菜,,,不過確實真正地自己能寫出來這樣一個完整的C++代碼也是挺不容易的,如果想實作AI貪吃蛇的話,等學了迷宮自動尋路之后就會寫了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/241918.html
標籤:其他
