Chipmunk2D中文手册
Chipmunk2D中文手冊(cè),由泰然翻譯組翻譯。轉(zhuǎn)載請(qǐng)注明出處。
翻譯:ChildhoodAndy(完成了大部分的翻譯), u0u0, gloryming。
校對(duì):涵紫
github貢獻(xiàn)地址:https://github.com/iTyran/ChipmunkDocsCN
歡迎大家斧正錯(cuò)誤,提交PR。
Chipmunk2D中文手冊(cè)
1. Chipmunk2D 6.2.1
Chipmunk2D是一個(gè)基于MIT協(xié)議的2D剛體物理仿真庫(kù)。設(shè)計(jì)宗旨:極快、可移植、穩(wěn)定、易用。出于這個(gè)原因,它已經(jīng)被用于數(shù)以百計(jì)的游戲,而且?guī)缀鯔M跨了所有系統(tǒng)。這些游戲包括了iPhoneAppStore上一些頂級(jí)出色的TOP1游戲,如Night Sky等。這幾年來(lái),我投入了大量的時(shí)間來(lái)發(fā)展Chipmunk,才使得Chipmunk走到今天。如果您發(fā)現(xiàn)Chipmunk2D為您節(jié)省了許多時(shí)間,不妨考慮捐贈(zèng)下。這么做會(huì)使一個(gè)獨(dú)立游戲制作者非常開(kāi)心!
首先,我要非常感謝ErinCatto(譯者注:Box2D作者), 早在2006年的時(shí)候,Chipmunk的沖量求解器便是受到他的范例代碼的啟發(fā)而完成(現(xiàn)在已經(jīng)發(fā)展成一個(gè)成熟的物理引擎:Box2D.org)。他持久接觸的想法允許對(duì)象的穩(wěn)定堆棧只進(jìn)行極少的求解器迭代,而我以前的求解器為了讓模擬穩(wěn)定模擬會(huì)產(chǎn)生大量的對(duì)象或者會(huì)消耗大量的CPU資源。
1.1 為什么是一個(gè)C庫(kù)
很多人問(wèn)我為什么用C來(lái)寫(xiě)Chipmunk2D,而不是一個(gè)我喜歡的其他語(yǔ)言。我通常會(huì)對(duì)不同的編程語(yǔ)言很興奮,幾個(gè)月來(lái),挑選的語(yǔ)言有Scheme, OCaml, Ruby, Objective-C, ooc, Lua, Io等等。它們都有一個(gè)共同點(diǎn),那就是都很容易綁定到C代碼。同時(shí)我也希望Chipmunk2D高效、易移植、優(yōu)化簡(jiǎn)單并且容易調(diào)試,而使用C語(yǔ)言就能很簡(jiǎn)單的達(dá)到這些目標(biāo)。
我從來(lái)沒(méi)有,將來(lái)也不太可能去用C來(lái)寫(xiě)一個(gè)完整的游戲。這里有很多比C有趣的語(yǔ)言,它們有垃圾回收,閉包,面向?qū)ο筮\(yùn)行時(shí)等高級(jí)特性。如果你在其它語(yǔ)言中使用Chipmunk2D,可以在Bindings and Ports中找到有用的信息。因?yàn)镃hipmunk2D基于C99的字集編寫(xiě),使得它很容易集成到C、C++、Object-C等其它開(kāi)發(fā)語(yǔ)言中。
1.2 C API的局限
如果您使用的是C++,Chipmunk提供了操作符*,+和 - (一元和二元)的重載,但如果使用的是C,那么需要退回使用cpvadd() 和 cpvsub()。這有一點(diǎn)點(diǎn)不利于代碼閱讀,不過(guò)當(dāng)你習(xí)慣之后這將不是個(gè)問(wèn)題。大部分的向量操作可能并沒(méi)任何形式的符號(hào)對(duì)應(yīng)(至少不在鍵盤(pán)上)。
C API的另一個(gè)問(wèn)題是訪問(wèn)限制。Chipmunk有許多結(jié)構(gòu)體,字段,函數(shù)只能內(nèi)部使用。要解決這個(gè)問(wèn)題,我把Chipmunk的全部私有API分離到頭文件chipmunk_private.h中,同時(shí)在共有結(jié)構(gòu)中使用CP_PRIVATE()來(lái)改名。你可以通過(guò)包含這個(gè)頭文件或使用這個(gè)宏來(lái)自由訪問(wèn)私有API,但請(qǐng)注意這些私有API可能在未來(lái)版本中改變或消失,并且不會(huì)在文檔中體現(xiàn),同時(shí)也沒(méi)有私有API的文檔計(jì)劃。
1.3 Chipmunk2D Pro
我們同時(shí)在出售Chipmunk2D的擴(kuò)展版本: Chipmunk2D Pro。主要的特性有:ARM和NEON指令優(yōu)化,多線程優(yōu)化,一個(gè)為iOS/Mac開(kāi)發(fā)提供的Objective-C封裝層,以及自動(dòng)幾何工具。優(yōu)化主要集中在提高移動(dòng)性能,同時(shí)多線程特性能在支持pthread的平臺(tái)運(yùn)行。Objective-C封裝層能讓你無(wú)縫整合到Cocos2D或UIKit等框架,并能獲得本地內(nèi)存管理的優(yōu)勢(shì)(包括ARC)。同時(shí)Pro版本有大量?jī)?yōu)秀的API擴(kuò)展。自動(dòng)幾何工具讓你能從圖像數(shù)據(jù)或程序生成并使用幾何。
另外,出售Chipmunk2D Pro讓我們得以生存,并保持Chipmunk2D的開(kāi)源。捐獻(xiàn)也很棒,但是購(gòu)買(mǎi)Pro版本你將獲得捐獻(xiàn)之外的某些東西。
1.4 下載與編譯
如果你還沒(méi)有下載,你總可以在這里獲取到Chipmunk2D的最新版本。里面包含了CMake的命令行編譯腳本, Xcode工程以及Visual Studio ’09 和 ’10工程。
Debug 或 Release?
Debug模式可能略慢,但是包含了大量的錯(cuò)誤檢測(cè)斷言,可以幫助你快速定位類(lèi)似重復(fù)移除對(duì)象或無(wú)法檢測(cè)的碰撞之類(lèi)的BUG。我強(qiáng)烈建議你使用Debug模式,直到你的游戲即將Release發(fā)售。
XCode (Mac/iPhone)
源碼中的Xcode工程可直接build出一個(gè)Mac或iOS靜態(tài)庫(kù)。另外,你可以運(yùn)行macosx/iphonestatic.command或macosx/macstatic.command來(lái)生成一個(gè)帶頭文件和debug/release靜態(tài)庫(kù)的目錄,以便你可以方便的集成到你的項(xiàng)目中。直接在你的項(xiàng)目中引入Chipmunk源碼以及正確的編譯選項(xiàng)并非易事。iPhone編譯腳本能生成一個(gè)可用在iOS模擬器和設(shè)備的通用庫(kù)(“fat” library),其中的模擬器版本用的debug模式編譯,而設(shè)備版本用的release模式編譯。
MSVC
我很少使用MSVC,其他開(kāi)發(fā)者幫忙維護(hù)了Visual Studio工程文件。MSVC 10工程應(yīng)該能正常運(yùn)行,因?yàn)槲医?jīng)常在發(fā)布穩(wěn)定版本前測(cè)試它。MSVC 9工程可能運(yùn)行不正常,我很少也沒(méi)有必要去運(yùn)行這個(gè)工程,如何你遇到問(wèn)題,請(qǐng)通知我。
命令行
CMake編譯腳本能在任何你安裝了CMake的系統(tǒng)上運(yùn)行。它甚至能生成XCode或MSVC工程(查看CMake文檔獲取更多信息)。
下面的命令編譯一個(gè)Debug的Chipmunk:
cmake -D CMAKE_BUILD_TYPE=Debug . make如何沒(méi)有-D CMAKE_BUILD_TYPE=Debug參數(shù),將生成一個(gè)release版本。
為什么使用CMake?一個(gè)非常好心的人完成了這個(gè)腳本的最初版本,然后我發(fā)現(xiàn)CMake能非常方便的解決跨平臺(tái)編譯問(wèn)題。我知道有些人非常討厭安裝一些胡亂的non-make編譯系統(tǒng)來(lái)編譯某些東西,但是CMake確實(shí)節(jié)省了我大量的時(shí)間和精力。
1.5 Hello Chipmunk(World)
下面的Hello World示例項(xiàng)目中,創(chuàng)建一個(gè)模擬世界,模擬一個(gè)球掉落到一個(gè)靜態(tài)線段上然后滾動(dòng)出去,并打印球的坐標(biāo)。
#include <stdio.h> #include <chipmunk.h>int main(void){// cpVect是2D矢量,cpv()為初始化矢量的簡(jiǎn)寫(xiě)形式cpVect gravity = cpv(0, -100);// 創(chuàng)建一個(gè)空白的物理世界cpSpace *space = cpSpaceNew();cpSpaceSetGravity(space, gravity);// 為地面創(chuàng)建一個(gè)靜態(tài)線段形狀// 我們稍微傾斜線段以便球可以滾下去// 我們將形狀關(guān)聯(lián)到space的默認(rèn)靜態(tài)剛體上,告訴Chipmunk該形狀是不可移動(dòng)的cpShape *ground = cpSegmentShapeNew(space->staticBody, cpv(-20, 5), cpv(20, -5), 0);cpShapeSetFriction(ground, 1);cpSpaceAddShape(space, ground);// 現(xiàn)在讓我們來(lái)構(gòu)建一個(gè)球體落到線上并滾下去// 首先我們需要構(gòu)建一個(gè) cpBody 來(lái)容納對(duì)象的物理屬性// 包括對(duì)象的質(zhì)量、位置、速度、角度等// 然后我們將碰撞形狀關(guān)聯(lián)到cpBody上以給它一個(gè)尺寸和形狀cpFloat radius = 5;cpFloat mass = 1;// 轉(zhuǎn)動(dòng)慣量就像質(zhì)量對(duì)于旋轉(zhuǎn)一樣// 使用 cpMomentFor*() 來(lái)近似計(jì)算它cpFloat moment = cpMomentForCircle(mass, 0, radius, cpvzero);// cpSpaceAdd*() 函數(shù)返回你添加的東西// 很便利在一行中創(chuàng)建并添加一個(gè)對(duì)象cpBody *ballBody = cpSpaceAddBody(space, cpBodyNew(mass, moment));cpBodySetPos(ballBody, cpv(0, 15));// 現(xiàn)在我們會(huì)球體創(chuàng)建碰撞形狀// 你可以為同一個(gè)剛體創(chuàng)建多個(gè)碰撞形狀// 它們將會(huì)附著關(guān)聯(lián)到剛體上并移動(dòng)更隨cpShape *ballShape = cpSpaceAddShape(space, cpCircleShapeNew(ballBody, radius, cpvzero));cpShapeSetFriction(ballShape, 0.7);// 現(xiàn)在一切都建立起來(lái)了,我們通過(guò)稱(chēng)作時(shí)間步的小幅度時(shí)間增量來(lái)步進(jìn)模擬空間中的所有物體// *高度*推薦使用固定長(zhǎng)的時(shí)間步cpFloat timeStep = 1.0/60.0;for(cpFloat time = 0; time < 2; time += timeStep){cpVect pos = cpBodyGetPos(ballBody);cpVect vel = cpBodyGetVel(ballBody);printf("Time is %5.2f. ballBody is at (%5.2f, %5.2f). It's velocity is (%5.2f, %5.2f)\n",time, pos.x, pos.y, vel.x, vel.y);cpSpaceStep(space, timeStep);}// 清理我們的對(duì)象并退出cpShapeFree(ballShape);cpBodyFree(ballBody); cpShapeFree(ground);cpSpaceFree(space);return 0;1.6 支持
獲得支持最好的方式就是訪問(wèn)Chipmunk論壇。上面有許多人使用Chipmunk,應(yīng)用在我知道的各個(gè)平臺(tái)上。如果你在做一個(gè)商業(yè)項(xiàng)目,Howling Moon Software(我的公司)可給與支持。我們可以幫助你實(shí)現(xiàn)自定義Chipmunk行為,以及bug修復(fù)和性能優(yōu)化。
1.7 聯(lián)系
如果你發(fā)現(xiàn)Chipmunk中的任何bug,錯(cuò)誤或者該文檔中壞掉的鏈接,又或者對(duì)于Chipmunk有任何疑問(wèn)、評(píng)論,都可以通過(guò) slembcke@gmail.com (email或者GTalk)聯(lián)系我。
1.8 開(kāi)源協(xié)議
Chipmunk基于MIT協(xié)議。
Copyright (c) 2007-2013 Scott Lembcke and Howling Moon SoftwarePermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.這項(xiàng)協(xié)議意味著對(duì)于商業(yè)項(xiàng)目你不必購(gòu)買(mǎi)許可證或者支付任何費(fèi)用就能使用Chipmunk。(雖然我們真的很感謝捐贈(zèng))
1.9 鏈接
- Chipmunk論壇 - Chipmunk2D官方論壇
- Howling Moon Software - 我合辦的軟件公司(我們提供外包工作)
- Chipmunk2D Pro - Chipmunk的增強(qiáng)版本,我們?yōu)锳RM或者多核平臺(tái)做了一些特定的優(yōu)化,如從圖像或程序數(shù)據(jù)中進(jìn)行自動(dòng)幾何操作,以及為Objective-C做了API封裝。
- 游戲 - 使用Chipmunk做的游戲清單。至少一小部分我們知道。
2. Chipmunk2D 基礎(chǔ)
2.1 概述
在Chipmunk中有4種基本對(duì)象類(lèi)型,分別是
- 剛體:一個(gè)剛體容納著一個(gè)對(duì)象的物理屬性(如質(zhì)量、位置、角度、速度等)。默認(rèn)情況下,它并不具有任何形狀,直到你為它添加一個(gè)或者多個(gè)碰撞形狀進(jìn)去。如果你以前做過(guò)物理粒子,你會(huì)發(fā)現(xiàn)它們的不同之處是剛體可以旋轉(zhuǎn)。在游戲中,通常剛體都是和一個(gè)精靈一一對(duì)應(yīng)關(guān)聯(lián)的。你應(yīng)該構(gòu)建你的游戲以便可以使用剛體的位置和角度來(lái)繪制你的精靈。
- 碰撞形狀:因?yàn)樾螤钆c剛體相關(guān)聯(lián),所以你可以為一個(gè)剛體定義形狀。為了定義一個(gè)復(fù)雜的形狀,你可以給剛體綁定足夠多的形狀。形狀包含著一個(gè)對(duì)象的表面屬性如摩擦力、彈性等。
- 約束/關(guān)節(jié):約束和關(guān)節(jié)被用來(lái)描述剛體之間是如何關(guān)聯(lián)的
- 空間:空間是Chipmunk中模擬對(duì)象的容器。你將剛體、形狀、關(guān)節(jié)添加進(jìn)入一個(gè)空間,然后將空間作為一個(gè)整體進(jìn)行更新??臻g控制著所有的剛體、形狀和約束之間的相互作用。
人們經(jīng)常對(duì)Chipmunk中的剛體和碰撞形狀以及兩者與精靈之間的關(guān)系產(chǎn)生混淆。精靈是對(duì)象的可視化表現(xiàn),而碰撞形狀是定義對(duì)象應(yīng)該如何碰撞的不可見(jiàn)的屬性。精靈和碰撞形狀兩者的位置和角度都是由剛體的運(yùn)動(dòng)控制的。通常你應(yīng)該創(chuàng)建一個(gè)游戲?qū)ο箢?lèi)型,把這些東西捆綁在一起。
2.2 內(nèi)存管理
對(duì)于你將使用的大多數(shù)結(jié)構(gòu)體來(lái)說(shuō),Chipmunk采用了一套或多或少的標(biāo)準(zhǔn)和簡(jiǎn)單直接的內(nèi)存管理方式。拿cpSpace結(jié)構(gòu)體來(lái)舉例:
- cpSpaceNew() - 分配并初始化一個(gè)cpSpace結(jié)構(gòu)體。它先后調(diào)用了cpSpaceAlloc()和cpSpaceInit(cpSpace *space)
- cpSpaceFree(cpSpace *space) - 銷(xiāo)毀并釋放cpSpace結(jié)構(gòu)體
你有責(zé)任釋放掉任何你分配了內(nèi)存空間的結(jié)構(gòu)體。 Chipmunk沒(méi)有采用引用計(jì)數(shù)和垃圾回收機(jī)制。 如果你調(diào)用了一個(gè)new函數(shù),則必須匹配調(diào)用free函數(shù)來(lái)釋放空間,否則會(huì)引起內(nèi)存泄漏。
另外當(dāng)你在棧上分配臨時(shí)結(jié)構(gòu)體,或者寫(xiě)一個(gè)語(yǔ)言綁定,又或者在一個(gè)內(nèi)存受限的環(huán)境下編碼的時(shí)候,如果你在內(nèi)存的分配和初始化上需要更多的控制權(quán),可以使用下面的函數(shù)。大部分人永遠(yuǎn)都不會(huì)使用這些函數(shù)。
- cpSpaceAlloc() - 為一個(gè)cpSpace結(jié)構(gòu)體分配空間,但不進(jìn)行初始化。所有的分配空間的函數(shù)看起來(lái)大致就像這樣:return (cpSpace *)cpcalloc(1, sizeof(cpSpace));。 如果需要的話你可以自己實(shí)現(xiàn)自己的分配空間函數(shù)。把內(nèi)存空間重置為0,不是硬性要求。
- cpSpaceInit(cpSpace *space) - 初始化cpSpace結(jié)構(gòu)體
- cpSpaceDestroy(cpSpace *space) - 釋放由cpSpaceInit()申請(qǐng)的所有內(nèi)存空間,但并不釋放cpSpace結(jié)構(gòu)體本身
就像new和free函數(shù)的對(duì)應(yīng)調(diào)用一樣,任何由alloc函數(shù)分配的內(nèi)存都要由cpfree()或類(lèi)似的函數(shù)來(lái)釋放,任何init函數(shù)調(diào)用都必須對(duì)應(yīng)destroy函數(shù)調(diào)用。
為了能夠更加輕松地集成垃圾回收或其他內(nèi)存管理機(jī)制,Chipmunk有若干可以被重寫(xiě)的編譯時(shí)定義(cpcalloc(), cprealloc(), cpfree())。如果你不是通過(guò)帶有垃圾回收的語(yǔ)言使用Chipmunk,我強(qiáng)烈推薦使用libGC。它為基于C的語(yǔ)言提供了一個(gè)幾乎透明的垃圾收集器。
2.3 基本類(lèi)型
chipmunk_types.h定義了Chipmunk使用的一些基本數(shù)據(jù)類(lèi)型。這些數(shù)據(jù)類(lèi)型可以在編譯時(shí)改變以便適應(yīng)你的需求:
- cpFloat: 浮點(diǎn)型,默認(rèn)為double
- cpVect: 2D矢量,[cpVect相關(guān)文檔](cpVect documentation)
- cpBool: 像每一個(gè)優(yōu)秀的C語(yǔ)言庫(kù)一樣,具有跨語(yǔ)言兼容性,你可以定義自己的布爾類(lèi)型,默認(rèn)為int
- cpDataPointer: 指針類(lèi)型,可以是回調(diào)、用戶(hù)自定義數(shù)據(jù)的指針,默認(rèn)是void*
- cpCollistionType: 碰撞形狀類(lèi)型的唯一標(biāo)識(shí)符,默認(rèn)是unsigned int。自定義類(lèi)型必須支持==運(yùn)算符
- cpGroup: 碰撞組唯一標(biāo)識(shí)符,默認(rèn)是unsigned int。當(dāng)你不想?yún)^(qū)分組別的時(shí)候,可以定義一個(gè)CP_NO_GROUP。自定義類(lèi)型必須支持==運(yùn)算符
- cpLayers: 該類(lèi)型被用作為層的掩碼,默認(rèn)是unsigned int。CP_ALL_LAYERS被用來(lái)定義為所有層位。自定義類(lèi)型必須支持位操作&運(yùn)算符
如果你正在寫(xiě)游戲引擎或者在Chipmunk之上進(jìn)行語(yǔ)言綁定,你可能希望使用對(duì)象的引用代替整數(shù)來(lái)表示碰撞類(lèi)型和碰撞組。我經(jīng)常使用類(lèi)指針來(lái)表示碰撞類(lèi)型,游戲?qū)ο笾羔榿?lái)表示碰撞組。這比到處定義枚舉表簡(jiǎn)單多了。
注意:在iphone上,為了性能和兼容性,cpFloat被定義為float,cpVect是CGPoint的別名。
2.4 數(shù)學(xué)運(yùn)算
首先,Chipmunk默認(rèn)使用雙精度浮點(diǎn)數(shù)進(jìn)行數(shù)學(xué)計(jì)算。在大多數(shù)現(xiàn)代臺(tái)式機(jī)處理器下這樣很可能更快點(diǎn),并意味著你可以不用過(guò)多擔(dān)心浮點(diǎn)舍入引起的誤差。在編譯庫(kù)的時(shí)候你可以修改Chipmunk使用的浮點(diǎn)類(lèi)型。請(qǐng)查看chipmunk_types.h。
Chipmunk為一些常用的數(shù)學(xué)函數(shù)定義了別名以便你可以用Chipmunk的浮點(diǎn)類(lèi)型來(lái)代表float或者double類(lèi)型。在你的代碼里,或許沒(méi)有充分的理由去使用這些別名,除非你預(yù)計(jì)今后你可能會(huì)改變Chipmunk的浮點(diǎn)類(lèi)型,而且你很介意錯(cuò)誤的使用float/double版本的數(shù)學(xué)函數(shù)所造成的2%的性能下降。
有一些函數(shù)或許你會(huì)發(fā)現(xiàn)非常有用:
- cpFloat cpfclamp(cpFloat f, cpFloat min, cpFloat max) - 截?cái)鄁在min和max之間
- cpFloat cpflerp(cpFloat f1, cpFloat f2, cpFloat t) - 對(duì)f1和f2進(jìn)行線性插值
- cpFloat cpflerpconst(cpFloat f1, cpFloat f2, cpFloat d) - 從f1到f2不超過(guò)d的線性插值
浮點(diǎn)數(shù)無(wú)窮大被定義為INFINITY, 很多數(shù)學(xué)庫(kù)中這樣定義,但這實(shí)際上并不是C標(biāo)準(zhǔn)庫(kù)的一部分。
3. Chipmunk矢量:cpVect
3.1 結(jié)構(gòu)體定義、常量和構(gòu)造函數(shù)
定義:
typedef struct cpVect{cpFloat x, y; } cpVect零向量常量:
static const cpVect cpvzero = {0.0f,0.0f};創(chuàng)建新結(jié)構(gòu)體所用的便捷構(gòu)造函數(shù):
cpVect cpv(const cpFloat x, const cpFloat y)3.2 操作運(yùn)算
- cpBool cpveql(const cpVect v1, const cpVect v2) – 檢測(cè)兩個(gè)向量是否相等。在使用C++程序時(shí),Chipmunk提供一個(gè)重載操作符==。(比較浮點(diǎn)數(shù)時(shí)要小心!)
- cpVect cpvadd(const cpVect v1, const cpVect v2) – 兩個(gè)向量相加。在使用C++程序時(shí),Chipmunk提供一個(gè)重載操作符+。
- cpVect cpvsub(const cpVect v1, const cpVect v2) – 兩個(gè)向量相減。在使用C++程序時(shí),Chipmunk提供一個(gè)重載操作符-。
- cpVect cpvneg(const cpVect v) – 使一個(gè)向量反向。在使用C++程序時(shí),Chipmunk提供一個(gè)重載一個(gè)一元負(fù)操作符-。
- cpVect cpvmult(const cpVect v, const cpFloat s) – 標(biāo)量乘法。在使用C++程序時(shí),Chipmunk提供一個(gè)重載操作符*。
- cpFloat cpvdot(const cpVect v1, const cpVect v2) – 向量的點(diǎn)積。
- cpFloat cpvcross(const cpVect v1, const cpVect v2) – 2D向量交叉相乘的模。2D向量交叉相乘的積作為一個(gè)只有z坐標(biāo)的3D向量的z值。函數(shù)返回z坐標(biāo)的值。
- cpVect cpvperp(const cpVect v) – 返回一個(gè)垂直向量。(旋轉(zhuǎn)90度)
- cpVect cpvrperp(const cpVect v) – 返回一個(gè)垂直向量。(旋轉(zhuǎn)-90度)
- cpVect cpvproject(const cpVect v1, const cpVect v2) – 返回向量v1在向量v2上的投影。
- cpVect cpvrotate(const cpVect v1, const cpVect v2) – 使用復(fù)雜的乘法運(yùn)算將向量v1按照向量v2旋轉(zhuǎn)。如果v1不是單位向量,則v1會(huì)被縮放。
- cpVect cpvunrotate(const cpVect v1, const cpVect v2) – 和cpvrotate()相反。
- cpFloat cpvlength(const cpVect v) – 返回v的長(zhǎng)度。
- cpFloat cpvlengthsq(const cpVect v) – 返回v的長(zhǎng)度的平方,如果只是比較長(zhǎng)度的話它的速度比cpvlength()快。
- cpVect cpvlerp(const cpVect v1, const cpVect v2, const cpFloat t) – 在v1和v2之間線性插值。
- cpVect cpvlerpconst(cpVect v1, cpVect v2, cpFloat d) – 以長(zhǎng)度d在v1和v2之間線性插值。
- cpVect cpvslerp(const cpVect v1, const cpVect v2, const cpFloat t) – 在v1和v2之間球形線性插值。
- cpVect cpvslerpconst(const cpVect v1, const cpVect v2, const cpFloat a) – 在v1和v2之間以不超過(guò)角a的弧度值球形線性插值。
- cpVect cpvnormalize(const cpVect v) – 返回a的一個(gè)歸一化副本。作為特殊例子,在調(diào)用cpvzero時(shí)返回cpvzero。
- cpVect cpvclamp(const cpVect v, const cpFloat len) – 將v固定到len上。
- cpFloat cpvdist(const cpVect v1, const cpVect v2) – 返回v1和v2間的距離。
- cpFloat cpvdistsq(const cpVect v1, const cpVect v2) – 返回v1和v2間的距離的平方。如果只是比較距離的話它比cpvdist()快。
- cpBool cpvnear(const cpVect v1, const cpVect v2, const cpFloat dist) – 如果v1和v2間的距離小于dist則返回真。
- cpVect cpvforangle(const cpFloat a) – 返回所給角(以弧度)單位向量。
- cpFloat cpvtoangle(const cpVect v) – 返回v所指的角度方向的弧度。
4. Chipmunk軸對(duì)齊包圍盒:cpBB
4.1 結(jié)構(gòu)體定義和構(gòu)造函數(shù)
- 簡(jiǎn)單的包圍盒結(jié)構(gòu)體,存儲(chǔ)著left,bottom,right,top等值。
- 便捷的構(gòu)造函數(shù),如cpv()函數(shù)一樣返回一個(gè)副本而不是一個(gè)申請(qǐng)的指針。
- 便捷的構(gòu)造函數(shù),用來(lái)構(gòu)造一個(gè)位置為p,半徑為r的一個(gè)圓的包圍盒
4.2 操作運(yùn)算
- cpBool cpBBIntersects(const cpBB a, const cpBB b) - 如果邊界框相交返回true
- cpBool cpBBContainsBB(const cpBB bb, const cpBB other) - 如果bb完全包含other返回true
- cpBool cpBBContainsVect(const cpBB bb, const cpVect v) - 如果bb包含v返回true
- cpBB cpBBMerge(const cpBB a, const cpBB b) - 返回包含a和b的最小的邊界框
- cpBB cpBBExpand(const cpBB bb, const cpVect v) - 返回包含bb和v的最小的邊界框
- cpVect cpBBCenter(const cpBB bb) - 返回bb的中心點(diǎn)矢量
- cpFloat cpBBArea(cpBB bb) - 返回bb矢量表示的邊界框的面積
- cpFloat cpBBMergedArea(cpBB a, cpBB b) - 合并a和b然后返回合并后的矢量的邊界框的面積
- cpFloat cpBBSegmentQuery(cpBB bb, cpVect a, cpVect b) - 返回分段查詢(xún)相交bb的相交點(diǎn)個(gè)數(shù),如果沒(méi)有相交,返回INFINITY
- cpBool cpBBIntersectsSegment(cpBB bb, cpVect a, cpVect b) - 如果由a和b兩端點(diǎn)定義的線段和bb相交返回true
- cpVect cpBBClampVect(const cpBB bb, const cpVect v) - 返回v在邊界框中被截?cái)嗟氖噶康母北?/li>
- cpVect cpBBWrapVect(const cpBB bb, const cpVect v) - 返回v包含邊界框的矢量的副本
5. Chipmunk剛體:cpBody
5.1 游離和靜態(tài)剛體
一般當(dāng)我們創(chuàng)建一個(gè)剛體并將它添加到空間上后,空間就開(kāi)始對(duì)之進(jìn)行模擬,包括了對(duì)剛體位置、速度、受力以及重力影響等的模擬。沒(méi)被添加到空間(沒(méi)有被模擬)的剛體我們把它稱(chēng)之為游離剛體。游離剛體最重要的用途就是用來(lái)當(dāng)作靜態(tài)剛體,但是你仍然可以使用它們來(lái)實(shí)現(xiàn)直接控制物體,如移動(dòng)平臺(tái)。
靜態(tài)剛體是游離剛體,但被設(shè)置了一個(gè)特殊的標(biāo)志以便讓Chipmunk知道它們從不移動(dòng)除非你要求這么做。靜態(tài)剛體有兩個(gè)目的。最初,它們被加入用來(lái)實(shí)現(xiàn)休眠功能。因?yàn)殪o態(tài)剛體不移動(dòng),Chipmunk知道讓那些與靜態(tài)剛體接觸或者連接的物體安全的進(jìn)入休眠。接觸或連接常規(guī)游離剛體的物體從不允許休眠。靜態(tài)剛體的第二個(gè)目的就是讓Chipmunk知道,關(guān)聯(lián)到靜態(tài)剛體的碰撞形狀是不需要更新碰撞檢測(cè)數(shù)據(jù)的。Chipmunk也不需要操心靜態(tài)物體之間的碰撞檢測(cè)。通常所有的關(guān)卡幾何圖形都會(huì)被關(guān)聯(lián)到一個(gè)靜態(tài)剛體上除了那些能夠移動(dòng)的東西,例如平臺(tái)或門(mén)等。
在Chipmunk5.3版本之前,你要?jiǎng)?chuàng)建一個(gè)無(wú)限大質(zhì)量的游離剛體,通過(guò)cpSpaceAddStaticShape()來(lái)添加靜態(tài)形狀。現(xiàn)在你不必這樣做了,并且如果你想使用休眠功能也不應(yīng)該這樣做了。每一個(gè)空間都有一個(gè)專(zhuān)用的靜態(tài)剛體,你可以使用它來(lái)添加靜態(tài)形狀。Chipmunk也會(huì)自動(dòng)將形狀作為靜態(tài)形狀添加到靜態(tài)剛體上。
5.2 內(nèi)存管理函數(shù)
cpBody *cpBodyAlloc(void) cpBody *cpBodyInit(cpBody *body, cpFloat m, cpFloat i) cpBody *cpBodyNew(cpFloat m, cpFloat i)void cpBodyDestroy(cpBody *body) void cpBodyFree(cpBody *body)如上是一套標(biāo)準(zhǔn)的Chipmunk內(nèi)存管理函數(shù)。m和i是剛體的質(zhì)量和轉(zhuǎn)動(dòng)慣量。猜想剛體的質(zhì)量通常是可行的,但是猜想剛體的轉(zhuǎn)動(dòng)慣量卻會(huì)導(dǎo)致一個(gè)很差的模擬。在任何關(guān)聯(lián)到剛體的形狀或者約束從空間移除之前注意不要釋放剛體。
5.3 創(chuàng)建額外靜態(tài)剛體
每一個(gè)cpSpace都有一個(gè)可以直接使用的內(nèi)置靜態(tài)剛體,同時(shí)你也可以便利地構(gòu)建自己的靜態(tài)剛體。一個(gè)潛在的用途就是用在關(guān)卡編輯器中。你可以把關(guān)卡的不同組塊關(guān)聯(lián)到不同的靜態(tài)剛體上,這樣你仍然可以獨(dú)立的移動(dòng)和旋轉(zhuǎn)每一個(gè)組塊。你要做的只是在操作完成之后調(diào)用cpSpaceRehashStatic()來(lái)重建靜態(tài)碰撞檢測(cè)的數(shù)據(jù)。
關(guān)于游離和靜態(tài)剛體的更多信息,請(qǐng)看Chipmunk空間。
cpBody *cpBodyAlloc(void); cpBody *cpBodyInitStatic(cpBody *body) cpBody *cpBodyNewStatic()創(chuàng)建額外的具有無(wú)限的質(zhì)量和轉(zhuǎn)動(dòng)慣量的靜態(tài)剛體。
5.4 屬性
Chipmunk為剛體的多個(gè)屬性提供了getter/setter函數(shù)。如果剛體在休眠狀態(tài),設(shè)置大多數(shù)屬性會(huì)自動(dòng)喚醒它們。如果你想,你也可以直接在cpBody結(jié)構(gòu)體內(nèi)設(shè)置字段。它們都在頭文件中有記錄。
cpFloat cpBodyGetMass(const cpBody *body) void cpBodySetMass(cpBody *body, cpFloat m)剛體的質(zhì)量。
cpFloat cpBodyGetMoment(const cpBody *body) void cpBodySetMoment(cpBody *body, cpFloat i)剛體的轉(zhuǎn)動(dòng)慣量(MoI(譯者注:Moment Of Inertia即轉(zhuǎn)動(dòng)慣量的縮寫(xiě))或有時(shí)只說(shuō)慣量)。慣量就像剛體的旋轉(zhuǎn)質(zhì)量。請(qǐng)查閱下面的函數(shù)來(lái)幫助計(jì)算慣量。
cpVect cpBodyGetPos(const cpBody *body) void cpBodySetPos(cpBody *body, cpVect pos)剛體重心的位置。當(dāng)改變位置的時(shí)候如果你要計(jì)劃對(duì)空間進(jìn)行任何查詢(xún),你可能還需要調(diào)用cpSpaceReindexShapesForBody()來(lái)更新關(guān)聯(lián)形狀的碰撞檢測(cè)信息。
cpVect cpBodyGetVel(const cpBody *body) void cpBodySetVel(cpBody *body, const cpVect value)剛體重心的線速度。
cpVect cpBodyGetForce(const cpBody *body) void cpBodySetForce(cpBody *body, const cpVect value)施加到剛體重心的力。
cpFloat cpBodyGetAngle(const cpBody *body) void cpBodySetAngle(cpBody *body, cpFloat a)剛體的角度,弧度制。當(dāng)改變角度的時(shí)候如果你要計(jì)劃對(duì)空間進(jìn)行任何查詢(xún),你可能還需要調(diào)用cpSpaceReindexShapesForBody()來(lái)更新關(guān)聯(lián)形狀的碰撞檢測(cè)信息。
cpFloat cpBodyGetAngVel(const cpBody *body) void cpBodySetAngVel(cpBody *body, const cpFloat value)剛體的角速度,弧度/秒,
cpFloat cpBodyGetTorque(const cpBody *body) void cpBodySetTorque(cpBody *body, const cpFloat value)施加到剛體的扭矩。
cpVect cpBodyGetRot(const cpBody *body)剛體的旋轉(zhuǎn)向量。可通過(guò)cpvrotate()或者cpvunrotate()進(jìn)行快速旋轉(zhuǎn)。
cpFloat cpBodyGetVelLimit(const cpBody *body) void cpBodySetVelLimit(cpBody *body, const cpFloat value)剛體的速度極限。、默認(rèn)為INFINITY(無(wú)限大),除非你專(zhuān)門(mén)設(shè)置它??梢员挥脕?lái)限制下落速度等。
cpFloat cpBodyGetAngVelLimit(const cpBody *body) void cpBodySetAngVelLimit(cpBody *body, const cpFloat value)剛體以弧度/秒的角速度限制。默認(rèn)為INFINITY,除非你專(zhuān)門(mén)設(shè)置它。
cpSpace* cpBodyGetSpace(const cpBody *body)獲取body所添加進(jìn)去的cpSpace。
cpDataPointer cpBodyGetUserData(const cpBody *body) void cpBodySetUserData(cpBody *body, const cpDataPointer value)使用數(shù)據(jù)指針。使用該指針從回調(diào)中獲取擁有該剛體的游戲?qū)ο蟮囊谩?/p>
5.5 轉(zhuǎn)動(dòng)慣量和面積幫助函數(shù)
使用以下函數(shù)來(lái)近似計(jì)算出剛體的轉(zhuǎn)動(dòng)慣量,如果想得到多個(gè),那就將結(jié)果相加在一起。
- cpFloat cpMomentForCircle(cpFloat m, cpFloat r1, cpFloat r2, cpVect offset) – 計(jì)算空心圓的轉(zhuǎn)動(dòng)慣性,r1和r2是在任何特定順序下的內(nèi)徑和外徑。 (實(shí)心圓圈的內(nèi)徑為0)
- cpFloat cpMomentForSegment(cpFloat m, cpVect a, cpVect b) – 計(jì)算線段的轉(zhuǎn)動(dòng)慣量。端點(diǎn)a和b相對(duì)于剛體。
- cpFloat cpMomentForPoly(cpFloat m, int numVerts, const cpVect *verts, cpVect offset) – 計(jì)算固定多邊形的轉(zhuǎn)動(dòng)慣量,假設(shè)它的中心在質(zhì)心上。offset偏移值被加到每個(gè)頂點(diǎn)。
- cpFloat cpMomentForBox(cpFloat m, cpFloat width, cpFloat height) – 計(jì)算居中于剛體的實(shí)心矩形的轉(zhuǎn)動(dòng)慣量。
轉(zhuǎn)動(dòng)慣量例子
// 質(zhì)量為2,半徑為5的實(shí)心圓的轉(zhuǎn)動(dòng)慣量 cpFloat circle1 = cpMomentForCircle(2, 0, 5, cpvzero);// 質(zhì)量為1,內(nèi)徑為1,外徑為6的空心圓的轉(zhuǎn)動(dòng)慣量 cpFloat circle2 = cpMomentForCircle(1, 2, 6, cpvzero);// 質(zhì)量為1,半徑為3,x軸方向偏離重心量為3的實(shí)心圓的轉(zhuǎn)動(dòng)慣量 cpFloat circle3 = cpMomentForCircle(2, 0, 5, cpv(3, 0));// 復(fù)合對(duì)象。居中于重心的1x4的矩形和y軸偏移重心量為3,半徑為1的實(shí)心圓 // 只需將轉(zhuǎn)動(dòng)慣量相加到一起 cpFloat composite = cpMomentForBox(boxMass, 1, 4) + cpMomentForCircle(circleMass, 0, 1, cpv(0, 3));如果你想近似計(jì)算諸如質(zhì)量或密度此類(lèi)的東西,可以使用下列函數(shù)來(lái)獲取Chipmunk形狀區(qū)域。
- cpFloat cpAreaForCircle(cpFloat r1, cpFloat r2) – 空心圓形狀面積
- cpFloat cpAreaForSegment(cpVect a, cpVect b, cpFloat r) – 斜線段面積。(如果半徑為0的話永遠(yuǎn)為0)
- cpFloat cpAreaForPoly(const int numVerts, const cpVect *verts) – 多邊形形狀的面積。多邊形為凹多邊形時(shí)返回一個(gè)負(fù)值。
5.6 坐標(biāo)系轉(zhuǎn)換函數(shù)
許多事情被定義在剛體的局部坐標(biāo),也就意味著(0,0)是剛體的重心和軸線旋轉(zhuǎn)中心。
- cpVect cpBodyLocal2World(const cpBody *body, const cpVect v) – 從剛體局部坐標(biāo)系轉(zhuǎn)換到世界坐標(biāo)系
- cpVect cpBodyWorld2Local(const cpBody *body, const cpVect v) – 從世界坐標(biāo)系轉(zhuǎn)換到剛體的局部坐標(biāo)系
5.7 施加力和力矩
人們有時(shí)候容易混淆力和沖量之間的區(qū)別。沖量基本上是一個(gè)在非常短的時(shí)間內(nèi)施加的一個(gè)非常大的力,就像一個(gè)球擊中一堵墻或者大炮射擊一樣。Chipmunk的沖量會(huì)在一瞬間直接施加在物體的速度上。無(wú)論是力還是沖量都受到物體質(zhì)量的影響。物體質(zhì)量翻倍,則效果減半。
- void cpBodyResetForces(cpBody *body) – 對(duì)剛體施加0值的力和扭矩
- void cpBodyApplyForce(cpBody *body, const cpVect f, const cpVect r) – 在離重心相對(duì)偏移量為r的位置施加f的力于body上
- void cpBodyApplyImpulse(cpBody *body, const cpVect j, const cpVect r) – 在離重心相對(duì)偏移量為r的位置施加j的沖量于body上。
注: cpBodyApplyForce()和cpBodyApplyImpulse()兩者都是在絕對(duì)坐標(biāo)系中施加力或者沖量,并在絕對(duì)坐標(biāo)系中產(chǎn)生相對(duì)的偏移。(偏移量相對(duì)于重心位置,但不隨剛體旋轉(zhuǎn))
5.8 休眠函數(shù)
Chipmunk支持休眠功能,以便其停止使用CPU時(shí)間來(lái)模擬移動(dòng)的對(duì)象組。更多信息請(qǐng)查閱cpSpace部分。
- cpBool cpBodyIsSleeping(const cpBody *body) – 如果剛體在休眠則返回true。
- void cpBodyActivate(cpBody *body) – 重設(shè)剛體的閑置時(shí)間。如果在休眠,則會(huì)喚醒它以及和它接觸的任何其他剛體。
- void cpBodySleep(cpBody *body) – 強(qiáng)制一個(gè)剛體立即進(jìn)入休眠,即使它在半空中。不能從回調(diào)中被調(diào)用。
- void cpBodyActivateStatic(cpBody *body, cpShape *filter) – 和cpBodyActivate()功能類(lèi)似。激活剛體接觸的所有剛體。如果filter不為NULL,那么只有通過(guò)篩選過(guò)濾的剛體才會(huì)被喚醒。
當(dāng)對(duì)象在Chipmunk中處于休眠時(shí),和它接觸或連接在一起的所有剛體都會(huì)作為一組進(jìn)入休眠。當(dāng)對(duì)象被喚醒時(shí),和它一組的所有對(duì)象都會(huì)被喚醒。 cpBodySleepWithGroup()允許你將群組中的對(duì)象一起休眠。如果你通過(guò)一個(gè)新的組給groups 傳遞NULL值,則它和cpBodySleep()功能一樣。如果你為groups傳入一個(gè)休眠的剛體,那么當(dāng)group是喚醒狀態(tài)時(shí),body也會(huì)被喚醒。你可以通過(guò)這來(lái)初始化關(guān)卡并將堆棧中的對(duì)象置為預(yù)休眠狀態(tài)。
休眠例子
// 構(gòu)建一堆箱子 // 強(qiáng)制它們進(jìn)入休眠直到他們第一次被接觸 // 將它們放進(jìn)一組以便接觸它們?nèi)我庖粋€(gè)都會(huì)喚醒他們 cpFloat size = 20; cpFloat mass = 1; cpFloat moment = cpMomentForBox(mass, size, size);cpBody *lastBody = NULL;for(int i=0; i<5; i++){cpBody *body = cpSpaceAddBody(space, cpBodyNew(mass, moment));cpBodySetPos(body, cpv(0, i*size));cpShape *shape = cpSpaceAddShape(space, cpBoxShapeNew(body, size, size));cpShapeSetFriction(shape, 0.7);// 你可以使用任意休眠剛體作為組別的標(biāo)識(shí)符// 這里我們只保存了我們初始化的最后一個(gè)剛體的引用// 傳入NULL值作為組別將啟動(dòng)一個(gè)新的休眠組// 你必須在完全初始化對(duì)象后這么做// 添加形狀或調(diào)用setter函數(shù)將會(huì)喚醒剛體cpBodySleepWithGroup(body, lastBody);lastBody = body; }5.9 迭代器
typedef void (*cpBodyShapeIteratorFunc)(cpBody *body, cpShape *shape, void *data) void cpBodyEachShape(cpBody *body, cpBodyShapeIteratorFunc func, void *data)迭代與body相關(guān)的且附加到空間上的所有形狀,每次迭代都會(huì)調(diào)用func函數(shù)。data作為上下文值傳遞。使用這些回調(diào)來(lái)刪除形狀是安全的。
typedef void (*cpBodyConstraintIteratorFunc)(cpBody *body, cpConstraint *constraint, void *data) void cpBodyEachConstraint(cpBody *body, cpBodyConstraintIteratorFunc func, void *data)迭代與body相關(guān)的且附加到空間上的所有約束,每次迭代都會(huì)調(diào)用func函數(shù)。data作為上下文值傳遞。使用這些回調(diào)來(lái)刪除約束是安全的。
typedef void (*cpBodyArbiterIteratorFunc)(cpBody *body, cpArbiter *arbiter, void *data) void cpBodyEachArbiter(cpBody *body, cpBodyArbiterIteratorFunc func, void *data)這個(gè)更有趣。迭代與body相關(guān)的碰撞對(duì),每次迭代都會(huì)調(diào)用func函數(shù)。調(diào)用cpArbiterGet[Bodies|Shapes]()或者CP_ARBITER_GET_[BODIES|SHAPES]()可以取到與此次碰撞相關(guān)的那兩個(gè)剛體或形狀。你可以用它來(lái)檢查各種碰撞信息,例如,是否接觸了地面,是否接觸了另一特定的物體,物體受到的碰撞力有多大等。那些被碰撞回調(diào)拒絕的傳感器類(lèi)型的形狀或是被cpArbiterIngnore()忽略的仲裁者是不會(huì)被接觸圖形跟蹤記錄的。
注:如果你的編譯器支持閉包(如Clang),還有另外一組函數(shù)可以調(diào)用,如cpBodyEachShape_b()等。更多信息見(jiàn)chipmunk.h。
Crushing例子
struct CrushingContext {cpFloat magnitudeSum;cpVect vectorSum; };static void EstimateCrushingHelper(cpBody *body, cpArbiter *arb, struct CrushingContext *context) {cpVect j = cpArbiterTotalImpulseWithFriction(arb);context->magnitudeSum += cpvlength(j);context->vectorSum = cpvadd(context->vectorSum, j); }cpFloat EstimateCrushForce(cpBody *body, cpFloat dt) {struct CrushingContext crush = {0.0f, cpvzero};cpBodyEachArbiter(body, (cpBodyArbiterIteratorFunc)EstimateCrushingHelper, &crush);// 通過(guò)比較向量和以及幅度和來(lái)查看碰撞的力量彼此相對(duì)有多大cpFloat crushForce = (crush.magnitudeSum - cpvlength(crush.vectorSum))*dt; }5.10 嵌入回調(diào)
這部分是殘留?,F(xiàn)在你可以看看星球演示這個(gè)例子,看如何使用嵌入回調(diào)來(lái)實(shí)現(xiàn)的行星重力。
5.11 雜項(xiàng)函數(shù)
- cpBool cpBodyIsStatic(const cpBody *body) - 如果body是靜態(tài)剛體的話,返回true。無(wú)論是cpSpace.staticBody,還是由cpBodyNewStatic()或者cpBodyInitStatic()創(chuàng)建的剛體。
- cpBool cpBodyIsRogue(const cpBody *body)- 如果剛體從來(lái)沒(méi)有被加入到空間的話返回true。
5.12 札記
- 如果可能的話使用力來(lái)修正剛體。這樣是最穩(wěn)定的。
- 修正剛體的速度是不可避免的,但是在每幀對(duì)剛體的速度做巨大的變化會(huì)造成一些奇怪的模擬。你可以自由實(shí)驗(yàn),但別說(shuō)我沒(méi)警告你哦。
- 不要在單步中修正剛體的位置除非你確實(shí)知道你在干什么。否則你得到的位置、速度則會(huì)不同步。
- 如果在調(diào)用cpSpaceRemoveShape()之前你就要釋放一個(gè)剛體,那么會(huì)引起崩潰。
6. Chipmunk碰撞形狀:cpShape
當(dāng)前有三種類(lèi)型的碰撞形狀:
如果你愿意,你可以在一個(gè)剛體上添加任意數(shù)量的形狀。這就是為什么兩種類(lèi)型(形狀和剛體)是分離開(kāi)的。這將會(huì)讓你足夠靈活的來(lái)給相同對(duì)象的不同區(qū)域提供不同的摩擦力、彈性以及回調(diào)值。
不管創(chuàng)建何種類(lèi)型的形狀,你總是會(huì)得到一個(gè)cpShape*指針。這是因?yàn)镃hipmunk的形狀是不透明的類(lèi)型。想象一下具體的碰撞形狀類(lèi)型,如cpCircleShape, cpSegmentShape和cpPolyShape, 他們都是cpShape的私有子類(lèi)。但是你仍然可以使用getter函數(shù)來(lái)獲取他們的屬性,而不需要將cpShape指針轉(zhuǎn)成他們特定的類(lèi)型指針。
6.1 札記
Chipmunk直到 6.1.2 版本才支持線段、線段碰撞。由于兼容性的原因,你必須調(diào)用cpEnableSegmentToSegmentCollisions()來(lái)全局明確地啟用它們。 (感謝LegoCylon對(duì)此的幫助)
6.2 屬性
Chipmunk為一些碰撞形狀屬性提供了getter/ setter函數(shù)。如果形狀關(guān)聯(lián)的剛體在休眠,設(shè)置多數(shù)屬性都會(huì)自動(dòng)喚醒它們。如果你想的話,也可以直接設(shè)置cpShape結(jié)構(gòu)的某些字段。他們?cè)陬^文件中都記錄有。
cpBody * cpShapeGetBody(const cpShape *shape) void cpShapeSetBody(cpShape *shape, cpBody *body)只有當(dāng)形狀尚未添加進(jìn)空間的時(shí)候才能關(guān)聯(lián)到一個(gè)剛體。
cpBB cpShapeGetBB(const cpShape *shape)上面得到的是形狀的碰撞包圍盒。只能保證在cpShapeCacheBB()或cpSpaceStep()調(diào)用后是有效的。移動(dòng)形狀所連接到剛體并不更新它的包圍盒。對(duì)于沒(méi)有關(guān)聯(lián)到剛體的用于查詢(xún)的形狀,也可以使用cpShapeUpdate()。
cpBool cpShapeGetSensor(const cpShape *shape) void cpShapeSetSensor(cpShape *shape, cpBool value)用來(lái)標(biāo)識(shí)形狀是否是一個(gè)感應(yīng)器的布爾值。感應(yīng)器只調(diào)用碰撞回調(diào),但卻不產(chǎn)生真實(shí)的碰撞。
cpFloat cpShapeGetElasticity(const cpShape *shape) void cpShapeSetElasticity(cpShape *shape, cpFloat value)形狀的彈性。0.0表示沒(méi)有彈性,1.0b表示“富有”彈性。然而由于存在模擬誤差,不推薦使用1.0或更高的值。碰撞的彈性是由單個(gè)形狀的彈性相乘得到。
cpFloat cpShapeGetFriction(const cpShape *shape) void cpShapeSetFriction(cpShape *shape, cpFloat value)摩擦系數(shù)。Chipmunk使用的是庫(kù)侖摩擦力模型,0.0值表示無(wú)摩擦。碰撞間的摩擦是由單個(gè)形狀的摩擦相乘找到。摩擦系數(shù)表
cpVect cpShapeGetSurfaceVelocity(const cpShape *shape) void cpShapeSetSurfaceVelocity(cpShape *shape, cpVect value)物體的表面速度。可用于創(chuàng)建傳送帶或移動(dòng)的玩家。此值在計(jì)算摩擦?xí)r才會(huì)使用,不影響碰撞。
cpCollisionType cpShapeGetCollisionType(const cpShape *shape) void cpShapeSetCollisionType(cpShape *shape, cpCollisionType value)你可以為Chipmunk的碰撞形狀指定類(lèi)型從而在接觸特定類(lèi)型物體的時(shí)候觸發(fā)回調(diào)。更多信息請(qǐng)參見(jiàn)回調(diào)部分。
cpGroup cpShapeGetGroup(const cpShape *shape) void cpShapeSetGroup(cpShape *shape, cpGroup value)在相同的非零組中,形狀間不產(chǎn)生碰撞。在創(chuàng)建了一個(gè)許多形狀組成的物體,但卻不想自身與自身之間發(fā)生碰撞,這會(huì)很有用。默認(rèn)值為CP_NO_GROUP。
cpLayers cpShapeGetLayers(const cpShape *shape) void cpShapeSetLayers(cpShape *shape, cpLayers value)只有在相同的位平面內(nèi)形狀間才發(fā)生碰撞。比如(a->layers & b->layers) != 0。默認(rèn)情況下,一個(gè)形狀占據(jù)所有的位平面。如果你不熟悉如何使用它們,維基百科有篇很好的文章介紹了位掩碼的相關(guān)知識(shí)你可以閱讀下。默認(rèn)值為CP_ALL_LAYERS。
cpSpace* cpShapeGetSpace(const cpShape *shape)獲取形狀所屬的空間。
cpDataPointer cpShapeGetUserData(const cpShape *shape) void cpShapeSetUserData(cpShape *shape, cpDataPointer value)用戶(hù)自定義數(shù)據(jù)的指針。如果你設(shè)置將其指向形狀關(guān)聯(lián)的游戲?qū)ο?#xff0c;那么你可以在Chipmunk回調(diào)中訪問(wèn)你的游戲?qū)ο蟆?/p>
6.3 碰撞過(guò)濾
Chipmunk 有兩種主要的途徑來(lái)忽略碰撞: 群組和層。
群組是為了忽略一個(gè)復(fù)雜對(duì)象內(nèi)部元素之間的碰撞。布娃娃是一個(gè)很好的例子。當(dāng)把手臂和軀干連接到一起的時(shí)候,你會(huì)希望它們可以部分重疊。群組允許這樣做。相同群組間的形狀不產(chǎn)生碰撞。所以通過(guò)將一個(gè)布娃娃的所有形狀放在同一群組中,就會(huì)阻止其碰撞自身的其它部件。
層允許你將碰撞的形狀分離在互斥的位面。形狀可以隸屬于一個(gè)或多個(gè)層,而兩個(gè)形狀要發(fā)生碰撞,必須有至少一個(gè)層是相同的。舉一個(gè)簡(jiǎn)單的例子,比如說(shuō)形狀A(yù)是在第1層,形狀B是在第2層和形狀C是在層1和2上。形狀A(yù)和B不會(huì)互相碰撞,但形狀C將與這兩個(gè)A和B發(fā)生碰撞
層也可以用于建立基于碰撞的規(guī)則。比如說(shuō)在你的游戲中有四種類(lèi)型的形狀。玩家,敵人,玩家子彈,敵人子彈。玩家應(yīng)該和敵人發(fā)生碰撞,但子彈卻不應(yīng)該和發(fā)射者碰撞。圖表類(lèi)似下圖:
| Player | - | (1) | |
| Enemy | - | - | (3) |
| Player Bullet | - | - | - |
| Enemy Bullet | - | - | - |
圖表中‘-’代表冗余,數(shù)字的地方應(yīng)該發(fā)生碰撞。你可以每一個(gè)規(guī)則對(duì)應(yīng)一個(gè)層。然后將層添加到類(lèi)型上:玩家應(yīng)該在層1和2中,敵人應(yīng)該是在層1和3中,玩家的子彈應(yīng)該是在層3中,敵人的子彈應(yīng)該是在層2中。這種把層當(dāng)作規(guī)則的方式,可以定義多達(dá)32個(gè)規(guī)則。默認(rèn)cpLayers類(lèi)型為unsigned int在大多數(shù)系統(tǒng)是32位的。如果你需要更多的比特來(lái)完成工作, 你可以在chipmunk_types.h中重新定義cpLayers類(lèi)型。
還有最后一個(gè)方法通過(guò)碰撞處理函數(shù)來(lái)過(guò)濾碰撞。詳情請(qǐng)見(jiàn)回調(diào)部分。碰撞處理程序可以更靈活,但它們也是最慢的方法。所以,你要優(yōu)先嘗試使用群組或?qū)印?/p>
6.4 內(nèi)存管理函數(shù)
void cpShapeDestroy(cpShape *shape) void cpShapeFree(cpShape *shape)Destroy和Free函數(shù)由所有形狀類(lèi)型共享。分配和初始化函數(shù)特定于每一個(gè)形狀。見(jiàn)下文。
6.5 其他函數(shù)
- cpBB cpShapeCacheBB(cpShape *shape) – 同步shape和與之關(guān)聯(lián)的剛體
- cpBB cpShapeUpdate(cpShape *shape, cpVect pos, cpVect rot) – 設(shè)置形狀的位置和旋轉(zhuǎn)角度
- void cpResetShapeIdCounter(void) – Chipmunk使用了一個(gè)計(jì)數(shù)器,以便每一個(gè)新的形狀都能在空間索引中使用唯一的哈希值。因?yàn)闀?huì)影響發(fā)現(xiàn)和處理碰撞的順序,所以每次用新的形狀重建空間時(shí)你可以重置形狀計(jì)數(shù)器。否則,模擬可能會(huì)有很(極)小的差別。
6.6 圓形形狀
cpCircleShape *cpCircleShapeAlloc(void) cpCircleShape *cpCircleShapeInit(cpCircleShape *circle, cpBody *body, cpFloat radius, cpVect offset) cpShape *cpCircleShapeNew(cpBody *body, cpFloat radius, cpVect offset)body是圓形形狀關(guān)聯(lián)的剛體。offset是在剛體局部坐標(biāo)系內(nèi),與剛體中心的偏移量。
cpVect cpCircleShapeGetOffset(cpShape *circleShape) cpFloat cpCircleShapeGetRadius(cpShape *circleShape)圓形形狀屬性的getter函數(shù)。傳一個(gè)非圓形形狀將會(huì)拋出一個(gè)斷言。
6.7 線段形狀
cpSegmentShape* cpSegmentShapeAlloc(void) cpSegmentShape* cpSegmentShapeInit(cpSegmentShape *seg, cpBody *body, cpVect a, cpVect b, cpFloat radius) cpShape* cpSegmentShapeNew(cpBody *body, cpVect a, cpVect b, cpFloat radius)body是線段形狀關(guān)聯(lián)的剛體,a和b是端點(diǎn),radius是線段的厚度。
cpVect cpSegmentShapeGetA(cpShape *shape) cpVect cpSegmentShapeGetA(cpShape *shape) cpVect cpSegmentShapeGetNormal(cpShape *shape) cpFloat cpSegmentShapeGetRadius(cpShape *shape)線段屬性的getter函數(shù)。傳入一個(gè)非線段形狀會(huì)拋出一個(gè)斷言。
void cpSegmentShapeSetNeighbors(cpShape *shape, cpVect prev, cpVect next)當(dāng)你有一些連接在一起的線段形狀時(shí),線段仍然可以與線段間的“裂縫”碰撞。通過(guò)設(shè)置相鄰線段的端點(diǎn),你告訴Chipmunk來(lái)避免裂縫內(nèi)部碰撞。
6.8 多邊形形狀
cpPolyShape *cpPolyShapeAlloc(void) cpPolyShape *cpPolyShapeInit(cpPolyShape *poly, cpBody *body, int numVerts, const cpVect *verts, cpVect offset) cpShape *cpPolyShapeNew(cpBody *body, int numVerts, const cpVect *verts, cpVect offset)body是多邊形關(guān)聯(lián)的剛體,verts是一個(gè)cpVect結(jié)構(gòu)體數(shù)組,定義了一個(gè)沿順時(shí)針圍成的凸多邊形(譯者注:原文是凸包,維基百科-凸包),offset是在剛體局部坐標(biāo)系中與剛體重心的偏移量。當(dāng)頂點(diǎn)沒(méi)形成凸多邊形或者不是順時(shí)針順序的時(shí)候會(huì)拋出一個(gè)斷言。
cpPolyShape *cpPolyShapeInit2(cpPolyShape *poly, cpBody *body, int numVerts, const cpVect *verts, cpVect offset, cpFloat radius) cpShape *cpPolyShapeNew2(cpBody *body, int numVerts, cpVect *verts, cpVect offset, cpFloat radius)和上面的一樣,但允許你創(chuàng)建一個(gè)帶有半徑的多邊形形狀。(我知道命名有些詞不達(dá)意,在Chipmunk7中將會(huì)清理)
int cpPolyShapeGetNumVerts(cpShape *shape) cpVect cpPolyShapeGetVert(cpShape *shape, int index) cpFloat cpPolyShapeGetRadius()多邊形形狀屬性的getter函數(shù)。傳遞一個(gè)非多邊形形狀或者不存在的index將會(huì)拋出一個(gè)斷言。
盒子
因?yàn)楹凶釉谖锢碛螒蛑刑毡?#xff0c;Chipmunk提供了創(chuàng)建盒形多邊形的快捷方式。盒子總是會(huì)被居中放置在它們所關(guān)聯(lián)的剛體的重心位置。如果你想創(chuàng)建一個(gè)偏離中心的盒子,必須使用cpPolyShapeNew()或cpPolyShapeInit()。
cpPolyShape *cpBoxShapeInit(cpPolyShape *poly, cpBody *body, cpFloat width, cpFloat height) cpPolyShape *cpBoxShapeInit2(cpPolyShape *poly, cpBody *body, cpBB box) cpPolyShape *cpBoxShapeInit3(cpPolyShape *poly, cpBody *body, cpBB box, cpFloat radius)cpShape *cpBoxShapeNew(cpBody *body, cpFloat width, cpFloat height) cpShape *cpBoxShapeNew2(cpBody *body, cpBB box) cpShape *cpBoxShapeNew3(cpBody *body, cpBB box, cpFloat radius)多邊形輔助函數(shù)
- cpBool cpPolyValidate(const cpVect *verts, const int numVerts) - 檢測(cè)定點(diǎn)數(shù)組是否能順時(shí)針圍成凸多邊形。
- cpVect cpCentroidForPoly(const int numVerts, const cpVect *verts) - 計(jì)算多邊形的幾何中心。
- void cpRecenterPoly(const int numVerts, cpVect *verts) - 把一個(gè)多邊形居中到(0,0),用定點(diǎn)減去幾何中心。
凸包輔助函數(shù)
int cpConvexHull(int count, cpVect *verts, cpVect *result, int *first, cpFloat tol)計(jì)算一個(gè)給定集合的凸包。返回凸包里點(diǎn)的數(shù)量。result必須是一個(gè)指向cpVect數(shù)組的指針,至少有count個(gè)元素。如果result是NULL,數(shù)組verts會(huì)被縮減。first是一個(gè)可選的整型指針用來(lái)存儲(chǔ)凸包的起點(diǎn)(即verts[first] == result[0])。tol是凸包允許被繼續(xù)收縮的幅度。0.0公差表示一個(gè)精確的凸包。
#define CP_CONVEX_HULL(inputCount, inputVerts, outputCount_varName, outputVerts_varName)cpConvexHull()的簡(jiǎn)化宏。在棧上用alloca()創(chuàng)建數(shù)組,然后調(diào)用cpConvexHull()。因?yàn)檩敵鰯?shù)組是在棧上創(chuàng)建的所以不需要釋放。
cpConvexHull例子
int first = 0;// 創(chuàng)建空間來(lái)存儲(chǔ)凸包。 // alloca(),或者可變長(zhǎng)度的數(shù)組會(huì)更好,但是不要總考慮可移植性。 cpVect *hullVerts = (cpVect *)calloc(vertCount, sizeof(cpVect)); int hullCount = cpConvexHull(vertCount, verts, hullVerts, &first, 0.0);// 這里hullVerts[0]和verts[first]將會(huì)是相等的。 // 如果你不關(guān)心`first`指針,可以傳NULL。cpBody *body = cpBodyNew(mass, cpMomentForPoly(mass, hullCount, hullVerts, cpvzero)); cpShape *shape = cpPolyShapeNew(body, hullCount, hullVerts, cpvzero);free(hullVerts);// ********* // 另外你可以使用CP_CONVEX_HULL()宏來(lái)省點(diǎn)事// 這個(gè)宏會(huì)聲明hullCount和hullVerts變量。 // hullVerts是在棧上申請(qǐng)的空間,不需要釋放。 CP_CONVEX_HULL(count, verts, hullCount, hullVerts)cpBody *body = cpBodyNew(mass, cpMomentForPoly(mass, hullCount, hullVerts, cpvzero)); cpShape *shape = cpPolyShapeNew(body, hullCount, hullVerts, cpvzero);6.9 修改cpShapes
簡(jiǎn)短的回答是你不能修改,因?yàn)檫@些更改都只會(huì)被提煉成形狀表面的位置的變化,連速度都不會(huì)變。長(zhǎng)一點(diǎn)兒的回答是,你可以使用“不安全”的API,但是你要知道現(xiàn)實(shí)生活中的物理實(shí)驗(yàn)是不會(huì)得到這樣的結(jié)果的。這些額外的功能都在單獨(dú)的頭文件chipmunk_unsafe.h中定義。
6.10 札記
- 你可以將多個(gè)碰撞形狀關(guān)聯(lián)到剛體上。這樣你就可以創(chuàng)建幾乎任何形狀。
- 關(guān)聯(lián)在同一個(gè)剛體上的形狀不會(huì)產(chǎn)生碰撞。你不必?fù)?dān)心同個(gè)剛體上的形狀的重疊問(wèn)題。
- 確保剛體和剛體的碰撞形狀都被添加進(jìn)了空間。除非你有一個(gè)外部剛體或者一個(gè)自己維護(hù)的剛體,這中情況下,只需把形狀添加進(jìn)空間。
7. Chipmunk空間:cpSpace
Chipmunk的空間是模擬的基本單元。你將剛體、形狀和約束添加進(jìn)去然后通過(guò)時(shí)間來(lái)步進(jìn)更新模擬。
7.1 什么是迭代?為什么我要關(guān)心?
Chipmunk使用一個(gè)迭代求解器來(lái)計(jì)算出空間剛體之間的力。也就是說(shuō)它建立了剛體間的所有碰撞、關(guān)節(jié)和約束的一個(gè)列表,并在列表中逐個(gè)考慮每一個(gè)剛體的若干條件。遍數(shù)這些條件便得到迭代次數(shù),且每次迭代會(huì)使求解更準(zhǔn)確。如果你使用太多的迭代,物理效果看起來(lái)應(yīng)該不錯(cuò)并且堅(jiān)實(shí)穩(wěn)定,但可能消耗太多的CPU時(shí)間。如果你使用過(guò)少的迭代,模擬仿真似乎看起來(lái)有些糊狀或彈性,而物體應(yīng)該是堅(jiān)硬的。設(shè)置迭代次數(shù)可以讓你在CPU使用率和物理精度上做出平衡。 Chipmunk中默認(rèn)的迭代值是10,足以滿(mǎn)足大多數(shù)簡(jiǎn)單的游戲。
7.2 休眠
休眠是Chipmunk5.3新功能,是指空間停用已停止移動(dòng)的整個(gè)對(duì)象群組,以節(jié)省CPU時(shí)間和電池壽命的能力。為了使用此功能,你必須做兩件事情。第一個(gè)是,你必須將你的所有靜態(tài)幾何關(guān)聯(lián)到靜態(tài)剛體。如果對(duì)象接觸的是非靜態(tài)游離體,則它們不能進(jìn)入休眠,即使它的形狀是作為靜態(tài)形狀添加的。第二個(gè)是,你必須通過(guò)cpSpace.sleepTimeThreshold設(shè)置一個(gè)時(shí)間閾值來(lái)顯式啟用休眠。如果你沒(méi)有明確設(shè)置cpSpace.idleSpeedThreshold,那么Chipmunk會(huì)基于當(dāng)前重力自動(dòng)產(chǎn)生一個(gè)休眠閾值。
7.3 屬性
int cpSpaceGetIterations(const cpSpace *space) void cpSpaceSetIterations(cpSpace *space, int value)迭代次數(shù)允許你控制求解器計(jì)算的精度。默認(rèn)值為10。更多信息見(jiàn)上面。
cpVect cpSpaceGetGravity(const cpSpace *space) void cpSpaceSetGravity(cpSpace *space, cpVect value)施加到空間的全局重力。默認(rèn)是cpvzero??梢酝ㄟ^(guò)編寫(xiě)自定義積分函數(shù)來(lái)重寫(xiě)每個(gè)剛體。
cpFloat cpSpaceGetDamping(const cpSpace *space) void cpSpaceSetDamping(cpSpace *space, cpFloat value)施加到空間的簡(jiǎn)單的阻尼值。數(shù)值0.9意味著每個(gè)剛體每秒會(huì)損失速度會(huì)損失掉10%。默認(rèn)值為1。像重力一樣,阻尼值也可以在每個(gè)剛體上重寫(xiě)。
cpFloat cpSpaceGetIdleSpeedThreshold(const cpSpace *space) void cpSpaceSetIdleSpeedThreshold(cpSpace *space, cpFloat value)剛體被考慮為靜止限制的速度閾值。默認(rèn)值為0,意味著讓空間來(lái)估算猜測(cè)基于重力的良好的閾值。
cpFloat cpSpaceGetSleepTimeThreshold(const cpSpace *space) void cpSpaceSetSleepTimeThreshold(cpSpace *space, cpFloat value)一組剛體休眠需要保持靜止閑置的時(shí)間閾值。默認(rèn)值為INFINITY, 禁用了休眠功能。
cpFloat cpSpaceGetCollisionSlop(const cpSpace *space) void cpSpaceSetCollisionSlop(cpSpace *space, cpFloat value)支持形狀間的重疊量。鼓勵(lì)將這個(gè)值設(shè)置高點(diǎn)而不必在意重疊,因?yàn)樗岣吡朔€(wěn)定性。它默認(rèn)值為0.1。
cpFloat cpSpaceGetCollisionBias(const cpSpace *space) void cpSpaceSetCollisionBias(cpSpace *space, cpFloat value)Chipmunk讓快速移動(dòng)的物體重疊,然后修復(fù)重疊。即使橫掃碰撞被支持,重疊對(duì)象也不可避免,并且這是一個(gè)高效,穩(wěn)定的方式來(lái)處理重疊的對(duì)象。控制重疊百分比的偏置值在1秒后仍然是不固定的,默認(rèn)~0.2%。有效值是在0到1的范圍內(nèi),但由于穩(wěn)定的原因不推薦使用0。默認(rèn)值的計(jì)算公式為cpfpow(1.0F - 0.1F,60.0f),這意味著Chipmunk試圖在1/60s內(nèi)糾正10%的錯(cuò)誤。注:非常非常少的游戲需要更改此值。
cpTimestamp cpSpaceGetCollisionPersistence(const cpSpace *space) void cpSpaceSetCollisionPersistence(cpSpace *space, cpTimestamp value)空間保持碰撞的幀數(shù)量。有助于防止抖動(dòng)接觸惡化。默認(rèn)值為3,非常非常非常少的游戲需要更改此值。
cpFloat cpSpaceGetCurrentTimeStep(const cpSpace *space)檢索當(dāng)前(如果你是從cpSpaceStep()回調(diào))或最近(在cpSpaceStep()之外調(diào)用)的時(shí)間步長(zhǎng)。
cpFloat cpSpaceIsLocked(const cpSpace *space)在回調(diào)中返回true時(shí),意味著你不能從空間添加/刪除對(duì)象??梢赃x擇創(chuàng)建一個(gè)post-step回調(diào)來(lái)替代。
cpDataPointer cpSpaceGetUserData(const cpSpace *space) void cpSpaceSetUserData(cpSpace *space, cpDataPointer value)用戶(hù)定義的數(shù)據(jù)指針。這點(diǎn)在游戲狀態(tài)對(duì)象或擁有空間的場(chǎng)景管理對(duì)象上是很有用的。
cpBody * cpSpaceGetStaticBody(const cpSpace *space)空間中專(zhuān)用的靜態(tài)剛體。你不必使用它,而是因?yàn)樗膬?nèi)存由空間自動(dòng)管理,非常方便。如果你想要做回調(diào)的話,你可以將它的數(shù)據(jù)指針指向一些有用的東西。
7.4 內(nèi)存管理函數(shù)
cpSpace* cpSpaceAlloc(void) cpSpace* cpSpaceInit(cpSpace *space) cpSpace* cpSpaceNew()void cpSpaceDestroy(cpSpace *space) void cpSpaceFree(cpSpace *space)更多標(biāo)準(zhǔn)的Chipmunk內(nèi)存函數(shù)。
void cpSpaceFreeChildren(cpSpace *space)這個(gè)函數(shù)將釋放所有已添加到空間中的的形狀、剛體和關(guān)節(jié)。不要釋放space空間。你仍然需要自己調(diào)用cpSpaceFree()。在一個(gè)真正的游戲中你可能永遠(yuǎn)不會(huì)使用這個(gè),因?yàn)槟愕挠螒驙顟B(tài)或者游戲控制器應(yīng)該會(huì)管理從空間移除并釋放對(duì)象。
7.5 操作運(yùn)算
cpShape *cpSpaceAddShape(cpSpace *space, cpShape *shape) cpShape *cpSpaceAddStaticShape(cpSpace *space, cpShape *shape) cpBody *cpSpaceAddBody(cpSpace *space, cpBody *body) cpConstraint *cpSpaceAddConstraint(cpSpace *space, cpConstraint *constraint)void cpSpaceRemoveShape(cpSpace *space, cpShape *shape) void cpSpaceRemoveBody(cpSpace *space, cpBody *body) void cpSpaceRemoveConstraint(cpSpace *space, cpConstraint *constraint)cpBool cpSpaceContainsShape(cpSpace *space, cpShape *shape) cpBool cpSpaceContainsBody(cpSpace *space, cpBody *body) cpBool cpSpaceContainsConstraint(cpSpace *space, cpConstraint *constraint)這些函數(shù)是從空間中添加和刪除形狀、剛體和約束。添加/刪除函數(shù)不能在postStep()回調(diào)之外的回調(diào)內(nèi)調(diào)用(這和postSolve()回調(diào)是不同的!)。當(dāng)cpSpaceStep()仍然在執(zhí)行時(shí),試圖從空間添加或刪除對(duì)象會(huì)拋出一個(gè)斷言。更多信息請(qǐng)參見(jiàn)回調(diào)部分。添加函數(shù)會(huì)返回被添加的對(duì)象以便你可以在一行中創(chuàng)建和添加一些東西。注意在移除關(guān)聯(lián)到剛體的形狀和約束之前不要去釋放剛體,否則會(huì)造成崩潰。contains函數(shù)允許你檢查一個(gè)對(duì)象有沒(méi)有被添加到空間中。
7.6 靜態(tài)動(dòng)態(tài)轉(zhuǎn)換函數(shù)
void cpSpaceConvertBodyToStatic(cpSpace *space, cpBody *body)將剛體轉(zhuǎn)換為靜態(tài)剛體。它的質(zhì)量和力矩將被設(shè)置為無(wú)窮大,并且速度為0。舊的質(zhì)量和力矩以及速度都不會(huì)被保存。這將有效地將一個(gè)剛體和它的形狀凍結(jié)到一個(gè)位置。這不能被一個(gè)激活的剛體調(diào)用,所以你可能需要先調(diào)用cpSpaceRemoveBody()。此外,因?yàn)樗薷牧伺鲎矙z測(cè)的數(shù)據(jù)結(jié)構(gòu),如果你想從另外一個(gè)回調(diào)函數(shù)或迭代器使用你必須使用后一步的回調(diào)。
7.7 空間索引
Chipmunk6正式支持2個(gè)空間索引。默認(rèn)是軸對(duì)齊包圍盒樹(shù),該靈感來(lái)自于Bullet物理庫(kù)中使用的包圍盒樹(shù),但是我將它與我自己的碰撞對(duì)緩存一起做了擴(kuò)展以便為樹(shù)實(shí)現(xiàn)非常好的時(shí)間相干性。樹(shù)無(wú)需調(diào)整優(yōu)化,而且在大多數(shù)游戲中會(huì)發(fā)現(xiàn)使用它能獲得更好的性能。另外一個(gè)可用的索引是空間哈希,當(dāng)你有著非常多數(shù)量且相同尺寸的物體時(shí),它會(huì)更快。
有時(shí),你可能需要更新形狀的碰撞檢測(cè)數(shù)據(jù)。如果你移動(dòng)靜態(tài)形狀或者剛體,你必須這樣做來(lái)讓Chipmunk知道它需要更新碰撞數(shù)據(jù)。你可能還希望手動(dòng)為移動(dòng)過(guò)的普通形狀更新碰撞數(shù)據(jù),并且仍然想進(jìn)行查詢(xún)。
- void cpSpaceReindexShape(cpSpace *space, cpShape *shape) – 重新索引一個(gè)指定的形狀
- void cpSpaceReindexShapesForBody(cpSpace *space, cpBody *body) - 重新索引指定剛體上的所有形狀
- void cpSpaceReindexStatic(cpSpace *space) – 重新索引所有靜態(tài)形狀。一般只更新改變的形狀會(huì)比較快
7.8 迭代器
typedef void (*cpSpaceBodyIteratorFunc)(cpBody *body, void *data) void cpSpaceEachBody(cpSpace *space, cpSpaceBodyIteratorFunc func, void *data)為空間中的每個(gè)剛體調(diào)用func函數(shù),同時(shí)傳遞data指針。休眠中的剛體包括在內(nèi),但是靜態(tài)和游離剛體不包括在內(nèi),因?yàn)樗麄儧](méi)有被添加進(jìn)空間。
cpSpaceEachBody例子:
// 檢測(cè)空間中是否所有剛體都在休眠的代碼片段// 這個(gè)函數(shù)被空間中的每個(gè)剛體調(diào)用 static void EachBody(cpBody *body, cpBool *allSleeping){if(!cpBodyIsSleeping(body)) *allSleeping = cpFalse; }// 然后在你的更新函數(shù)中這樣做 cpBool allSleeping = true; cpSpaceEachBody(space, (cpSpaceBodyIteratorFunc)EachBody, &allSleeping); printf("All are sleeping: %s\n", allSleeping ? "true" : "false"); typedef void (*cpSpaceShapeIteratorFunc)(cpShape *shape, void *data) void cpSpaceEachShape(cpSpace *space, cpSpaceShapeIteratorFunc func, void *data)為空間中的每個(gè)形狀調(diào)用func函數(shù),同時(shí)傳遞data指針。休眠和靜態(tài)形狀被包括在內(nèi)。
typedef void (*cpSpaceConstraintIteratorFunc)(cpConstraint *constraint, void *data) void cpSpaceEachConstraint(cpSpace *space, cpSpaceConstraintIteratorFunc func, void *data)為空間中的每個(gè)約束調(diào)用func函數(shù)同時(shí)傳遞data指針。
注意:如果你的編譯器支持閉包(如Clang), 那么有另外一組函數(shù)你可以調(diào)用。cpSpaceEachBody_b()等等。更多信息請(qǐng)查看chipmunk.h。
7.9 空間模擬
void cpSpaceStep(cpSpace *space, cpFloat dt)通過(guò)給定的時(shí)間步來(lái)更新空間。強(qiáng)烈推薦使用一個(gè)固定的時(shí)間步長(zhǎng)。這樣做能大大提高模擬的質(zhì)量。實(shí)現(xiàn)固定的時(shí)間步,最簡(jiǎn)單的方法就是簡(jiǎn)單的每個(gè)幀頻步進(jìn)1/60s(或任何你的目標(biāo)幀率),而無(wú)論花去了多少渲染時(shí)間。在許多游戲中這樣很有效,但是將物理時(shí)間步進(jìn)和渲染分離是一個(gè)更好的方式。這是一篇介紹如何做的好文章。
7.10 啟用和調(diào)優(yōu)空間哈希(散列)
如果你有成千上萬(wàn)個(gè)大小大致相同的物體,空間哈??赡軙?huì)很適合你。
void cpSpaceUseSpatialHash(cpSpace *space, cpFloat dim, int count)使空間從碰撞包圍盒樹(shù)切換到空間哈希。 空間哈希數(shù)據(jù)對(duì)大小相當(dāng)敏感。dim是哈希單元的尺寸。設(shè)置dim為碰撞形狀大小的平均尺寸可能會(huì)得到最好的性能。設(shè)置dim太小會(huì)導(dǎo)致形狀填充進(jìn)去很多哈希單元,太低會(huì)造成過(guò)多的物體插入同一個(gè)哈希槽。
count是在哈希表中建議的最小的單元數(shù)量。如果單元太少,空間哈希會(huì)產(chǎn)生很多誤報(bào)。過(guò)多的單元將難以做高速緩存并且浪費(fèi)內(nèi)存。將count設(shè)置成10倍于空間物體的個(gè)數(shù)可能是一個(gè)很好的起點(diǎn)。如果必要的話從那里調(diào)優(yōu)。
關(guān)于使用空間哈希有個(gè)可視化的演示程序,通過(guò)它你可以明白我的意思?;疑叫芜_(dá)標(biāo)空間哈希單元。單元顏色越深,就意味著越多的物體被映射到那個(gè)單元。一個(gè)好的dim尺寸也就是你的物體能夠很好的融入格子中。
注意,淺色的灰色意味著每個(gè)單元沒(méi)有太多的物體映射到它。
當(dāng)你使用太小的尺寸,Chipmunk不得不在每個(gè)物體上插入很多哈希單元。這個(gè)代價(jià)有些昂貴。
注意到灰色的單元和碰撞形狀相比是非常小的。
當(dāng)你使用過(guò)大的尺寸,就會(huì)有很多形狀填充進(jìn)每個(gè)單元。每個(gè)形狀不得不和單元中的其他形狀進(jìn)行檢查,所以這會(huì)造成許多不必要的碰撞檢測(cè)。
注意深灰色的單元意味著很多物體映射到了他們。
Chipmunk6也有一個(gè)實(shí)驗(yàn)性的單軸排序和范圍實(shí)現(xiàn)。在移動(dòng)游戲中如果你的世界是很長(zhǎng)且扁就像賽車(chē)游戲,它是非常高效。如果你想嘗試啟用它, 可以查閱cpSpaceUseSpatialHash()的代碼。
7.11 札記
- 當(dāng)從空間中刪除對(duì)象時(shí),請(qǐng)確保你已經(jīng)刪除了任何引用它的其他對(duì)象。例如,當(dāng)你刪除一個(gè)剛體時(shí),要先刪除掉關(guān)聯(lián)到剛體的關(guān)節(jié)和形狀。
- 迭代次數(shù)和時(shí)間步長(zhǎng)的大小決定了模擬的質(zhì)量。越多的迭代次數(shù),或者更小的時(shí)間步會(huì)提高模擬的質(zhì)量。請(qǐng)記住,更高質(zhì)量的同時(shí)也意味著更高的CPU使用率。
- 因?yàn)殪o態(tài)形狀只有當(dāng)你需要的時(shí)候才重新哈希,所以可能會(huì)使用一個(gè)更大的count參數(shù)來(lái)cpHashResizeStaticHash()而不是cpSpaceResizeActiveHash()。如果你有大量靜態(tài)形狀的話,這樣做會(huì)使用更多的內(nèi)存但是會(huì)提升性能。
8. Chipmunk約束:cpConstraint
約束是用來(lái)描述兩個(gè)剛體如何相互作用的(他們是如何約束彼此的)。約束可以是允許剛體像我們身體的骨頭一樣軸轉(zhuǎn)動(dòng)的簡(jiǎn)單關(guān)節(jié),也可以是更抽象的比如齒輪關(guān)節(jié)或馬達(dá)關(guān)節(jié)。
8.1 約束是什么,不是什么
在Chipmunk中,約束都是基于速度的約束。這意味著他們主要通過(guò)同步兩個(gè)剛體的速度進(jìn)行作用。一個(gè)軸關(guān)節(jié)將兩個(gè)獨(dú)立剛體的兩個(gè)錨點(diǎn)連接起來(lái),公式定義要求兩個(gè)錨點(diǎn)的速度必須相同并且計(jì)算施加在剛體上的沖量以便試圖保持這個(gè)狀態(tài)。約束將速度視為主要的輸入并且產(chǎn)生一個(gè)速度變化作為它的輸出。一些約束(尤其是關(guān)節(jié))通過(guò)改變速度來(lái)修正位置的差異。更多詳情見(jiàn)下一節(jié)。
連接兩個(gè)剛體的彈簧不是一個(gè)約束。它很像約束因?yàn)樗鼤?huì)創(chuàng)建一個(gè)力來(lái)影響兩個(gè)剛體的速度,但是彈簧將距離作為輸入,將力作為輸出。如果彈簧不是一個(gè)約束,你會(huì)問(wèn)為什么還會(huì)有兩種類(lèi)型的彈簧約束。原因是他們是阻尼彈簧。彈簧關(guān)聯(lián)的阻尼才是真正的約束,這個(gè)約束會(huì)根據(jù)關(guān)聯(lián)的兩個(gè)剛體的相對(duì)速度來(lái)創(chuàng)建速度的變化。因?yàn)榇蟛糠智闆r將一個(gè)阻尼器和一個(gè)彈簧放在一起很方便,我想我還不如將彈簧力作為約束的一部分,而不是用一個(gè)阻尼器約束然后讓用戶(hù)單獨(dú)計(jì)算和施加彈簧力。
8.2 屬性
- 得到約束關(guān)聯(lián)的兩個(gè)剛體
- 約束能夠作用于兩個(gè)剛體的最大力。默認(rèn)為INFINITY。
- 關(guān)節(jié)誤差百分比一秒鐘后仍然沒(méi)得到修正。這和碰撞偏差機(jī)制完全一樣,但是這會(huì)修正關(guān)節(jié)的誤差而不是重疊碰撞。
- 約束可以糾錯(cuò)的最大速度。默認(rèn)為INFINITY。
- 得到約束所添加進(jìn)去的空間
- 使用數(shù)據(jù)指針。使用指針來(lái)從回調(diào)中得到擁有該約束的游戲?qū)ο蟮囊粋€(gè)引用。
約束被施加的最新的沖量。為了轉(zhuǎn)化成力,除以cpSpaceStep()傳進(jìn)的時(shí)間步。你可以使用這點(diǎn)來(lái)檢查施加的力是否超過(guò)了一定的閾值從而實(shí)現(xiàn)可斷裂的關(guān)節(jié)。
斷裂關(guān)節(jié)例子
要訪問(wèn)特定關(guān)節(jié)類(lèi)型的屬性,使用提供的getter和setter函數(shù)(如cpPinJointGetAnchr1())。更多信息請(qǐng)查看屬性列表。
8.3 反饋糾錯(cuò)
Chipmunk的關(guān)節(jié)并不完美。銷(xiāo)關(guān)節(jié)并不能維系兩個(gè)錨點(diǎn)之間確切的距離,樞軸關(guān)節(jié)同樣也不能保持關(guān)聯(lián)的錨點(diǎn)完全在一起。他們通過(guò)自糾錯(cuò)來(lái)處理這個(gè)問(wèn)題。在Chipmunk5中,你有很多額外的控制來(lái)實(shí)現(xiàn)關(guān)節(jié)對(duì)自身的糾錯(cuò),甚至可以使用這個(gè)特性,以獨(dú)特的方式使用關(guān)節(jié)來(lái)創(chuàng)建一些物理效果。
- 伺服馬達(dá):如 打開(kāi)/關(guān)閉門(mén)或者旋轉(zhuǎn)物件,無(wú)需用最大的力
- 起貨機(jī):朝著另外一個(gè)物體拉一個(gè)物體無(wú)需用最大的力
- 鼠標(biāo)操作:以粗暴、搖晃的鼠標(biāo)輸入方式自如的與物體交互
cpConstraint結(jié)構(gòu)體有3個(gè)屬性控制著誤差糾正,maxForce,maxBias以及biasCoef.maxForce。關(guān)節(jié)或者約束在不超過(guò)該數(shù)值大小的力時(shí)才能發(fā)揮作用。如果它需要更多的力來(lái)維系自己,它將會(huì)散架。maxBias是誤差糾正可以應(yīng)用的最大速度了。如果你改變了一個(gè)關(guān)節(jié)的屬性,這個(gè)關(guān)節(jié)將不得不自行糾正,一般情況下很快會(huì)這么做。通過(guò)設(shè)置最大速度,你可以像伺服一樣使得關(guān)節(jié)工作,在一段較長(zhǎng)的時(shí)間以恒定的速率校正自身。最后,biasCoef是在鉗位最大值速度前每一步誤差糾正的百分比。你可以使用它來(lái)使得關(guān)節(jié)平滑的糾正自身而不是以一個(gè)恒定的速度,但可能是三個(gè)屬性中迄今為止最沒(méi)用的。
// 在一個(gè)頂視角的游戲中,采用這種配置的樞軸關(guān)節(jié)將會(huì)計(jì)算與地面之間的摩擦 // 因?yàn)殛P(guān)節(jié)糾正被禁用,所以關(guān)節(jié)不會(huì)重新擺正自身并只會(huì)影響速度。 // 當(dāng)速度改變時(shí),關(guān)節(jié)施加的力會(huì)被最大力鉗位 // 這樣它就會(huì)像摩擦一樣工作 cpConstraint *pivot = cpSpaceAddConstraint(space, cpPivotJointNew2(staticBody, body, cpvzero, cpvzero)); pivot->maxBias = 0.0f; // disable joint correction pivot->maxForce = 1000.0f;// 樞軸關(guān)節(jié)并不施加旋轉(zhuǎn)力,使用一個(gè)比率為1.0的齒輪關(guān)節(jié)來(lái)替代 cpConstraint *gear = cpSpaceAddConstraint(space, cpGearJointNew(staticBody, body, 0.0f, 1.0f)); gear->maxBias = 0.0f; // disable joint correction gear->maxForce = 5000.0f;// 另外,你可以將關(guān)節(jié)連接到一個(gè)無(wú)限大質(zhì)量的游離剛體上來(lái)取代連接到一個(gè)靜態(tài)剛體上 // 你可以使用游離剛體作為控制剛體來(lái)連接??梢圆榭?#96;Tank`演示例子。8.4 約束和碰撞形狀
約束和碰撞形狀互不了解雙方信息。當(dāng)為剛體連接關(guān)節(jié)時(shí),錨點(diǎn)不必處于剛體形狀的內(nèi)部,這么做通常是有意義的。同樣的,為兩個(gè)剛體添加約束并不能阻止剛體形狀碰撞。事實(shí)上,這便是碰撞組屬性存在的主要原因。
8.5 現(xiàn)有關(guān)節(jié)類(lèi)型視頻演示
- Youtube地址
- 優(yōu)酷地址
8.6 共享內(nèi)存管理函數(shù)
Destroy和Free函數(shù)由所有關(guān)節(jié)類(lèi)型共享。Allocation和init函數(shù)對(duì)于每種關(guān)節(jié)類(lèi)型都是特定的。
9. 約束類(lèi)型
9.1 銷(xiāo)關(guān)節(jié)
cpPinJoint *cpPinJointAlloc(void) cpPinJoint *cpPinJointInit(cpPinJoint *joint, cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2) cpConstraint *cpPinJointNew(cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2)a和b是被連接的兩個(gè)剛體,anchr1和anchr2是這兩個(gè)剛體的錨點(diǎn)。當(dāng)關(guān)節(jié)被創(chuàng)建的時(shí)候距離便被確定,如果你想要設(shè)定一個(gè)特定的距離,使用setter函數(shù)來(lái)重新設(shè)定該值。
屬性
- cpVect cpPinJointGetAnchr1(const cpConstraint *constraint)
- void cpPinJointSetAnchr1(cpConstraint *constraint, cpVect value)
- cpVect cpPinJointGetAnchr2(const cpConstraint *constraint)
- void cpPinJointSetAnchr2(cpConstraint *constraint, cpVect value)
- cpFloat cpPinJointGetDist(const cpConstraint *constraint)
- void cpPinJointSetDist(cpConstraint *constraint, cpFloat value)
9.2 滑動(dòng)關(guān)節(jié)
cpSlideJoint *cpSlideJointAlloc(void)cpSlideJoint *cpSlideJointInit(cpSlideJoint *joint, cpBody *a, cpBody *b,cpVect anchr1, cpVect anchr2, cpFloat min, cpFloat max )cpConstraint *cpSlideJointNew(cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2, cpFloat min, cpFloat max)a和b是被連接的兩個(gè)剛體,anchr1和anchr2是這兩個(gè)剛體的錨點(diǎn), min和max定義了兩個(gè)錨點(diǎn)間的最小最大距離。
屬性
- cpVect cpSlideJointGetAnchr1(const cpConstraint *constraint)
- void cpSlideJointSetAnchr1(cpConstraint *constraint, cpVect value)
- cpVect cpSlideJointGetAnchr2(const cpConstraint *constraint)
- void cpSlideJointSetAnchr2(cpConstraint *constraint, cpVect value)
- cpFloat cpSlideJointGetMin(const cpConstraint *constraint)
- void cpSlideJointSetMin(cpConstraint *constraint, cpFloat value)
- cpFloat cpSlideJointGetMax(const cpConstraint *constraint)
- void cpSlideJointSetMax(cpConstraint *constraint, cpFloat value)
9.3 樞軸關(guān)節(jié)
cpPivotJoint *cpPivotJointAlloc(void) cpPivotJoint *cpPivotJointInit(cpPivotJoint *joint, cpBody *a, cpBody *b, cpVect pivot) cpConstraint *cpPivotJointNew(cpBody *a, cpBody *b, cpVect pivot) cpConstraint *cpPivotJointNew2(cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2)a和b是關(guān)節(jié)連接的兩個(gè)剛體,pivot是世界坐標(biāo)系下的樞軸點(diǎn)。因?yàn)闃休S點(diǎn)位置是在世界坐標(biāo)系下,所以你必須確保兩個(gè)剛體已經(jīng)處于正確的位置上。另外你可以指定基于一對(duì)錨點(diǎn)的軸關(guān)節(jié),但是要確保剛體處于正確的位置上因?yàn)橐坏┠憧臻g開(kāi)啟了模擬,關(guān)節(jié)將會(huì)修正它自身。
屬性
- cpVect cpPivotJointGetAnchr1(const cpConstraint *constraint)
- void cpPivotJointSetAnchr1(cpConstraint *constraint, cpVect value)
- cpVect cpPivotJointGetAnchr2(const cpConstraint *constraint)
- void cpPivotJointSetAnchr2(cpConstraint *constraint, cpVect value)
9.4 溝槽關(guān)節(jié)
cpGrooveJoint *cpGrooveJointAlloc(void)cpGrooveJoint *cpGrooveJointInit(cpGrooveJoint *joint, cpBody *a, cpBody *b,cpVect groove_a, cpVect groove_b, cpVect anchr2 )cpConstraint *cpGrooveJointNew(cpBody *a, cpBody *b, cpVect groove_a, cpVect groove_b, cpVect anchr2)溝槽在剛體a上從groov_a到groov_b,樞軸被附加在剛體b的anchr2錨點(diǎn)上。所有的坐標(biāo)都是剛體局部坐標(biāo)。
屬性
- cpVect cpGrooveJointGetGrooveA(const cpConstraint *constraint)
- void cpGrooveJointSetGrooveA(cpConstraint *constraint, cpVect value)
- cpVect cpGrooveJointGetGrooveB(const cpConstraint *constraint)
- void cpGrooveJointSetGrooveB(cpConstraint *constraint, cpVect value)
- cpVect cpGrooveJointGetAnchr2(const cpConstraint *constraint)
- void cpGrooveJointSetAnchr2(cpConstraint *constraint, cpVect value)
9.5 阻尼彈簧
cpDampedSpring *cpDampedSpringAlloc(void)cpDampedSpring *cpDampedSpringInit(cpDampedSpring *joint, cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2,cpFloat restLength, cpFloat stiffness, cpFloat damping )cpConstraint *cpDampedSpringNew(cpBody *a, cpBody *b, cpVect anchr1, cpVect anchr2,cpFloat restLength, cpFloat stiffness, cpFloat damping )和滑動(dòng)關(guān)節(jié)的定義很類(lèi)似。restLength是彈簧想要的長(zhǎng)度,stiffness是彈簧系數(shù)(Young’s modulus),dampling用來(lái)描述彈簧阻尼的柔軟度。
屬性
- cpVect cpDampedSpringGetAnchr1(const cpConstraint *constraint)
- void cpDampedSpringSetAnchr1(cpConstraint *constraint, cpVect value)
- cpVect cpDampedSpringGetAnchr2(const cpConstraint *constraint)
- void cpDampedSpringSetAnchr2(cpConstraint *constraint, cpVect value)
- cpFloat cpDampedSpringGetRestLength(const cpConstraint *constraint)
- void cpDampedSpringSetRestLength(cpConstraint *constraint, cpFloat value)
- cpFloat cpDampedSpringGetStiffness(const cpConstraint *constraint)
- void cpDampedSpringSetStiffness(cpConstraint *constraint, cpFloat value)
- cpFloat cpDampedSpringGetDamping(const cpConstraint *constraint)
- void cpDampedSpringSetDamping(cpConstraint *constraint, cpFloat value)
9.6 阻尼旋轉(zhuǎn)彈簧
cpDampedRotarySpring *cpDampedRotarySpringAlloc(void)cpDampedRotarySpring *cpDampedRotarySpringInit(cpDampedRotarySpring *joint, cpBody *a, cpBody *b,cpFloat restAngle, cpFloat stiffness, cpFloat damping )cpConstraint *cpDampedRotarySpringNew(cpBody *a, cpBody *b, cpFloat restAngle, cpFloat stiffness, cpFloat damping)猶如阻尼彈簧,但卻在角度層面起作用。restAngle是剛體間想要的相對(duì)角度,stiffness和dampling和阻尼彈簧的基本一樣。
屬性
- cpFloat cpDampedRotarySpringGetRestAngle(const cpConstraint *constraint)
- void cpDampedRotarySpringSetRestAngle(cpConstraint *constraint, cpFloat value)
- cpFloat cpDampedRotarySpringGetStiffness(const cpConstraint *constraint)
- void cpDampedRotarySpringSetStiffness(cpConstraint *constraint, cpFloat value)
- cpFloat cpDampedRotarySpringGetDamping(const cpConstraint *constraint)
- void cpDampedRotarySpringSetDamping(cpConstraint *constraint, cpFloat value)
9.7 旋轉(zhuǎn)限位關(guān)節(jié)
cpRotaryLimitJoint *cpRotaryLimitJointAlloc(void) cpRotaryLimitJoint *cpRotaryLimitJointInit(cpRotaryLimitJoint *joint, cpBody *a, cpBody *b, cpFloat min, cpFloat max) cpConstraint *cpRotaryLimitJointNew(cpBody *a, cpBody *b, cpFloat min, cpFloat max)旋轉(zhuǎn)限位關(guān)節(jié)約束著兩個(gè)剛體間的相對(duì)角度。min和max就是最小和最大的相對(duì)角度,單位為弧度。它被實(shí)現(xiàn)以便可能使范圍大于一整圈。
屬性
- cpFloat cpRotaryLimitJointGetMin(const cpConstraint *constraint)
- void cpRotaryLimitJointSetMin(cpConstraint *constraint, cpFloat value)
- cpFloat cpRotaryLimitJointGetMax(const cpConstraint *constraint)
- void cpRotaryLimitJointSetMax(cpConstraint *constraint, cpFloat value)
9.8 棘輪關(guān)節(jié)
cpRatchetJoint *cpRatchetJointAlloc(void); cpRatchetJoint *cpRatchetJointInit(cpRatchetJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet); cpConstraint *cpRatchetJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratchet);工作起來(lái)像套筒扳手。ratchet是”clicks”間的距離,phase是當(dāng)決定棘輪角度的時(shí)候的初始位移。
屬性
- cpFloat cpRatchetJointGetAngle(const cpConstraint *constraint)
- void cpRatchetJointSetAngle(cpConstraint *constraint, cpFloat value)
- cpFloat cpRatchetJointGetPhase(const cpConstraint *constraint)
- void cpRatchetJointSetPhase(cpConstraint *constraint, cpFloat value)
- cpFloat cpRatchetJointGetRatchet(const cpConstraint *constraint)
- void cpRatchetJointSetRatchet(cpConstraint *constraint, cpFloat value)
9.9 齒輪關(guān)節(jié)
cpGearJoint *cpGearJointAlloc(void); cpGearJoint *cpGearJointInit(cpGearJoint *joint, cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio); cpConstraint *cpGearJointNew(cpBody *a, cpBody *b, cpFloat phase, cpFloat ratio);齒輪關(guān)節(jié)保持著一對(duì)剛體恒定的角速度比。ratio總是測(cè)量絕對(duì)值,目前無(wú)法設(shè)定相對(duì)于第三個(gè)剛體的角速度。phase是兩個(gè)剛體的初始角度偏移量。
屬性
- cpFloat cpGearJointGetPhase(const cpConstraint *constraint)
- void cpGearJointSetPhase(cpConstraint *constraint, cpFloat value)
- cpFloat cpGearJointGetRatio(const cpConstraint *constraint)
- void cpGearJointSetRatio(cpConstraint *constraint, cpFloat value)
9.10 簡(jiǎn)單馬達(dá)
cpSimpleMotor *cpSimpleMotorAlloc(void); cpSimpleMotor *cpSimpleMotorInit(cpSimpleMotor *joint, cpBody *a, cpBody *b, cpFloat rate); cpConstraint *cpSimpleMotorNew(cpBody *a, cpBody *b, cpFloat rate);簡(jiǎn)單馬達(dá)保持著一對(duì)剛體恒定的角速度比。rate是所需的相對(duì)角速度。通常你會(huì)給馬達(dá)設(shè)定一個(gè)最大力(扭矩)否則他們會(huì)申請(qǐng)一個(gè)無(wú)限大的扭矩來(lái)使得剛體移動(dòng)。
屬性
- cpFloat cpSimpleMotorGetRate(const cpConstraint *constraint)
- void cpSimpleMotorSetRate(cpConstraint *constraint, cpFloat value)
9.11 札記
- 你可以為兩個(gè)剛體添加多個(gè)關(guān)節(jié),但要確保他們彼此不會(huì)沖突。否則會(huì)引起剛體抖動(dòng)或者劇烈的旋轉(zhuǎn)。
10. Chipmunk碰撞檢測(cè)概述
Chipmunk為了使得碰撞檢測(cè)盡可能快,將處理過(guò)程分成了若干階段。雖然我一直試圖保持它概念簡(jiǎn)單,但實(shí)現(xiàn)卻有點(diǎn)讓人生畏。幸運(yùn)的是作為Chipmunk庫(kù)的使用者,你并不需要了解一切關(guān)于它是如何工作的。但如果你在嘗試發(fā)揮Chipmunk的極致,理解這一部分會(huì)有所幫助。
10.1 空間索引
在場(chǎng)景中用一個(gè)for循環(huán)來(lái)檢查每一個(gè)對(duì)象是否與其他對(duì)象發(fā)生碰撞會(huì)很慢。所以碰撞檢測(cè)的第一步(或者就像通常稱(chēng)作的階段),就是使用高層次空間算法來(lái)找出哪些對(duì)象應(yīng)該被檢查碰撞。目前Chipmunk支持兩種空間索引,軸對(duì)齊包圍盒樹(shù)和空間散列。這些空間索引能夠快速識(shí)別哪些形狀彼此靠近,并應(yīng)做碰撞檢查。
10.2 碰撞過(guò)濾(篩選)
在空間索引找出彼此靠近的形狀對(duì)后,將它們傳給space,然后再執(zhí)行一些額外的篩選。在進(jìn)行任何操作前,Chipmunk會(huì)執(zhí)行幾個(gè)簡(jiǎn)單的測(cè)試來(lái)檢測(cè)形狀是否會(huì)發(fā)生碰撞。
- 包圍盒測(cè)試:如果形狀的包圍盒沒(méi)有重疊,那么形狀便沒(méi)發(fā)生碰撞。對(duì)象如對(duì)角線線段會(huì)引發(fā)許多誤報(bào),但你不應(yīng)該擔(dān)心。
- 層測(cè)試:如果形狀不在同一層內(nèi)則不會(huì)發(fā)生碰撞。(他們的層掩碼按位與運(yùn)算結(jié)果為0)
- 群組測(cè)試:在相同的非零群組中的形狀不會(huì)發(fā)生碰撞。
10.3 基本形狀與形狀間的碰撞檢測(cè)
最昂貴的測(cè)試其實(shí)就是檢測(cè)基于幾何形狀的重疊。圓與圓,圓與線之間的碰撞檢測(cè)相當(dāng)快,多邊形和多邊形的碰撞檢測(cè)隨著頂點(diǎn)數(shù)的增加而更加昂貴。形狀越簡(jiǎn)單,碰撞檢測(cè)就越快(更重要的是求解器檢測(cè)的碰撞點(diǎn)就越少)。Chipmunk使用了一個(gè)分發(fā)表來(lái)描述應(yīng)該使用哪個(gè)函數(shù)來(lái)檢測(cè)形狀是否重疊。
10.4 碰撞處理函數(shù)過(guò)濾
在檢測(cè)到兩個(gè)形狀間重疊之后,Chipmunk會(huì)查看你是否為該碰撞形狀的類(lèi)型定義了一個(gè)碰撞處理函數(shù)。對(duì)于游戲這樣去處理碰撞事件是至關(guān)重要的,同時(shí)也為你提供了一個(gè)非常靈活的方式來(lái)過(guò)濾掉碰撞。begin()和preSolve()回調(diào)函數(shù)的返回值決定了碰撞的形狀對(duì)是否該舍棄掉。返回true會(huì)保留形狀對(duì),false則會(huì)舍棄。在begin()回調(diào)中中止一個(gè)碰撞是永久性的,在preSolve()回調(diào)中中止只是應(yīng)用于當(dāng)前所處的時(shí)間步。如果你沒(méi)有為碰撞類(lèi)型定義一個(gè)處理函數(shù),Chipmunk將會(huì)調(diào)用space的默認(rèn)處理函數(shù),默認(rèn)會(huì)簡(jiǎn)單的接受所有碰撞。
使用回調(diào)過(guò)濾碰撞是最靈活的方式,記住,到那時(shí)候所有最昂貴的碰撞檢測(cè)通過(guò)你的回調(diào)都已經(jīng)完成。對(duì)于每幀有大量碰撞對(duì)象的模擬,尋找碰撞所消耗的時(shí)間和解決碰撞所消耗的時(shí)間相比要小很多,所以這不是一個(gè)大問(wèn)題。不過(guò),如果可以的話先使用層或者群組。
11. 碰撞回調(diào)
沒(méi)有任何事件或反饋的物理庫(kù)對(duì)游戲而言幫助并不大。你怎么知道當(dāng)玩家碰到了一個(gè)敵人,以便你扣除一些生命點(diǎn)數(shù)?你怎么知道汽車(chē)撞擊一個(gè)東西的力度,這樣你就不會(huì)在石子擊中它的時(shí)候播放一個(gè)巨響轟隆音?如果你需要決定在特定條件下的碰撞是否應(yīng)該被忽略,比如要實(shí)現(xiàn)單向平臺(tái)?Chipmunk擁有一套強(qiáng)大的回調(diào)系統(tǒng),你可以實(shí)現(xiàn)他們來(lái)完成這一切。
11.1 碰撞處理
碰撞處理函數(shù)是Chipmunk能夠識(shí)別的不同碰撞事件的一組4個(gè)回調(diào)函數(shù)。事件類(lèi)型是:
- begin():該步中兩個(gè)形狀剛開(kāi)始第一次接觸?;卣{(diào)返回true則會(huì)處理正常碰撞,返回falseChipmunk會(huì)完全忽略碰撞。如果返回false,則preSolve()和postSolve()回調(diào)將永遠(yuǎn)不會(huì)被執(zhí)行,但你仍然會(huì)在形狀停止重疊的時(shí)候接收到一個(gè)單獨(dú)的事件。
- preSolve():該步中兩個(gè)形狀相互接觸。回調(diào)返回falseChipmunk在這一步會(huì)忽略碰撞,返回true來(lái)正常處理它。此外,你可以使用cpArbiterSetFriction(),cpArbiterSetElasticity()或cpArbiterSetSurfaceVelocity()來(lái)提供自定義的摩擦,彈性,或表面速度值來(lái)覆蓋碰撞值。更多信息請(qǐng)查看cpArbiter。
- postSolve():兩種形狀相互接觸并且它們的碰撞響應(yīng)已被處理。如果你想使用它來(lái)計(jì)算音量或者傷害值,這時(shí)你可以檢索碰撞沖力或動(dòng)能。更多信息請(qǐng)查看cpArbiter。
- separate():該步中兩個(gè)形狀剛第一次停止接觸。確保begin()/separate()總是被成對(duì)調(diào)用,當(dāng)刪除接觸中的形狀時(shí)或者析構(gòu)space時(shí)它也會(huì)被調(diào)用。
碰撞回調(diào)都與cpArbiter結(jié)構(gòu)緊密相關(guān)。你應(yīng)該熟悉那些為好。
注:標(biāo)記為傳感器的形狀(cpShape.sensor == true)從來(lái)不會(huì)得到碰撞處理,所以傳感器形狀和其他形狀間永遠(yuǎn)不會(huì)調(diào)用postSolve()回調(diào)。它們?nèi)匀粫?huì)調(diào)用begin()和separate()回調(diào),而preSolve()仍然會(huì)在每幀調(diào)用回調(diào),即使這里不存在真正的碰撞。
注2:preSolve()回調(diào)在休眠算法運(yùn)行之前被調(diào)用。如果一個(gè)對(duì)象進(jìn)入休眠狀態(tài),postSolve()回調(diào)將不會(huì)被調(diào)用,直到它被喚醒。
11.2 碰撞處理API
typedef int (*cpCollisionBeginFunc)(cpArbiter *arb, struct cpSpace *space, void *data) typedef int (*cpCollisionPreSolveFunc)(cpArbiter *arb, cpSpace *space, void *data) typedef void (*cpCollisionPostSolveFunc)(cpArbiter *arb, cpSpace *space, void *data) typedef void (*cpCollisionSeparateFunc)(cpArbiter *arb, cpSpace *space, void *data)碰撞處理函數(shù)類(lèi)型。所有這些函數(shù)都附帶一個(gè)arbiter,space和用戶(hù)data指針,只有begin()和preSolve()回調(diào)會(huì)返回值。更多信息請(qǐng)查看上方碰撞處理。
void cpSpaceAddCollisionHandler(cpSpace *space,cpCollisionType a, cpCollisionType b,cpCollisionBeginFunc begin,cpCollisionPreSolveFunc preSolve,cpCollisionPostSolveFunc postSolve,cpCollisionSeparateFunc separate,void *data )為指定的碰撞類(lèi)型對(duì)添加一個(gè)碰撞處理函數(shù)。每當(dāng)碰撞類(lèi)型(cpShape.collision_type)為a的形狀與碰撞類(lèi)型為b的形狀碰撞時(shí),這些回調(diào)就會(huì)被調(diào)用來(lái)處理碰撞。data是用戶(hù)定義的上下文指針,用來(lái)傳遞到每個(gè)回調(diào)中。你不想實(shí)現(xiàn)的話可以使用NULL,然而Chipmunk會(huì)調(diào)用它自身的默認(rèn)版本而不是你為space設(shè)置的默認(rèn)值。如果你需要依賴(lài)space的默認(rèn)回調(diào),你必須單獨(dú)為每個(gè)處理函數(shù)定義提供實(shí)現(xiàn)。
void cpSpaceRemoveCollisionHandler(cpSpace *space, cpCollisionType a, cpCollisionType b)移除指定碰撞類(lèi)型對(duì)的碰撞處理函數(shù)。
void cpSpaceSetDefaultCollisionHandler(cpSpace *space,cpCollisionBeginFunc begin,cpCollisionPreSolveFunc preSolve,cpCollisionPostSolveFunc postSolve,cpCollisionSeparateFunc separate,void *data )當(dāng)沒(méi)有具體的碰撞處理時(shí)Chipmunk會(huì)使用一個(gè)默認(rèn)的注冊(cè)碰撞處理函數(shù)。space在創(chuàng)建時(shí)被指定了一個(gè)默認(rèn)的處理函數(shù),該函數(shù)在begin()和preSolve()回調(diào)中返回true,在postSolve()和separate()回調(diào)中不做任何事情。
11.3 后步回調(diào)
后步回調(diào)允許你打破在一個(gè)回調(diào)內(nèi)增加或刪除對(duì)象的規(guī)則。事實(shí)上,它們的主要功能就是幫助你安全的從你想要禁用/破壞一個(gè)碰撞/查詢(xún)回調(diào)的空間中移除對(duì)象。
后步回調(diào)被注冊(cè)為一個(gè)函數(shù)和用作鍵的一個(gè)指針。你只能為每個(gè)鍵注冊(cè)一個(gè)postStep()回調(diào)。這可以防止你不小心多次刪除對(duì)象。例如,假設(shè)你有子彈和對(duì)象A之間的碰撞回調(diào)。你想摧毀子彈和對(duì)象A,因此你注冊(cè)一個(gè)postStep()回調(diào)來(lái)從游戲中安全地移除它們。然后,你得到子彈和對(duì)象B之間的碰撞回調(diào),你注冊(cè)一個(gè)postStep()回調(diào)來(lái)刪除對(duì)象B,第二次postStep()回調(diào)來(lái)移除子彈。因?yàn)槟阒荒転槊總€(gè)鍵注冊(cè)一次回調(diào), postStep()回調(diào)對(duì)于子彈而言只會(huì)被調(diào)用一次,你不可能意外刪除兩次。
11.4 例子
更多信息請(qǐng)查看碰撞回調(diào)范例。
12. Chipmunk碰撞對(duì):cpArbiter
Chipmunk的cpArbiter結(jié)構(gòu)封裝了一對(duì)碰撞的形狀和關(guān)于它們的所有碰撞數(shù)據(jù)。
為什么稱(chēng)之為仲裁者?簡(jiǎn)短來(lái)說(shuō),我一直用的是“仲裁”來(lái)形容碰撞解決的方式,然后早在2006年當(dāng)我在看Box2D的求解器的時(shí)候看到了Box2D居然叫它們仲裁者。仲裁者就像是一個(gè)法官,有權(quán)力來(lái)解決兩個(gè)人之間的糾紛。這是有趣的,使用了合適的名字并且輸入比我以前用的CollisionPair要短。它最初只是被設(shè)定為一個(gè)私有的內(nèi)部結(jié)構(gòu),但卻在回調(diào)中很有用。
12.1 內(nèi)存管理
你永遠(yuǎn)不需要?jiǎng)?chuàng)建或釋放一個(gè)仲裁者。更重要的是,因?yàn)樗鼈兺耆煽臻g管理,所以你永遠(yuǎn)不應(yīng)該存儲(chǔ)一個(gè)仲裁者的引用,因?yàn)槟悴恢浪鼈兪裁磿r(shí)候會(huì)被釋放或重新使用。在回調(diào)中使用它們,然后忘記它們或復(fù)制出你需要的信息。
12.2 屬性
cpFloat cpArbiterGetElasticity(const cpArbiter *arb) void cpArbiterSetElasticity(cpArbiter *arb, cpFloat value)計(jì)算碰撞對(duì)的彈性。在preSolve()回調(diào)中設(shè)定該值將會(huì)覆蓋由空間計(jì)算的值。默認(rèn)計(jì)算會(huì)將兩個(gè)形狀的彈性相乘。
cpFloat cpArbiterGetFriction(const cpArbiter *arb) void cpArbiterSetFriction(cpArbiter *arb, cpFloat value)計(jì)算碰撞對(duì)的摩擦力。在preSolve()回調(diào)中設(shè)定該值將會(huì)覆蓋由空間計(jì)算的值。默認(rèn)計(jì)算會(huì)將兩個(gè)形狀的摩擦力相乘。
cpVect cpArbiterGetSurfaceVelocity(const cpArbiter *arb) void cpArbiterSetSurfaceVelocity(cpArbiter *arb, cpVect value)計(jì)算碰撞對(duì)的表面速度。在preSolve()回調(diào)中設(shè)定該值將會(huì)覆蓋由空間計(jì)算的值。默認(rèn)計(jì)算會(huì)將第二個(gè)形狀的表面速度從第一個(gè)形狀的表面速度中減去,然后投射到碰撞的切線上。這使得只有摩擦力受到默認(rèn)計(jì)算的影響。使用自定義計(jì)算,你可以使得響應(yīng)就像一個(gè)彈球保險(xiǎn)杠一樣,或使得表面速度依賴(lài)于接觸點(diǎn)的位置。
注:不幸的是,有一個(gè)老的bug會(huì)讓表面速度計(jì)算逆向(負(fù)值)。我真的很久沒(méi)有注意到這點(diǎn)了。這將在Chipmunk7中得到修正,但現(xiàn)在由于向后兼容的原因我已經(jīng)先不管它了。
cpDataPointer cpArbiterGetUserData(const cpArbiter *arb) void cpArbiterSetUserData(cpArbiter *arb, cpDataPointer data)用戶(hù)自定義指針。該值將維持形狀對(duì)直到separate()回調(diào)被調(diào)用。
注:如果你需要清理這個(gè)指針,你應(yīng)該實(shí)現(xiàn)separate()回調(diào)來(lái)這么做。同時(shí)在摧毀空間的時(shí)候要小心,因?yàn)槿匀挥锌赡苡屑せ畹呐鲎泊嬖凇榱擞|發(fā)separate()回調(diào),在處置它之前你需要先移除空間中的所有形狀。這正是我建議的方式。見(jiàn)ChipmunkDemo.c:ChipmunkDemoFreeSpaceChildren()演示了如何輕松做到這一點(diǎn)。
int cpArbiterGetCount(const cpArbiter *arb) cpVect cpArbiterGetNormal(const cpArbiter *arb, int i) cpVect cpArbiterGetPoint(const cpArbiter *arb, int i) cpFloat cpArbiterGetDepth(const cpArbiter *arb, int i)得到由這仲裁者或特定碰撞點(diǎn),碰撞點(diǎn)的法向量或深度穿透跟蹤的觸點(diǎn)的數(shù)目。
cpBool cpArbiterIsFirstContact(const cpArbiter *arb)如果這是兩個(gè)形狀開(kāi)始接觸的第一步則返回true。舉例來(lái)說(shuō)這對(duì)于聲音效果很有用。如果這是特定碰撞的第一幀,在postStep()回調(diào)中檢測(cè)碰撞能量,并用它來(lái)確定播放的聲效音量。
void cpArbiterGetShapes(const cpArbiter *arb, cpShape **a, cpShape **b) void cpArbiterGetBodies(const cpArbiter *arb, cpBody **a, cpBody **b)按照形狀或者剛體在該仲裁者關(guān)聯(lián)的碰撞對(duì)中定義的順序一樣得到它們。如果你像cpSpaceAddCollisionHandler(space, 1, 2, …)定義了個(gè)函數(shù),你會(huì)發(fā)現(xiàn)a->collision_type == 1且b->collision_type == 2。
碰撞回調(diào)例子
static void postStepRemove(cpSpace *space, cpShape *shape, void *unused) {cpSpaceRemoveShape(space, shape);cpSpaceRemoveBody(space, shape->body);cpShapeFree(shape);cpBodyFree(shape->body); }static int begin(cpArbiter *arb, cpSpace *space, void *unused) {// 得到參與碰撞的形狀// 順序和你在函數(shù)定義中的順序一致// a->collision_type將是BULLET_TYPE, b->collision_type將是MONSTER_TYPE CP_ARBITER_GET_SHAPES(arb, a, b);// 宏展開(kāi)后和下面輸入一樣// cpShape *a, *b; cpArbiterGetShapes(arb, &a, &b);// 添加一個(gè)后步回調(diào)來(lái)安全從空間中移除和剛體// 直接從碰撞處理函數(shù)回調(diào)中調(diào)用 cpSpaceRemove() 會(huì)引起崩潰cpSpaceAddPostStepCallback(space, (cpPostStepFunc)postStepRemove, b, NULL);// 物體死亡,不再處理碰撞return 0; }#define BULLET_TYPE 1 #define MONSTER_TYPE 2// 為子彈和怪物定義一個(gè)碰撞處理函數(shù) // 一旦怪物被子彈擊中則通過(guò)移除它的形狀和剛體來(lái)立馬殺死怪物 cpSpaceAddCollisionHandler(space, BULLET_TYPE, MONSTER_TYPE, begin, NULL, NULL, NULL, NULL);12.3 觸點(diǎn)集
通過(guò)觸點(diǎn)集我們得到接觸信息變得更為容易。
cpContactPointSet cpArbiterGetContactPointSet(const cpArbiter *arb)從仲裁者中得到的觸點(diǎn)集結(jié)構(gòu)域。
你可能通過(guò)下面的方式來(lái)得到并且處理一個(gè)觸點(diǎn)集:
cpContactPointSet set = cpArbiterGetContactPointSet(arbiter); for(int i=0; i<set.count; i++){// 得到并使用觸點(diǎn)的法向量和穿透距離set.points[i].pointset.points[i].normalset.points[i].dist } void cpArbiterSetContactPointSet(cpArbiter *arb, cpContactPointSet *set)替換仲裁者的觸點(diǎn)集。你不能改變觸點(diǎn)的數(shù)目,但是可以改變它們的位置,法向量或穿透距離。Sticky演示使用它來(lái)使得物體能夠獲得額外量的重疊。你也可以在乒乓式風(fēng)格游戲中使用它來(lái)修改基于碰撞x軸的碰撞法向量,即使板子是扁平形狀。
12.4 幫助函數(shù)
void cpArbiterGetShapes(cpArbiter *arb, cpShape **a, cpShape **b) void cpArbiterGetBodies(const cpArbiter *arb, cpBody **a, cpBody **b)得到在仲裁者關(guān)聯(lián)的碰撞處理中所定義的形狀(或者剛體)。如果你定義了一個(gè)處理函數(shù)如cpSpaceAddCollisionHandler(space, 1, 2, …),你會(huì)發(fā)現(xiàn)a->collision_type == 1并且b->collision_type == 2。便捷的宏為你定義并且初始化了兩個(gè)形狀變量。默認(rèn)的碰撞處理函數(shù)不會(huì)使用碰撞類(lèi)型,所以順序是未定義的。
#define CP_ARBITER_GET_SHAPES(arb, a, b) cpShape *a, *b; cpArbiterGetShapes(arb, &a, &b) #define CP_ARBITER_GET_BODIES(arb, a, b) cpBody *a, *b; cpArbiterGetBodies(arb, &a, &b);定義變量并且從仲裁者中檢索形狀/剛體所用的縮略宏。
cpVect cpArbiterTotalImpulseWithFriction(cpArbiter *arb); cpVect cpArbiterTotalImpulse(cpArbiter *arb);返回為解決碰撞而施加于此步驟的沖量。這些函數(shù)應(yīng)該只在postStep()或cpBodyEachArbiter()回調(diào)中被調(diào)用,否則結(jié)果將不可確定。如有疑問(wèn)不知道該使用哪個(gè)函數(shù),就使用cpArbiterTotalImpulseWithFriction()。
cpFloat cpArbiterTotalKE(const cpArbiter *arb);計(jì)算在碰撞中的能量損失值,包括靜摩擦不包括動(dòng)摩擦。這個(gè)函數(shù)應(yīng)該在postSolve(), postStep()或者cpBodyEachArbiter()回調(diào)中被調(diào)用。
13. 查詢(xún)
Chipmunk空間支持4種空間查詢(xún),包括最近點(diǎn)查詢(xún)、線段查詢(xún)、形狀查詢(xún)和快速包圍盒查詢(xún)。任何一種類(lèi)型都可在空間里有效運(yùn)行,并且點(diǎn)和線段查詢(xún)可以針對(duì)單個(gè)形狀來(lái)進(jìn)行。所有類(lèi)型的查詢(xún)需要一個(gè)碰撞組和層,并使用和過(guò)濾形狀間碰撞一樣的規(guī)則來(lái)過(guò)濾出匹配。如果你不希望過(guò)濾掉任何匹配,使用CP_ALL_LAYERS作為層,CP_NO_GROUP作為組。
13.1 最近點(diǎn)查詢(xún)
點(diǎn)查詢(xún)對(duì)于像鼠標(biāo)拾取和簡(jiǎn)單的感應(yīng)器來(lái)說(shuō)非常有用。它允許你檢查離給定點(diǎn)一定距離內(nèi)是否存在著形狀,找到形狀上離給定點(diǎn)最近的點(diǎn)或者找到離給定點(diǎn)最近的形狀。
typedef struct cpNearestPointQueryInfo {/// 最近的形狀。如果在范圍內(nèi)沒(méi)有形狀返回NULL。cpShape *shape;/// 形狀表面上最近點(diǎn)(世界坐標(biāo)系)cpVect p;/// 離給定點(diǎn)的距離。如果點(diǎn)在形狀內(nèi)部距離則為負(fù)值cpFloat d;/// 距離函數(shù)的梯度/// 和info.p/info.d相同,即使當(dāng)info.d是非常小的值時(shí),仍然精確cpVect g; } cpNearestPointQueryInfo;13.2 線段查詢(xún)
線段查詢(xún)就像射線投射一樣,但由于并非所有的空間索引都允許處理無(wú)限長(zhǎng)的射線查詢(xún)所以它僅限于線段。在實(shí)踐中這仍然非??焖?#xff0c;你不用過(guò)多的擔(dān)心過(guò)長(zhǎng)的線段查詢(xún)會(huì)影響到性能。
typedef struct cpSegmentQueryInfo {//碰撞的形狀,如果沒(méi)有碰撞發(fā)生則為NULLcpShape *shape;// 線段查詢(xún)的歸一化距離,在[0,1]范圍內(nèi)cpFloat t;// 表面命中點(diǎn)的法向量cpVect n; } cpSegmentQueryInfo;分類(lèi)查詢(xún)返回的信息不只是一個(gè)簡(jiǎn)單的是或否,它們也會(huì)返回形狀被擊中的位置以及被擊中位置的表面的法向量。t是該查詢(xún)的開(kāi)始點(diǎn)和結(jié)束點(diǎn)之間的百分比。如果你需要世界空間中的擊中點(diǎn)或者到開(kāi)始點(diǎn)的絕對(duì)距離,可以查看下面的線段查詢(xún)幫助函數(shù)。如果線段查詢(xún)的開(kāi)始點(diǎn)在形狀內(nèi)部,則t = 0并且n = cpvzero。
cpBool cpShapeSegmentQuery(cpShape *shape, cpVect a, cpVect b, cpSegmentQueryInfo *info)執(zhí)行從a到b的線段與單一形狀shape的線段查詢(xún)。info必須是一個(gè)指向cpSegmentQueryInfo結(jié)構(gòu)體的有效的指針,該結(jié)構(gòu)體會(huì)被光線投射信息(raycast info)初始化。
typedef void (*cpSpaceSegmentQueryFunc)(cpShape *shape, cpFloat t, cpVect n, void *data)void cpSpaceSegmentQuery(cpSpace *space, cpVect start, cpVect end,cpLayers layers, cpGroup group,cpSpaceSegmentQueryFunc func, void *data )沿著線段的start到end使用給定的layers和groups來(lái)查詢(xún)space過(guò)濾篩選出匹配。func函數(shù)被調(diào)用,附帶著線段和任何被發(fā)現(xiàn)的形狀表面的法向量之間的歸一化距離,還有傳遞給cpSpacePointQuery()的data參數(shù)。傳感器類(lèi)形狀也被包括在內(nèi)。
cpShape *cpSpaceSegmentQueryFirst(cpSpace *space, cpVect start, cpVect end,cpLayers layers, cpGroup group,cpSegmentQueryInfo *info )沿著線段的start到end使用給定的layers和groups來(lái)查詢(xún)space過(guò)濾篩選出匹配。只有遇到的第一個(gè)形狀會(huì)被返回并結(jié)束搜索,如果沒(méi)有發(fā)現(xiàn)形狀則返回NULL。info指向的結(jié)構(gòu)體將會(huì)被光線投射信息初始化,除非info是NULL。傳感器類(lèi)形狀將被忽略。
線段查詢(xún)輔助函數(shù):
cpVect cpSegmentQueryHitPoint(cpVect start, cpVect end, cpSegmentQueryInfo info)返回在世界坐標(biāo)系內(nèi)線段與形狀相交的第一個(gè)相交點(diǎn)。
cpFloat cpSegmentQueryHitDist(cpVect start, cpVect end, cpSegmentQueryInfo info)返回線段與形狀第一個(gè)相交點(diǎn)的絕對(duì)距離。
13.3 AABB查詢(xún)
AABB查詢(xún)提供一個(gè)快速的方式來(lái)粗略檢測(cè)一個(gè)范圍內(nèi)存在的形狀。
typedef void (*cpSpaceBBQueryFunc)(cpShape *shape, void *data)void cpSpaceBBQuery(cpSpace *space, cpBB bb,cpLayers layers, cpGroup group,cpSpaceBBQueryFunc func, void *data )查詢(xún)space找到bb附近并篩選出符合給定層和組的所有形狀。每個(gè)包圍盒和bb有重疊的形狀,都會(huì)調(diào)用func, 并將data參數(shù)傳給cpSpaceBBQuery()。傳感器類(lèi)形狀也包括在內(nèi)。
13.4 形狀查詢(xún)
形狀查詢(xún)?cè)试S你檢測(cè)空間中的形狀是否和一個(gè)指定的區(qū)域發(fā)生了重疊。如果你想在該位置添加另外一個(gè)形狀,又或者在AI中使用它進(jìn)行感應(yīng)查詢(xún)的話。你可以通過(guò)形狀查詢(xún)來(lái)檢測(cè)物體是否已經(jīng)存在于一個(gè)位置上。
在查詢(xún)前,你可以創(chuàng)建一個(gè)剛體對(duì)或者形狀對(duì),或者你創(chuàng)建一個(gè)shape值為NULL的剛體,通過(guò)調(diào)用cpShapeUpdate()函數(shù)來(lái)設(shè)置形狀的位置和旋轉(zhuǎn)角度。
typedef void (*cpSpaceShapeQueryFunc)(cpShape *shape, cpContactPointSet *points, void *data);cpBool cpSpaceShapeQuery(cpSpace *space, cpShape *shape, cpSpaceShapeQueryFunc func, void *data);查詢(xún)space來(lái)找到和shape重疊的所有形狀。使用shape的層和群組來(lái)過(guò)濾篩選得到匹配。func函數(shù)由每個(gè)重疊的形狀調(diào)用,附帶一個(gè)臨時(shí)的cpContactPointSet的一個(gè)指針和傳遞給cpSpaceBBQuery()的data參數(shù)。傳感器類(lèi)形狀也包括在內(nèi)。
13.5 閉包
如果你的編譯器支持閉包(如Clang), 還有另外一組函數(shù)可以調(diào)用,如cpSpaceNearestPointQuery_b()等。詳情請(qǐng)參考chipmunk.h。
13.6 例子
更多信息見(jiàn)查詢(xún)例子
總結(jié)
以上是生活随笔為你收集整理的Chipmunk2D中文手册的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 离线安装宝塔lnmp_宝塔LNMP环境
- 下一篇: Spark实战之读写HBase