一洋机、什么是Box2D
Box2D是一個(gè)強(qiáng)大的開源物理游戲引擎,用來模擬2D剛體物體運(yùn)動(dòng)和碰撞,由Erin Catto早在2007年用C++語言開發(fā)概漱。
Box2D集成了大量的物理力學(xué)和運(yùn)動(dòng)學(xué)的計(jì)算暂题,并將物理模擬過程封裝到類對(duì)象中移剪,將對(duì)物體的操作,以簡(jiǎn)單友好的接口提供給開發(fā)者薪者。我們只需要調(diào)用引擎中相應(yīng)的對(duì)象或函數(shù)纵苛,就可以模擬現(xiàn)實(shí)生活中的加速、減速言津、拋物線運(yùn)動(dòng)攻人、萬有引力、碰撞反彈等等各種真實(shí)的物理運(yùn)動(dòng)悬槽。(引用百度百科)
簡(jiǎn)單的說怀吻,Box2D就是一個(gè)物理剛體仿真庫。
二初婆、如何學(xué)習(xí)使用Box2D
Box2D是一個(gè)獨(dú)立的引擎框架蓬坡,它的作用是幫助游戲開發(fā)者進(jìn)行一些復(fù)雜的物理模擬運(yùn)算,但是很多情況下它是作為某些游戲引擎的一個(gè)子模塊存在的磅叛。所以屑咳,我們可以借助一些游戲引擎來進(jìn)行學(xué)習(xí)。對(duì)于iPhone宪躯、iPad和Android游戲乔宿,可以使用Cocos2d游戲引擎和Corona Framework。
關(guān)于原生Box2D的學(xué)習(xí)資料访雪,網(wǎng)絡(luò)上面是多如牛毛详瑞,博客的話在CSDN和博客園上面都有。書籍方面也有諸如《Box2D物理游戲編程初學(xué)者指南》之類的東西臣缀。至于這些教程寫的到底好不好坝橡,這也只能仁者見仁,智者見智了精置。(反正我沒看)
我主要是基于bbframework框架開發(fā)游戲應(yīng)用的時(shí)候使用到了這款出色的物理引擎计寇,所以本文就在bbframework上進(jìn)行介紹。
使用的工具:
- Sublime Text
- quick-x-player
關(guān)于bbframework(簡(jiǎn)稱:bb),它是基于quick-cocos2d-x框架的一個(gè)再封裝框架番宁,其核心應(yīng)該可以說是cocos2d-Lua引擎(或者cocos2d-X)元莫。而cocos2d其本身并不支持box2d物理引擎(cocos2d-JS除外),所以目前在lua上的box2d接口都是公司通過lua綁定將原生的C++接口綁定到lua上的蝶押。(難免有些API和C++原生不太一樣)
三踱蠢、基本概念
Box2D物理引擎里面的所有類名都是以“b2”作為前綴的,以下是幾個(gè)比較重要的類棋电。
1茎截、世界(b2World)
物理世界只是一個(gè)抽象的概念,可以將其理解成是一個(gè)盒子赶盔,盒子里面放的是各種個(gè)樣的數(shù)學(xué)模型和物理模型(或者說就是N多的數(shù)學(xué)公式和物理公式)企锌,所有的物理模擬都在這個(gè)盒子內(nèi)完成。
物理世界和cocos2d的渲染世界不同于未,渲染世界由場(chǎng)景撕攒、層和精靈等組成,在游戲運(yùn)行時(shí)烘浦,渲染世界是可以看見(渲染顯示)打却、可以摸到(綁定觸摸事件)真實(shí)存在的。而物理世界的一切就跟萬物的靈魂一樣谎倔,看不見也摸不著,都是默默在后臺(tái)運(yùn)行的一些數(shù)據(jù)片段猿推。
萬物都是因?yàn)榛煦绯蹰_片习,世界形成才存在的。同樣蹬叭,要使用物理引擎里面的東西藕咏,一切也都要從創(chuàng)建世界開始。代碼如下:
-- 創(chuàng)建世界
local world = b2World(b2Vec2(0, -9.8))
-- 允許靜止的物體休眠
world:SetAllowSleeping(true)
-- 開啟連續(xù)物理檢測(cè)秽五,使模擬更加的真實(shí)
world:SetContinuousPhysics(true)
創(chuàng)建世界可以通過調(diào)用b2World(gravity)函數(shù)進(jìn)行創(chuàng)建孽查,該函數(shù)的參數(shù)gravity是物理世界的重力加速度(g)。在物理學(xué)中坦喘,加速度是具有大小和方向的矢量盲再,所以該參數(shù)可以使用二維向量來表示,其數(shù)據(jù)類型是b2Vec2瓣铣,創(chuàng)建矢量可以直接調(diào)用b2Vec2(x, y)函數(shù)答朋。
由于物理運(yùn)算經(jīng)常伴隨著平方、立方棠笑、開平方和開立方梦碗,甚至是更高次的冪運(yùn)算,所以其計(jì)算量是非常大的,對(duì)于性能的消耗也是非澈楣妫可觀印屁。而游戲恰恰又非常強(qiáng)調(diào)運(yùn)行的流暢性,所以很多時(shí)候當(dāng)物體處于禁止?fàn)顟B(tài)的時(shí)候并不需要實(shí)時(shí)進(jìn)行物理運(yùn)算斩例,這時(shí)候就可以將其從物理模擬中暫時(shí)的剔除出去雄人,以提高整體的計(jì)算效率。調(diào)用物理世界對(duì)象的SetAllowSleeping(isSleep)方法就可以設(shè)置世界內(nèi)的物體是否在禁止的時(shí)候休眠樱拴,處于休眠狀態(tài)的物體將不參與物理運(yùn)算柠衍。
同時(shí),為了物理模擬的更加真實(shí)晶乔,通常還需要開啟物理世界的連續(xù)檢測(cè)珍坊。調(diào)用物理世界對(duì)象的SetContinuousPhysics(bool)方法便可以設(shè)置是否開啟連續(xù)檢測(cè)。(連續(xù)檢測(cè)會(huì)消耗一定的性能)
世界非常的大正罢,可以說是無邊無際阵漏,然而游戲設(shè)備的屏幕是固定大小的,游戲的渲染畫面也就那么大翻具,所以為了保證物理模擬的物體處于可見的畫面中履怯,通常還需要給定一個(gè)邊緣,用于表示物理模擬的世界大小裆泳,所有的物體都添加到這個(gè)邊界里面叹洲。
物理世界里面的東西都可以看成是由剛體組成的,所以世界的邊界我們也可以創(chuàng)建一個(gè)四邊形剛體來表示工禾,關(guān)于剛體的創(chuàng)建詳見下文运提。
2、剛體(b2Body)
首先闻葵,要知道什么是剛體民泵?以《憤怒的小鳥》這款游戲?yàn)槔▲B在離開彈弓之后的運(yùn)行狀態(tài)完全是根據(jù)真實(shí)世界的物理效果進(jìn)行變化的槽畔,那么在物理運(yùn)算的時(shí)候栈妆,就需要一個(gè)剛體來表示小鳥(但不是小鳥本身),以參與物理運(yùn)算厢钧。所以鳞尔,剛體就是物理世界里面要進(jìn)行物理模擬的物體。
在cocos2d中坏快,你可以簡(jiǎn)單的把剛體當(dāng)成是一個(gè)數(shù)據(jù)對(duì)象铅檩,這個(gè)對(duì)象里面包含了各種各樣用于進(jìn)行物理運(yùn)算的數(shù)據(jù)(比如:質(zhì)量、位置莽鸿、旋轉(zhuǎn)角度等)昧旨。
那么拾给,什么樣的東西適合在物理世界里面創(chuàng)建成剛體呢?物理世界簡(jiǎn)單的可以包含氣體兔沃、液體和固體蒋得,Box2D是一個(gè)剛體仿真庫,對(duì)于氣體和液體的模擬并不是它的職責(zé)乒疏,所以它適合模擬的東西只剩下固體了额衙,而且是那種在物理模擬中不會(huì)發(fā)生形變的固體(任何物體都會(huì)發(fā)生形變,這里只是一種理想狀態(tài))怕吴。
由于剛體是現(xiàn)實(shí)世界物體的一個(gè)仿真模擬窍侧,所以剛體也必須包含一些現(xiàn)實(shí)物體的物理屬性,這些屬性可以簡(jiǎn)單的稱之為對(duì)剛體的描述或者是定義转绷。所以在創(chuàng)建剛體之前伟件,需要先創(chuàng)建該剛體的剛體描述,用來描述剛體的物理屬性议经。
首先來看下Box2D原生對(duì)剛體描述的定義:
b2BodyDef()
{
// 用戶數(shù)據(jù)
userData = NULL;
// 剛體位置
position.Set(0.0f, 0.0f);
// 剛體角度
angle = 0.0f;
// 剛體線性速度
linearVelocity.Set(0.0f, 0.0f);
// 剛體角速度
angularVelocity = 0.0f;
// 剛體線性阻尼
linearDamping = 0.0f;
// 剛體角度阻尼
angularDamping = 0.0f;
// 剛體是否可以進(jìn)行休眠
allowSleep = true;
// 剛體初始狀態(tài)是否處于喚醒狀態(tài)
awake = true;
// 剛體是否固定旋轉(zhuǎn)角度
fixedRotation = false;
// 剛體是否是一個(gè)快速移動(dòng)的物體斧账,為了防止發(fā)生擊穿想象,開啟它會(huì)增加處理時(shí)間
bullet = false;
// 剛體類型
type = b2_staticBody;
// 剛體是否處于活躍狀態(tài)
active = true;
// 剛體所受重力加速度影響的倍數(shù)
gravityScale = 1.0f;
}
b2BodyDef是剛體描述的結(jié)構(gòu)體類型煞肾,它可以包含以上14種物理信息咧织。創(chuàng)建b2BodyDef的代碼如下:
local bodyDef = b2BodyDef()
-- 類型:靜態(tài)(b2_staticBody),平臺(tái)(b2_kinematicBody),動(dòng)態(tài)(b2_dynamicBody)
bodyDef.type = b2_staticBody
bodyDef.position = b2Vec2(0, 0)
bodyDef.angle = math.rad(0)
-- 用戶數(shù)據(jù):存儲(chǔ)用戶的數(shù)據(jù),可以是任何類型的數(shù)據(jù)籍救。一般要求存儲(chǔ)的數(shù)據(jù)的類型是一致的
bodyDef.userData = nil
bodyDef.angularDamping = 0
bodyDef.linearDamping = 0
bodyDef.fixedRotation = false
調(diào)用b2BodyDef()函數(shù)便可以創(chuàng)建一個(gè)剛體描述對(duì)象习绢,然后我們可以隨意設(shè)置一些描述信息(不設(shè)置的時(shí)候,它們都有默認(rèn)值)蝙昙。
這里有注意的是:
1毯炮、剛體的類型:剛體類型分為靜態(tài)剛體、平臺(tái)剛體和動(dòng)態(tài)剛體耸黑,對(duì)應(yīng)值分別是:0、1和2篮幢。靜態(tài)剛體是不受力的作用而進(jìn)行移動(dòng)的大刊,用于模擬地面、墻面等禁止的物體三椿;平臺(tái)剛體可用于模擬游戲內(nèi)的移動(dòng)平臺(tái)等物體缺菌,這些物體和地面等幾乎一樣,但是可以進(jìn)行位置移動(dòng)等搜锰;動(dòng)態(tài)剛體是最常見的伴郁,所有會(huì)動(dòng)的物體都創(chuàng)建為動(dòng)態(tài)剛體。
2蛋叼、剛體的位置:在Box2D中可以使用b2Vec2類型的向量來表示坐標(biāo)點(diǎn)焊傅。
3剂陡、剛體的角度:在Box2D中,剛體的角度是使用弧度制狐胎,并非和cocos2d一樣的角度制鸭栖。
4、用戶數(shù)據(jù):用戶數(shù)據(jù)用于保存一些程序員想要附加給剛體的信息握巢,任何數(shù)據(jù)類型都可以晕鹊,一般我們用于保存剛體對(duì)應(yīng)的那個(gè)精靈節(jié)點(diǎn)對(duì)象(CCSprite)。
創(chuàng)建完剛體的描述暴浦,就可以通過描述對(duì)象告訴物理世界需要?jiǎng)?chuàng)建一個(gè)什么樣子的物體了溅话。創(chuàng)建剛體的代碼如下:
-- 創(chuàng)建一個(gè)剛體對(duì)象,根據(jù)剛體定義創(chuàng)建
local body = world:CreateBody(bodyDef)
通過調(diào)用物理世界對(duì)象的CreateBody(bodyDef)方法歌焦,物理世界就可以根據(jù)傳遞進(jìn)去的bodyDef對(duì)象創(chuàng)建一個(gè)對(duì)應(yīng)的剛體對(duì)象飞几。
3、形狀(b2Shap)
創(chuàng)建好的剛體其實(shí)只是一個(gè)包含一些物理量的一個(gè)質(zhì)點(diǎn)(有質(zhì)量但是沒有大小的點(diǎn))同规,然而現(xiàn)實(shí)世界中的物體是有各種各樣的大小和形狀的循狰,所以我們還需要為剛體創(chuàng)建對(duì)應(yīng)的形狀。(物體的碰撞模擬也需要借助于形狀)
Box2D內(nèi)置了以下幾種簡(jiǎn)單形狀:
- 鏈條(b2ChainShape)
- 圓形(b2CircleShape)
- 邊線(b2EdgeShape)
- 多邊形(b2PolygonShape)
除了以上幾種之外券勺,還可以借助PhysicsEditor等物理形狀編輯器進(jìn)行描點(diǎn)來創(chuàng)建更加復(fù)雜的形狀绪钥。
在上文介紹世界的時(shí)候說到需要?jiǎng)?chuàng)建一個(gè)四邊形當(dāng)成物理世界的邊界,那么這里可以選擇用四條邊首位相連关炼,圍成一個(gè)四邊形程腹。代碼如下:
local shape1 = b2EdgeShape()
shape1:Set(b2Vec2(0 / 32, 0 / 32), b2Vec2(960 / 32, 0 / 32))
local shape2 = b2EdgeShape()
shape2:Set(b2Vec2(0 / 32, 0 / 32), b2Vec2(0 / 32, 540 / 32))
local shape3 = b2EdgeShape()
shape3:Set(b2Vec2(0 / 32, 540 / 32), b2Vec2(960 / 32, 540 / 32))
local shape4 = b2EdgeShape()
shape4:Set(b2Vec2(960 / 32, 0 / 32), b2Vec2(960 / 32, 540 / 32))
創(chuàng)建b2EdgeShape同樣可以通過調(diào)用b2EdgeShape()函數(shù)來實(shí)現(xiàn),然后調(diào)用b2EdgeShape對(duì)象的Set(fromPoint, toPoint)方法來指定邊線的起點(diǎn)和終點(diǎn)儒拂。
這里我游戲的設(shè)計(jì)分辨率是 960 X 540 寸潦,然后我創(chuàng)建的是一個(gè)和游戲設(shè)計(jì)分辨率同等尺寸的四邊形,但是可以看到起點(diǎn)和終點(diǎn)的x社痛、y坐標(biāo)值都被我除以了32见转,這是因?yàn)锽ox2D使用的度量是以“米”為單位,而cocos2d的坐標(biāo)系是以像素為單位的蒜哀,通常設(shè)置其轉(zhuǎn)換比例是1:32斩箫,也就是32像素的距離等價(jià)于Box2D中的1米,這樣的模擬效果是比較好的撵儿。
4乘客、夾具(b2Fixture)
創(chuàng)建好形狀之后,需要將形狀和對(duì)應(yīng)的剛體進(jìn)行綁定淀歇,這樣剛體才能擁有形狀易核。b2Fixture類就是用于見形狀綁定到剛體上的,b2Fixture我們可以將其稱為“夾具”或者“材質(zhì)”浪默。
在創(chuàng)建b2Fixture之前牡直,也需要先創(chuàng)建對(duì)應(yīng)的材質(zhì)描述對(duì)象(b2FixtureDef)缀匕,設(shè)定一些材質(zhì)信息。材質(zhì)描述的定義如下:
b2FixtureDef()
{
// 形狀
shape = NULL;
// 用戶數(shù)據(jù)
userData = NULL;
// 摩擦系數(shù)
friction = 0.2f;
// 恢復(fù)系數(shù)
restitution = 0.0f;
// 密度
density = 0.0f;
// 是否為傳感器
isSensor = false;
}
材質(zhì)信息中的形狀和用戶數(shù)據(jù)請(qǐng)參考上文井氢,這里就不在贅述了弦追。重點(diǎn)看下以下幾個(gè)屬性:
- 摩擦系數(shù):用于影響剛體的運(yùn)動(dòng),取值通常在區(qū)間[0, 1]花竞,當(dāng)然也可以更大劲件。
- 恢復(fù)系數(shù):或者稱之為“彈性系數(shù)”,用于剛體碰撞后能量的損失計(jì)算约急。取值通常在區(qū)間[0, 1]零远,當(dāng)然也可以更大。0表示發(fā)生非躺下碰撞厌蔽,1表示發(fā)生完全彈性碰撞牵辣。
- 密度:密度通常用于計(jì)算剛體的質(zhì)量,間接的影響剛體的慣性奴饮。
- 是否為傳感器:當(dāng)設(shè)置isSensor為true時(shí)纬向,剛體發(fā)生碰撞的時(shí)候并不會(huì)發(fā)生碰撞響應(yīng)(反彈),但是會(huì)接收到碰撞的信號(hào)戴卜,所以該屬性可以理解為傳感器逾条。
同樣,根據(jù)上面創(chuàng)建好的四條邊來創(chuàng)建四個(gè)材質(zhì)定義對(duì)象投剥,代碼如下:
-- 創(chuàng)建材質(zhì)描述
local fixtureDef1 = b2FixtureDef()
fixtureDef1.shape = shape1
local fixtureDef2 = b2FixtureDef()
fixtureDef2.shape = shape2
local fixtureDef3 = b2FixtureDef()
fixtureDef3.shape = shape3
local fixtureDef4 = b2FixtureDef()
fixtureDef4.shape = shape4
這里創(chuàng)建材質(zhì)描述是調(diào)用b2FixtureDef()函數(shù)來實(shí)現(xiàn)师脂,然后設(shè)置了描述對(duì)象的形狀信息,其它的信息全部使用默認(rèn)的即可江锨。
有了材質(zhì)描述吃警,接下來就可以創(chuàng)建對(duì)應(yīng)的夾具(材質(zhì))了,代碼如下:
-- 創(chuàng)建四個(gè)夾具
body:CreateFixture(fixtureDef1)
body:CreateFixture(fixtureDef2)
body:CreateFixture(fixtureDef3)
body:CreateFixture(fixtureDef4)
創(chuàng)建夾具的方法是調(diào)用剛體的CreateFixture(b2FixtureDef)方法來實(shí)現(xiàn)的啄育,并且夾具會(huì)見材質(zhì)上的信息與該剛體進(jìn)行綁定酌心,一個(gè)剛體可以擁有多個(gè)夾具。
四挑豌、物理調(diào)試(Debug)
上文說過谒府,物理世界的一切都是看不見的,但是有時(shí)候?yàn)榱朔奖闩佩e(cuò)浮毯,可以用其它的方法讓物理模擬變得可見。
比如:我們創(chuàng)建好了一個(gè)剛體泰鸡,我們想要知道剛體對(duì)應(yīng)到cocos2d渲染世界里面的位置债蓝,那么我們可以在cocos2d渲染世界里面創(chuàng)建一個(gè)lable標(biāo)簽或者一個(gè)sprite精靈,并放到剛體的位置上面盛龄,這樣我們就等同于是讓剛體可見了饰迹。而對(duì)于邊線芳誓、圓之類的剛體形狀,我們可以使用一些游戲引擎的繪圖API在渲染世界內(nèi)對(duì)應(yīng)的進(jìn)行繪制啊鸭,這樣形狀也可以看到了锹淌。很多時(shí)候?qū)⑦@些數(shù)據(jù)進(jìn)行可視化會(huì)幫助游戲開發(fā)者更好的進(jìn)行物理排錯(cuò)。
在bbframework中赠制,可以使用以下代碼進(jìn)行物理的可視化操作:
local debugDraw = GB2DebugDrawLayer:create(world, 32)
self:add(debugDraw, 9999)
GB2DebugDrawLayer這個(gè)類專門用于負(fù)責(zé)物理對(duì)象的可視化模擬赂摆,調(diào)用該類的create(b2World, PTM_RATIO)方法進(jìn)行構(gòu)造時(shí),需要傳入物理世界對(duì)象和cocos2d與Box2D的度量單位比例(像素/米)钟些。然后將GB2DebugDrawLayer的實(shí)例對(duì)象添加到當(dāng)前場(chǎng)景的Layer上烟号。
這樣我們便可以在渲染世界里面看到物理模擬的效果了。
如上圖所示政恍,我們可以看到屏幕的邊緣有紅色或者綠色的邊線汪拥,那就是上面創(chuàng)建的世界邊緣的四條邊。
由于篇幅過長篙耗,關(guān)于自定義形狀和Box2D關(guān)節(jié)以及接觸檢測(cè)留在下一篇介紹迫筑。