日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Chipmunk2D中文手册

發布時間:2023/12/10 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Chipmunk2D中文手册 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Chipmunk2D中文手冊,由泰然翻譯組翻譯。轉載請注明出處。

翻譯:ChildhoodAndy(完成了大部分的翻譯), u0u0, gloryming。

校對:涵紫

github貢獻地址:https://github.com/iTyran/ChipmunkDocsCN

歡迎大家斧正錯誤,提交PR。

Chipmunk2D中文手冊


1. Chipmunk2D 6.2.1

Chipmunk2D是一個基于MIT協議的2D剛體物理仿真庫。設計宗旨:極快、可移植、穩定、易用。出于這個原因,它已經被用于數以百計的游戲,而且幾乎橫跨了所有系統。這些游戲包括了iPhoneAppStore上一些頂級出色的TOP1游戲,如Night Sky等。這幾年來,我投入了大量的時間來發展Chipmunk,才使得Chipmunk走到今天。如果您發現Chipmunk2D為您節省了許多時間,不妨考慮捐贈下。這么做會使一個獨立游戲制作者非常開心!

首先,我要非常感謝ErinCatto(譯者注:Box2D作者), 早在2006年的時候,Chipmunk的沖量求解器便是受到他的范例代碼的啟發而完成(現在已經發展成一個成熟的物理引擎:Box2D.org)。他持久接觸的想法允許對象的穩定堆棧只進行極少的求解器迭代,而我以前的求解器為了讓模擬穩定模擬會產生大量的對象或者會消耗大量的CPU資源。

1.1 為什么是一個C庫

很多人問我為什么用C來寫Chipmunk2D,而不是一個我喜歡的其他語言。我通常會對不同的編程語言很興奮,幾個月來,挑選的語言有Scheme, OCaml, Ruby, Objective-C, ooc, Lua, Io等等。它們都有一個共同點,那就是都很容易綁定到C代碼。同時我也希望Chipmunk2D高效、易移植、優化簡單并且容易調試,而使用C語言就能很簡單的達到這些目標。

我從來沒有,將來也不太可能去用C來寫一個完整的游戲。這里有很多比C有趣的語言,它們有垃圾回收,閉包,面向對象運行時等高級特性。如果你在其它語言中使用Chipmunk2D,可以在Bindings and Ports中找到有用的信息。因為Chipmunk2D基于C99的字集編寫,使得它很容易集成到C、C++、Object-C等其它開發語言中。

1.2 C API的局限

如果您使用的是C++,Chipmunk提供了操作符*,+和 - (一元和二元)的重載,但如果使用的是C,那么需要退回使用cpvadd() 和 cpvsub()。這有一點點不利于代碼閱讀,不過當你習慣之后這將不是個問題。大部分的向量操作可能并沒任何形式的符號對應(至少不在鍵盤上)。

C API的另一個問題是訪問限制。Chipmunk有許多結構體,字段,函數只能內部使用。要解決這個問題,我把Chipmunk的全部私有API分離到頭文件chipmunk_private.h中,同時在共有結構中使用CP_PRIVATE()來改名。你可以通過包含這個頭文件或使用這個宏來自由訪問私有API,但請注意這些私有API可能在未來版本中改變或消失,并且不會在文檔中體現,同時也沒有私有API的文檔計劃。

1.3 Chipmunk2D Pro

我們同時在出售Chipmunk2D的擴展版本: Chipmunk2D Pro。主要的特性有:ARM和NEON指令優化,多線程優化,一個為iOS/Mac開發提供的Objective-C封裝層,以及自動幾何工具。優化主要集中在提高移動性能,同時多線程特性能在支持pthread的平臺運行。Objective-C封裝層能讓你無縫整合到Cocos2D或UIKit等框架,并能獲得本地內存管理的優勢(包括ARC)。同時Pro版本有大量優秀的API擴展。自動幾何工具讓你能從圖像數據或程序生成并使用幾何。

另外,出售Chipmunk2D Pro讓我們得以生存,并保持Chipmunk2D的開源。捐獻也很棒,但是購買Pro版本你將獲得捐獻之外的某些東西。

1.4 下載與編譯

如果你還沒有下載,你總可以在這里獲取到Chipmunk2D的最新版本。里面包含了CMake的命令行編譯腳本, Xcode工程以及Visual Studio ’09 和 ’10工程。

Debug 或 Release?

Debug模式可能略慢,但是包含了大量的錯誤檢測斷言,可以幫助你快速定位類似重復移除對象或無法檢測的碰撞之類的BUG。我強烈建議你使用Debug模式,直到你的游戲即將Release發售。

XCode (Mac/iPhone)

源碼中的Xcode工程可直接build出一個Mac或iOS靜態庫。另外,你可以運行macosx/iphonestatic.commandmacosx/macstatic.command來生成一個帶頭文件和debug/release靜態庫的目錄,以便你可以方便的集成到你的項目中。直接在你的項目中引入Chipmunk源碼以及正確的編譯選項并非易事。iPhone編譯腳本能生成一個可用在iOS模擬器和設備的通用庫(“fat” library),其中的模擬器版本用的debug模式編譯,而設備版本用的release模式編譯。

MSVC

我很少使用MSVC,其他開發者幫忙維護了Visual Studio工程文件。MSVC 10工程應該能正常運行,因為我經常在發布穩定版本前測試它。MSVC 9工程可能運行不正常,我很少也沒有必要去運行這個工程,如何你遇到問題,請通知我。

命令行

CMake編譯腳本能在任何你安裝了CMake的系統上運行。它甚至能生成XCode或MSVC工程(查看CMake文檔獲取更多信息)。

下面的命令編譯一個Debug的Chipmunk:

cmake -D CMAKE_BUILD_TYPE=Debug . make

如何沒有-D CMAKE_BUILD_TYPE=Debug參數,將生成一個release版本。

為什么使用CMake?一個非常好心的人完成了這個腳本的最初版本,然后我發現CMake能非常方便的解決跨平臺編譯問題。我知道有些人非常討厭安裝一些胡亂的non-make編譯系統來編譯某些東西,但是CMake確實節省了我大量的時間和精力。

1.5 Hello Chipmunk(World)

下面的Hello World示例項目中,創建一個模擬世界,模擬一個球掉落到一個靜態線段上然后滾動出去,并打印球的坐標。

#include <stdio.h> #include <chipmunk.h>int main(void){// cpVect是2D矢量,cpv()為初始化矢量的簡寫形式cpVect gravity = cpv(0, -100);// 創建一個空白的物理世界cpSpace *space = cpSpaceNew();cpSpaceSetGravity(space, gravity);// 為地面創建一個靜態線段形狀// 我們稍微傾斜線段以便球可以滾下去// 我們將形狀關聯到space的默認靜態剛體上,告訴Chipmunk該形狀是不可移動的cpShape *ground = cpSegmentShapeNew(space->staticBody, cpv(-20, 5), cpv(20, -5), 0);cpShapeSetFriction(ground, 1);cpSpaceAddShape(space, ground);// 現在讓我們來構建一個球體落到線上并滾下去// 首先我們需要構建一個 cpBody 來容納對象的物理屬性// 包括對象的質量、位置、速度、角度等// 然后我們將碰撞形狀關聯到cpBody上以給它一個尺寸和形狀cpFloat radius = 5;cpFloat mass = 1;// 轉動慣量就像質量對于旋轉一樣// 使用 cpMomentFor*() 來近似計算它cpFloat moment = cpMomentForCircle(mass, 0, radius, cpvzero);// cpSpaceAdd*() 函數返回你添加的東西// 很便利在一行中創建并添加一個對象cpBody *ballBody = cpSpaceAddBody(space, cpBodyNew(mass, moment));cpBodySetPos(ballBody, cpv(0, 15));// 現在我們會球體創建碰撞形狀// 你可以為同一個剛體創建多個碰撞形狀// 它們將會附著關聯到剛體上并移動更隨cpShape *ballShape = cpSpaceAddShape(space, cpCircleShapeNew(ballBody, radius, cpvzero));cpShapeSetFriction(ballShape, 0.7);// 現在一切都建立起來了,我們通過稱作時間步的小幅度時間增量來步進模擬空間中的所有物體// *高度*推薦使用固定長的時間步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);}// 清理我們的對象并退出cpShapeFree(ballShape);cpBodyFree(ballBody); cpShapeFree(ground);cpSpaceFree(space);return 0;

1.6 支持

獲得支持最好的方式就是訪問Chipmunk論壇。上面有許多人使用Chipmunk,應用在我知道的各個平臺上。如果你在做一個商業項目,Howling Moon Software(我的公司)可給與支持。我們可以幫助你實現自定義Chipmunk行為,以及bug修復和性能優化。

1.7 聯系

如果你發現Chipmunk中的任何bug,錯誤或者該文檔中壞掉的鏈接,又或者對于Chipmunk有任何疑問、評論,都可以通過 slembcke@gmail.com (email或者GTalk)聯系我。

1.8 開源協議

Chipmunk基于MIT協議。

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.

這項協議意味著對于商業項目你不必購買許可證或者支付任何費用就能使用Chipmunk。(雖然我們真的很感謝捐贈)

1.9 鏈接

  • Chipmunk論壇 - Chipmunk2D官方論壇
  • Howling Moon Software - 我合辦的軟件公司(我們提供外包工作)
  • Chipmunk2D Pro - Chipmunk的增強版本,我們為ARM或者多核平臺做了一些特定的優化,如從圖像或程序數據中進行自動幾何操作,以及為Objective-C做了API封裝。
  • 游戲 - 使用Chipmunk做的游戲清單。至少一小部分我們知道。

2. Chipmunk2D 基礎

2.1 概述

在Chipmunk中有4種基本對象類型,分別是

  • 剛體:一個剛體容納著一個對象的物理屬性(如質量、位置、角度、速度等)。默認情況下,它并不具有任何形狀,直到你為它添加一個或者多個碰撞形狀進去。如果你以前做過物理粒子,你會發現它們的不同之處是剛體可以旋轉。在游戲中,通常剛體都是和一個精靈一一對應關聯的。你應該構建你的游戲以便可以使用剛體的位置和角度來繪制你的精靈。
  • 碰撞形狀:因為形狀與剛體相關聯,所以你可以為一個剛體定義形狀。為了定義一個復雜的形狀,你可以給剛體綁定足夠多的形狀。形狀包含著一個對象的表面屬性如摩擦力、彈性等。
  • 約束/關節:約束和關節被用來描述剛體之間是如何關聯的
  • 空間:空間是Chipmunk中模擬對象的容器。你將剛體、形狀、關節添加進入一個空間,然后將空間作為一個整體進行更新??臻g控制著所有的剛體、形狀和約束之間的相互作用。

人們經常對Chipmunk中的剛體和碰撞形狀以及兩者與精靈之間的關系產生混淆。精靈是對象的可視化表現,而碰撞形狀是定義對象應該如何碰撞的不可見的屬性。精靈和碰撞形狀兩者的位置和角度都是由剛體的運動控制的。通常你應該創建一個游戲對象類型,把這些東西捆綁在一起。

2.2 內存管理

對于你將使用的大多數結構體來說,Chipmunk采用了一套或多或少的標準和簡單直接的內存管理方式。拿cpSpace結構體來舉例:

  • cpSpaceNew() - 分配并初始化一個cpSpace結構體。它先后調用了cpSpaceAlloc()和cpSpaceInit(cpSpace *space)
  • cpSpaceFree(cpSpace *space) - 銷毀并釋放cpSpace結構體

你有責任釋放掉任何你分配了內存空間的結構體。 Chipmunk沒有采用引用計數和垃圾回收機制。 如果你調用了一個new函數,則必須匹配調用free函數來釋放空間,否則會引起內存泄漏。

另外當你在棧上分配臨時結構體,或者寫一個語言綁定,又或者在一個內存受限的環境下編碼的時候,如果你在內存的分配和初始化上需要更多的控制權,可以使用下面的函數。大部分人永遠都不會使用這些函數。

  • cpSpaceAlloc() - 為一個cpSpace結構體分配空間,但不進行初始化。所有的分配空間的函數看起來大致就像這樣:return (cpSpace *)cpcalloc(1, sizeof(cpSpace));。 如果需要的話你可以自己實現自己的分配空間函數。把內存空間重置為0,不是硬性要求。
  • cpSpaceInit(cpSpace *space) - 初始化cpSpace結構體
  • cpSpaceDestroy(cpSpace *space) - 釋放由cpSpaceInit()申請的所有內存空間,但并不釋放cpSpace結構體本身

就像new和free函數的對應調用一樣,任何由alloc函數分配的內存都要由cpfree()或類似的函數來釋放,任何init函數調用都必須對應destroy函數調用。

為了能夠更加輕松地集成垃圾回收或其他內存管理機制,Chipmunk有若干可以被重寫的編譯時定義(cpcalloc(), cprealloc(), cpfree())。如果你不是通過帶有垃圾回收的語言使用Chipmunk,我強烈推薦使用libGC。它為基于C的語言提供了一個幾乎透明的垃圾收集器。

2.3 基本類型

chipmunk_types.h定義了Chipmunk使用的一些基本數據類型。這些數據類型可以在編譯時改變以便適應你的需求:

  • cpFloat: 浮點型,默認為double
  • cpVect: 2D矢量,[cpVect相關文檔](cpVect documentation)
  • cpBool: 像每一個優秀的C語言庫一樣,具有跨語言兼容性,你可以定義自己的布爾類型,默認為int
  • cpDataPointer: 指針類型,可以是回調、用戶自定義數據的指針,默認是void*
  • cpCollistionType: 碰撞形狀類型的唯一標識符,默認是unsigned int。自定義類型必須支持==運算符
  • cpGroup: 碰撞組唯一標識符,默認是unsigned int。當你不想區分組別的時候,可以定義一個CP_NO_GROUP。自定義類型必須支持==運算符
  • cpLayers: 該類型被用作為層的掩碼,默認是unsigned int。CP_ALL_LAYERS被用來定義為所有層位。自定義類型必須支持位操作&運算符

如果你正在寫游戲引擎或者在Chipmunk之上進行語言綁定,你可能希望使用對象的引用代替整數來表示碰撞類型和碰撞組。我經常使用類指針來表示碰撞類型,游戲對象指針來表示碰撞組。這比到處定義枚舉表簡單多了。

注意:在iphone上,為了性能和兼容性,cpFloat被定義為float,cpVect是CGPoint的別名。

2.4 數學運算

首先,Chipmunk默認使用雙精度浮點數進行數學計算。在大多數現代臺式機處理器下這樣很可能更快點,并意味著你可以不用過多擔心浮點舍入引起的誤差。在編譯庫的時候你可以修改Chipmunk使用的浮點類型。請查看chipmunk_types.h。

Chipmunk為一些常用的數學函數定義了別名以便你可以用Chipmunk的浮點類型來代表float或者double類型。在你的代碼里,或許沒有充分的理由去使用這些別名,除非你預計今后你可能會改變Chipmunk的浮點類型,而且你很介意錯誤的使用float/double版本的數學函數所造成的2%的性能下降。

有一些函數或許你會發現非常有用:

  • cpFloat cpfclamp(cpFloat f, cpFloat min, cpFloat max) - 截斷f在min和max之間
  • cpFloat cpflerp(cpFloat f1, cpFloat f2, cpFloat t) - 對f1和f2進行線性插值
  • cpFloat cpflerpconst(cpFloat f1, cpFloat f2, cpFloat d) - 從f1到f2不超過d的線性插值

浮點數無窮大被定義為INFINITY, 很多數學庫中這樣定義,但這實際上并不是C標準庫的一部分。

3. Chipmunk矢量:cpVect

3.1 結構體定義、常量和構造函數

定義:

typedef struct cpVect{cpFloat x, y; } cpVect

零向量常量:

static const cpVect cpvzero = {0.0f,0.0f};

創建新結構體所用的便捷構造函數:

cpVect cpv(const cpFloat x, const cpFloat y)

3.2 操作運算

  • cpBool cpveql(const cpVect v1, const cpVect v2) – 檢測兩個向量是否相等。在使用C++程序時,Chipmunk提供一個重載操作符==。(比較浮點數時要小心!)
  • cpVect cpvadd(const cpVect v1, const cpVect v2) – 兩個向量相加。在使用C++程序時,Chipmunk提供一個重載操作符+。
  • cpVect cpvsub(const cpVect v1, const cpVect v2) – 兩個向量相減。在使用C++程序時,Chipmunk提供一個重載操作符-。
  • cpVect cpvneg(const cpVect v) – 使一個向量反向。在使用C++程序時,Chipmunk提供一個重載一個一元負操作符-。
  • cpVect cpvmult(const cpVect v, const cpFloat s) – 標量乘法。在使用C++程序時,Chipmunk提供一個重載操作符*。
  • cpFloat cpvdot(const cpVect v1, const cpVect v2) – 向量的點積。
  • cpFloat cpvcross(const cpVect v1, const cpVect v2) – 2D向量交叉相乘的模。2D向量交叉相乘的積作為一個只有z坐標的3D向量的z值。函數返回z坐標的值。
  • cpVect cpvperp(const cpVect v) – 返回一個垂直向量。(旋轉90度)
  • cpVect cpvrperp(const cpVect v) – 返回一個垂直向量。(旋轉-90度)
  • cpVect cpvproject(const cpVect v1, const cpVect v2) – 返回向量v1在向量v2上的投影。
  • cpVect cpvrotate(const cpVect v1, const cpVect v2) – 使用復雜的乘法運算將向量v1按照向量v2旋轉。如果v1不是單位向量,則v1會被縮放。
  • cpVect cpvunrotate(const cpVect v1, const cpVect v2) – 和cpvrotate()相反。
  • cpFloat cpvlength(const cpVect v) – 返回v的長度。
  • cpFloat cpvlengthsq(const cpVect v) – 返回v的長度的平方,如果只是比較長度的話它的速度比cpvlength()快。
  • cpVect cpvlerp(const cpVect v1, const cpVect v2, const cpFloat t) – 在v1和v2之間線性插值。
  • cpVect cpvlerpconst(cpVect v1, cpVect v2, cpFloat d) – 以長度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之間以不超過角a的弧度值球形線性插值。
  • cpVect cpvnormalize(const cpVect v) – 返回a的一個歸一化副本。作為特殊例子,在調用cpvzero時返回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軸對齊包圍盒:cpBB

4.1 結構體定義和構造函數

  • 簡單的包圍盒結構體,存儲著left,bottom,right,top等值。
typedef struct cpBB{cpFloat l, b, r ,t; } cpBB
  • 便捷的構造函數,如cpv()函數一樣返回一個副本而不是一個申請的指針。
cpBB cpBBNew(const cpFloat l, const cpFloat b, const cpFloat r, const cpFloat t)
  • 便捷的構造函數,用來構造一個位置為p,半徑為r的一個圓的包圍盒
cpBB cpBBNewForCircle(const cpVect p, const cpFloat r)

4.2 操作運算

  • 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的中心點矢量
  • cpFloat cpBBArea(cpBB bb) - 返回bb矢量表示的邊界框的面積
  • cpFloat cpBBMergedArea(cpBB a, cpBB b) - 合并a和b然后返回合并后的矢量的邊界框的面積
  • cpFloat cpBBSegmentQuery(cpBB bb, cpVect a, cpVect b) - 返回分段查詢相交bb的相交點個數,如果沒有相交,返回INFINITY
  • cpBool cpBBIntersectsSegment(cpBB bb, cpVect a, cpVect b) - 如果由a和b兩端點定義的線段和bb相交返回true
  • cpVect cpBBClampVect(const cpBB bb, const cpVect v) - 返回v在邊界框中被截斷的矢量的副本
  • cpVect cpBBWrapVect(const cpBB bb, const cpVect v) - 返回v包含邊界框的矢量的副本

5. Chipmunk剛體:cpBody

5.1 游離和靜態剛體

一般當我們創建一個剛體并將它添加到空間上后,空間就開始對之進行模擬,包括了對剛體位置、速度、受力以及重力影響等的模擬。沒被添加到空間(沒有被模擬)的剛體我們把它稱之為游離剛體。游離剛體最重要的用途就是用來當作靜態剛體,但是你仍然可以使用它們來實現直接控制物體,如移動平臺。

靜態剛體是游離剛體,但被設置了一個特殊的標志以便讓Chipmunk知道它們從不移動除非你要求這么做。靜態剛體有兩個目的。最初,它們被加入用來實現休眠功能。因為靜態剛體不移動,Chipmunk知道讓那些與靜態剛體接觸或者連接的物體安全的進入休眠。接觸或連接常規游離剛體的物體從不允許休眠。靜態剛體的第二個目的就是讓Chipmunk知道,關聯到靜態剛體的碰撞形狀是不需要更新碰撞檢測數據的。Chipmunk也不需要操心靜態物體之間的碰撞檢測。通常所有的關卡幾何圖形都會被關聯到一個靜態剛體上除了那些能夠移動的東西,例如平臺或門等。

在Chipmunk5.3版本之前,你要創建一個無限大質量的游離剛體,通過cpSpaceAddStaticShape()來添加靜態形狀?,F在你不必這樣做了,并且如果你想使用休眠功能也不應該這樣做了。每一個空間都有一個專用的靜態剛體,你可以使用它來添加靜態形狀。Chipmunk也會自動將形狀作為靜態形狀添加到靜態剛體上。

5.2 內存管理函數

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)

如上是一套標準的Chipmunk內存管理函數。m和i是剛體的質量和轉動慣量。猜想剛體的質量通常是可行的,但是猜想剛體的轉動慣量卻會導致一個很差的模擬。在任何關聯到剛體的形狀或者約束從空間移除之前注意不要釋放剛體。

5.3 創建額外靜態剛體

每一個cpSpace都有一個可以直接使用的內置靜態剛體,同時你也可以便利地構建自己的靜態剛體。一個潛在的用途就是用在關卡編輯器中。你可以把關卡的不同組塊關聯到不同的靜態剛體上,這樣你仍然可以獨立的移動和旋轉每一個組塊。你要做的只是在操作完成之后調用cpSpaceRehashStatic()來重建靜態碰撞檢測的數據。

關于游離和靜態剛體的更多信息,請看Chipmunk空間。

cpBody *cpBodyAlloc(void); cpBody *cpBodyInitStatic(cpBody *body) cpBody *cpBodyNewStatic()

創建額外的具有無限的質量和轉動慣量的靜態剛體。

5.4 屬性

Chipmunk為剛體的多個屬性提供了getter/setter函數。如果剛體在休眠狀態,設置大多數屬性會自動喚醒它們。如果你想,你也可以直接在cpBody結構體內設置字段。它們都在頭文件中有記錄。

cpFloat cpBodyGetMass(const cpBody *body) void cpBodySetMass(cpBody *body, cpFloat m)

剛體的質量。

cpFloat cpBodyGetMoment(const cpBody *body) void cpBodySetMoment(cpBody *body, cpFloat i)

剛體的轉動慣量(MoI(譯者注:Moment Of Inertia即轉動慣量的縮寫)或有時只說慣量)。慣量就像剛體的旋轉質量。請查閱下面的函數來幫助計算慣量。

cpVect cpBodyGetPos(const cpBody *body) void cpBodySetPos(cpBody *body, cpVect pos)

剛體重心的位置。當改變位置的時候如果你要計劃對空間進行任何查詢,你可能還需要調用cpSpaceReindexShapesForBody()來更新關聯形狀的碰撞檢測信息。

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)

剛體的角度,弧度制。當改變角度的時候如果你要計劃對空間進行任何查詢,你可能還需要調用cpSpaceReindexShapesForBody()來更新關聯形狀的碰撞檢測信息。

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)

剛體的旋轉向量??赏ㄟ^cpvrotate()或者cpvunrotate()進行快速旋轉。

cpFloat cpBodyGetVelLimit(const cpBody *body) void cpBodySetVelLimit(cpBody *body, const cpFloat value)

剛體的速度極限。、默認為INFINITY(無限大),除非你專門設置它??梢员挥脕硐拗葡侣渌俣鹊?。

cpFloat cpBodyGetAngVelLimit(const cpBody *body) void cpBodySetAngVelLimit(cpBody *body, const cpFloat value)

剛體以弧度/秒的角速度限制。默認為INFINITY,除非你專門設置它。

cpSpace* cpBodyGetSpace(const cpBody *body)

獲取body所添加進去的cpSpace。

cpDataPointer cpBodyGetUserData(const cpBody *body) void cpBodySetUserData(cpBody *body, const cpDataPointer value)

使用數據指針。使用該指針從回調中獲取擁有該剛體的游戲對象的引用。

5.5 轉動慣量和面積幫助函數

使用以下函數來近似計算出剛體的轉動慣量,如果想得到多個,那就將結果相加在一起。

  • cpFloat cpMomentForCircle(cpFloat m, cpFloat r1, cpFloat r2, cpVect offset) – 計算空心圓的轉動慣性,r1和r2是在任何特定順序下的內徑和外徑。 (實心圓圈的內徑為0)
  • cpFloat cpMomentForSegment(cpFloat m, cpVect a, cpVect b) – 計算線段的轉動慣量。端點a和b相對于剛體。
  • cpFloat cpMomentForPoly(cpFloat m, int numVerts, const cpVect *verts, cpVect offset) – 計算固定多邊形的轉動慣量,假設它的中心在質心上。offset偏移值被加到每個頂點。
  • cpFloat cpMomentForBox(cpFloat m, cpFloat width, cpFloat height) – 計算居中于剛體的實心矩形的轉動慣量。

轉動慣量例子

// 質量為2,半徑為5的實心圓的轉動慣量 cpFloat circle1 = cpMomentForCircle(2, 0, 5, cpvzero);// 質量為1,內徑為1,外徑為6的空心圓的轉動慣量 cpFloat circle2 = cpMomentForCircle(1, 2, 6, cpvzero);// 質量為1,半徑為3,x軸方向偏離重心量為3的實心圓的轉動慣量 cpFloat circle3 = cpMomentForCircle(2, 0, 5, cpv(3, 0));// 復合對象。居中于重心的1x4的矩形和y軸偏移重心量為3,半徑為1的實心圓 // 只需將轉動慣量相加到一起 cpFloat composite = cpMomentForBox(boxMass, 1, 4) + cpMomentForCircle(circleMass, 0, 1, cpv(0, 3));

如果你想近似計算諸如質量或密度此類的東西,可以使用下列函數來獲取Chipmunk形狀區域。

  • cpFloat cpAreaForCircle(cpFloat r1, cpFloat r2) – 空心圓形狀面積
  • cpFloat cpAreaForSegment(cpVect a, cpVect b, cpFloat r) – 斜線段面積。(如果半徑為0的話永遠為0)
  • cpFloat cpAreaForPoly(const int numVerts, const cpVect *verts) – 多邊形形狀的面積。多邊形為凹多邊形時返回一個負值。

5.6 坐標系轉換函數

許多事情被定義在剛體的局部坐標,也就意味著(0,0)是剛體的重心和軸線旋轉中心。

  • cpVect cpBodyLocal2World(const cpBody *body, const cpVect v) – 從剛體局部坐標系轉換到世界坐標系
  • cpVect cpBodyWorld2Local(const cpBody *body, const cpVect v) – 從世界坐標系轉換到剛體的局部坐標系

5.7 施加力和力矩

人們有時候容易混淆力和沖量之間的區別。沖量基本上是一個在非常短的時間內施加的一個非常大的力,就像一個球擊中一堵墻或者大炮射擊一樣。Chipmunk的沖量會在一瞬間直接施加在物體的速度上。無論是力還是沖量都受到物體質量的影響。物體質量翻倍,則效果減半。

  • void cpBodyResetForces(cpBody *body) – 對剛體施加0值的力和扭矩
  • void cpBodyApplyForce(cpBody *body, const cpVect f, const cpVect r) – 在離重心相對偏移量為r的位置施加f的力于body上
  • void cpBodyApplyImpulse(cpBody *body, const cpVect j, const cpVect r) – 在離重心相對偏移量為r的位置施加j的沖量于body上。

注: cpBodyApplyForce()和cpBodyApplyImpulse()兩者都是在絕對坐標系中施加力或者沖量,并在絕對坐標系中產生相對的偏移。(偏移量相對于重心位置,但不隨剛體旋轉)

5.8 休眠函數

Chipmunk支持休眠功能,以便其停止使用CPU時間來模擬移動的對象組。更多信息請查閱cpSpace部分。

  • cpBool cpBodyIsSleeping(const cpBody *body) – 如果剛體在休眠則返回true。
  • void cpBodyActivate(cpBody *body) – 重設剛體的閑置時間。如果在休眠,則會喚醒它以及和它接觸的任何其他剛體。
  • void cpBodySleep(cpBody *body) – 強制一個剛體立即進入休眠,即使它在半空中。不能從回調中被調用。
  • void cpBodyActivateStatic(cpBody *body, cpShape *filter) – 和cpBodyActivate()功能類似。激活剛體接觸的所有剛體。如果filter不為NULL,那么只有通過篩選過濾的剛體才會被喚醒。
void cpBodySleepWithGroup(cpBody *body, cpBody *group)

當對象在Chipmunk中處于休眠時,和它接觸或連接在一起的所有剛體都會作為一組進入休眠。當對象被喚醒時,和它一組的所有對象都會被喚醒。 cpBodySleepWithGroup()允許你將群組中的對象一起休眠。如果你通過一個新的組給groups 傳遞NULL值,則它和cpBodySleep()功能一樣。如果你為groups傳入一個休眠的剛體,那么當group是喚醒狀態時,body也會被喚醒。你可以通過這來初始化關卡并將堆棧中的對象置為預休眠狀態。

休眠例子

// 構建一堆箱子 // 強制它們進入休眠直到他們第一次被接觸 // 將它們放進一組以便接觸它們任意一個都會喚醒他們 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);// 你可以使用任意休眠剛體作為組別的標識符// 這里我們只保存了我們初始化的最后一個剛體的引用// 傳入NULL值作為組別將啟動一個新的休眠組// 你必須在完全初始化對象后這么做// 添加形狀或調用setter函數將會喚醒剛體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相關的且附加到空間上的所有形狀,每次迭代都會調用func函數。data作為上下文值傳遞。使用這些回調來刪除形狀是安全的。

typedef void (*cpBodyConstraintIteratorFunc)(cpBody *body, cpConstraint *constraint, void *data) void cpBodyEachConstraint(cpBody *body, cpBodyConstraintIteratorFunc func, void *data)

迭代與body相關的且附加到空間上的所有約束,每次迭代都會調用func函數。data作為上下文值傳遞。使用這些回調來刪除約束是安全的。

typedef void (*cpBodyArbiterIteratorFunc)(cpBody *body, cpArbiter *arbiter, void *data) void cpBodyEachArbiter(cpBody *body, cpBodyArbiterIteratorFunc func, void *data)

這個更有趣。迭代與body相關的碰撞對,每次迭代都會調用func函數。調用cpArbiterGet[Bodies|Shapes]()或者CP_ARBITER_GET_[BODIES|SHAPES]()可以取到與此次碰撞相關的那兩個剛體或形狀。你可以用它來檢查各種碰撞信息,例如,是否接觸了地面,是否接觸了另一特定的物體,物體受到的碰撞力有多大等。那些被碰撞回調拒絕的傳感器類型的形狀或是被cpArbiterIngnore()忽略的仲裁者是不會被接觸圖形跟蹤記錄的。

注:如果你的編譯器支持閉包(如Clang),還有另外一組函數可以調用,如cpBodyEachShape_b()等。更多信息見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);// 通過比較向量和以及幅度和來查看碰撞的力量彼此相對有多大cpFloat crushForce = (crush.magnitudeSum - cpvlength(crush.vectorSum))*dt; }

5.10 嵌入回調

這部分是殘留?,F在你可以看看星球演示這個例子,看如何使用嵌入回調來實現的行星重力。

5.11 雜項函數

  • cpBool cpBodyIsStatic(const cpBody *body) - 如果body是靜態剛體的話,返回true。無論是cpSpace.staticBody,還是由cpBodyNewStatic()或者cpBodyInitStatic()創建的剛體。
  • cpBool cpBodyIsRogue(const cpBody *body)- 如果剛體從來沒有被加入到空間的話返回true。

5.12 札記

  • 如果可能的話使用力來修正剛體。這樣是最穩定的。
  • 修正剛體的速度是不可避免的,但是在每幀對剛體的速度做巨大的變化會造成一些奇怪的模擬。你可以自由實驗,但別說我沒警告你哦。
  • 不要在單步中修正剛體的位置除非你確實知道你在干什么。否則你得到的位置、速度則會不同步。
  • 如果在調用cpSpaceRemoveShape()之前你就要釋放一個剛體,那么會引起崩潰。

6. Chipmunk碰撞形狀:cpShape

當前有三種類型的碰撞形狀:

  • 圓形:快速簡單的碰撞形狀
  • 線段:主要作為靜態形狀??梢詢A斜以便給之一個厚度。
  • 凸多邊形:最慢,但卻為最靈活的碰撞形狀。
  • 如果你愿意,你可以在一個剛體上添加任意數量的形狀。這就是為什么兩種類型(形狀和剛體)是分離開的。這將會讓你足夠靈活的來給相同對象的不同區域提供不同的摩擦力、彈性以及回調值。

    不管創建何種類型的形狀,你總是會得到一個cpShape*指針。這是因為Chipmunk的形狀是不透明的類型。想象一下具體的碰撞形狀類型,如cpCircleShape, cpSegmentShape和cpPolyShape, 他們都是cpShape的私有子類。但是你仍然可以使用getter函數來獲取他們的屬性,而不需要將cpShape指針轉成他們特定的類型指針。

    6.1 札記

    Chipmunk直到 6.1.2 版本才支持線段、線段碰撞。由于兼容性的原因,你必須調用cpEnableSegmentToSegmentCollisions()來全局明確地啟用它們。 (感謝LegoCylon對此的幫助)

    6.2 屬性

    Chipmunk為一些碰撞形狀屬性提供了getter/ setter函數。如果形狀關聯的剛體在休眠,設置多數屬性都會自動喚醒它們。如果你想的話,也可以直接設置cpShape結構的某些字段。他們在頭文件中都記錄有。

    cpBody * cpShapeGetBody(const cpShape *shape) void cpShapeSetBody(cpShape *shape, cpBody *body)

    只有當形狀尚未添加進空間的時候才能關聯到一個剛體。

    cpBB cpShapeGetBB(const cpShape *shape)

    上面得到的是形狀的碰撞包圍盒。只能保證在cpShapeCacheBB()或cpSpaceStep()調用后是有效的。移動形狀所連接到剛體并不更新它的包圍盒。對于沒有關聯到剛體的用于查詢的形狀,也可以使用cpShapeUpdate()。

    cpBool cpShapeGetSensor(const cpShape *shape) void cpShapeSetSensor(cpShape *shape, cpBool value)

    用來標識形狀是否是一個感應器的布爾值。感應器只調用碰撞回調,但卻不產生真實的碰撞。

    cpFloat cpShapeGetElasticity(const cpShape *shape) void cpShapeSetElasticity(cpShape *shape, cpFloat value)

    形狀的彈性。0.0表示沒有彈性,1.0b表示“富有”彈性。然而由于存在模擬誤差,不推薦使用1.0或更高的值。碰撞的彈性是由單個形狀的彈性相乘得到。

    cpFloat cpShapeGetFriction(const cpShape *shape) void cpShapeSetFriction(cpShape *shape, cpFloat value)

    摩擦系數。Chipmunk使用的是庫侖摩擦力模型,0.0值表示無摩擦。碰撞間的摩擦是由單個形狀的摩擦相乘找到。摩擦系數表

    cpVect cpShapeGetSurfaceVelocity(const cpShape *shape) void cpShapeSetSurfaceVelocity(cpShape *shape, cpVect value)

    物體的表面速度??捎糜趧摻▊魉蛶Щ蛞苿拥耐婕摇4酥翟谟嬎隳Σ習r才會使用,不影響碰撞。

    cpCollisionType cpShapeGetCollisionType(const cpShape *shape) void cpShapeSetCollisionType(cpShape *shape, cpCollisionType value)

    你可以為Chipmunk的碰撞形狀指定類型從而在接觸特定類型物體的時候觸發回調。更多信息請參見回調部分。

    cpGroup cpShapeGetGroup(const cpShape *shape) void cpShapeSetGroup(cpShape *shape, cpGroup value)

    在相同的非零組中,形狀間不產生碰撞。在創建了一個許多形狀組成的物體,但卻不想自身與自身之間發生碰撞,這會很有用。默認值為CP_NO_GROUP。

    cpLayers cpShapeGetLayers(const cpShape *shape) void cpShapeSetLayers(cpShape *shape, cpLayers value)

    只有在相同的位平面內形狀間才發生碰撞。比如(a->layers & b->layers) != 0。默認情況下,一個形狀占據所有的位平面。如果你不熟悉如何使用它們,維基百科有篇很好的文章介紹了位掩碼的相關知識你可以閱讀下。默認值為CP_ALL_LAYERS。

    cpSpace* cpShapeGetSpace(const cpShape *shape)

    獲取形狀所屬的空間。

    cpDataPointer cpShapeGetUserData(const cpShape *shape) void cpShapeSetUserData(cpShape *shape, cpDataPointer value)

    用戶自定義數據的指針。如果你設置將其指向形狀關聯的游戲對象,那么你可以在Chipmunk回調中訪問你的游戲對象。

    6.3 碰撞過濾

    Chipmunk 有兩種主要的途徑來忽略碰撞: 群組和層

    群組是為了忽略一個復雜對象內部元素之間的碰撞。布娃娃是一個很好的例子。當把手臂和軀干連接到一起的時候,你會希望它們可以部分重疊。群組允許這樣做。相同群組間的形狀不產生碰撞。所以通過將一個布娃娃的所有形狀放在同一群組中,就會阻止其碰撞自身的其它部件。

    層允許你將碰撞的形狀分離在互斥的位面。形狀可以隸屬于一個或多個層,而兩個形狀要發生碰撞,必須有至少一個層是相同的。舉一個簡單的例子,比如說形狀A是在第1層,形狀B是在第2層和形狀C是在層1和2上。形狀A和B不會互相碰撞,但形狀C將與這兩個A和B發生碰撞

    層也可以用于建立基于碰撞的規則。比如說在你的游戲中有四種類型的形狀。玩家,敵人,玩家子彈,敵人子彈。玩家應該和敵人發生碰撞,但子彈卻不應該和發射者碰撞。圖表類似下圖:

    PlayerEnemyPlayer BulletEnemy Bullet
    Player-(1)
    Enemy--(3)
    Player Bullet---
    Enemy Bullet---

    圖表中‘-’代表冗余,數字的地方應該發生碰撞。你可以每一個規則對應一個層。然后將層添加到類型上:玩家應該在層1和2中,敵人應該是在層1和3中,玩家的子彈應該是在層3中,敵人的子彈應該是在層2中。這種把層當作規則的方式,可以定義多達32個規則。默認cpLayers類型為unsigned int在大多數系統是32位的。如果你需要更多的比特來完成工作, 你可以在chipmunk_types.h中重新定義cpLayers類型。

    還有最后一個方法通過碰撞處理函數來過濾碰撞。詳情請見回調部分。碰撞處理程序可以更靈活,但它們也是最慢的方法。所以,你要優先嘗試使用群組或層。

    6.4 內存管理函數

    void cpShapeDestroy(cpShape *shape) void cpShapeFree(cpShape *shape)

    Destroy和Free函數由所有形狀類型共享。分配和初始化函數特定于每一個形狀。見下文。

    6.5 其他函數

    • cpBB cpShapeCacheBB(cpShape *shape) – 同步shape和與之關聯的剛體
    • cpBB cpShapeUpdate(cpShape *shape, cpVect pos, cpVect rot) – 設置形狀的位置和旋轉角度
    • void cpResetShapeIdCounter(void) – Chipmunk使用了一個計數器,以便每一個新的形狀都能在空間索引中使用唯一的哈希值。因為會影響發現和處理碰撞的順序,所以每次用新的形狀重建空間時你可以重置形狀計數器。否則,模擬可能會有很(極)小的差別。

    6.6 圓形形狀

    cpCircleShape *cpCircleShapeAlloc(void) cpCircleShape *cpCircleShapeInit(cpCircleShape *circle, cpBody *body, cpFloat radius, cpVect offset) cpShape *cpCircleShapeNew(cpBody *body, cpFloat radius, cpVect offset)

    body是圓形形狀關聯的剛體。offset是在剛體局部坐標系內,與剛體中心的偏移量。

    cpVect cpCircleShapeGetOffset(cpShape *circleShape) cpFloat cpCircleShapeGetRadius(cpShape *circleShape)

    圓形形狀屬性的getter函數。傳一個非圓形形狀將會拋出一個斷言。

    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是線段形狀關聯的剛體,a和b是端點,radius是線段的厚度。

    cpVect cpSegmentShapeGetA(cpShape *shape) cpVect cpSegmentShapeGetA(cpShape *shape) cpVect cpSegmentShapeGetNormal(cpShape *shape) cpFloat cpSegmentShapeGetRadius(cpShape *shape)

    線段屬性的getter函數。傳入一個非線段形狀會拋出一個斷言。

    void cpSegmentShapeSetNeighbors(cpShape *shape, cpVect prev, cpVect next)

    當你有一些連接在一起的線段形狀時,線段仍然可以與線段間的“裂縫”碰撞。通過設置相鄰線段的端點,你告訴Chipmunk來避免裂縫內部碰撞。

    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是多邊形關聯的剛體,verts是一個cpVect結構體數組,定義了一個沿順時針圍成的凸多邊形(譯者注:原文是凸包,維基百科-凸包),offset是在剛體局部坐標系中與剛體重心的偏移量。當頂點沒形成凸多邊形或者不是順時針順序的時候會拋出一個斷言。

    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)

    和上面的一樣,但允許你創建一個帶有半徑的多邊形形狀。(我知道命名有些詞不達意,在Chipmunk7中將會清理)

    int cpPolyShapeGetNumVerts(cpShape *shape) cpVect cpPolyShapeGetVert(cpShape *shape, int index) cpFloat cpPolyShapeGetRadius()

    多邊形形狀屬性的getter函數。傳遞一個非多邊形形狀或者不存在的index將會拋出一個斷言。

    盒子

    因為盒子在物理游戲中太普遍,Chipmunk提供了創建盒形多邊形的快捷方式。盒子總是會被居中放置在它們所關聯的剛體的重心位置。如果你想創建一個偏離中心的盒子,必須使用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)

    多邊形輔助函數

    • cpBool cpPolyValidate(const cpVect *verts, const int numVerts) - 檢測定點數組是否能順時針圍成凸多邊形。
    • cpVect cpCentroidForPoly(const int numVerts, const cpVect *verts) - 計算多邊形的幾何中心。
    • void cpRecenterPoly(const int numVerts, cpVect *verts) - 把一個多邊形居中到(0,0),用定點減去幾何中心。

    凸包輔助函數

    int cpConvexHull(int count, cpVect *verts, cpVect *result, int *first, cpFloat tol)

    計算一個給定集合的凸包。返回凸包里點的數量。result必須是一個指向cpVect數組的指針,至少有count個元素。如果result是NULL,數組verts會被縮減。first是一個可選的整型指針用來存儲凸包的起點(即verts[first] == result[0])。tol是凸包允許被繼續收縮的幅度。0.0公差表示一個精確的凸包。

    #define CP_CONVEX_HULL(inputCount, inputVerts, outputCount_varName, outputVerts_varName)

    cpConvexHull()的簡化宏。在棧上用alloca()創建數組,然后調用cpConvexHull()。因為輸出數組是在棧上創建的所以不需要釋放。

    cpConvexHull例子

    int first = 0;// 創建空間來存儲凸包。 // alloca(),或者可變長度的數組會更好,但是不要總考慮可移植性。 cpVect *hullVerts = (cpVect *)calloc(vertCount, sizeof(cpVect)); int hullCount = cpConvexHull(vertCount, verts, hullVerts, &first, 0.0);// 這里hullVerts[0]和verts[first]將會是相等的。 // 如果你不關心`first`指針,可以傳NULL。cpBody *body = cpBodyNew(mass, cpMomentForPoly(mass, hullCount, hullVerts, cpvzero)); cpShape *shape = cpPolyShapeNew(body, hullCount, hullVerts, cpvzero);free(hullVerts);// ********* // 另外你可以使用CP_CONVEX_HULL()宏來省點事// 這個宏會聲明hullCount和hullVerts變量。 // hullVerts是在棧上申請的空間,不需要釋放。 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

    簡短的回答是你不能修改,因為這些更改都只會被提煉成形狀表面的位置的變化,連速度都不會變。長一點兒的回答是,你可以使用“不安全”的API,但是你要知道現實生活中的物理實驗是不會得到這樣的結果的。這些額外的功能都在單獨的頭文件chipmunk_unsafe.h中定義。

    6.10 札記

    • 你可以將多個碰撞形狀關聯到剛體上。這樣你就可以創建幾乎任何形狀。
    • 關聯在同一個剛體上的形狀不會產生碰撞。你不必擔心同個剛體上的形狀的重疊問題。
    • 確保剛體和剛體的碰撞形狀都被添加進了空間。除非你有一個外部剛體或者一個自己維護的剛體,這中情況下,只需把形狀添加進空間。

    7. Chipmunk空間:cpSpace

    Chipmunk的空間是模擬的基本單元。你將剛體、形狀和約束添加進去然后通過時間來步進更新模擬。

    7.1 什么是迭代?為什么我要關心?

    Chipmunk使用一個迭代求解器來計算出空間剛體之間的力。也就是說它建立了剛體間的所有碰撞、關節和約束的一個列表,并在列表中逐個考慮每一個剛體的若干條件。遍數這些條件便得到迭代次數,且每次迭代會使求解更準確。如果你使用太多的迭代,物理效果看起來應該不錯并且堅實穩定,但可能消耗太多的CPU時間。如果你使用過少的迭代,模擬仿真似乎看起來有些糊狀或彈性,而物體應該是堅硬的。設置迭代次數可以讓你在CPU使用率和物理精度上做出平衡。 Chipmunk中默認的迭代值是10,足以滿足大多數簡單的游戲。

    7.2 休眠

    休眠是Chipmunk5.3新功能,是指空間停用已停止移動的整個對象群組,以節省CPU時間和電池壽命的能力。為了使用此功能,你必須做兩件事情。第一個是,你必須將你的所有靜態幾何關聯到靜態剛體。如果對象接觸的是非靜態游離體,則它們不能進入休眠,即使它的形狀是作為靜態形狀添加的。第二個是,你必須通過cpSpace.sleepTimeThreshold設置一個時間閾值來顯式啟用休眠。如果你沒有明確設置cpSpace.idleSpeedThreshold,那么Chipmunk會基于當前重力自動產生一個休眠閾值。

    7.3 屬性

    int cpSpaceGetIterations(const cpSpace *space) void cpSpaceSetIterations(cpSpace *space, int value)

    迭代次數允許你控制求解器計算的精度。默認值為10。更多信息見上面。

    cpVect cpSpaceGetGravity(const cpSpace *space) void cpSpaceSetGravity(cpSpace *space, cpVect value)

    施加到空間的全局重力。默認是cpvzero??梢酝ㄟ^編寫自定義積分函數來重寫每個剛體。

    cpFloat cpSpaceGetDamping(const cpSpace *space) void cpSpaceSetDamping(cpSpace *space, cpFloat value)

    施加到空間的簡單的阻尼值。數值0.9意味著每個剛體每秒會損失速度會損失掉10%。默認值為1。像重力一樣,阻尼值也可以在每個剛體上重寫。

    cpFloat cpSpaceGetIdleSpeedThreshold(const cpSpace *space) void cpSpaceSetIdleSpeedThreshold(cpSpace *space, cpFloat value)

    剛體被考慮為靜止限制的速度閾值。默認值為0,意味著讓空間來估算猜測基于重力的良好的閾值。

    cpFloat cpSpaceGetSleepTimeThreshold(const cpSpace *space) void cpSpaceSetSleepTimeThreshold(cpSpace *space, cpFloat value)

    一組剛體休眠需要保持靜止閑置的時間閾值。默認值為INFINITY, 禁用了休眠功能。

    cpFloat cpSpaceGetCollisionSlop(const cpSpace *space) void cpSpaceSetCollisionSlop(cpSpace *space, cpFloat value)

    支持形狀間的重疊量。鼓勵將這個值設置高點而不必在意重疊,因為它提高了穩定性。它默認值為0.1。

    cpFloat cpSpaceGetCollisionBias(const cpSpace *space) void cpSpaceSetCollisionBias(cpSpace *space, cpFloat value)

    Chipmunk讓快速移動的物體重疊,然后修復重疊。即使橫掃碰撞被支持,重疊對象也不可避免,并且這是一個高效,穩定的方式來處理重疊的對象。控制重疊百分比的偏置值在1秒后仍然是不固定的,默認~0.2%。有效值是在0到1的范圍內,但由于穩定的原因不推薦使用0。默認值的計算公式為cpfpow(1.0F - 0.1F,60.0f),這意味著Chipmunk試圖在1/60s內糾正10%的錯誤。注:非常非常少的游戲需要更改此值。

    cpTimestamp cpSpaceGetCollisionPersistence(const cpSpace *space) void cpSpaceSetCollisionPersistence(cpSpace *space, cpTimestamp value)

    空間保持碰撞的幀數量。有助于防止抖動接觸惡化。默認值為3,非常非常非常少的游戲需要更改此值。

    cpFloat cpSpaceGetCurrentTimeStep(const cpSpace *space)

    檢索當前(如果你是從cpSpaceStep()回調)或最近(在cpSpaceStep()之外調用)的時間步長。

    cpFloat cpSpaceIsLocked(const cpSpace *space)

    在回調中返回true時,意味著你不能從空間添加/刪除對象。可以選擇創建一個post-step回調來替代。

    cpDataPointer cpSpaceGetUserData(const cpSpace *space) void cpSpaceSetUserData(cpSpace *space, cpDataPointer value)

    用戶定義的數據指針。這點在游戲狀態對象或擁有空間的場景管理對象上是很有用的。

    cpBody * cpSpaceGetStaticBody(const cpSpace *space)

    空間中專用的靜態剛體。你不必使用它,而是因為它的內存由空間自動管理,非常方便。如果你想要做回調的話,你可以將它的數據指針指向一些有用的東西。

    7.4 內存管理函數

    cpSpace* cpSpaceAlloc(void) cpSpace* cpSpaceInit(cpSpace *space) cpSpace* cpSpaceNew()void cpSpaceDestroy(cpSpace *space) void cpSpaceFree(cpSpace *space)

    更多標準的Chipmunk內存函數。

    void cpSpaceFreeChildren(cpSpace *space)

    這個函數將釋放所有已添加到空間中的的形狀、剛體和關節。不要釋放space空間。你仍然需要自己調用cpSpaceFree()。在一個真正的游戲中你可能永遠不會使用這個,因為你的游戲狀態或者游戲控制器應該會管理從空間移除并釋放對象。

    7.5 操作運算

    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)

    這些函數是從空間中添加和刪除形狀、剛體和約束。添加/刪除函數不能在postStep()回調之外的回調內調用(這和postSolve()回調是不同的!)。當cpSpaceStep()仍然在執行時,試圖從空間添加或刪除對象會拋出一個斷言。更多信息請參見回調部分。添加函數會返回被添加的對象以便你可以在一行中創建和添加一些東西。注意在移除關聯到剛體的形狀和約束之前不要去釋放剛體,否則會造成崩潰。contains函數允許你檢查一個對象有沒有被添加到空間中。

    7.6 靜態動態轉換函數

    void cpSpaceConvertBodyToStatic(cpSpace *space, cpBody *body)

    將剛體轉換為靜態剛體。它的質量和力矩將被設置為無窮大,并且速度為0。舊的質量和力矩以及速度都不會被保存。這將有效地將一個剛體和它的形狀凍結到一個位置。這不能被一個激活的剛體調用,所以你可能需要先調用cpSpaceRemoveBody()。此外,因為它修改了碰撞檢測的數據結構,如果你想從另外一個回調函數或迭代器使用你必須使用后一步的回調。

    7.7 空間索引

    Chipmunk6正式支持2個空間索引。默認是軸對齊包圍盒樹,該靈感來自于Bullet物理庫中使用的包圍盒樹,但是我將它與我自己的碰撞對緩存一起做了擴展以便為樹實現非常好的時間相干性。樹無需調整優化,而且在大多數游戲中會發現使用它能獲得更好的性能。另外一個可用的索引是空間哈希,當你有著非常多數量且相同尺寸的物體時,它會更快。

    有時,你可能需要更新形狀的碰撞檢測數據。如果你移動靜態形狀或者剛體,你必須這樣做來讓Chipmunk知道它需要更新碰撞數據。你可能還希望手動為移動過的普通形狀更新碰撞數據,并且仍然想進行查詢。

    • void cpSpaceReindexShape(cpSpace *space, cpShape *shape) – 重新索引一個指定的形狀
    • void cpSpaceReindexShapesForBody(cpSpace *space, cpBody *body) - 重新索引指定剛體上的所有形狀
    • void cpSpaceReindexStatic(cpSpace *space) – 重新索引所有靜態形狀。一般只更新改變的形狀會比較快

    7.8 迭代器

    typedef void (*cpSpaceBodyIteratorFunc)(cpBody *body, void *data) void cpSpaceEachBody(cpSpace *space, cpSpaceBodyIteratorFunc func, void *data)

    為空間中的每個剛體調用func函數,同時傳遞data指針。休眠中的剛體包括在內,但是靜態和游離剛體不包括在內,因為他們沒有被添加進空間。

    cpSpaceEachBody例子:

    // 檢測空間中是否所有剛體都在休眠的代碼片段// 這個函數被空間中的每個剛體調用 static void EachBody(cpBody *body, cpBool *allSleeping){if(!cpBodyIsSleeping(body)) *allSleeping = cpFalse; }// 然后在你的更新函數中這樣做 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)

    為空間中的每個形狀調用func函數,同時傳遞data指針。休眠和靜態形狀被包括在內。

    typedef void (*cpSpaceConstraintIteratorFunc)(cpConstraint *constraint, void *data) void cpSpaceEachConstraint(cpSpace *space, cpSpaceConstraintIteratorFunc func, void *data)

    為空間中的每個約束調用func函數同時傳遞data指針。

    注意:如果你的編譯器支持閉包(如Clang), 那么有另外一組函數你可以調用。cpSpaceEachBody_b()等等。更多信息請查看chipmunk.h。

    7.9 空間模擬

    void cpSpaceStep(cpSpace *space, cpFloat dt)

    通過給定的時間步來更新空間。強烈推薦使用一個固定的時間步長。這樣做能大大提高模擬的質量。實現固定的時間步,最簡單的方法就是簡單的每個幀頻步進1/60s(或任何你的目標幀率),而無論花去了多少渲染時間。在許多游戲中這樣很有效,但是將物理時間步進和渲染分離是一個更好的方式。這是一篇介紹如何做的好文章。

    7.10 啟用和調優空間哈希(散列)

    如果你有成千上萬個大小大致相同的物體,空間哈??赡軙苓m合你。

    void cpSpaceUseSpatialHash(cpSpace *space, cpFloat dim, int count)

    使空間從碰撞包圍盒樹切換到空間哈希。 空間哈希數據對大小相當敏感。dim是哈希單元的尺寸。設置dim為碰撞形狀大小的平均尺寸可能會得到最好的性能。設置dim太小會導致形狀填充進去很多哈希單元,太低會造成過多的物體插入同一個哈希槽。

    count是在哈希表中建議的最小的單元數量。如果單元太少,空間哈希會產生很多誤報。過多的單元將難以做高速緩存并且浪費內存。將count設置成10倍于空間物體的個數可能是一個很好的起點。如果必要的話從那里調優。

    關于使用空間哈希有個可視化的演示程序,通過它你可以明白我的意思?;疑叫芜_標空間哈希單元。單元顏色越深,就意味著越多的物體被映射到那個單元。一個好的dim尺寸也就是你的物體能夠很好的融入格子中。

    注意,淺色的灰色意味著每個單元沒有太多的物體映射到它。

    當你使用太小的尺寸,Chipmunk不得不在每個物體上插入很多哈希單元。這個代價有些昂貴。

    注意到灰色的單元和碰撞形狀相比是非常小的。

    當你使用過大的尺寸,就會有很多形狀填充進每個單元。每個形狀不得不和單元中的其他形狀進行檢查,所以這會造成許多不必要的碰撞檢測。

    注意深灰色的單元意味著很多物體映射到了他們。

    Chipmunk6也有一個實驗性的單軸排序和范圍實現。在移動游戲中如果你的世界是很長且扁就像賽車游戲,它是非常高效。如果你想嘗試啟用它, 可以查閱cpSpaceUseSpatialHash()的代碼。

    7.11 札記

    • 當從空間中刪除對象時,請確保你已經刪除了任何引用它的其他對象。例如,當你刪除一個剛體時,要先刪除掉關聯到剛體的關節和形狀。
    • 迭代次數和時間步長的大小決定了模擬的質量。越多的迭代次數,或者更小的時間步會提高模擬的質量。請記住,更高質量的同時也意味著更高的CPU使用率。
    • 因為靜態形狀只有當你需要的時候才重新哈希,所以可能會使用一個更大的count參數來cpHashResizeStaticHash()而不是cpSpaceResizeActiveHash()。如果你有大量靜態形狀的話,這樣做會使用更多的內存但是會提升性能。

    8. Chipmunk約束:cpConstraint

    約束是用來描述兩個剛體如何相互作用的(他們是如何約束彼此的)。約束可以是允許剛體像我們身體的骨頭一樣軸轉動的簡單關節,也可以是更抽象的比如齒輪關節或馬達關節。

    8.1 約束是什么,不是什么

    在Chipmunk中,約束都是基于速度的約束。這意味著他們主要通過同步兩個剛體的速度進行作用。一個軸關節將兩個獨立剛體的兩個錨點連接起來,公式定義要求兩個錨點的速度必須相同并且計算施加在剛體上的沖量以便試圖保持這個狀態。約束將速度視為主要的輸入并且產生一個速度變化作為它的輸出。一些約束(尤其是關節)通過改變速度來修正位置的差異。更多詳情見下一節。

    連接兩個剛體的彈簧不是一個約束。它很像約束因為它會創建一個力來影響兩個剛體的速度,但是彈簧將距離作為輸入,將力作為輸出。如果彈簧不是一個約束,你會問為什么還會有兩種類型的彈簧約束。原因是他們是阻尼彈簧。彈簧關聯的阻尼才是真正的約束,這個約束會根據關聯的兩個剛體的相對速度來創建速度的變化。因為大部分情況將一個阻尼器和一個彈簧放在一起很方便,我想我還不如將彈簧力作為約束的一部分,而不是用一個阻尼器約束然后讓用戶單獨計算和施加彈簧力。

    8.2 屬性

    • 得到約束關聯的兩個剛體
    cpBody * cpConstraintGetA(const cpConstraint *constraint) cpBody * cpConstraintGetB(const cpConstraint *constraint)
    • 約束能夠作用于兩個剛體的最大力。默認為INFINITY。
    cpFloat cpConstraintGetMaxForce(const cpConstraint *constraint) void cpConstraintSetMaxForce(cpConstraint *constraint, cpFloat value)
    • 關節誤差百分比一秒鐘后仍然沒得到修正。這和碰撞偏差機制完全一樣,但是這會修正關節的誤差而不是重疊碰撞。
    cpFloat cpConstraintGetErrorBias(const cpConstraint *constraint) void cpConstraintSetErrorBias(cpConstraint *constraint, cpFloat value)
    • 約束可以糾錯的最大速度。默認為INFINITY。
    cpFloat cpConstraintGetMaxBias(const cpConstraint *constraint) void cpConstraintSetMaxBias(cpConstraint *constraint, cpFloat value)
    • 得到約束所添加進去的空間
    cpSpace* cpConstraintGetSpace(const cpConstraint *constraint)
    • 使用數據指針。使用指針來從回調中得到擁有該約束的游戲對象的一個引用。
    cpDataPointer cpConstraintGetUserData(const cpConstraint *constraint) void cpConstraintSetUserData(cpConstraint *constraint, cpDataPointer value)
    • 約束被施加的最新的沖量。為了轉化成力,除以cpSpaceStep()傳進的時間步。你可以使用這點來檢查施加的力是否超過了一定的閾值從而實現可斷裂的關節。

    • 斷裂關節例子

    // 創建關節且設置最大力屬性 breakableJoint = cpSpaceAddConstraint(space, cpPinJointNew(body1, body2, cpv(15,0), cpv(-15,0))); cpConstraintSetMaxForce(breakableJoint, 4000);// 在update函數中,正常步進模擬空間... cpFloat dt = 1.0/60.0; cpSpaceStep(space, dt);if(breakableJoint){// 將沖量除以時間步得到施加的力cpFloat force = cpConstraintGetImpulse(breakableJoint)/dt;cpFloat maxForce = cpConstraintGetMaxForce(breakableJoint);// 如果該力大于設定的閾值則斷裂關節if(force > 0.9*maxForce){cpSpaceRemoveConstraint(space, breakableJoint);breakableJoint = NULL;} }

    要訪問特定關節類型的屬性,使用提供的getter和setter函數(如cpPinJointGetAnchr1())。更多信息請查看屬性列表。

    8.3 反饋糾錯

    Chipmunk的關節并不完美。銷關節并不能維系兩個錨點之間確切的距離,樞軸關節同樣也不能保持關聯的錨點完全在一起。他們通過自糾錯來處理這個問題。在Chipmunk5中,你有很多額外的控制來實現關節對自身的糾錯,甚至可以使用這個特性,以獨特的方式使用關節來創建一些物理效果。

    • 伺服馬達:如 打開/關閉門或者旋轉物件,無需用最大的力
    • 起貨機:朝著另外一個物體拉一個物體無需用最大的力
    • 鼠標操作:以粗暴、搖晃的鼠標輸入方式自如的與物體交互

    cpConstraint結構體有3個屬性控制著誤差糾正,maxForce,maxBias以及biasCoef.maxForce。關節或者約束在不超過該數值大小的力時才能發揮作用。如果它需要更多的力來維系自己,它將會散架。maxBias是誤差糾正可以應用的最大速度了。如果你改變了一個關節的屬性,這個關節將不得不自行糾正,一般情況下很快會這么做。通過設置最大速度,你可以像伺服一樣使得關節工作,在一段較長的時間以恒定的速率校正自身。最后,biasCoef是在鉗位最大值速度前每一步誤差糾正的百分比。你可以使用它來使得關節平滑的糾正自身而不是以一個恒定的速度,但可能是三個屬性中迄今為止最沒用的。

    // 在一個頂視角的游戲中,采用這種配置的樞軸關節將會計算與地面之間的摩擦 // 因為關節糾正被禁用,所以關節不會重新擺正自身并只會影響速度。 // 當速度改變時,關節施加的力會被最大力鉗位 // 這樣它就會像摩擦一樣工作 cpConstraint *pivot = cpSpaceAddConstraint(space, cpPivotJointNew2(staticBody, body, cpvzero, cpvzero)); pivot->maxBias = 0.0f; // disable joint correction pivot->maxForce = 1000.0f;// 樞軸關節并不施加旋轉力,使用一個比率為1.0的齒輪關節來替代 cpConstraint *gear = cpSpaceAddConstraint(space, cpGearJointNew(staticBody, body, 0.0f, 1.0f)); gear->maxBias = 0.0f; // disable joint correction gear->maxForce = 5000.0f;// 另外,你可以將關節連接到一個無限大質量的游離剛體上來取代連接到一個靜態剛體上 // 你可以使用游離剛體作為控制剛體來連接。可以查看`Tank`演示例子。

    8.4 約束和碰撞形狀

    約束和碰撞形狀互不了解雙方信息。當為剛體連接關節時,錨點不必處于剛體形狀的內部,這么做通常是有意義的。同樣的,為兩個剛體添加約束并不能阻止剛體形狀碰撞。事實上,這便是碰撞組屬性存在的主要原因。

    8.5 現有關節類型視頻演示

    • Youtube地址
    • 優酷地址

    8.6 共享內存管理函數

    Destroy和Free函數由所有關節類型共享。Allocation和init函數對于每種關節類型都是特定的。

    9. 約束類型

    9.1 銷關節

    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是被連接的兩個剛體,anchr1和anchr2是這兩個剛體的錨點。當關節被創建的時候距離便被確定,如果你想要設定一個特定的距離,使用setter函數來重新設定該值。

    屬性

    • 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 滑動關節

    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是被連接的兩個剛體,anchr1和anchr2是這兩個剛體的錨點, min和max定義了兩個錨點間的最小最大距離。

    屬性

    • 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 樞軸關節

    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是關節連接的兩個剛體,pivot是世界坐標系下的樞軸點。因為樞軸點位置是在世界坐標系下,所以你必須確保兩個剛體已經處于正確的位置上。另外你可以指定基于一對錨點的軸關節,但是要確保剛體處于正確的位置上因為一旦你空間開啟了模擬,關節將會修正它自身。

    屬性

    • cpVect cpPivotJointGetAnchr1(const cpConstraint *constraint)
    • void cpPivotJointSetAnchr1(cpConstraint *constraint, cpVect value)
    • cpVect cpPivotJointGetAnchr2(const cpConstraint *constraint)
    • void cpPivotJointSetAnchr2(cpConstraint *constraint, cpVect value)

    9.4 溝槽關節

    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錨點上。所有的坐標都是剛體局部坐標。

    屬性

    • 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 )

    和滑動關節的定義很類似。restLength是彈簧想要的長度,stiffness是彈簧系數(Young’s modulus),dampling用來描述彈簧阻尼的柔軟度。

    屬性

    • 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 阻尼旋轉彈簧

    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是剛體間想要的相對角度,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 旋轉限位關節

    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)

    旋轉限位關節約束著兩個剛體間的相對角度。min和max就是最小和最大的相對角度,單位為弧度。它被實現以便可能使范圍大于一整圈。

    屬性

    • cpFloat cpRotaryLimitJointGetMin(const cpConstraint *constraint)
    • void cpRotaryLimitJointSetMin(cpConstraint *constraint, cpFloat value)
    • cpFloat cpRotaryLimitJointGetMax(const cpConstraint *constraint)
    • void cpRotaryLimitJointSetMax(cpConstraint *constraint, cpFloat value)

    9.8 棘輪關節

    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);

    工作起來像套筒扳手。ratchet是”clicks”間的距離,phase是當決定棘輪角度的時候的初始位移。

    屬性

    • 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 齒輪關節

    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);

    齒輪關節保持著一對剛體恒定的角速度比。ratio總是測量絕對值,目前無法設定相對于第三個剛體的角速度。phase是兩個剛體的初始角度偏移量。

    屬性

    • cpFloat cpGearJointGetPhase(const cpConstraint *constraint)
    • void cpGearJointSetPhase(cpConstraint *constraint, cpFloat value)
    • cpFloat cpGearJointGetRatio(const cpConstraint *constraint)
    • void cpGearJointSetRatio(cpConstraint *constraint, cpFloat value)

    9.10 簡單馬達

    cpSimpleMotor *cpSimpleMotorAlloc(void); cpSimpleMotor *cpSimpleMotorInit(cpSimpleMotor *joint, cpBody *a, cpBody *b, cpFloat rate); cpConstraint *cpSimpleMotorNew(cpBody *a, cpBody *b, cpFloat rate);

    簡單馬達保持著一對剛體恒定的角速度比。rate是所需的相對角速度。通常你會給馬達設定一個最大力(扭矩)否則他們會申請一個無限大的扭矩來使得剛體移動。

    屬性

    • cpFloat cpSimpleMotorGetRate(const cpConstraint *constraint)
    • void cpSimpleMotorSetRate(cpConstraint *constraint, cpFloat value)

    9.11 札記

    • 你可以為兩個剛體添加多個關節,但要確保他們彼此不會沖突。否則會引起剛體抖動或者劇烈的旋轉。

    10. Chipmunk碰撞檢測概述

    Chipmunk為了使得碰撞檢測盡可能快,將處理過程分成了若干階段。雖然我一直試圖保持它概念簡單,但實現卻有點讓人生畏。幸運的是作為Chipmunk庫的使用者,你并不需要了解一切關于它是如何工作的。但如果你在嘗試發揮Chipmunk的極致,理解這一部分會有所幫助。

    10.1 空間索引

    在場景中用一個for循環來檢查每一個對象是否與其他對象發生碰撞會很慢。所以碰撞檢測的第一步(或者就像通常稱作的階段),就是使用高層次空間算法來找出哪些對象應該被檢查碰撞。目前Chipmunk支持兩種空間索引,軸對齊包圍盒樹和空間散列。這些空間索引能夠快速識別哪些形狀彼此靠近,并應做碰撞檢查。

    10.2 碰撞過濾(篩選)

    在空間索引找出彼此靠近的形狀對后,將它們傳給space,然后再執行一些額外的篩選。在進行任何操作前,Chipmunk會執行幾個簡單的測試來檢測形狀是否會發生碰撞。

    • 包圍盒測試:如果形狀的包圍盒沒有重疊,那么形狀便沒發生碰撞。對象如對角線線段會引發許多誤報,但你不應該擔心。
    • 層測試:如果形狀不在同一層內則不會發生碰撞。(他們的層掩碼按位與運算結果為0)
    • 群組測試:在相同的非零群組中的形狀不會發生碰撞。

    10.3 基本形狀與形狀間的碰撞檢測

    最昂貴的測試其實就是檢測基于幾何形狀的重疊。圓與圓,圓與線之間的碰撞檢測相當快,多邊形和多邊形的碰撞檢測隨著頂點數的增加而更加昂貴。形狀越簡單,碰撞檢測就越快(更重要的是求解器檢測的碰撞點就越少)。Chipmunk使用了一個分發表來描述應該使用哪個函數來檢測形狀是否重疊。

    10.4 碰撞處理函數過濾

    在檢測到兩個形狀間重疊之后,Chipmunk會查看你是否為該碰撞形狀的類型定義了一個碰撞處理函數。對于游戲這樣去處理碰撞事件是至關重要的,同時也為你提供了一個非常靈活的方式來過濾掉碰撞。begin()和preSolve()回調函數的返回值決定了碰撞的形狀對是否該舍棄掉。返回true會保留形狀對,false則會舍棄。在begin()回調中中止一個碰撞是永久性的,在preSolve()回調中中止只是應用于當前所處的時間步。如果你沒有為碰撞類型定義一個處理函數,Chipmunk將會調用space的默認處理函數,默認會簡單的接受所有碰撞。

    使用回調過濾碰撞是最靈活的方式,記住,到那時候所有最昂貴的碰撞檢測通過你的回調都已經完成。對于每幀有大量碰撞對象的模擬,尋找碰撞所消耗的時間和解決碰撞所消耗的時間相比要小很多,所以這不是一個大問題。不過,如果可以的話先使用層或者群組。

    11. 碰撞回調

    沒有任何事件或反饋的物理庫對游戲而言幫助并不大。你怎么知道當玩家碰到了一個敵人,以便你扣除一些生命點數?你怎么知道汽車撞擊一個東西的力度,這樣你就不會在石子擊中它的時候播放一個巨響轟隆音?如果你需要決定在特定條件下的碰撞是否應該被忽略,比如要實現單向平臺?Chipmunk擁有一套強大的回調系統,你可以實現他們來完成這一切。

    11.1 碰撞處理

    碰撞處理函數是Chipmunk能夠識別的不同碰撞事件的一組4個回調函數。事件類型是:

    • begin():該步中兩個形狀剛開始第一次接觸?;卣{返回true則會處理正常碰撞,返回falseChipmunk會完全忽略碰撞。如果返回false,則preSolve()和postSolve()回調將永遠不會被執行,但你仍然會在形狀停止重疊的時候接收到一個單獨的事件。
    • preSolve():該步中兩個形狀相互接觸。回調返回falseChipmunk在這一步會忽略碰撞,返回true來正常處理它。此外,你可以使用cpArbiterSetFriction(),cpArbiterSetElasticity()或cpArbiterSetSurfaceVelocity()來提供自定義的摩擦,彈性,或表面速度值來覆蓋碰撞值。更多信息請查看cpArbiter。
    • postSolve():兩種形狀相互接觸并且它們的碰撞響應已被處理。如果你想使用它來計算音量或者傷害值,這時你可以檢索碰撞沖力或動能。更多信息請查看cpArbiter。
    • separate():該步中兩個形狀剛第一次停止接觸。確保begin()/separate()總是被成對調用,當刪除接觸中的形狀時或者析構space時它也會被調用。

    碰撞回調都與cpArbiter結構緊密相關。你應該熟悉那些為好。

    注:標記為傳感器的形狀(cpShape.sensor == true)從來不會得到碰撞處理,所以傳感器形狀和其他形狀間永遠不會調用postSolve()回調。它們仍然會調用begin()和separate()回調,而preSolve()仍然會在每幀調用回調,即使這里不存在真正的碰撞。

    注2:preSolve()回調在休眠算法運行之前被調用。如果一個對象進入休眠狀態,postSolve()回調將不會被調用,直到它被喚醒。

    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)

    碰撞處理函數類型。所有這些函數都附帶一個arbiter,space和用戶data指針,只有begin()和preSolve()回調會返回值。更多信息請查看上方碰撞處理。

    void cpSpaceAddCollisionHandler(cpSpace *space,cpCollisionType a, cpCollisionType b,cpCollisionBeginFunc begin,cpCollisionPreSolveFunc preSolve,cpCollisionPostSolveFunc postSolve,cpCollisionSeparateFunc separate,void *data )

    為指定的碰撞類型對添加一個碰撞處理函數。每當碰撞類型(cpShape.collision_type)為a的形狀與碰撞類型為b的形狀碰撞時,這些回調就會被調用來處理碰撞。data是用戶定義的上下文指針,用來傳遞到每個回調中。你不想實現的話可以使用NULL,然而Chipmunk會調用它自身的默認版本而不是你為space設置的默認值。如果你需要依賴space的默認回調,你必須單獨為每個處理函數定義提供實現。

    void cpSpaceRemoveCollisionHandler(cpSpace *space, cpCollisionType a, cpCollisionType b)

    移除指定碰撞類型對的碰撞處理函數。

    void cpSpaceSetDefaultCollisionHandler(cpSpace *space,cpCollisionBeginFunc begin,cpCollisionPreSolveFunc preSolve,cpCollisionPostSolveFunc postSolve,cpCollisionSeparateFunc separate,void *data )

    當沒有具體的碰撞處理時Chipmunk會使用一個默認的注冊碰撞處理函數。space在創建時被指定了一個默認的處理函數,該函數在begin()和preSolve()回調中返回true,在postSolve()和separate()回調中不做任何事情。

    11.3 后步回調

    后步回調允許你打破在一個回調內增加或刪除對象的規則。事實上,它們的主要功能就是幫助你安全的從你想要禁用/破壞一個碰撞/查詢回調的空間中移除對象。

    后步回調被注冊為一個函數和用作鍵的一個指針。你只能為每個鍵注冊一個postStep()回調。這可以防止你不小心多次刪除對象。例如,假設你有子彈和對象A之間的碰撞回調。你想摧毀子彈和對象A,因此你注冊一個postStep()回調來從游戲中安全地移除它們。然后,你得到子彈和對象B之間的碰撞回調,你注冊一個postStep()回調來刪除對象B,第二次postStep()回調來移除子彈。因為你只能為每個鍵注冊一次回調, postStep()回調對于子彈而言只會被調用一次,你不可能意外刪除兩次。

    11.4 例子

    更多信息請查看碰撞回調范例。

    12. Chipmunk碰撞對:cpArbiter

    Chipmunk的cpArbiter結構封裝了一對碰撞的形狀和關于它們的所有碰撞數據。

    為什么稱之為仲裁者?簡短來說,我一直用的是“仲裁”來形容碰撞解決的方式,然后早在2006年當我在看Box2D的求解器的時候看到了Box2D居然叫它們仲裁者。仲裁者就像是一個法官,有權力來解決兩個人之間的糾紛。這是有趣的,使用了合適的名字并且輸入比我以前用的CollisionPair要短。它最初只是被設定為一個私有的內部結構,但卻在回調中很有用。

    12.1 內存管理

    你永遠不需要創建或釋放一個仲裁者。更重要的是,因為它們完全由空間管理,所以你永遠不應該存儲一個仲裁者的引用,因為你不知道它們什么時候會被釋放或重新使用。在回調中使用它們,然后忘記它們或復制出你需要的信息。

    12.2 屬性

    cpFloat cpArbiterGetElasticity(const cpArbiter *arb) void cpArbiterSetElasticity(cpArbiter *arb, cpFloat value)

    計算碰撞對的彈性。在preSolve()回調中設定該值將會覆蓋由空間計算的值。默認計算會將兩個形狀的彈性相乘。

    cpFloat cpArbiterGetFriction(const cpArbiter *arb) void cpArbiterSetFriction(cpArbiter *arb, cpFloat value)

    計算碰撞對的摩擦力。在preSolve()回調中設定該值將會覆蓋由空間計算的值。默認計算會將兩個形狀的摩擦力相乘。

    cpVect cpArbiterGetSurfaceVelocity(const cpArbiter *arb) void cpArbiterSetSurfaceVelocity(cpArbiter *arb, cpVect value)

    計算碰撞對的表面速度。在preSolve()回調中設定該值將會覆蓋由空間計算的值。默認計算會將第二個形狀的表面速度從第一個形狀的表面速度中減去,然后投射到碰撞的切線上。這使得只有摩擦力受到默認計算的影響。使用自定義計算,你可以使得響應就像一個彈球保險杠一樣,或使得表面速度依賴于接觸點的位置。

    注:不幸的是,有一個老的bug會讓表面速度計算逆向(負值)。我真的很久沒有注意到這點了。這將在Chipmunk7中得到修正,但現在由于向后兼容的原因我已經先不管它了。

    cpDataPointer cpArbiterGetUserData(const cpArbiter *arb) void cpArbiterSetUserData(cpArbiter *arb, cpDataPointer data)

    用戶自定義指針。該值將維持形狀對直到separate()回調被調用。

    注:如果你需要清理這個指針,你應該實現separate()回調來這么做。同時在摧毀空間的時候要小心,因為仍然有可能有激活的碰撞存在。為了觸發separate()回調,在處置它之前你需要先移除空間中的所有形狀。這正是我建議的方式。見ChipmunkDemo.c:ChipmunkDemoFreeSpaceChildren()演示了如何輕松做到這一點。

    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)

    得到由這仲裁者或特定碰撞點,碰撞點的法向量或深度穿透跟蹤的觸點的數目。

    cpBool cpArbiterIsFirstContact(const cpArbiter *arb)

    如果這是兩個形狀開始接觸的第一步則返回true。舉例來說這對于聲音效果很有用。如果這是特定碰撞的第一幀,在postStep()回調中檢測碰撞能量,并用它來確定播放的聲效音量。

    void cpArbiterGetShapes(const cpArbiter *arb, cpShape **a, cpShape **b) void cpArbiterGetBodies(const cpArbiter *arb, cpBody **a, cpBody **b)

    按照形狀或者剛體在該仲裁者關聯的碰撞對中定義的順序一樣得到它們。如果你像cpSpaceAddCollisionHandler(space, 1, 2, …)定義了個函數,你會發現a->collision_type == 1且b->collision_type == 2。

    碰撞回調例子

    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) {// 得到參與碰撞的形狀// 順序和你在函數定義中的順序一致// a->collision_type將是BULLET_TYPE, b->collision_type將是MONSTER_TYPE CP_ARBITER_GET_SHAPES(arb, a, b);// 宏展開后和下面輸入一樣// cpShape *a, *b; cpArbiterGetShapes(arb, &a, &b);// 添加一個后步回調來安全從空間中移除和剛體// 直接從碰撞處理函數回調中調用 cpSpaceRemove() 會引起崩潰cpSpaceAddPostStepCallback(space, (cpPostStepFunc)postStepRemove, b, NULL);// 物體死亡,不再處理碰撞return 0; }#define BULLET_TYPE 1 #define MONSTER_TYPE 2// 為子彈和怪物定義一個碰撞處理函數 // 一旦怪物被子彈擊中則通過移除它的形狀和剛體來立馬殺死怪物 cpSpaceAddCollisionHandler(space, BULLET_TYPE, MONSTER_TYPE, begin, NULL, NULL, NULL, NULL);

    12.3 觸點集

    通過觸點集我們得到接觸信息變得更為容易。

    cpContactPointSet cpArbiterGetContactPointSet(const cpArbiter *arb)

    從仲裁者中得到的觸點集結構域。

    你可能通過下面的方式來得到并且處理一個觸點集:

    cpContactPointSet set = cpArbiterGetContactPointSet(arbiter); for(int i=0; i<set.count; i++){// 得到并使用觸點的法向量和穿透距離set.points[i].pointset.points[i].normalset.points[i].dist } void cpArbiterSetContactPointSet(cpArbiter *arb, cpContactPointSet *set)

    替換仲裁者的觸點集。你不能改變觸點的數目,但是可以改變它們的位置,法向量或穿透距離。Sticky演示使用它來使得物體能夠獲得額外量的重疊。你也可以在乒乓式風格游戲中使用它來修改基于碰撞x軸的碰撞法向量,即使板子是扁平形狀。

    12.4 幫助函數

    void cpArbiterGetShapes(cpArbiter *arb, cpShape **a, cpShape **b) void cpArbiterGetBodies(const cpArbiter *arb, cpBody **a, cpBody **b)

    得到在仲裁者關聯的碰撞處理中所定義的形狀(或者剛體)。如果你定義了一個處理函數如cpSpaceAddCollisionHandler(space, 1, 2, …),你會發現a->collision_type == 1并且b->collision_type == 2。便捷的宏為你定義并且初始化了兩個形狀變量。默認的碰撞處理函數不會使用碰撞類型,所以順序是未定義的。

    #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);

    返回為解決碰撞而施加于此步驟的沖量。這些函數應該只在postStep()或cpBodyEachArbiter()回調中被調用,否則結果將不可確定。如有疑問不知道該使用哪個函數,就使用cpArbiterTotalImpulseWithFriction()。

    cpFloat cpArbiterTotalKE(const cpArbiter *arb);

    計算在碰撞中的能量損失值,包括靜摩擦不包括動摩擦。這個函數應該在postSolve(), postStep()或者cpBodyEachArbiter()回調中被調用。

    13. 查詢

    Chipmunk空間支持4種空間查詢,包括最近點查詢、線段查詢、形狀查詢和快速包圍盒查詢。任何一種類型都可在空間里有效運行,并且點和線段查詢可以針對單個形狀來進行。所有類型的查詢需要一個碰撞組和層,并使用和過濾形狀間碰撞一樣的規則來過濾出匹配。如果你不希望過濾掉任何匹配,使用CP_ALL_LAYERS作為層,CP_NO_GROUP作為組。

    13.1 最近點查詢

    點查詢對于像鼠標拾取和簡單的感應器來說非常有用。它允許你檢查離給定點一定距離內是否存在著形狀,找到形狀上離給定點最近的點或者找到離給定點最近的形狀。

    typedef struct cpNearestPointQueryInfo {/// 最近的形狀。如果在范圍內沒有形狀返回NULL。cpShape *shape;/// 形狀表面上最近點(世界坐標系)cpVect p;/// 離給定點的距離。如果點在形狀內部距離則為負值cpFloat d;/// 距離函數的梯度/// 和info.p/info.d相同,即使當info.d是非常小的值時,仍然精確cpVect g; } cpNearestPointQueryInfo;

    13.2 線段查詢

    線段查詢就像射線投射一樣,但由于并非所有的空間索引都允許處理無限長的射線查詢所以它僅限于線段。在實踐中這仍然非??焖?#xff0c;你不用過多的擔心過長的線段查詢會影響到性能。

    typedef struct cpSegmentQueryInfo {//碰撞的形狀,如果沒有碰撞發生則為NULLcpShape *shape;// 線段查詢的歸一化距離,在[0,1]范圍內cpFloat t;// 表面命中點的法向量cpVect n; } cpSegmentQueryInfo;

    分類查詢返回的信息不只是一個簡單的是或否,它們也會返回形狀被擊中的位置以及被擊中位置的表面的法向量。t是該查詢的開始點和結束點之間的百分比。如果你需要世界空間中的擊中點或者到開始點的絕對距離,可以查看下面的線段查詢幫助函數。如果線段查詢的開始點在形狀內部,則t = 0并且n = cpvzero。

    cpBool cpShapeSegmentQuery(cpShape *shape, cpVect a, cpVect b, cpSegmentQueryInfo *info)

    執行從a到b的線段與單一形狀shape的線段查詢。info必須是一個指向cpSegmentQueryInfo結構體的有效的指針,該結構體會被光線投射信息(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來查詢space過濾篩選出匹配。func函數被調用,附帶著線段和任何被發現的形狀表面的法向量之間的歸一化距離,還有傳遞給cpSpacePointQuery()的data參數。傳感器類形狀也被包括在內。

    cpShape *cpSpaceSegmentQueryFirst(cpSpace *space, cpVect start, cpVect end,cpLayers layers, cpGroup group,cpSegmentQueryInfo *info )

    沿著線段的start到end使用給定的layers和groups來查詢space過濾篩選出匹配。只有遇到的第一個形狀會被返回并結束搜索,如果沒有發現形狀則返回NULL。info指向的結構體將會被光線投射信息初始化,除非info是NULL。傳感器類形狀將被忽略。

    線段查詢輔助函數:

    cpVect cpSegmentQueryHitPoint(cpVect start, cpVect end, cpSegmentQueryInfo info)

    返回在世界坐標系內線段與形狀相交的第一個相交點。

    cpFloat cpSegmentQueryHitDist(cpVect start, cpVect end, cpSegmentQueryInfo info)

    返回線段與形狀第一個相交點的絕對距離。

    13.3 AABB查詢

    AABB查詢提供一個快速的方式來粗略檢測一個范圍內存在的形狀。

    typedef void (*cpSpaceBBQueryFunc)(cpShape *shape, void *data)void cpSpaceBBQuery(cpSpace *space, cpBB bb,cpLayers layers, cpGroup group,cpSpaceBBQueryFunc func, void *data )

    查詢space找到bb附近并篩選出符合給定層和組的所有形狀。每個包圍盒和bb有重疊的形狀,都會調用func, 并將data參數傳給cpSpaceBBQuery()。傳感器類形狀也包括在內。

    13.4 形狀查詢

    形狀查詢允許你檢測空間中的形狀是否和一個指定的區域發生了重疊。如果你想在該位置添加另外一個形狀,又或者在AI中使用它進行感應查詢的話。你可以通過形狀查詢來檢測物體是否已經存在于一個位置上。

    在查詢前,你可以創建一個剛體對或者形狀對,或者你創建一個shape值為NULL的剛體,通過調用cpShapeUpdate()函數來設置形狀的位置和旋轉角度。

    typedef void (*cpSpaceShapeQueryFunc)(cpShape *shape, cpContactPointSet *points, void *data);cpBool cpSpaceShapeQuery(cpSpace *space, cpShape *shape, cpSpaceShapeQueryFunc func, void *data);

    查詢space來找到和shape重疊的所有形狀。使用shape的層和群組來過濾篩選得到匹配。func函數由每個重疊的形狀調用,附帶一個臨時的cpContactPointSet的一個指針和傳遞給cpSpaceBBQuery()的data參數。傳感器類形狀也包括在內。

    13.5 閉包

    如果你的編譯器支持閉包(如Clang), 還有另外一組函數可以調用,如cpSpaceNearestPointQuery_b()等。詳情請參考chipmunk.h。

    13.6 例子

    更多信息見查詢例子

    總結

    以上是生活随笔為你收集整理的Chipmunk2D中文手册的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。