1. 原理解釋
先來看下頭文件中包含哪些東西,包括容器deque、vector,以及之前介紹過的Point類,Point聲明的對象,主要就是負責控制檯上點信息的打印和清除,後面的具體實現部分中也會使用其Print()和Clear()成員函數,沒看過的小夥伴,可以先去瀏覽下面這個地址:
https://blog.csdn.net/AnDiXL/article/details/105148754
開頭動畫整體分爲兩大部分,第一部分是蛇移動的動畫,第二部分是SNAKE文字進入的動畫,動畫示意圖如下:
其中第一部分用deque實現,分爲如下三個階段:
1). deque是一個雙端隊列,用它存儲點的對象,這些點組成完整的蛇身,用一個for-loop將其中的點依次打印,每打印一個點停頓一會兒,實現動態效果,全部打印完之後,蛇的整個身體出現,接下來進入第二階段;
2). 蛇開始從左邊界移動至右邊界,這部分蛇每次移動都要計算其下一個點的座標,然後將其打印出來,同時還要將蛇尾即deque的首元素去掉,並清除對應座標的圖像信息;
3). 第三階段,從deque的隊尾開始依次擦除圖像信息即可完成蛇消失的過程。
第二部分SNAKE文字的移動過程與第一部分類似,但最後只需SNAKE停到屏幕中央,因此不需要同時對首尾對象進行操作,選用vector存儲其中的點。
其頭文件代碼如下,聲明對象時就在初始化列表中完成了點的存儲:
#ifndef STRATINTERFACE_H
#define STARTINTERFACE_H
#include <deque>
#include <vector>
#include "point.h"
class StartInterface
{
public:
StartInterface() : speed(35) {
startsnake.emplace_back(Point(0,14));//蛇的形狀
startsnake.emplace_back(Point(1,14));
startsnake.emplace_back(Point(2,15));
startsnake.emplace_back(Point(3,16));
startsnake.emplace_back(Point(4,17));
startsnake.emplace_back(Point(5,18));
startsnake.emplace_back(Point(6,17));
startsnake.emplace_back(Point(7,16));
startsnake.emplace_back(Point(8,15));
startsnake.emplace_back(Point(9,14));
//下面是SNAKE五個字母的實現
textsnake.emplace_back(Point(-26, 14));//S
textsnake.emplace_back(Point(-25, 14));
textsnake.emplace_back(Point(-27, 15));
textsnake.emplace_back(Point(-26, 16));
textsnake.emplace_back(Point(-25, 17));
textsnake.emplace_back(Point(-27, 18));
textsnake.emplace_back(Point(-26, 18));
textsnake.emplace_back(Point(-23, 14));//N
textsnake.emplace_back(Point(-23, 15));
textsnake.emplace_back(Point(-23, 16));
textsnake.emplace_back(Point(-23, 17));
textsnake.emplace_back(Point(-23, 18));
textsnake.emplace_back(Point(-22, 15));
textsnake.emplace_back(Point(-21, 16));
textsnake.emplace_back(Point(-20, 17));
textsnake.emplace_back(Point(-19, 14));
textsnake.emplace_back(Point(-19, 15));
textsnake.emplace_back(Point(-19, 16));
textsnake.emplace_back(Point(-19, 17));
textsnake.emplace_back(Point(-19, 18));
textsnake.emplace_back(Point(-17, 18));//A
textsnake.emplace_back(Point(-16, 17));
textsnake.emplace_back(Point(-15, 16));
textsnake.emplace_back(Point(-14, 15));
textsnake.emplace_back(Point(-14, 16));
textsnake.emplace_back(Point(-13, 14));
textsnake.emplace_back(Point(-13, 16));
textsnake.emplace_back(Point(-12, 15));
textsnake.emplace_back(Point(-12, 16));
textsnake.emplace_back(Point(-11, 16));
textsnake.emplace_back(Point(-10, 17));
textsnake.emplace_back(Point(-9, 18));
textsnake.emplace_back(Point(-7, 14));//K
textsnake.emplace_back(Point(-7, 15));
textsnake.emplace_back(Point(-7, 16));
textsnake.emplace_back(Point(-7, 17));
textsnake.emplace_back(Point(-7, 18));
textsnake.emplace_back(Point(-6, 16));
textsnake.emplace_back(Point(-5, 15));
textsnake.emplace_back(Point(-5, 17));
textsnake.emplace_back(Point(-4, 14));
textsnake.emplace_back(Point(-4, 18));
textsnake.emplace_back(Point(-2, 14));//E
textsnake.emplace_back(Point(-2, 15));
textsnake.emplace_back(Point(-2, 16));
textsnake.emplace_back(Point(-2, 17));
textsnake.emplace_back(Point(-2, 18));
textsnake.emplace_back(Point(-1, 14));
textsnake.emplace_back(Point(-1, 16));
textsnake.emplace_back(Point(-1, 18));
textsnake.emplace_back(Point(0, 14));
textsnake.emplace_back(Point(0, 16));
textsnake.emplace_back(Point(0, 18));
}
void PrintFirst();
void PrintSecond();
void PrintThird();
void PrintText();
void ClearText();
void Action();
private:
std::deque<Point> startsnake;//開始動畫中的蛇
std::vector<Point> textsnake;//開始動畫中的文字
int speed;//動畫的速度
};
#endif // STRATINTERFACE_H
2. 具體實現
第一部分:
1). 一個for循環遍歷打印,每打印一個點停頓一下,這裏使用包含在window.h中的Sleep()實現停頓,具體代碼如下:
void StartInterface::PrintFirst()//蛇從左邊出現到完全出現的過程
{
for (auto& point : startsnake)
{
point.Print();
Sleep(speed);
}
}
2). 由於控制檯大小設置的寬度爲40,整個蛇身出現佔據了0~9列,因此第二階段移動是從第10列移動至39列,每次移動只需要計算出蛇頭要前進的下一個位置即可,而由於是從左向右移動,因此列座標 i 的增加是線性的,只需要計算下個位置的橫座標 j 即可,具體代碼如下:
void StartInterface::PrintSecond()//蛇從左向右移動的過程
{
for (int i = 10; i != 40; ++i) //蛇頭需要從10移動到40
{
/*計算蛇頭的下一個位置,並將其壓入startsnake中,繪製出來,將蛇尾去掉*/
int j = ( ((i-2)%8) < 4 )?( 15 + (i-2)%8 ) : ( 21 - (i-2)%8 );
startsnake.emplace_back( Point(i, j) );
startsnake.back().Print();
startsnake.front().Clear();
startsnake.pop_front();
Sleep(speed);
}
}
3). 用於實現第一部分的第三階段,以及第二部分的文字平移階段,即在蛇身消失的同時,SNAKE文字開始向右平移,具體代碼如下:
void StartInterface::PrintThird()//蛇從接觸右邊到消失的過程
{
while ( !startsnake.empty() || textsnake.back().GetX() < 33 ) //當蛇還沒消失或文字沒移動到指定位置
{
if ( !startsnake.empty() ) //如果蛇還沒消失,繼續移動
{
startsnake.front().Clear();
startsnake.pop_front();
}
ClearText();//清除已有文字
PrintText();//繪製更新位置後的文字
Sleep(speed);
}
}
第二部分:
SNAKE的平移過程即上段代碼中的 ClearText() 和 PrintText(),需要注意的是,GetX() 函數是獲取點的縱座標而不是橫座標,具體實現如下:
void StartInterface::PrintText()
{
for (auto& point : textsnake)
{
if (point.GetX() >= 0)
point.Print();
}
}
void StartInterface::ClearText()
{
for (auto& point : textsnake) //清除的同時將文字整體向右移動一格
{
if (point.GetX() >= 0)
point.Clear();
point.ChangePosition(point.GetX() + 1, point.GetY());
}
}
因此,整個打印流程,只需創建一個StartInterface對象後,執行下面的 Action() 函數即可完成:
void StartInterface::Action()
{
PrintFirst();
PrintSecond();
PrintThird();
}
最後,附上原作者的代碼鏈接:
https://github.com/silence1772/GreedySnake/blob/master/startinterface.cpp
https://github.com/silence1772/GreedySnake/blob/master/startinterface.h