国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當(dāng)前位置: 首頁(yè) > news >正文

網(wǎng)易做網(wǎng)站百度助手安卓版下載

網(wǎng)易做網(wǎng)站,百度助手安卓版下載,wordpress無(wú)法編輯,做網(wǎng)站買什么空間文章目錄 游戲背景介紹實(shí)現(xiàn)目標(biāo)適合人群所需技術(shù)淺玩Window API什么是API控制臺(tái)程序窗口大小,名稱設(shè)置 Handle(句柄)獲取句柄 坐標(biāo)結(jié)構(gòu)體設(shè)置光標(biāo)位置 光標(biāo)屬性獲取光標(biāo)屬性設(shè)置光標(biāo)屬性 按鍵信息獲取 貪吃蛇游戲設(shè)計(jì)游戲前的初始化設(shè)置窗口的大小和名稱本地化設(shè)置 寬字符Waht …

文章目錄

      • 游戲背景介紹
      • 實(shí)現(xiàn)目標(biāo)
      • 適合人群
      • 所需技術(shù)
      • 淺玩Window API
        • 什么是API
        • 控制臺(tái)程序
          • 窗口大小,名稱設(shè)置
        • Handle(句柄)
          • 獲取句柄
        • 坐標(biāo)結(jié)構(gòu)體
          • 設(shè)置光標(biāo)位置
        • 光標(biāo)屬性
          • 獲取光標(biāo)屬性
          • 設(shè)置光標(biāo)屬性
        • 按鍵信息獲取
      • 貪吃蛇游戲設(shè)計(jì)
        • 游戲前的初始化
          • 設(shè)置窗口的大小和名稱
          • 本地化設(shè)置
        • 寬字符
          • Waht is 寬字符
          • 寬字符的打印
          • 光標(biāo)的設(shè)置
          • 歡迎界面打印
          • 地圖繪制
          • 幫助信息
          • 蛇身的管理
          • 貪吃蛇游戲的管理(插播,重要)
          • 蛇的初始化
          • 食物的管理
        • 蛇的移動(dòng)
      • 游戲結(jié)束
      • 拓展建議
      • 寄語(yǔ)
      • 源碼

游戲背景介紹

貪吃蛇是一個(gè)非常經(jīng)典的小游戲,筆者曾今在古早的案件手機(jī),mp4上面玩過(guò)這款游戲,今天就讓我們使用C語(yǔ)言一起復(fù)刻這個(gè)簡(jiǎn)單的小游戲吧~,好玩簡(jiǎn)單-

實(shí)現(xiàn)目標(biāo)

在這個(gè)游戲中,我們需要控制一條可以上下左右移動(dòng)的小蛇,在指定的墻體內(nèi)進(jìn)行移動(dòng),吃到食物后,蛇的身體會(huì)變長(zhǎng),當(dāng)蛇撞墻或者撞到自己的身體的時(shí)候,游戲結(jié)束,我們需要實(shí)現(xiàn)的功能有:

  • 蛇的移動(dòng)
  • 食物的生成
  • 蛇的身體的增長(zhǎng)
  • 游戲結(jié)束的判斷
  • 游戲結(jié)束后的處理

適合人群

這是我學(xué)習(xí)完C語(yǔ)言語(yǔ)法和順序表,鏈表之后的一個(gè)小項(xiàng)目,也同樣適合像我一樣剛學(xué)完C語(yǔ)言的同學(xué),通過(guò)這個(gè)項(xiàng)目,鞏固一下C語(yǔ)言的基礎(chǔ)知識(shí),也可以學(xué)習(xí)一下C語(yǔ)言的一些高級(jí)知識(shí),比如Windows API的使用,寬字符的打印等等

所需技術(shù)

  1. C語(yǔ)言基礎(chǔ)
  2. 鏈表
  3. 簡(jiǎn)單的Windows API
  4. 動(dòng)態(tài)內(nèi)存分配

淺玩Window API

什么是API

API全稱Application Programming Interface,翻譯過(guò)來(lái)就是應(yīng)用程序接口,是一組預(yù)先定義的函數(shù),類,協(xié)議的集合,這些函數(shù),類,協(xié)議可以被其他程序調(diào)用,用來(lái)實(shí)現(xiàn)一些功能,比如Windows API就是用來(lái)實(shí)現(xiàn)Windows系統(tǒng)的一些功能的

上面說(shuō)的有些復(fù)雜,我們可以把API想象成一個(gè)工廠,我們只需要知道向工廠輸入什么,工廠會(huì)輸出什么,而不需要知道工廠內(nèi)部是怎么實(shí)現(xiàn)的

比如你向一個(gè)面包工廠輸入了一些小麥,工廠會(huì)給你輸出一些面包,而工廠內(nèi)部可能是用了一些機(jī)器,工人等等來(lái)實(shí)現(xiàn)的,但是你不需要知道這些,你只需要知道你給他小麥,他給你面包就行了

小麥
面包工廠加工
面包
控制臺(tái)程序

控制臺(tái)程序其實(shí)就是我們平時(shí)編譯完文件之后運(yùn)行打開(kāi)的那個(gè)黑框框,我們可以在這個(gè)黑框框里面輸入一些命令,然后程序會(huì)給我們輸出一些結(jié)果,這個(gè)黑框框就是一個(gè)控制臺(tái),我們可以通過(guò)控制臺(tái)程序來(lái)和用戶進(jìn)行交互

窗口大小,名稱設(shè)置

既然是貪吃蛇小游戲,那么我們需要一個(gè)固定的窗口和一個(gè)有趣的名字,我們可以怎么做呢?
可以使用cmd命令在控制臺(tái)程序中設(shè)置窗口的大小,名稱

mode con cols=100 lines=30
title 貪吃蛇
  • mode con cols=100 lines=30 設(shè)置窗口的大小為100列,30行
  • title 貪吃蛇 設(shè)置窗口的名稱為貪吃蛇

但是,我們只希望貪吃蛇程序運(yùn)行后自動(dòng)設(shè)置窗口的大小和名稱,而不是讓用戶手動(dòng)輸入這些命令,因此我們可以使用system函數(shù)來(lái)調(diào)用這些命令,讓程序自動(dòng)設(shè)置窗口的大小和名稱

#include <stdlib.h>
int main()
{system("mode con cols=100 lines=30");system("title 貪吃蛇");return 0;
}
Handle(句柄)

如果我們想要對(duì)控制臺(tái)的一些屬性進(jìn)行設(shè)置,比如設(shè)置光標(biāo)的位置,設(shè)置控制臺(tái)的顏色等等,我們就需要使用句柄來(lái)進(jìn)行操作,我們可以把句柄想象成是控制臺(tái)大哥的身份標(biāo)識(shí),只有知道大哥的身份標(biāo)識(shí)才能找到大哥并更改他的一些屬性

獲取句柄

我們可以使用GetStdHandle函數(shù)來(lái)獲取控制臺(tái)的句柄,這個(gè)函數(shù)的原型是

HANDLE GetStdHandle(DWORD nStdHandle);

我們可以使用一個(gè)HANDLE類型變量來(lái)接受返回值

坐標(biāo)結(jié)構(gòu)體

控制臺(tái)上面的每一個(gè)字符都有一個(gè)坐標(biāo),
坐標(biāo)是從左上角開(kāi)始計(jì)算的,左上角的坐標(biāo)是(0,0),向右是x軸正方向,向下是y軸正方向
我們可以使用一個(gè)結(jié)構(gòu)體來(lái)表示這個(gè)坐標(biāo)
COORD是Windows API中的一個(gè)結(jié)構(gòu)體,定義如下

typedef struct _COORD {SHORT X;SHORT Y;
} COORD, *PCOORD;
設(shè)置光標(biāo)位置

我們平時(shí)在使用printf打印字符的時(shí)候,每打印一個(gè)字符,光標(biāo)都會(huì)自動(dòng)向后移動(dòng)一個(gè)位置,我們可以使用SetConsoleCursorPosition函數(shù)來(lái)手動(dòng)設(shè)置光標(biāo)的位置

BOOL SetConsoleCursorPosition(HANDLE hConsoleOutput,COORD  dwCursorPosition
);

eg:

#include <windows.h>int main()
{HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 獲取控制臺(tái)句柄COORD pos = {10, 10};                          // 設(shè)置坐標(biāo)SetConsoleCursorPosition(hOut, pos);           // 設(shè)置光標(biāo)位置printf("hello world");return 0;
}
光標(biāo)屬性

我們平時(shí)運(yùn)行控制臺(tái)程序的時(shí)候,會(huì)發(fā)現(xiàn)光標(biāo)是一個(gè)閃爍的小方塊,那我們有沒(méi)有什么辦法可以讓小方塊變大變小或是直接隱藏呢?
答案肯定是有噠

我們先來(lái)介紹一下光標(biāo)的兩個(gè)屬性

  • bVisible : 是否可見(jiàn)
  • dwSize : 光標(biāo)的大小 當(dāng)這個(gè)值是100的時(shí)候,光標(biāo)是一個(gè)小方塊█,這個(gè)值也就是顯示這個(gè)方塊的百分比,比如50就是顯示一半的方塊
獲取光標(biāo)屬性

我們可以使用GetConsoleCursorInfo函數(shù)來(lái)獲取光標(biāo)的屬性

BOOL GetConsoleCursorInfo(HANDLE               hConsoleOutput,PCONSOLE_CURSOR_INFO lpConsoleCursorInfo
);

eg:

#include <windows.h>int main()
{HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 獲取控制臺(tái)句柄CONSOLE_CURSOR_INFO cursorInfo;GetConsoleCursorInfo(hOut, &cursorInfo;);printf("bVisible:%d, dwSize:%d\n", cursorInfo.bVisible, cursorInfo.dwSize);return 0;
}

大家可以自行運(yùn)行試一試

設(shè)置光標(biāo)屬性

在貪吃蛇游戲中,我們肯定希望沒(méi)有光標(biāo)的出現(xiàn),因此我們可以使用SetConsoleCursorInfo函數(shù)來(lái)設(shè)置光標(biāo)的屬性

BOOL SetConsoleCursorInfo(HANDLE               hConsoleOutput,const CONSOLE_CURSOR_INFO *lpConsoleCursorInfo
);

eg:

#include <windows.h>int main()
{HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 獲取控制臺(tái)句柄CONSOLE_CURSOR_INFO cursorInfo;cursorInfo.bVisible = 0; // 設(shè)置光標(biāo)不可見(jiàn) 也可以寫flasecursorInfo.dwSize = 100;  // 設(shè)置光標(biāo)大小SetConsoleCursorInfo(hOut, &cursorInfo);return 0;
}
按鍵信息獲取

我們需要使用↑↓←→來(lái)控制蛇的移動(dòng),因此我們可以使用GetAsyncKeyState函數(shù)來(lái)獲取按鍵信息

SHORT GetAsyncKeyState(int vKey
);

在返回的值中,如果最高位是1,表示這個(gè)鍵正在被按下,如果最低位是1,表示這個(gè)鍵被按過(guò)
我們可以使用一個(gè)預(yù)定義的宏來(lái)判斷這個(gè)鍵是否被按下

#define KEY_PRESS(VK) (GetAsyncKeyState(VK) & 0x01 ? 1 : 0)

這個(gè)大家肯定看的懂,如果不懂的話可以去學(xué)習(xí)一下預(yù)編譯和位運(yùn)算

貪吃蛇游戲設(shè)計(jì)

我們可以把貪吃蛇游戲分為三個(gè)部分:

  1. 游戲前的初始化,包括窗口的設(shè)置,光標(biāo)的設(shè)置,歡迎界面打印,地圖的初始化,蛇的初始化,食物的初始化等等
  2. 游戲中的循環(huán),包括蛇的移動(dòng),食物的生成,蛇的身體的增長(zhǎng),游戲結(jié)束的判斷等等
  3. 游戲結(jié)束后的處理,包括釋放資源,打印游戲結(jié)束的信息等等

補(bǔ)充:當(dāng)程序需要實(shí)現(xiàn)按任意鍵繼續(xù)的時(shí)候,我們使用system(“pause”)函數(shù)即可

游戲前的初始化
設(shè)置窗口的大小和名稱
void CmdInit(void)
{// 設(shè)置控制臺(tái)窗口大小 100列 30行system("mode con cols=100 lines=30");// 設(shè)置控制臺(tái)名稱system("title snake");
}
本地化設(shè)置

正常在C語(yǔ)言中,我們使用的是ASCII字符集,他只使用了一個(gè)字節(jié)來(lái)表示一個(gè)字符,而在不同的國(guó)家和地區(qū),字符集是不一樣的,因此我們可以使用setlocale函數(shù)來(lái)設(shè)置字符集

char *setlocale(int category, const char *locale);

在C標(biāo)準(zhǔn)庫(kù)中,我們可以更改的地區(qū)設(shè)置有以下這些:

  • LC_ALL:所有的地區(qū)設(shè)置
  • LC_COLLATE:字符串比較
  • LC_CTYPE:字符分類和轉(zhuǎn)換
  • LC_MONETARY:貨幣格式
  • LC_NUMERIC:非貨幣數(shù)字格式
  • LC_TIME:時(shí)間格式

我么可以使用setlocale函數(shù)來(lái)設(shè)置地區(qū),比如把地區(qū)設(shè)置為當(dāng)前地區(qū)

#include <locale.h>
int main()
{setlocale(LC_ALL, "");return 0;
}
寬字符
Waht is 寬字符

寬字符是指一個(gè)字符占用兩個(gè)字節(jié)的字符,中文以及一些特殊字符都是寬字符,
而且寬字符在控制臺(tái)中是占用兩個(gè)x坐標(biāo)

graph LR
A[寬字符] --> B[占用兩個(gè)坐標(biāo)位置] --> D[普通字符:█]
B --> E[寬字符:██]
A --> C[占用兩個(gè)字節(jié)]
寬字符的打印

在C語(yǔ)言中,我們可以使用wprintf函數(shù)來(lái)打印寬字符,在打印寬字符之前,我們需要進(jìn)行本地化設(shè)置

int wprintf(const wchar_t *format, ...);

eg:

#include <stdio.h>
int main()
{setlocale(LC_ALL, "");wchar_t str[] = L"你好,世界\n";wprintf(L"%s", str);return 0;
}
光標(biāo)的設(shè)置
  1. 定義一個(gè)變量來(lái)接受控制臺(tái)的句柄
  2. 定義一個(gè)變量來(lái)接受光標(biāo)的屬性
  3. 改變光標(biāo)的屬性
  4. 通過(guò)SetConsoleCursorInfo函數(shù),輸入句柄和光標(biāo)屬性,設(shè)置光標(biāo)的屬性
歡迎界面打印

這里我們需要設(shè)置光標(biāo)位置并打印所需信息,因此我們可以封裝一個(gè)函數(shù)來(lái)快捷地設(shè)置光標(biāo)位置

void SetPos(int x, int y)
{HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);COORD pos = {x, y};SetConsoleCursorPosition(hOut, pos);
}

然后我們就可以打印歡迎信息和游戲規(guī)則了

// 打印歡迎信息
void WelcomeToGame(void)
{SetPos(45, 10);wprintf(L"歡迎來(lái)到貪吃蛇游戲");// 暫停SetPos(45, 20);system("pause");
}// 游戲介紹
void GameIntroduction(void)
{// 清屏system("cls");SetPos(45, 10);wprintf(L"游戲介紹:");SetPos(45, 12);wprintf(L"1. 使用↑,↓,←,→控制蛇的移動(dòng)");SetPos(45, 14);wprintf(L"2. 吃到食物蛇的長(zhǎng)度加1");SetPos(45, 16);wprintf(L"2. F3加速, F4減速");SetPos(45, 18);wprintf(L"3. 空格暫停");SetPos(45, 20);wprintf(L"4. Esc退出游戲");// 暫停SetPos(45, 22);system("pause");
}

在這里插入圖片描述

在這里插入圖片描述

地圖繪制

我們這里使用□來(lái)表示墻體,然后運(yùn)用寬字符的打印知識(shí)來(lái)繪制一個(gè)27 * 27的地圖

// 地圖繪制 
// 上(0,0) - (56,0)
// 下(0,26) - (56,26)
// 左(0,0) - (0,26)
// 右(56,0) - (56,26)
// 注意打印的是寬字符 占兩個(gè)x坐標(biāo) 因此左右打印的時(shí)候要每打印一個(gè)x坐標(biāo)加2
void MapDraw(void)
{   // 清屏system("cls");// 上墻for (int i = 0; i < 57; i+=2){SetPos(i, 0);wprintf(L"%c", WALL);}// 下墻for (int i = 0; i < 57; i+=2){SetPos(i, 26);wprintf(L"%c", WALL);}// 最上面和最下面已經(jīng)打印過(guò)了// 左墻for (int i = 1; i < 26; i++){SetPos(0, i);wprintf(L"%c", WALL);}// 右墻for (int i = 1; i < 26; i++){SetPos(56, i);wprintf(L"%c", WALL);} 
}
幫助信息

我們?cè)诘貓D的右側(cè)打印一些幫助信息,比如當(dāng)前的分?jǐn)?shù),按鍵操作等等

// 打印靜態(tài)幫助信息
// 單個(gè)食物分?jǐn)?shù),總分?jǐn)?shù)的名稱
// 操作說(shuō)明
void PrintStaticHelp(void)
{SetPos(70, 5);wprintf(L"單個(gè)食物分?jǐn)?shù): 10");SetPos(70, 7);wprintf(L"總分?jǐn)?shù): 0");SetPos(70, 9);wprintf(L"操作說(shuō)明:");SetPos(70, 11);wprintf(L"↑ : 上移");SetPos(70, 13);wprintf(L"↓ : 下移");SetPos(70, 15);wprintf(L"← : 左移");SetPos(70, 17);wprintf(L"→ : 右移");SetPos(70, 19);wprintf(L"F3: 加速");SetPos(70, 21);wprintf(L"F4: 減速");SetPos(70, 23);wprintf(L"空格: 暫停");SetPos(70, 25);wprintf(L"Esc: 退出");
}

在這里插入圖片描述

蛇身的管理

我們可以將蛇看作是一個(gè)一個(gè)節(jié)點(diǎn)相互連接組成,因此我們可以使用鏈表來(lái)管理蛇的身體,我們先創(chuàng)建一個(gè)結(jié)構(gòu)體來(lái)管理蛇身的節(jié)點(diǎn)

// 蛇節(jié)點(diǎn)
typedef struct SnakeNode
{int x;int y;struct SnakeNode *next;
} SnakeNode, * pSnakeNode;
貪吃蛇游戲的管理(插播,重要)

使用一個(gè)結(jié)構(gòu)體來(lái)管理整個(gè)貪吃蛇游戲,包括:

  • 蛇身
  • 食物
  • 單個(gè)食物分?jǐn)?shù)
  • 總分?jǐn)?shù)
  • 睡眠時(shí)間
  • 方向
  • 游戲狀態(tài)
// 方向
typedef enum Direction
{UP = 1,     // 上DOWN,       // 下LEFT,       // 左RIGHT       // 右} Direction;// 游戲狀態(tài)
typedef enum GameStatus
{OK = 0,         //運(yùn)行中KILL_BY_WALL,   //撞墻KILL_BY_SELF,   //撞自己PAUSE,          //暫停ESC             //退出
} GameStatus;// 貪吃蛇游戲
typedef struct Snake
{pSnakeNode _pSnake;     // 蛇頭pSnakeNode _pFood;      // 食物 可以共用蛇節(jié)點(diǎn)比較方便,下面會(huì)提int _foodScore;         // 單個(gè)食物分?jǐn)?shù)int _totalScore;        // 總分?jǐn)?shù)int _sleepTime;         // 睡眠時(shí)間 -- 控制速度Direction _dir;         // 方向GameStatus _status;     // 游戲狀態(tài)
} Snake, * pSnake;

在游戲開(kāi)始前對(duì)這個(gè)結(jié)構(gòu)體進(jìn)行初始化

pSnake ps = (pSnake)malloc(sizeof(Snake));
蛇的初始化

我們可以使用一個(gè)鏈表來(lái)存儲(chǔ)蛇的身體,這里我們創(chuàng)建一個(gè)有5個(gè)節(jié)點(diǎn)的蛇,并且初始化蛇的位置


#define BODY L'●'
#define POS_X 6
#define POS_Y 6// 蛇初始化
// 在地圖的(6,6)位置初始化蛇
// 蛇的長(zhǎng)度為5
// 使用頭插法
void InitSnake(pSnake ps)
{pSnakeNode cur = NULL;int i = 0;// 創(chuàng)建蛇身節(jié)點(diǎn) 并初始化坐標(biāo)    for (i = 0; i < 5; i++){pSnakeNode node = (pSnakeNode)malloc(sizeof(SnakeNode));if (node == NULL){perror("pSnakeNode malloc failed");exit(1);}node->x = POS_X + i * 2;node->y = POS_Y;node->next = NULL;// 頭插法if (ps->_pSnake == NULL){ps->_pSnake = node;}else{node->next = ps->_pSnake;ps->_pSnake = node;}}// 打印蛇cur = ps->_pSnake;while (cur != NULL){SetPos(cur->x, cur->y);wprintf(L"%c", BODY);cur = cur->next;}
}

在這里插入圖片描述

食物的管理

我們可以把食物也看作是一個(gè)蛇節(jié)點(diǎn),只不過(guò)還沒(méi)有加入到蛇身體中,因此我們可以使用蛇身結(jié)構(gòu)體來(lái)存儲(chǔ)食物
我們?cè)谟螒蜷_(kāi)始和蛇吃到食物的時(shí)候,生成一個(gè)食物節(jié)點(diǎn),并且打印食物到屏幕上

// 創(chuàng)建食物節(jié)點(diǎn)
void CreatFood(pSnake ps)
{// 申請(qǐng)節(jié)點(diǎn)空間pSnakeNode foodnode = (pSnakeNode)malloc(sizeof(SnakeNode));if (foodnode == NULL){perror("foodnode malloc failed");exit(1);}foodnode->next = NULL;// 隨機(jī)生成食物坐標(biāo) 在墻范圍內(nèi)但是不能在蛇身上while (1){int x = rand() % 54 + 2;int y = rand() % 24 + 2;pSnakeNode cur = ps->_pSnake;while (cur != NULL){if (cur->x == x && cur->y == y){break;}cur = cur->next;}if (cur == NULL){foodnode->x = x;foodnode->y = y;break;}}// 打印食物SetPos(foodnode->x, foodnode->y);wprintf(L"%c", FOOD);
}

這里的隨機(jī)數(shù)并不是真正的隨機(jī)數(shù),而是偽隨機(jī)數(shù),因此我們可以在mian里面使用srand函數(shù)來(lái)設(shè)置隨機(jī)數(shù)的種子

int main()
{srand(time(NULL));return 0;
}

在這里插入圖片描述

蛇的移動(dòng)
  1. 按鍵檢測(cè), 改變方向
  2. 判斷是否吃到食物
    • 吃到食物,頭插食物節(jié)點(diǎn),創(chuàng)建新的食物節(jié)點(diǎn)
    • 沒(méi)吃到食物,頭插一個(gè)新的節(jié)點(diǎn),新的節(jié)點(diǎn)是蛇頭移動(dòng)到的下一個(gè)坐標(biāo),尾刪一個(gè)節(jié)點(diǎn)
  3. 蛇移動(dòng)后進(jìn)行打印,判斷是否撞到自己或者墻壁,是則游戲結(jié)束

這里封裝了一個(gè)宏來(lái)讀取按鍵狀態(tài),如果最低位是1,則返回1,反之返回0
也就是一個(gè)按鍵按下過(guò)返回1,否則返回0

#define KEY_PRESS(VK) (GetAsyncKeyState(VK) & 0x01 ? 1 : 0)
// 蛇的移動(dòng) -- 主游戲程序 在此循環(huán)
// 吃到食物 創(chuàng)建新的食物節(jié)點(diǎn)
// 沒(méi)有吃到食物頭插新節(jié)點(diǎn)刪除尾節(jié)點(diǎn)并釋放空間
// 減速睡眠時(shí)間-30 睡眠時(shí)間最少減4次 單個(gè)食物分?jǐn)?shù)加2
// 加速睡眠時(shí)間+30 睡眠時(shí)間最多加4次 單個(gè)食物分?jǐn)?shù)減2
void SnakeMove(pSnake ps)
{int x = 0;int y = 0;again:while (ps->_status == OK){// 按鍵檢測(cè)if (KEY_PRESS(VK_UP) && ps->_dir != DOWN){ps->_dir = UP;}else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP){ps->_dir = DOWN;}else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT){ps->_dir = LEFT;}else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT){ps->_dir = RIGHT;}else if (KEY_PRESS(VK_SPACE)){ps->_status = PAUSE;}else if (KEY_PRESS(VK_F3) && ps->_foodScore < 20){ps->_sleepTime -= 30;ps->_foodScore += 2;// 更改單個(gè)食物分?jǐn)?shù)SetPos(84, 5);printf("%2d", ps->_foodScore);}else if (KEY_PRESS(VK_F4) && ps->_foodScore > 2){ps->_sleepTime += 30;ps->_foodScore -= 2;// 更改單個(gè)食物分?jǐn)?shù)SetPos(84, 5);printf("%2d", ps->_foodScore);}else if (KEY_PRESS(VK_ESCAPE)){ps->_status = ESC;}// 設(shè)置下一個(gè)節(jié)點(diǎn)的x,y坐標(biāo)switch (ps->_dir){case UP:x = ps->_pSnake->x;y = ps->_pSnake->y - 1;break;case DOWN:x = ps->_pSnake->x;y = ps->_pSnake->y + 1;break;case LEFT:x = ps->_pSnake->x - 2;y = ps->_pSnake->y;break;case RIGHT:x = ps->_pSnake->x + 2;y = ps->_pSnake->y;break;}// 判斷是否吃到食物if (ps->_pFood->x == x && ps->_pFood->y == y){EatFood(ps);}else {NotEatFood(ps, x, y);}// 撞墻檢測(cè)if (IsKillByWall(ps)){ps->_status = KILL_BY_WALL;}// 撞自己檢測(cè)if (IsKillBySelf(ps)){ps->_status = KILL_BY_SELF;}Sleep(ps->_sleepTime);}while (ps->_status == PAUSE){if (KEY_PRESS(VK_SPACE)){ps->_status = OK;}goto again; //使用goto 返回到前面}// 撞墻打印信息if (IsKillByWall(ps)){SetPos(45, 10);wprintf(L"墻墻被你撞西了,110把你抓走了");}// 撞自己打印信息if (IsKillBySelf(ps)){SetPos(45, 10);wprintf(L"自己撞自己了,120把你帶走了");}return;
}

EatFood函數(shù)

void EatFood(pSnake ps)
{// 加分ps->_totalScore += ps->_foodScore;// 打印總分?jǐn)?shù)SetPos(82, 7);printf("%4d", ps->_totalScore);// 是 頭插食物節(jié)點(diǎn) 創(chuàng)建新節(jié)點(diǎn)ps->_pFood->next = ps->_pSnake;ps->_pSnake = ps->_pFood;// 打印新蛇頭SetPos(ps->_pSnake->x, ps->_pSnake->y);wprintf(L"%c", BODY);// 創(chuàng)建新食物CreatFood(ps);
}

NotEatFood函數(shù)

// 沒(méi)有遲到食物的處理
void NotEatFood(pSnake ps, int x, int y)
{// 創(chuàng)建新節(jié)點(diǎn)并頭插pSnakeNode node = (pSnakeNode)malloc(sizeof(SnakeNode));if (node == NULL){perror("node malloc failed");exit(1);}node->x = x;node->y = y;node->next = ps->_pSnake;ps->_pSnake = node;// 打印新蛇 順便刪除尾節(jié)點(diǎn) 釋放空間 打印空格pSnakeNode cur = ps->_pSnake;while (cur->next->next != NULL){SetPos(cur->x, cur->y);wprintf(L"%c", BODY);cur = cur->next;}SetPos(cur->x, cur->y);wprintf(L"%c", BODY);SetPos(cur->next->x, cur->next->y);wprintf(L"  ");free(cur->next);cur->next = NULL;
}

IsKillByWall函數(shù)

// 撞墻檢測(cè)
// 撞墻返回1 否則返回0
int IsKillByWall(pSnake ps)
{if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56 || ps->_pSnake->y == 0 || ps->_pSnake->y == 26){return 1;}return 0;
}

在這里插入圖片描述

IsKillBySelf函數(shù)

// 撞自己檢測(cè)
// 撞自己返回1 否則返回0
int IsKillBySelf(pSnake ps)
{pSnakeNode cur = ps->_pSnake->next;// 從第二個(gè)節(jié)點(diǎn)開(kāi)始遍歷 并判斷是否和蛇頭坐標(biāo)相同while (cur != NULL){if (cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y){return 1;}cur = cur->next;}return 0;
}

在這里插入圖片描述

除了提到的以外,我們還要對(duì)加速減速的按鍵進(jìn)行處理,還有總分的統(tǒng)計(jì),失敗的打印信息等等,這里就由大家自由發(fā)揮啦,不行的話也可以看我的源碼

游戲結(jié)束

恭喜你,你已經(jīng)得到了一個(gè)屬于你的小小貪吃蛇游戲了,童年的游戲已經(jīng)被你復(fù)刻,但是你現(xiàn)在還不可以拍拍屁股走人哦,我們還需要做好善后工作啦

  1. 釋放資源
// 善后 釋放蛇節(jié)點(diǎn) 以及食物節(jié)點(diǎn)
void GameEnd(pSnake ps)
{// 釋放蛇節(jié)點(diǎn)pSnakeNode cur = NULL;while (ps->_pSnake != NULL){cur = ps->_pSnake;ps->_pSnake = ps->_pSnake->next;free(cur);}// 釋放食物節(jié)點(diǎn)free(ps->_pFood);
}

拓展建議

本文會(huì)有一些不足之處,但不影響整體的學(xué)習(xí),如果大家有問(wèn)題可以用自己的方法解決或者發(fā)在評(píng)論區(qū)
拓展建議:

  1. 可以優(yōu)化畫面,多加一些符號(hào),可以fancy一點(diǎn),比如這樣
// 使用--,|,/ 繪制英文SNAKE
/* _____                  _                 _____|    \   |\_    |     / \      |   _/   ||_____   |  \   |    /   \     |__/     |_____|   |   \_ |   /_____\    |  \_    |\____|   |     \|  /       \   |    \   |_____*/
void PrintSnake(void)
{SetPos(32, 5);printf(" _____                  _                 _____");SetPos(32, 6);printf(" |    \\   |\\_    |     / \\      |   _/   |");SetPos(32, 7);printf(" |_____   |  \\   |    /   \\     |__/     |_____");SetPos(32, 8);printf("      |   |   \\_ |   /_____\\    |  \\_    |");SetPos(32, 9);printf(" \\____|   |     \\|  /       \\   |    \\   |_____");
}

在這里插入圖片描述

  1. 可以配合EasyX圖形庫(kù),做一個(gè)圖形化的貪吃蛇游戲
  2. 可以加入再來(lái)一局的功能
  3. 可以加入排行榜,計(jì)分等,配合寫入讀取文件
  4. 可以加入音效,配合Windows API的Beep函數(shù),不過(guò)會(huì)有阻塞
  5. 加入雙人模式,可以使用WSAD控制第二條蛇
  6. 等等等等還有好多啦,大家可以自行探索

寄語(yǔ)

感謝每一位看到這里的自己🌹🌹🌹
分享一句話:
我將玫瑰藏于身后,風(fēng)起花落,從此鮮花贈(zèng)自己,縱馬踏花向自由

源碼

test.c

#include "snake.h"int main()
{// 使用當(dāng)前的時(shí)間作為種子值srand(time(NULL));pSnake ps = (pSnake)malloc(sizeof(Snake));SnakeInit(ps);GameStart(ps); // 游戲初始化GameRun(ps); // 游戲運(yùn)行GameEnd(ps); // 游戲結(jié)束return 0;
}

snake.h

#pragma once#include <Windows.h>
#include <locale.h>
#include <wchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>// 方向
typedef enum Direction
{UP = 1,     // 上DOWN,       // 下LEFT,       // 左RIGHT       // 右} Direction;// 游戲狀態(tài)
typedef enum GameStatus
{OK = 0,         //運(yùn)行中KILL_BY_WALL,   //撞墻KILL_BY_SELF,   //撞自己PAUSE,          //暫停ESC             //退出
} GameStatus;// 蛇節(jié)點(diǎn)
// struct SnakeNode 取別名 SnakeNode
// struct SnakeNode * 取別名 pSnakeNode
typedef struct SnakeNode
{int x;int y;struct SnakeNode *next;
} SnakeNode, * pSnakeNode;// 貪吃蛇游戲
typedef struct Snake
{pSnakeNode _pSnake;     // 蛇頭pSnakeNode _pFood;      // 食物int _foodScore;         // 單個(gè)食物分?jǐn)?shù)int _totalScore;        // 總分?jǐn)?shù)int _sleepTime;         // 睡眠時(shí)間 -- 控制速度Direction _dir;         // 方向GameStatus _status;     // 游戲狀態(tài)
} Snake, * pSnake;void GameStart(pSnake ps);
void SnakeInit(pSnake ps);
void GameRun(pSnake ps);
void GameEnd(pSnake ps);

snake.c

#include "snake.h"
#include <stdio.h>
#include <stdlib.h>#define KEY_PRESS(VK) (GetAsyncKeyState(VK) & 0x01 ? 1 : 0)
#define WALL L'□'
#define BODY L'●'
#define FOOD L'★'
#define POS_X 6
#define POS_Y 6// ↑↓←→●□★ // 貪吃蛇游戲結(jié)構(gòu)體初始化
void SnakeInit(pSnake ps)
{ps->_pSnake = NULL;ps->_pFood = NULL;ps->_foodScore = 10;ps->_totalScore = 0;ps->_status = OK;ps->_dir = RIGHT;ps->_sleepTime = 200;
}// 設(shè)置控制臺(tái)窗口大小和名稱
void CmdInit(void)
{// 設(shè)置控制臺(tái)窗口大小 100列 30行system("mode con cols=100 lines=30");// 設(shè)置控制臺(tái)名稱system("title snake");
}// 光標(biāo)隱藏
void CursorHide(void)
{// 獲取句柄HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);// 光標(biāo)信息CONSOLE_CURSOR_INFO cursor_info = {0};// 獲取光標(biāo)信息GetConsoleCursorInfo(handle, &cursor_info);// 設(shè)置光標(biāo)屬性cursor_info.dwSize = 10;cursor_info.bVisible = 0;SetConsoleCursorInfo(handle, &cursor_info);
}// 設(shè)置光標(biāo)位置
void SetPos(int x, int y)
{// 獲取句柄HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);// 設(shè)置光標(biāo)位置COORD pos = {x, y};SetConsoleCursorPosition(handle, pos);
}void PrintSnake(void)
{SetPos(45, 10);wprintf(L"歡迎來(lái)到貪吃蛇游戲");
}// 打印歡迎信息
void WelcomeToGame(void)
{PrintSnake();// 暫停SetPos(45, 20);system("pause");
}// 游戲介紹
void GameIntroduction(void)
{// 清屏system("cls");SetPos(45, 10);wprintf(L"游戲介紹:");SetPos(45, 12);wprintf(L"1. 使用↑,↓,←,→控制蛇的移動(dòng)");SetPos(45, 14);wprintf(L"2. 吃到食物蛇的長(zhǎng)度加1");SetPos(45, 16);wprintf(L"2. F3加速, F4減速");SetPos(45, 18);wprintf(L"3. 空格暫停");SetPos(45, 20);wprintf(L"4. Esc退出游戲");// 暫停SetPos(45, 22);system("pause");
}// 地圖繪制 
// 上(0,0) - (56,0)
// 下(0,26) - (56,26)
// 左(0,0) - (0,26)
// 右(56,0) - (56,26)
// 注意打印的是寬字符 占兩個(gè)x坐標(biāo) 因此左右打印的時(shí)候要每打印一個(gè)x坐標(biāo)加2
void MapDraw(void)
{   // 清屏system("cls");// 上墻for (int i = 0; i < 57; i+=2){SetPos(i, 0);wprintf(L"%c", WALL);}// 下墻for (int i = 0; i < 57; i+=2){SetPos(i, 26);wprintf(L"%c", WALL);}// 最上面和最下面已經(jīng)打印過(guò)了// 左墻for (int i = 1; i < 26; i++){SetPos(0, i);wprintf(L"%c", WALL);}// 右墻for (int i = 1; i < 26; i++){SetPos(56, i);wprintf(L"%c", WALL);} 
}// 打印靜態(tài)幫助信息
// 單個(gè)食物分?jǐn)?shù),總分?jǐn)?shù)的名稱
// 操作說(shuō)明
void PrintStaticHelp(void)
{SetPos(70, 5);wprintf(L"單個(gè)食物分?jǐn)?shù): 10");SetPos(70, 7);wprintf(L"總分?jǐn)?shù):        0");SetPos(70, 9);wprintf(L"操作說(shuō)明:");SetPos(70, 11);wprintf(L"↑ : 上移");SetPos(70, 13);wprintf(L"↓ : 下移");SetPos(70, 15);wprintf(L"← : 左移");SetPos(70, 17);wprintf(L"→ : 右移");SetPos(70, 19);wprintf(L"F3: 加速");SetPos(70, 21);wprintf(L"F4: 減速");SetPos(70, 23);wprintf(L"空格: 暫停");SetPos(70, 25);wprintf(L"Esc: 退出");
}// 蛇初始化
// 在地圖的(6,6)位置初始化蛇
// 蛇的長(zhǎng)度為5
// 使用頭插法
void InitSnake(pSnake ps)
{pSnakeNode cur = NULL;int i = 0;// 創(chuàng)建蛇身節(jié)點(diǎn) 并初始化坐標(biāo)    for (i = 0; i < 5; i++){pSnakeNode node = (pSnakeNode)malloc(sizeof(SnakeNode));if (node == NULL){perror("pSnakeNode malloc failed");exit(1);}node->x = POS_X + i * 2;node->y = POS_Y;node->next = NULL;// 頭插法if (ps->_pSnake == NULL){ps->_pSnake = node;}else{node->next = ps->_pSnake;ps->_pSnake = node;}}// 打印蛇cur = ps->_pSnake;while (cur != NULL){SetPos(cur->x, cur->y);wprintf(L"%c", BODY);cur = cur->next;}
}// 創(chuàng)建食物節(jié)點(diǎn)
void CreatFood(pSnake ps)
{// 申請(qǐng)節(jié)點(diǎn)空間pSnakeNode foodnode = (pSnakeNode)malloc(sizeof(SnakeNode));if (foodnode == NULL){perror("foodnode malloc failed");exit(1);}foodnode->next = NULL;// 隨機(jī)生成食物坐標(biāo) 在墻范圍內(nèi)但是不能在蛇身上 而且x坐標(biāo)是偶數(shù)while (1){int x = rand() % 54 + 2;int y = rand() % 24 + 2;pSnakeNode cur = ps->_pSnake;while (cur != NULL){if (cur->x == x && cur->y == y || x % 2 != 0){break;}cur = cur->next;}if (cur == NULL){foodnode->x = x;foodnode->y = y;break;}}// 打印食物SetPos(foodnode->x, foodnode->y);wprintf(L"%c", FOOD);ps->_pFood = foodnode;
}// 撞墻檢測(cè)
// 撞墻返回1 否則返回0
int IsKillByWall(pSnake ps)
{if (ps->_pSnake->x == 0 || ps->_pSnake->x == 56 || ps->_pSnake->y == 0 || ps->_pSnake->y == 26){return 1;}return 0;
}// 撞自己檢測(cè)
// 撞自己返回1 否則返回0
int IsKillBySelf(pSnake ps)
{pSnakeNode cur = ps->_pSnake->next;// 從第二個(gè)節(jié)點(diǎn)開(kāi)始遍歷 并判斷是否和蛇頭坐標(biāo)相同while (cur != NULL){if (cur->x == ps->_pSnake->x && cur->y == ps->_pSnake->y){return 1;}cur = cur->next;}return 0;
}// 吃掉食物后的處理
void EatFood(pSnake ps)
{// 加分ps->_totalScore += ps->_foodScore;// 打印總分?jǐn)?shù)SetPos(82, 7);printf("%4d", ps->_totalScore);// 是 頭插食物節(jié)點(diǎn) 創(chuàng)建新節(jié)點(diǎn)ps->_pFood->next = ps->_pSnake;ps->_pSnake = ps->_pFood;// 打印新蛇頭SetPos(ps->_pSnake->x, ps->_pSnake->y);wprintf(L"%c", BODY);// 創(chuàng)建新食物CreatFood(ps);
}// 沒(méi)有遲到食物的處理
void NotEatFood(pSnake ps, int x, int y)
{// 創(chuàng)建新節(jié)點(diǎn)并頭插pSnakeNode node = (pSnakeNode)malloc(sizeof(SnakeNode));if (node == NULL){perror("node malloc failed");exit(1);}node->x = x;node->y = y;node->next = ps->_pSnake;ps->_pSnake = node;// 打印新蛇 順便刪除尾節(jié)點(diǎn) 釋放空間 打印空格pSnakeNode cur = ps->_pSnake;while (cur->next->next != NULL){SetPos(cur->x, cur->y);wprintf(L"%c", BODY);cur = cur->next;}SetPos(cur->x, cur->y);wprintf(L"%c", BODY);SetPos(cur->next->x, cur->next->y);wprintf(L"  ");free(cur->next);cur->next = NULL;
}// 蛇的移動(dòng) -- 主游戲程序 在此循環(huán)
// 吃到食物 創(chuàng)建新的食物節(jié)點(diǎn)
// 沒(méi)有吃到食物頭插新節(jié)點(diǎn)刪除尾節(jié)點(diǎn)并釋放空間
// 減速睡眠時(shí)間-30 睡眠時(shí)間最少減4次 單個(gè)食物分?jǐn)?shù)加2
// 加速睡眠時(shí)間+30 睡眠時(shí)間最多加4次 單個(gè)食物分?jǐn)?shù)減2
void SnakeMove(pSnake ps)
{int x = 0;int y = 0;again:while (ps->_status == OK){// 按鍵檢測(cè)if (KEY_PRESS(VK_UP) && ps->_dir != DOWN){ps->_dir = UP;}else if (KEY_PRESS(VK_DOWN) && ps->_dir != UP){ps->_dir = DOWN;}else if (KEY_PRESS(VK_LEFT) && ps->_dir != RIGHT){ps->_dir = LEFT;}else if (KEY_PRESS(VK_RIGHT) && ps->_dir != LEFT){ps->_dir = RIGHT;}else if (KEY_PRESS(VK_SPACE)){ps->_status = PAUSE;}else if (KEY_PRESS(VK_F3) && ps->_foodScore < 20){ps->_sleepTime -= 30;ps->_foodScore += 2;// 更改單個(gè)食物分?jǐn)?shù)SetPos(84, 5);printf("%2d", ps->_foodScore);}else if (KEY_PRESS(VK_F4) && ps->_foodScore > 2){ps->_sleepTime += 30;ps->_foodScore -= 2;// 更改單個(gè)食物分?jǐn)?shù)SetPos(84, 5);printf("%2d", ps->_foodScore);}else if (KEY_PRESS(VK_ESCAPE)){ps->_status = ESC;}// 設(shè)置下一個(gè)節(jié)點(diǎn)的x,y坐標(biāo)switch (ps->_dir){case UP:x = ps->_pSnake->x;y = ps->_pSnake->y - 1;break;case DOWN:x = ps->_pSnake->x;y = ps->_pSnake->y + 1;break;case LEFT:x = ps->_pSnake->x - 2;y = ps->_pSnake->y;break;case RIGHT:x = ps->_pSnake->x + 2;y = ps->_pSnake->y;break;}// 判斷是否吃到食物if (ps->_pFood->x == x && ps->_pFood->y == y){EatFood(ps);}else {NotEatFood(ps, x, y);}// 撞墻檢測(cè)if (IsKillByWall(ps)){ps->_status = KILL_BY_WALL;}// 撞自己檢測(cè)if (IsKillBySelf(ps)){ps->_status = KILL_BY_SELF;}Sleep(ps->_sleepTime);}while (ps->_status == PAUSE){if (KEY_PRESS(VK_SPACE)){ps->_status = OK;}goto again;}// 撞墻打印信息if (IsKillByWall(ps)){SetPos(45, 10);wprintf(L"墻墻被你撞西了,110把你抓走了");}// 撞自己打印信息if (IsKillBySelf(ps)){SetPos(45, 10);wprintf(L"自己撞自己了,120把你帶走了");}return;
}// 游戲前的初始化
void GameStart(pSnake ps)
{// 初始化控制臺(tái)CmdInit();CursorHide();// 本地化配置setlocale(LC_ALL, "");WelcomeToGame();GameIntroduction();MapDraw();InitSnake(ps);CreatFood(ps);
}// 游戲運(yùn)行
void GameRun(pSnake ps)
{PrintStaticHelp();SnakeMove(ps);SetPos(45, 28);system("pause");
}// 善后 釋放蛇節(jié)點(diǎn) 以及食物節(jié)點(diǎn)
void GameEnd(pSnake ps)
{// 釋放蛇節(jié)點(diǎn)pSnakeNode cur = NULL;while (ps->_pSnake != NULL){cur = ps->_pSnake;ps->_pSnake = ps->_pSnake->next;free(cur);}// 釋放食物節(jié)點(diǎn)free(ps->_pFood);
}
http://aloenet.com.cn/news/48061.html

相關(guān)文章:

  • 網(wǎng)站建設(shè)通路品牌網(wǎng)站建設(shè)
  • 如何查一個(gè)網(wǎng)站有沒(méi)有做外鏈鄭州網(wǎng)站制作選擇樂(lè)云seo
  • 代刷網(wǎng)站系統(tǒng)怎么做天貓關(guān)鍵詞排名怎么控制
  • 做網(wǎng)站需要學(xué)那幾個(gè)軟件百度網(wǎng)址大全設(shè)為主頁(yè)
  • 微網(wǎng)站建設(shè)高端網(wǎng)站定制排名seo公司哪家好
  • 網(wǎng)站側(cè)邊欄代碼企業(yè)培訓(xùn)考試平臺(tái)官網(wǎng)
  • 有實(shí)力自適應(yīng)網(wǎng)站建設(shè)哪家好視頻號(hào)推廣
  • 網(wǎng)站開(kāi)發(fā)技術(shù)的現(xiàn)狀及發(fā)展趨勢(shì)今日十大熱點(diǎn)新聞
  • 做字的網(wǎng)站關(guān)鍵詞優(yōu)化公司網(wǎng)站
  • 做網(wǎng)站百度網(wǎng)盤網(wǎng)頁(yè)版登錄入口
  • 漳州最專業(yè)的網(wǎng)站建設(shè)公司南寧百度首頁(yè)優(yōu)化
  • seo優(yōu)化操作淘寶怎么優(yōu)化關(guān)鍵詞步驟
  • 網(wǎng)站設(shè)計(jì)用什么軟件實(shí)現(xiàn)營(yíng)銷渠道有哪幾種
  • 做品管圈網(wǎng)站企業(yè)seo培訓(xùn)
  • 怎么在網(wǎng)站中做彈窗廣告百度官方官網(wǎng)
  • 做網(wǎng)站需要ui設(shè)計(jì)嗎淘寶新店怎么快速做起來(lái)
  • 東莞大嶺山有什么好玩的地方seo網(wǎng)站關(guān)鍵詞優(yōu)化軟件
  • 湖北人工智能建站系統(tǒng)軟件百度一下免費(fèi)下載
  • 知名網(wǎng)站制作企業(yè)品牌宣傳策劃公司
  • 哪個(gè)網(wǎng)站做國(guó)際生意性價(jià)比高seo的排名優(yōu)化
  • 連云港網(wǎng)站建設(shè)案例網(wǎng)店運(yùn)營(yíng)教學(xué)
  • 網(wǎng)站的黏度百度筆記排名優(yōu)化
  • 長(zhǎng)壽網(wǎng)站建設(shè)網(wǎng)站優(yōu)化排名公司
  • 什么是網(wǎng)絡(luò)營(yíng)銷型網(wǎng)站網(wǎng)絡(luò)營(yíng)銷和傳統(tǒng)營(yíng)銷的關(guān)系
  • 用自己照片做衣服 杯子的是哪個(gè)網(wǎng)站aso排名
  • wordpress設(shè)定主頁(yè)紹興seo
  • 手機(jī)微官網(wǎng)和pc端網(wǎng)站怎么做成功的品牌推廣案例分析
  • 武漢教育網(wǎng)站建設(shè)公司排名營(yíng)銷推廣策劃方案范文
  • app導(dǎo)航網(wǎng)站建設(shè)多少錢seo含義
  • 廣州化妝品網(wǎng)站制作山西網(wǎng)站seo