本系列博客是由扭曲45原創,歡迎轉載,轉載時註明出處,http://blog.csdn.net/cg0206/article/details/8280463
今天我們要說在公共模塊剩下的三個小模塊的實現,分別是:計時器類、調試輔助類、和box2d引擎設置部分。
1、 計時器b2Timer
計時器主要是用來計算一段時間內的時間,通過對某個函數執行計時,可用來查看相關函數的效率和性能。Box2d中主要針對window系統和類unix系統(如linux、OS X)進行了實現。它們主要是通過宏開關控制的,像window系統上的宏是_WIN32,linux系統上的宏是__linux__,OS X系統上的宏則是__APPLE__,這些在不同系統的編譯器中一般是有定義的,不要我們手動去改,如果真的沒有不妨自己在文件中手動定義一下,碰碰運氣。
好了,廢話不多說,上代碼:
//計時器。這是基於特定平臺上的代碼,可能無法在每個平臺都正常工作
class b2Timer
{
public:
/**************************************************************************
* 功能描述:構造函數
* 參數說明: (void)
* 返 回 值: (void)
***************************************************************************/
b2Timer();
/**************************************************************************
* 功能描述:重置timer
* 參數說明: (void)
* 返 回 值: (void)
***************************************************************************/
void Reset();
/**************************************************************************
* 功能描述:獲取time從構造或最近的重置開始
* 參數說明: (void)
* 返 回 值: 毫秒數
***************************************************************************/
float32 GetMilliseconds() const;
private:
#if defined(_WIN32)
//開始計數變量,記錄開始值
float64 m_start;
//獲取每毫秒計數的次數
static float64 s_invFrequency;
#elif defined(__linux__) || defined (__APPLE__)
//開始計數的秒數、微秒數
unsigned long m_start_sec;
unsigned long m_start_msec;
#endif
};
上面有何詳細的註解,不多說了,下面我們看下實現部分:
#if defined(_WIN32) && !defined(SHP)
//獲取每毫秒計數的次數
float64 b2Timer::s_invFrequency = 0.0f;
#include <windows.h>
//構造函數
b2Timer::b2Timer()
{
//
LARGE_INTEGER largeInteger;
//第一次開始的時候
if (s_invFrequency == 0.0f)
{
//獲取高精度計數器的頻率
QueryPerformanceFrequency(&largeInteger);
s_invFrequency = float64(largeInteger.QuadPart);
//獲取每毫秒計數的次數
if (s_invFrequency > 0.0f)
{
s_invFrequency = 1000.0f / s_invFrequency;
}
}
//定時器的計數值
QueryPerformanceCounter(&largeInteger);
m_start = float64(largeInteger.QuadPart);
}
//重置
void b2Timer::Reset()
{
LARGE_INTEGER largeInteger;
QueryPerformanceCounter(&largeInteger);
m_start = float64(largeInteger.QuadPart);
}
//獲取毫秒數
float32 b2Timer::GetMilliseconds() const
{
LARGE_INTEGER largeInteger;
QueryPerformanceCounter(&largeInteger);
//開始計數
float64 count = float64(largeInteger.QuadPart);
//毫秒數
float32 ms = float32(s_invFrequency * (count - m_start));
return ms;
}
#elif defined(__linux__) || defined (__APPLE__)
#include <sys/time.h>
//構造函數
b2Timer::b2Timer()
{
Reset();
}
//重置
void b2Timer::Reset()
{
timeval t;
gettimeofday(&t, 0);
m_start_sec = t.tv_sec;
m_start_msec = t.tv_usec * 0.001f;
}
//獲取毫秒數
float32 b2Timer::GetMilliseconds() const
{
timeval t;
gettimeofday(&t, 0);
return (t.tv_sec - m_start_sec) * 1000 + t.tv_usec * 0.001f - m_start_msec;
}
#else
b2Timer::b2Timer()
{
}
void b2Timer::Reset()
{
}
float32 b2Timer::GetMilliseconds() const
{
return 0.0f;
}
#endif
通過宏的編譯可以看出,window和類nuix上使用的內部API是不一樣的,就像軟件有debug和Release版本一樣,有時候debug沒問題,relase則不然,孰不知,它們在編譯時編譯器調用的程序很有可能是不同的。同時我們也要注意下,還有其他的類型的系統沒有實現此類(估計一般人也碰不上)。
2、 調試輔助類b2Draw
調試輔助類主要輔助box2d中物體的調試,通過繪製不同的調試輔助的形狀,來監控並改正物體行爲的正確性。首先我們看下b2Draw.h文件。
// 調試繪製的顏色,每個值都在[0,1]之間
struct b2Color
{
/**************************************************************************
* 功能描述:默認構造函數
* 參數說明: (void)
* 返 回 值: (void)
***************************************************************************/
b2Color() {}
/**************************************************************************
* 功能描述:構造函數
* 參數說明: r : 紅色值部分
g :綠色值部分
b :藍色值部分
* 返 回 值: (void)
***************************************************************************/
b2Color(float32 r, float32 g, float32 b) : r(r), g(g), b(b) {}
/**************************************************************************
* 功能描述:設置顏色函數
* 參數說明: ri : 紅色值部分
gi :綠色值部分
bi :藍色值部分
* 返 回 值: (void)
***************************************************************************/
void Set(float32 ri, float32 gi, float32 bi) { r = ri; g = gi; b = bi; }
//代表紅、綠、藍的變量
float32 r, g, b;
};
//在b2World中實現並註冊這個類,以便提供調試繪製不同的物理實體在你的遊戲中
class b2Draw
{
public:
/**************************************************************************
* 功能描述:構造函數
* 參數說明: (void)
* 返 回 值: (void)
***************************************************************************/
b2Draw();
/**************************************************************************
* 功能描述:析構函數
* 參數說明: (void)
* 返 回 值: (void)
***************************************************************************/
virtual ~b2Draw() {}
enum
{
e_shapeBit = 0x0001, ///< 繪製形狀
e_jointBit = 0x0002, ///< 繪製關節聯繫
e_aabbBit = 0x0004, ///< 繪製軸對齊邊框
e_pairBit = 0x0008, ///< 繪製broad-phase pairs
e_centerOfMassBit = 0x0010 ///< 繪製質心框架
};
/**************************************************************************
* 功能描述:設置繪製標誌位
* 參數說明: flags:標誌
* 返 回 值: (void)
***************************************************************************/
void SetFlags(uint32 flags);
/**************************************************************************
* 功能描述:獲得繪製標誌位
* 參數說明: (void)
* 返 回 值: 繪製標誌
***************************************************************************/
uint32 GetFlags() const;
/**************************************************************************
* 功能描述:追加繪製標誌位
* 參數說明: flags:繪製標誌
* 返 回 值: (void)
***************************************************************************/
void AppendFlags(uint32 flags);
/**************************************************************************
* 功能描述:從當前標誌中清除標誌
* 參數說明: flags:繪製標誌
* 返 回 值: (void)
***************************************************************************/
void ClearFlags(uint32 flags);
/**************************************************************************
* 功能描述:按照提供的頂點繪製逆時針方式閉合的多邊形
* 參數說明: vertices :頂點
vertextexCount: 頂點數量
color : 顏色
* 返 回 值: (void)
***************************************************************************/
virtual void DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) = 0;
/**************************************************************************
* 功能描述:按照提供的頂點繪製逆時針方式閉合的並填充顏色的多邊形
* 參數說明: vertices :頂點
vertextexCount: 頂點數量
color : 顏色
* 返 回 值: (void)
***************************************************************************/
virtual void DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) = 0;
/**************************************************************************
* 功能描述:繪製一個圓
* 參數說明: center :向量
radius : 半徑
color : 顏色
* 返 回 值: (void)
***************************************************************************/
virtual void DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color) = 0;
/**************************************************************************
* 功能描述:繪製一個填充顏色的圓
* 參數說明: center :向量
radius : 半徑
color : 顏色
* 返 回 值: (void)
***************************************************************************/
virtual void DrawSolidCircle(const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color) = 0;
/**************************************************************************
* 功能描述:繪製一段線段
* 參數說明: p1 :開始點
p2 : 結束點
color : 顏色
* 返 回 值: (void)
***************************************************************************/
virtual void DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) = 0;
/**************************************************************************
* 功能描述:繪製一個變換,選擇你的長度比例。
* 參數說明: xf :變換
* 返 回 值: (void)
***************************************************************************/
virtual void DrawTransform(const b2Transform& xf) = 0;
protected:
//繪製標誌
uint32 m_drawFlags;
};
通過代碼我們可以看到有個b2Color的結構體的定義,它主要作爲調試顏色使用的。
再看看b2Draw的實現部分
//構造函數
b2Draw::b2Draw()
{
m_drawFlags = 0;
}
//設置標誌位
void b2Draw::SetFlags(uint32 flags)
{
m_drawFlags = flags;
}
//獲取標誌位
uint32 b2Draw::GetFlags() const
{
return m_drawFlags;
}
//追加標誌位
void b2Draw::AppendFlags(uint32 flags)
{
m_drawFlags |= flags;
}
//清除標誌位
void b2Draw::ClearFlags(uint32 flags)
{
m_drawFlags &= ~flags;
}
通過觀察b2Draw的實現,我們發現有點不對勁,定義的函數那麼多,怎麼實現就這幾個函數呢?那其他的函數在哪裏實現的呢?我們不禁要問難道是要我們使用者自己實現嗎?真被我們猜中了,這個是要我們那實現的,可以看到沒有實現的函數前面都有virtual做修飾,這就是虛函數,是等着用戶自己用的時候去實現的,不能直接調用。
3、 Box2d設置
設置中主要定義了宏、常量、和一些輔助的公共函數。我們就來看看相關的定義。
在b2Settings.h文件中:
//主要是因爲有的編譯器將提示相關語句的值是未使用的,
//所以你必須告訴它忽略所有(void)。
//即消除編譯器的警告
#define B2_NOT_USED(x) ((void)(x))
//對斷言宏進行重新封裝
#define b2Assert(A) assert(A)
//重新封裝類型,這樣做爲了方便而且很好的移植到不同的平臺
typedef signed char int8;
typedef signed short int16;
typedef signed int int32;
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedef float float32;
typedef double float64;
//float類型最大值
#define b2_maxFloat FLT_MAX
//float類型最小值
#define b2_epsilon FLT_EPSILON
//pi的值
#define b2_pi 3.14159265359f
/// 全局常量參數 基於米-千克-秒(MKS)單位
//碰撞
//在兩個凸形狀上的接觸點最大數量,不要修改它的值
#define b2_maxManifoldPoints 2
//凸多邊形的頂點數量的最大值。你不能將這個宏修改的太大,因爲b2BolckAlloctor函數有一個物體內存大小的上限
#define b2_maxPolygonVertices 8
// 用這個在動態樹上填充AABB,它允許代理少量移動不必去調整這個樹
// 單位是米
#define b2_aabbExtension 0.1f
// 用這個在動態樹上填充AABBs,這是基於當前位移用來預測未來的位置。
// 沒有單位
#define b2_aabbMultiplier 2.0f
//一個小的長度作爲碰撞和約束誤差,通常它被選爲數字意義重大,但視覺上無足輕重。
#define b2_linearSlop 0.005f
//一個小的角度作爲碰撞和約束誤差,通常它被選爲數字意義重大,但視覺上無足輕重。
#define b2_angularSlop (2.0f / 180.0f * b2_pi)
///半徑的多邊形/邊緣形狀的皮膚。這應該不會被修改。使
///這意味着將有一個小的多邊形數不足爲連續碰撞緩衝。
///使它更大的可能創建工件爲頂點碰撞。
#define b2_polygonRadius (2.0f * b2_linearSlop)
///在連續物理模擬裏,在每次接觸中子步驟的最大數值
#define b2_maxSubSteps 8
//動態
//接觸的最大數用於處理解決一個撞擊時間內的撞擊
#define b2_maxTOIContacts 32
//彈性碰撞的一個速度閾值,任何與一個相對線速度碰撞,低於這個閾值的將被視爲非彈性碰撞
#define b2_velocityThreshold 1.0f
// 線性速度位置的最大值校正當解決約束使用,這有助於阻止穿越物體
#define b2_maxLinearCorrection 0.2f
// 角位置的最大值校正當解決約束使用,這有助於阻止穿越物體
#define b2_maxAngularCorrection (8.0f / 180.0f * b2_pi)
// 物體【剛體】的最大線速度,這限制是非常大的,用於防止數值問題。你不需要去適應這個
#define b2_maxTranslation 2.0f
#define b2_maxTranslationSquared (b2_maxTranslation * b2_maxTranslation)
// 物體【剛體】的最大線速度,這限制是非常大的,用於防止數值問題。你不需要去適應這個
#define b2_maxRotation (0.5f * b2_pi)
#define b2_maxRotationSquared (b2_maxRotation * b2_maxRotation)
//這個比例因子控制怎樣快速解決重疊問題。理想的情況下,這將是1,這樣重疊將在一個時間步內被移除
#define b2_baumgarte 0.2f
#define b2_toiBaugarte 0.75f
// 休眠
//最小休眠時間
#define b2_timeToSleep 0.5f
//剛體[body]要想休眠,線的最大值,即當角速度超過它時剛體[body]無法休眠。
#define b2_linearSleepTolerance 0.01f
//剛體[body]要想休眠,角速度的最大值,即當角速度超過它時剛體[body]無法休眠。
#define b2_angularSleepTolerance (2.0f / 180.0f * b2_pi)
// 內存申請
//申請內存函數,實現這個函數作爲你自己的內存分配器。
void* b2Alloc(int32 size);
//釋放內存函數,如果你實現b2Alloc,你也應該實現這個功能。
void b2Free(void* mem);
//打印日誌函數
void b2Log(const char* string, ...);
//版本編號方案
//見 http://en.wikipedia.org/wiki/Software_versioning
struct b2Version
{
int32 major; ///重大更新
int32 minor; ///較大更新
int32 revision; ///修復bug
};
//box2d當前版本號
extern b2Version b2_version;
在這裏還想再強調說明下B2_NOT_USED(x),很多人不理解爲什麼要轉化爲((void)x),這樣做的有什麼作用或者好處嗎?當然是有的,以下網上查找的解釋
Mainly becauseat least one compiler will complain that the resulting statement's value isunused, so you have to tell it to ignore it all with (void).
翻譯了一下,也就是消除有的編譯器編譯時候發出的警告。這裏藉此說一下,編程時不要無視編譯器時候發出的警告,警告的出現往往多是我們編寫的時候不規範造成的,當然有一部分是錯誤和還有一部分編譯器的原因。我們要儘量去修復它,不要因爲一個無視了一個未初始化一個指針的警告,你的程序出現了莫名其妙的情況時,再滿頭大汗的到處說指針很坑爹。
接着看b2Settings.cpp文件中的實現:
//box2d當前版本號
b2Version b2_version = {2, 2, 1};
/**************************************************************************
* 功能描述:申請內存
* 參數說明:size : 申請大小
* 返 回 值: (void)
**************************************************************************/
void* b2Alloc(int32 size)
{
return malloc(size);
}
/**************************************************************************
* 功能描述:釋放內存
* 參數說明:mem : 內存頭
* 返 回 值: (void)
**************************************************************************/
void b2Free(void* mem)
{
free(mem);
}
/**************************************************************************
* 函數名稱:b2Log
* 功能描述:打印log
* 參數說明:string :打印字符串
... :參數列表
* 返 回 值: (void)
**************************************************************************/
void b2Log(const char* string, ...)
{
#if defined(SHP)
#ifdef _DEBUG
__App_info(__PRETTY_FUNCTION__ , __LINE__, string);
#endif
#else
va_list args;
va_start(args, string);
vprintf(string, args);
va_end(args);
#endif
}
通過這過我們可以看到,我們使用的是box2d 版本號2.2.1,接下來是對c中內存管理函數malloc和free函數的封裝,好處就是如果使用了不同與malloc/free的接口,我們可以在此次快速的替換。而不需要找其他任何文件。
公共部分終於講完了,下面我們將會學習碰撞部分。以上部分,根據本人理解所寫,若有不妥或錯誤之處,還請大家多多指正。