<2021SC@SDUSC>
開源游戲引擎 Overload 代碼模塊分析 之 OvTools(三)—— Time
目錄
- 前言
- 分析
- 1、Time 模塊概述
- 2、Clock
- 2.1 Clock.h
- 2.1.1 頭檔案
- 2.1.2 Clock 類
- 2.2 Clock.cpp
- Initialize() 函式
- Update() 函式
- 3、Date
- 3.1 Date.h
- Date 類
- 3.2 Date.cpp
- GetDateAsString() 函式
- 總結
前言
本文已是 Overload 引擎相關的第五篇文章了,也是它的 OvTools 功能模塊的第三篇探究文章,想大致了解 Overload 可前往這篇文章,想看其他相關文章請前往筆者的 Overload 專欄自主選擇,
本篇將探究 OvTools 的第三個小模塊 Time 時間操作,讓我們馬上開始吧!
分析
1、Time 模塊概述
義如其名,作為一個時間模塊,當然負責處理時間,它包括了下列 **Clock(時鐘)**與 **Date(日期)**兩類檔案:


接下來,我們先了解 Clock 類,
2、Clock
2.1 Clock.h
2.1.1 頭檔案
該檔案引入了下列頭檔案:
#include <string>
#include <chrono>
string 檔案不必贅述;chrono 檔案是 C++11 的一個頭檔案,主要依靠 duration(持續時間)、time_point(時間點)等類實作處理時間的功能,該庫是 Clock 類功能的一個核心,
值得一提的是,chrono 包含元素的命名空間不是直接在 std:: 下,而是在 std::chrono:: 下,我們也將在 Clock 類的變數定義中看到,
2.1.2 Clock 類
該類負責處理時間計算,.h 檔案代碼僅包含了各種函式變數的宣告定義:
class Clock
{
public:
/**
* Update the clock
*/
void Update();
/**
* Return the actual framerate (Depend on the delta time)
*/
float GetFramerate();
/**
* Return the actual delta time (Scale applied)
*/
float GetDeltaTime();
/**
* Return the actual delta time (Scale not applied)
*/
float GetDeltaTimeUnscaled();
/**
* Return the time elapsed since clock creation
*/
float GetTimeSinceStart();
/**
* Return the current timescale
*/
float GetTimeScale();
/**
* Multiply the timescale by the given coefficient
* @param p_coeff
*/
void Scale(float p_coeff);
/**
* Define a timescale
* @param p_timescale
*/
void SetTimeScale(float p_timeScale);
private:
void Initialize();
std::chrono::steady_clock::time_point __START_TIME;
std::chrono::steady_clock::time_point __LAST_TIME;
std::chrono::steady_clock::time_point __CURRENT_TIME;
std::chrono::duration<double> __ELAPSED;
bool __INITIALIZED = false;
float __TIME_SCALE = 1.0f;
float __DELTA_TIME = 0.0f;
float __TIME_SINCE_START = 0.0f;
};
代碼的注釋已經大致告知了各函式的作用,筆者不多贅述,
關于該類定義的變數,代碼用到了 std::chrono::steady_clock::time_point :正如上文所述,time_point 是一個類模板,其意義是時間點,該類會設定一個參考的固定時間點,所有使用相同時鐘的 time_point 物件都將參考該固定點,物件通過相對該固定時間點的 duration(持續時間)—— time_point 類內含 duration 類的物件 —— 表示一個時間點,
關于 duration,代碼中的變數 __ELAPSED 定義也用到了它:std::chrono::duration(持續時間),其意思也如其名,表示流逝了的時間;顯然,該變數也會是其他函式的一個重點操作物件,
其他變數與命名的英文意思一致,也無需贅述,
2.2 Clock.cpp
該檔案僅有 Clock.h 頭檔案,主體代碼對上述函式進行了具體定義,其中一部分函式是按要求回傳計算后的值,簡單易懂,在此直接給出代碼:
float OvTools::Time::Clock::GetFramerate()
{
return 1.0f / (__DELTA_TIME);
}
float OvTools::Time::Clock::GetDeltaTime()
{
return __DELTA_TIME * __TIME_SCALE;
}
float OvTools::Time::Clock::GetDeltaTimeUnscaled()
{
return __DELTA_TIME;
}
float OvTools::Time::Clock::GetTimeSinceStart()
{
return __TIME_SINCE_START;
}
float OvTools::Time::Clock::GetTimeScale()
{
return __TIME_SCALE;
}
void OvTools::Time::Clock::Scale(float p_coeff)
{
__TIME_SCALE *= p_coeff;
}
void OvTools::Time::Clock::SetTimeScale(float p_timeScale)
{
__TIME_SCALE = p_timeScale;
}
除了上列的函式外,僅剩下兩個函式,它們會略微復雜一些,我們依次理解:
Initialize() 函式
void OvTools::Time::Clock::Initialize()
{
__DELTA_TIME = 0.0f;
__START_TIME = std::chrono::steady_clock::now();
__CURRENT_TIME = __START_TIME;
__LAST_TIME = __START_TIME;
__INITIALIZED = true;
}
該函式是初始化時間差變數 __DELTA_TIME 、開始時間 __START_TIME 、當前時間 __CURRENT_TIME 、最后記錄的時間 __LAST_TIME,其中,初始化后三者用到了函式 std::chrono::steady_clock::now(),該函式的作用也很簡單,是將當前時間作為值 time_point 回傳,
Update() 函式
void OvTools::Time::Clock::Update()
{
__LAST_TIME = __CURRENT_TIME;
__CURRENT_TIME = std::chrono::steady_clock::now();
__ELAPSED = __CURRENT_TIME - __LAST_TIME;
if (__INITIALIZED)
{
__DELTA_TIME = __ELAPSED.count() > 0.1 ? 0.1f : static_cast<float>(__ELAPSED.count());
__TIME_SINCE_START += __DELTA_TIME * __TIME_SCALE;
}
else
Initialize();
}
Update() 函式負責實作更新時間點的功能:它先用最后記錄的時間與當前的時間(函式 now() )計算出時間差,作為 duration 記錄在 __ELAPSED 中;然后判斷是否初始化了 __DELTA_TIME 等變數;接著呼叫 duration 的函式 count() —— 在 chrono 檔案中可以看到,該函式負責檢索時間間隔內的時鐘計時周期數 —— 經過判斷后將值 0.1 或用 static_cast 把周期數強制轉換為 float 賦值時差變數 __DELTA_TIME,以此控制時差在 0.1 以內;最后,將時差縮放后得到新時間點 __TIME_SINCE_START(相對于 time_point 設定的固定點),
由此可見,Clock 類雖然有各種各樣的計算操作,但是并不復雜,依靠的多是 chrono 庫,接下來開始探索 Date 部分:
3、Date
3.1 Date.h
Date.h 的頭檔案僅包含了 string,不多贅述:
#include <string>
Date 類
檔案主體代碼是 Date 類,一個日期系統,以字串格式獲取當前日期,其定義僅有兩個函式,代碼如下:
class Date
{
public:
/**
* Default constructor
*/
Date() = delete;
/*
* Return the current date in a string format
*/
static std::string GetDateAsString();
};
函式作用已有注釋,不贅述,值得一提的是,代碼應用了 C++11 的關鍵詞 delete,表示洗掉默認建構式,這可以為簡單功能類省下存盤空間;相對的,關鍵詞 default 表示默認存在建構式,
定義很短,所以我們直接看 Date.cpp,
3.2 Date.cpp
頭檔案包含了 Date.h,與 C++ 標準庫的 ctime,不多贅述:
#include <ctime>
#include "OvTools/Time/Date.h"
GetDateAsString() 函式
正如定義,主體代碼只有函式 GetDateAsString():
std::string OvTools::Time::Date::GetDateAsString()
首先,讓我們看看該函式幾個元素的定義與簡單處理:
std::string date;
const auto now = time(nullptr);
tm ltm;
localtime_s(<m, &now);
std::string dateData[6] =
{
std::to_string(1900 + ltm.tm_year),
std::to_string(1 + ltm.tm_mon),
std::to_string(ltm.tm_mday),
std::to_string(ltm.tm_hour),
std::to_string(ltm.tm_min),
std::to_string(ltm.tm_sec)
};
1、string 型的日期變數 date,顯然將用作回傳值,以回傳一個字串形式的日期,
2、const auto 關鍵詞限定的自動判斷型別的常量 now:通過 time.h 中的函式 time() 獲得系統日期與時間,其中 time() 回傳值的型別經過層層解開其重命名,是 __int64,
3、corecrt_wtime.h 中 struct 結構體 tm 的物件 ltm:該結構體包含多個 int 型的時間變數,例如 tm_sec(記錄 0~60 秒)、tm_wday(記錄一周7天)等等,
代碼對其做了簡單處理:呼叫 time.h 的函式 localtime_s(),該函式需要傳入 struct tm* _tm 與 const time_t *time 兩個引數 —— 顯然就是上述的兩個元素 —— 并把 time 存盤在上述的 tm 結構體中,如果成功,回傳值為零;如果失敗,回傳值將是錯誤代碼(在 Errno.h 中定義,不贅述),
簡單一提,該函式有兩個兄弟:localtime() 和 localtime_r(),雖然三兄弟功能是一樣的,但是有一定的區別:localtime 是最早定義的,但它因為存在另外兩者沒有的問題而被淘汰 —— 執行緒不安全,簡單解釋為該函式只申請了一個指標,所以有多個執行緒呼叫時會互相篡改資料,另外兩者區別在于:localtime_s 用于 Windows 系統;localtime_r 用于 Linux 系統,
4、string 型的日期資料陣列 dateDate:將上述經過 localtime_s() 初次格式化后的日期通過呼叫 to_string 轉為 string 型存盤,依次記錄下年、月、日、時、分、秒,
了解了定義的元素與部分處理,我們就能快速理解函式的剩余部分 —— 兩個 for 回圈:
for (uint8_t i = 1; i < 6; ++i)
if (dateData[i].size() < 2)
dateData[i].insert(dateData[i].begin(), '0');
for (uint8_t i = 0; i < 6; ++i)
{
date += dateData[i];
if (i == 2)
date += '_';
else if (i != 5)
date += '-';
}
return date;
第一個回圈是依次判斷并補充 dateDate 中各變數的位數,可以理解為:當月 / 日 / 時 / 分 / 秒是個位數時,十位數補充一個 “ 0 ” 使其成為 “ 兩位數 ”,完成最終的格式化;第二個回圈顯然是按照要求將 dateDate 中的值依次賦值給字串 date,使其成為這樣的形式:yyyy-mm-dd_hh-mm-ss,最后回傳輸出 date,完成以字串格式回傳當前日期的功能,
總結
Time 模塊的探究就到此結束了,總的來說大多是在呼叫 C++ 的庫來完成一些核心功能,所以內容不多也不復雜,
因此,筆者將很快繼續探究下一個小模塊 Utils,目測內容有點兒多,可能會分兩 P 講述,一起加油吧!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/330436.html
標籤:其他
下一篇:Unity之Shader基礎探索
