Box2d源碼學習b2Timer、b2Draw和b2Settings的實現

本系列博客是由扭曲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的接口,我們可以在此次快速的替換。而不需要找其他任何文件。

 

公共部分終於講完了,下面我們將會學習碰撞部分。以上部分,根據本人理解所寫,若有不妥或錯誤之處,還請大家多多指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章