花了點時間,用C語言實現了一個貪喫蛇小遊戲,開發工具是VS2010。
以下是代碼代碼鏈接,我放在了github上,需要的可自行下載:
https://github.com/zhengzebin525/basic_algorithm
本文會着重於思路講解,最後會放上完整代碼。
先來看看運行後的效果圖:
總體來說,貪喫蛇小遊戲大體需要實現以下幾個最主要的函數功能:
1、遊戲區域展示;
2、運用鏈表操作初始化小蛇
3、在遊戲區域內隨機出現方塊食物
4、利用API函數獲取用戶的按鍵狀態
5、根據按鍵狀態控制小蛇的不同移動方向,小蛇移動中一旦喫到食物,分數增加;
6、遊戲的錯誤函數,比如小蛇喫到自己,撞牆等;
那麼接下來,就是對以上六大部分進行詳細說明。
第一部分:遊戲區域展示
//遊戲區域展示
void Area_Show()
{
int num;
for(num=0;num<70;num+=2) //如果改成num++的話,上下 邊框會出現錯誤
{
Goto_Coord(num,0);
printf("■");
Goto_Coord(num,26);
printf("■");
}
for(num=0;num<=26;num++)
{
Goto_Coord(0,num);
printf("■");
Goto_Coord(70,num);
printf("■");
}
}
這是個比較簡單的功能,利用方塊“■”來劃定遊戲的區域,而Goto_Coord(num,0)用於配置文本顯示的座標,這是個自定義函數,用到了GetStdHandle()和SetConsoleCursorPosition()這兩個window自帶的API函數,前一個用來獲取標準輸入/標準輸出/標準錯誤中句柄,後一個用來定位要顯示的座標。
句柄分別有:標準輸入:STD_INPUT_HANDLE、標準輸出:STD_OUTPUT_HANDLE、標準錯誤:STD_ERROR_HANDLE。
很明顯,因爲是要在屏幕上顯示,所以是獲取標準輸出STD_OUTPUT_HANDLE的句柄。
//定位輸出座標 void Goto_Coord(int x,int y) { COORD pos; pos.X = x; pos.Y = y; HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleCursorPosition(handle,pos); } |
第二部分:運用鏈表操作初始化小蛇
void Snake_Init()
{
snake *newnode = (snake *)malloc(sizeof(struct node));
newnode->x = 10; //賦予初始座標
newnode->y = 10; //賦予初始座標
newnode->next = NULL;
for(int i=1;i<snake_len_init;i++)
{
head = (snake*)malloc(sizeof(struct node));
head->x = 10 + 2*i;
head->y = 10;
head->next = newnode;
newnode = head;
}
while(newnode!= NULL)
{
Goto_Coord(newnode->x,newnode->y);
printf("■");
newnode = newnode->next;
}
}
初始化小蛇是用若干個方塊組成的,所以需要存儲每個方塊的數據比如內存信息,顯示座標,並用鏈表將方塊聯繫起來,最後一併打印出來,從而呈現方塊小蛇的樣子。
爲了跟遊戲區域對應,因此橫座標是偶數單位增加,縱座標是奇數單位增加。
因此肯定會有一個結構體,來存儲小蛇每個方塊的數據信息:
typedef struct node { int x; //橫座標 int y; //縱座標 int color; //方塊顏色 struct node *next; }snake; |
第三部分:在遊戲區域內隨機出現方塊食物
int Food_Show()
{
int color_num;
snake *temp1 = NULL,*temp2 = NULL;
food1 = (snake *)malloc(sizeof(struct node));
food2 = (snake *)malloc(sizeof(struct node));
if(food1 == NULL || food2 == NULL)
{
return FALSE;
}
srand((unsigned)time(NULL)); //產生隨機數必須要的種子
//產生第一個食物
while((food1->x%2)!= 0 || food1->x == 70) //食物x座標不是偶數,跟邊界沒有對齊
{
food1->x = rand()%70 + 2;
}
do
{
food1->y = rand()%26 + 1;
}
while(food1->y == 26);
temp1 = head;
while(temp1 != NULL)
{
if(food1->x == temp1->x && food1->y == temp1->y)
{
free(food1);
Food_Show();
}
temp1 = temp1->next;
}
Goto_Coord(food1->x,food1->y);
color_num = rand()%6;
food1->color = color_table[color_num];
color(food1->color);
printf("■");
//產生第二個食物
while((food2->x%2)!= 0 || food2->x == 70) //食物x座標不是偶數,跟邊界沒有對齊
{
food2->x = rand()%70 + 2;
}
do
{
food2->y = rand()%26 + 1;
}
while(food2->y == 26);
temp2 = head;
while(temp2 != NULL)
{
if(food2->x == temp2->x && food2->y == temp2->y)
{
free(food2);
Food_Show();
}
temp2 = temp2->next;
}
Goto_Coord(food2->x,food2->y);
color_num = rand()%6;
food2->color = color_table[color_num];
color(food2->color);
printf("■");
}
每個食物的出現,都是一小塊內存,因此malloc()申請內存是必須的步驟,此處需要注意的是隨機的座標必須位於遊戲區域座標內,並且不能跟區域邊界方塊重疊,也不能跟小蛇身上的每一塊“蛇身”方塊重疊,而且我給每個食物隨機分配了不同的顏色,使得小蛇喫到不同的顏色就會有不一樣的分數加成。
第四部分:利用API函數獲取用戶的按鍵狀態
//遊戲控制
void Game_Control()
{
Goto_Coord(80,18);
color(0x07);
printf("當前獲得分數:%d\n",total_score);
//判斷按下了哪一個按鍵,並且避免上一次的按鍵狀態與其方向相反
if(GetAsyncKeyState(VK_UP) && Key_Status != Key_Down)
Key_Status = Key_Up;
else if(GetAsyncKeyState(VK_DOWN) && Key_Status != Key_Up)
Key_Status = Key_Down;
else if(GetAsyncKeyState(VK_LEFT) && Key_Status != Key_Right)
Key_Status = Key_Left;
else if(GetAsyncKeyState(VK_RIGHT) && Key_Status != Key_Left)
Key_Status = Key_Right;
else if(GetAsyncKeyState(VK_SPACE))
{
Save_Status = Key_Status; //當按下了暫停健的時候,暫停前的運動方向狀態需要保存起來
Key_Status = Key_Space;
}
else if(GetAsyncKeyState(VK_ESCAPE ))
Key_Status = Key_Esc;
}
這一部分點主要在GetAsyncKeyState(int vkey)這個window的API函數,用於判斷某個物理按鍵是處於按下狀態(非0)還是沒按下狀態(0),而vkey 是256個虛擬按鍵中的一個,返回值是一個非0的數值。
常用的大概是以下幾個:
VK_SHIFT Shift鍵 VK_LSHIFT 左Shift鍵 VK_RSHIFT 右Shift鍵 VK_CONTROL Ctrl鍵 VK_LCONTROL 左Ctrl鍵 VK_RCONTROL 右Ctril鍵 VK_MENU Alt鍵 VK_LMENU 左Alt鍵 VK_RMENU 右Alt鍵 VK_LBUTTON 鼠標左鍵 VK_RBUTTON 鼠標右鍵 VK_ESCAPE ESC鍵 VK_RETURN回車鍵 VK_TABTAB鍵 VK_SPACE空格鍵 VK_UP↑鍵 VK_DOWN↓鍵 VK_LEFT←鍵 VK_RIGHT→鍵 |
比如if(GetAsyncKeyState(VK_UP) && Key_Status != Key_Down) ,前一個條件判斷用戶是否按下了↑鍵,而後一個條件則判斷上一次的按鍵狀態是不是↓鍵,這麼做是爲了避免小蛇由向上移動突變爲向下移動的情況出現。
第五部分:根據按鍵狀態控制小蛇的不同移動方向,小蛇移動中一旦喫到食物,分數增加
//蛇的移動
void Snake_Move()
{
snake *nextnode = (snake *)malloc(sizeof(struct node));
snake *temp = NULL;
if(Key_Status == Key_Up)
{
nextnode->x = head->x;
nextnode->y = (head->y) - 1;
//Goto_Coord(80,28);
//printf("蛇頭head的座標 %d,%d",head->x,head->y);
if((nextnode->x == food1->x && nextnode->y == food1->y) ||
(nextnode->x == food2->x && nextnode->y == food2->y)) //移動過程中喫到了食物
{
nextnode->next = head;
head = nextnode;
temp = head;
while(temp != NULL)
{
Goto_Coord(temp->x,temp->y);
printf("■");
temp = temp->next;
}
Food_Score(food1->color);
free(food1);
Food_Show();
}
else //移動過程沒有遇到食物
{
nextnode->next = head;
head = nextnode;
temp = head;
while(temp->next->next != NULL)
{
Goto_Coord(temp->x,temp->y);
printf("■");
temp = temp->next;
}
Goto_Coord(temp->next->x,temp->next->y);
printf(" ");
free(temp->next);
temp->next = NULL;
}
}
此處只截取了小蛇向上移動的狀態代碼,小蛇一旦運動起來,第一步就需要配置下一步要顯示的方塊的座標,然後判斷此方塊有沒有與食物的座標重疊,重疊了就說明喫到了食物,獲得分數加成;沒重疊的話,就把新顯示的方塊作爲新的蛇頭添加進小蛇鏈表,蛇尾最後的方塊從鏈表上釋放消除掉,然後重新打印小蛇鏈表,這樣就出現了小蛇向上移動一步的現象。
利用Food_Score(food1->color)函數進行分數加成,這是個自定義函數,根據喫到的食物方塊顏色進行判斷,不同的顏色獲取不同的分數加成。
void Food_Score(int food_color) { switch (food_color) { case red: total_score += 50;break; case orange: total_score += 40;break; case green: total_score += 30;break; case blue: total_score += 20;break; case purple: total_score += 10;break; } } |
第六部分:遊戲的錯誤函數,比如小蛇喫到自己,撞牆等
//穿牆錯誤函數
void Through_Walls()
{
if(head->x == 0 || head->x == 70 ||
head->y == 0 || head->y == 26)
{
system("cls");
Goto_Coord(30,10);
printf("抱歉,事故判斷,小蛇死亡撞牆\n");
Goto_Coord(30,12);
color(0x07);
printf("當前獲得分數:%d\n",total_score);
exit(0);
}
}
只要判斷蛇頭方塊的座標與遊戲區域邊界方塊座標重疊,那就是撞牆的,撞得死死的。
//咬到自己錯誤函數
void Bit_Oneself()
{
snake *temp;
temp = head->next;
while(temp!=NULL)
{
if(head->x == temp->x && head->y == temp->y)
{
system("cls");
Goto_Coord(30,10);
printf("抱歉,事故判斷,小蛇咬死了自己\n");
Goto_Coord(30,12);
color(0x07);
printf("當前獲得分數:%d\n",total_score);
exit(0);
}
temp = temp->next;
}
}
只要判斷蛇頭方塊的座標與蛇身任何一個方塊座標重疊,那小蛇就是咬到自己了,咬得死死的。
以下是完整代碼,直接copy能用的。
Gluttonous_Snake.h
#ifndef __GLUTTONOUS_SNAKE_H
#define __GLUTTONOUS_SNAKE_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <time.h>
#define FALSE 0
#define TRUE 1
#define snake_len_init 3 //蛇的初始化長度爲3
#define Key_Up 1 //↑按鍵狀態
#define Key_Down 2 //↓按鍵狀態
#define Key_Left 3 //←按鍵狀態
#define Key_Right 4 //→按鍵狀態
#define Key_Space 5
#define Key_Esc 6
#define red FOREGROUND_RED
#define orange FOREGROUND_RED|FOREGROUND_RED|FOREGROUND_GREEN
#define green FOREGROUND_GREEN
#define blue FOREGROUND_BLUE
#define purple FOREGROUND_RED|FOREGROUND_BLUE
typedef struct node
{
int x;
int y;
int color;
struct node *next;
}snake;
void Goto_Coord(int x,int y);
void color(int color);
void Area_Show();
void Side_Show();
void Snake_Init();
int Food_Show();
void Game_Init();
void Food_Score(int food_color);
void Game_Control();
void Snake_Move();
void Pause();
void Through_Walls();
void Bit_Oneself();
#endif
Gluttonous_Snake.cpp
#include "Gluttonous_Snake.h"
int snake_x,snake_y;
int food_score = 10; //每個食物的分數
int total_score = 0; //獲得的總分數
int highest_score = 0; //最高分
int Key_Status = Key_Right; //蛇一開始是向右行進
int Save_Status;
snake *head;
snake *newnode; //中間變量,放在while(1)中會內存溢出
snake *food1,*food2;
//顏色表
int color_table[6] = {
red, //紅
orange, //橙
green, //綠
blue, //藍
purple //紫
};
//定位輸出座標
void Goto_Coord(int x,int y)
{
COORD pos;
pos.X = x;
pos.Y = y;
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(handle,pos);
}
//設置文本顏色
void color(int color)
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(handle,color);
}
//遊戲區域展示
void Area_Show()
{
int num;
for(num=0;num<70;num+=2) //如果改成num++的話,上下 邊框會出現錯誤
{
Goto_Coord(num,0);
printf("■");
Goto_Coord(num,26);
printf("■");
}
for(num=0;num<=26;num++)
{
Goto_Coord(0,num);
printf("■");
Goto_Coord(70,num);
printf("■");
}
}
//側面說明展示
void Side_Show()
{
Goto_Coord(80,5);
printf("歡迎來到貪喫蛇遊戲\n");
printf("\n");
Goto_Coord(80,7);
printf("↑ ↓ ← → 是上下左右移動\n");
printf("\n");
Goto_Coord(80,9);
printf("規則1:不能穿牆,不能喫自己\n");
printf("\n");
Goto_Coord(80,11);
printf("規則2:ESC退出,SPACE是暫停\n");
printf("\n");
Goto_Coord(80,13);
printf("規則2:喫的食物越多,分數越高\n");
printf("\n");
Goto_Coord(80,15);
printf("規則3:其中會出現喫到了也沒分數的假食物\n");
printf("\n");
}
void Snake_Init()
{
snake *newnode = (snake *)malloc(sizeof(struct node));
newnode->x = 10; //賦予初始座標
newnode->y = 10; //賦予初始座標
newnode->next = NULL;
for(int i=1;i<snake_len_init;i++)
{
head = (snake*)malloc(sizeof(struct node));
head->x = 10 + 2*i;
head->y = 10;
head->next = newnode;
newnode = head;
}
while(newnode!= NULL)
{
Goto_Coord(newnode->x,newnode->y);
printf("■");
newnode = newnode->next;
}
}
int Food_Show()
{
int color_num;
snake *temp1 = NULL,*temp2 = NULL;
food1 = (snake *)malloc(sizeof(struct node));
food2 = (snake *)malloc(sizeof(struct node));
if(food1 == NULL || food2 == NULL)
{
return FALSE;
}
srand((unsigned)time(NULL)); //產生隨機數必須要的種子
//產生第一個食物
while((food1->x%2)!= 0 || food1->x == 70) //食物x座標不是偶數,跟邊界沒有對齊
{
food1->x = rand()%70 + 2;
}
do
{
food1->y = rand()%26 + 1;
}
while(food1->y == 26);
temp1 = head;
while(temp1 != NULL)
{
if(food1->x == temp1->x && food1->y == temp1->y)
{
free(food1);
Food_Show();
}
temp1 = temp1->next;
}
Goto_Coord(food1->x,food1->y);
color_num = rand()%6;
food1->color = color_table[color_num];
color(food1->color);
printf("■");
//Goto_Coord(80,21);
//printf("食物1座標 %d,%d",food1->x,food1->y);
//產生第二個食物
while((food2->x%2)!= 0 || food2->x == 70) //食物x座標不是偶數,跟邊界沒有對齊
{
food2->x = rand()%70 + 2;
}
do
{
food2->y = rand()%26 + 1;
}
while(food2->y == 26);
temp2 = head;
while(temp2 != NULL)
{
if(food2->x == temp2->x && food2->y == temp2->y)
{
free(food2);
Food_Show();
}
temp2 = temp2->next;
}
Goto_Coord(food2->x,food2->y);
color_num = rand()%6;
food2->color = color_table[color_num];
color(food2->color);
printf("■");
//Goto_Coord(80,23);
//printf("食物2座標 %d,%d",food2->x,food2->y);
}
//遊戲界面初始化
void Game_Init()
{
system("title 貪喫蛇");
Goto_Coord(40,10);
printf("歡迎來到貪喫蛇小遊戲\n");
system("pause");
system("cls");
Goto_Coord(40,10);
printf("希望能帶給你良好的遊戲體驗\n");
system("pause");
system("cls");
Area_Show(); //遊戲區域初始化
Side_Show(); //側面說明初始化
Snake_Init(); //蛇初始化
Food_Show(); //產生方塊食物
}
void Food_Score(int food_color)
{
switch (food_color)
{
case red:
total_score += 50;break;
case orange:
total_score += 40;break;
case green:
total_score += 30;break;
case blue:
total_score += 20;break;
case purple:
total_score += 10;break;
}
}
//遊戲控制
void Game_Control()
{
Goto_Coord(80,18);
color(0x07);
printf("當前獲得分數:%d\n",total_score);
//判斷按下了哪一個按鍵,並且避免上一次的按鍵狀態與其方向相反
if(GetAsyncKeyState(VK_UP) && Key_Status != Key_Down)
Key_Status = Key_Up;
else if(GetAsyncKeyState(VK_DOWN) && Key_Status != Key_Up)
Key_Status = Key_Down;
else if(GetAsyncKeyState(VK_LEFT) && Key_Status != Key_Right)
Key_Status = Key_Left;
else if(GetAsyncKeyState(VK_RIGHT) && Key_Status != Key_Left)
Key_Status = Key_Right;
else if(GetAsyncKeyState(VK_SPACE))
{
Save_Status = Key_Status; //當按下了暫停健的時候,暫停前的運動方向狀態需要保存起來
Key_Status = Key_Space;
}
else if(GetAsyncKeyState(VK_ESCAPE ))
Key_Status = Key_Esc;
}
//蛇的移動
void Snake_Move()
{
snake *nextnode = (snake *)malloc(sizeof(struct node));
snake *temp = NULL;
if(Key_Status == Key_Up)
{
nextnode->x = head->x;
nextnode->y = (head->y) - 1;
//Goto_Coord(80,28);
//printf("蛇頭head的座標 %d,%d",head->x,head->y);
if((nextnode->x == food1->x && nextnode->y == food1->y) ||
(nextnode->x == food2->x && nextnode->y == food2->y)) //移動過程中喫到了食物
{
nextnode->next = head;
head = nextnode;
temp = head;
while(temp != NULL)
{
Goto_Coord(temp->x,temp->y);
printf("■");
temp = temp->next;
}
Food_Score(food1->color);
free(food1);
Food_Show();
}
else //移動過程沒有遇到食物
{
nextnode->next = head;
head = nextnode;
temp = head;
while(temp->next->next != NULL)
{
Goto_Coord(temp->x,temp->y);
printf("■");
temp = temp->next;
}
Goto_Coord(temp->next->x,temp->next->y);
printf(" ");
free(temp->next);
temp->next = NULL;
}
}
if(Key_Status == Key_Down)
{
nextnode->x = head->x;
nextnode->y = (head->y) + 1;
//Goto_Coord(80,28);
//printf("蛇頭head的座標 %d,%d",head->x,head->y);
if((nextnode->x == food1->x && nextnode->y == food1->y) ||
(nextnode->x == food2->x && nextnode->y == food2->y)) //移動過程中喫到了食物
{
nextnode->next = head;
head = nextnode;
temp = head;
while(temp != NULL)
{
Goto_Coord(temp->x,temp->y);
printf("■");
temp = temp->next;
}
Food_Score(food1->color);
free(food1);
Food_Show();
}
else //移動過程沒有遇到食物
{
nextnode->next = head;
head = nextnode;
temp = head;
while(temp->next->next != NULL)
{
Goto_Coord(temp->x,temp->y);
printf("■");
temp = temp->next;
}
Goto_Coord(temp->next->x,temp->next->y);
printf(" ");
free(temp->next);
temp->next = NULL;
}
}
if(Key_Status == Key_Left)
{
nextnode->x = (head->x) - 2;
nextnode->y = head->y;
//Goto_Coord(80,28);
//printf("蛇頭head的座標 %d,%d",head->x,head->y);
if((nextnode->x == food1->x && nextnode->y == food1->y) ||
(nextnode->x == food2->x && nextnode->y == food2->y)) //移動過程中喫到了食物
{
nextnode->next = head;
head = nextnode;
temp = head;
while(temp != NULL)
{
Goto_Coord(temp->x,temp->y);
printf("■");
temp = temp->next;
}
Food_Score(food1->color);
free(food1);
Food_Show();
}
else //移動過程沒有遇到食物
{
nextnode->next = head;
head = nextnode;
temp = head;
while(temp->next->next != NULL)
{
Goto_Coord(temp->x,temp->y);
printf("■");
temp = temp->next;
}
Goto_Coord(temp->next->x,temp->next->y);
printf(" ");
free(temp->next);
temp->next = NULL;
}
}
if(Key_Status == Key_Right)
{
nextnode->x = (head->x) + 2;
nextnode->y = head->y;
//Goto_Coord(80,28);
//printf("蛇頭head的座標 %d,%d",head->x,head->y);
if((nextnode->x == food1->x && nextnode->y == food1->y) ||
(nextnode->x == food2->x && nextnode->y == food2->y)) //移動過程中喫到了食物
{
nextnode->next = head;
head = nextnode;
temp = head;
while(temp != NULL)
{
Goto_Coord(temp->x,temp->y);
printf("■");
temp = temp->next;
}
Food_Score(food1->color);
free(food1);
Food_Show();
}
else //移動過程沒有遇到食物
{
nextnode->next = head;
head = nextnode;
temp = head;
while(temp->next->next != NULL)
{
Goto_Coord(temp->x,temp->y);
printf("■");
temp = temp->next;
}
Goto_Coord(temp->next->x,temp->next->y);
printf(" ");
free(temp->next);
temp->next = NULL;
}
}
while(Key_Status == Key_Space) //按空格健暫停
{
Sleep(300);
if(!(GetAsyncKeyState(VK_SPACE)))
{
Pause(); //暫停函數
break;
}
}
if(Key_Status == Key_Esc)
{
system("cls");
Goto_Coord(40,10);
printf("您已結束遊戲!\n");
}
Through_Walls(); //判斷是否撞牆
Bit_Oneself(); //判斷是否咬到了自己
}
void Pause()
{
while(1)
{
system("title 暫停中····");
Sleep(300);
if(GetAsyncKeyState(VK_SPACE)) //第二次按空格的時候,解除暫停狀態
{
Sleep(200);
Key_Status = Save_Status;
break;
}
}
}
//穿牆錯誤函數
void Through_Walls()
{
if(head->x == 0 || head->x == 70 ||
head->y == 0 || head->y == 26)
{
system("cls");
Goto_Coord(30,10);
printf("抱歉,事故判斷,小蛇死亡撞牆\n");
Goto_Coord(30,12);
color(0x07);
printf("當前獲得分數:%d\n",total_score);
exit(0);
}
}
//咬到自己錯誤函數
void Bit_Oneself()
{
snake *temp;
temp = head->next;
while(temp!=NULL)
{
if(head->x == temp->x && head->y == temp->y)
{
system("cls");
Goto_Coord(30,10);
printf("抱歉,事故判斷,小蛇咬死了自己\n");
Goto_Coord(30,12);
color(0x07);
printf("當前獲得分數:%d\n",total_score);
exit(0);
}
temp = temp->next;
}
}
main.cpp
#include "Gluttonous_Snake.h"
int sleep_time = 200;
int main()
{
Game_Init(); //遊戲界面初始化
while(1)
{
system("title 遊戲中····");
Game_Control(); //遊戲控制,判斷按下的是哪一個按鍵
Sleep(sleep_time); //延時200ms
Snake_Move();
}
}
當然,這個小遊戲個人覺得還有很多自己的設想沒有實現,以後會抽出時間來實現它。
總結:1、在初始化過程中,如果有某一參數只初始化依次,該參數直接使用常量,如果使用變量,還需要佔用一定的存儲空間,浪費內存;
2、在鏈表操作中,用最少的步驟完成操作,不要花裏胡哨,鏈表邏輯要清晰,多看幾遍,確保無誤;
3、遞歸函數越少用越好,在一個函數中最好不要超過一個遞歸函數;
4、如果需要在界面上顯示肉眼可見的運動,延時操作必不可少,畢竟系統的運行速度太快了;