動畫系統(tǒng)
可以參考 博客 Cocos Creator Animation 組件
Animation 也不例外,它也是節(jié)點(diǎn)上的一個組件。
AnimaitonClip動畫剪輯就是一份動畫的聲明數(shù)據(jù),我們將它掛載到 Animation 組件上,就能夠?qū)⑦@份動畫數(shù)據(jù)應(yīng)用到節(jié)點(diǎn)上。
動畫編輯器
left:向前移動一幀,如果已經(jīng)在第 0 幀,則忽略當(dāng)前操作
right:向后移動一幀
delete:刪除當(dāng)前所選中的關(guān)鍵幀
k:正向的播放動畫,抬起后停止
j:反向播放動畫,抬起后停止
ctrl / cmd + left:跳轉(zhuǎn)到第 0 幀
ctrl / cmd + right:跳轉(zhuǎn)到有效的最后一幀
時(shí)間軸上刻度的表示法是 01-05。
該數(shù)值由兩部分組成,冒號前面的是表示當(dāng)前秒數(shù),冒號后面的表示在當(dāng)前這一秒里的第幾幀。
01-05 表示該刻度在時(shí)間軸上位于從動畫開始經(jīng)過了 1 秒又 5 幀的時(shí)間。
節(jié)點(diǎn)數(shù)據(jù) 動畫剪輯通過節(jié)點(diǎn)的名字定義數(shù)據(jù)的位置,本身忽略了根節(jié)點(diǎn),其余的子節(jié)點(diǎn)通過與根節(jié)點(diǎn)的 相對路徑 索引找到對應(yīng)的數(shù)據(jù)。
動畫屬性包括了節(jié)點(diǎn)自有的 position
、rotation
等屬性,也包含了組件 Component
中自定義的屬性。 組件包含的屬性前會加上組件的名字,比如 cc.Sprite.spriteFrame
。
添加關(guān)鍵幀 在屬性列表中點(diǎn)擊對應(yīng)屬性軌道右側(cè)的選項(xiàng)按鈕,在彈出的菜單中選擇 插入關(guān)鍵幀
按鈕。也可以直接在編輯模式下直接更改
雙擊關(guān)鍵幀的連接線,就可以打開時(shí)間曲線編輯器
選中某個位置,然后點(diǎn)擊按鈕區(qū)域最左側(cè)的按鈕(add event),這時(shí)候在時(shí)間軸上會出現(xiàn)一個白色的矩形,這就是我們添加的事件。雙擊剛剛出現(xiàn)的白色矩形,可以打開事件編輯器,在編輯器內(nèi),我們可以手動輸入需要觸發(fā)的 function 名字,觸發(fā)的時(shí)候會根據(jù)這個函數(shù)名,去各個組件內(nèi)匹配相應(yīng)的方法。
用腳本控制動畫
var anim = this.getComponent(cc.Animation);
// 如果沒有指定播放哪個動畫,并且有設(shè)置 defaultClip 的話,則會播放 defaultClip 動畫
anim.play();
// 指定播放 test 動畫
anim.play('test');
// 指定從 1s 開始播放 test 動畫
anim.play('test', 1);
// 使用 play 接口播放一個動畫時(shí),如果還有其他的動畫正在播放,則會先停止其他動畫
anim.play('test2');
// 播放第一個動畫
anim.playAdditive('position-anim');
// 播放第二個動畫
// 使用 playAdditive 播放動畫時(shí),不會停止其他動畫的播放。如果還有其他動畫正在播放,則同時(shí)會有多個動畫進(jìn)行播放
anim.playAdditive('rotation-anim');
// 指定暫停 test 動畫
anim.pause('test');
// 暫停所有動畫
anim.pause();
// 指定恢復(fù) test 動畫
anim.resume('test');
// 恢復(fù)所有動畫
anim.resume();
// 指定停止 test 動畫
anim.stop('test');
// 停止所有動畫
anim.stop();
// 設(shè)置 test 動畫的當(dāng)前播放時(shí)間為 1s
anim.setCurrentTime(1, 'test');
// 設(shè)置所有動畫的當(dāng)前播放時(shí)間為 1s
anim.setCurrentTime(1);
Animation State
如果說 AnimationClip 是作為動畫數(shù)據(jù)的承載,那么 AnimationState 則是 AnimationClip 在運(yùn)行時(shí)的實(shí)例,它將動畫數(shù)據(jù)解析為方便程序中做計(jì)算的數(shù)值。
Animation 在播放一個 AnimationClip 的時(shí)候,會將 AnimationClip 解析成 AnimationState。
Animation 的播放狀態(tài)實(shí)際都是由 AnimationState 來計(jì)算的,包括動畫是否循環(huán)、怎么循環(huán)、播放速度等
獲取
var anim = this.getComponent(cc.Animation);
// play 會返回關(guān)聯(lián)的 AnimationState
var animState = anim.play('test');
// 或者直接獲取
var animState = anim.getAnimationState('test');
// 獲取動畫關(guān)聯(lián)的 clip
var clip = animState.clip;
// 獲取動畫的名字
var name = animState.name;
// 獲取動畫的播放速度
var speed = animState.speed;
// 使動畫播放速度加速
animState.speed = 2;
// 使動畫播放速度減速
animState.speed = 0.5;
// 獲取動畫的播放總時(shí)長
var duration = animState.duration;
// 獲取動畫的播放時(shí)間
var time = animState.time;
// 獲取動畫的重復(fù)次數(shù)
var repeatCount = animState.repeatCount;
// 獲取動畫的循環(huán)模式
var wrapMode = animState.wrapMode
// 設(shè)置循環(huán)模式為 Normal
animState.wrapMode = cc.WrapMode.Normal;
// 設(shè)置循環(huán)模式為 Loop
animState.wrapMode = cc.WrapMode.Loop;
// 設(shè)置動畫循環(huán)次數(shù)為 2 次
animState.repeatCount = 2;
// 設(shè)置動畫循環(huán)次數(shù)為無限次
animState.repeatCount = Infinity;
// 獲取動畫是否正在播放
var playing = animState.isPlaying;
// 獲取動畫是否已經(jīng)暫停
var paused = animState.isPaused;
// 獲取動畫的幀率
var frameRate = animState.frameRate;
動畫幀事件:
cc.Class({
extends: cc.Component,
onAnimCompleted: function (num, string) {
console.log('onAnimCompleted: param1[%s], param2[%s]', num, string);
}
});
將上面的組件加到動畫的 根節(jié)點(diǎn) 上,當(dāng)動畫播放到結(jié)尾時(shí),動畫系統(tǒng)會自動調(diào)用腳本中的 onAnimCompleted 函數(shù)。動畫系統(tǒng)會搜索動畫根節(jié)點(diǎn)中的所有組件,如果組件中有實(shí)現(xiàn)動畫事件中指定的函數(shù)的話,就會對它進(jìn)行調(diào)用,并傳入事件中填的參數(shù)。
并且可以提供注冊動畫回調(diào)以及 動態(tài)創(chuàng)建 Animation Clip
音樂與音效
audioSource: { type: cc.AudioSource,default: null },
AudioSource 播放
cc.audioEngine.play(audio, loop, volume);
AudioEngine 播放
AudioEngine 與 AudioSource 都能播放音頻,它們的區(qū)別在于 AudioSource 是組件,可以添加到場景中,由編輯器設(shè)置。而 AudioEngine 是引擎提供的純 API,只能在腳本中進(jìn)行調(diào)用。
一些移動端的瀏覽器或 WebView 不允許自動播放音頻,用戶需要在觸摸事件中手動播放音頻。
cc.Class({
extends: cc.Component,
properties: {
audioSource: cc.AudioSource
},
start () {
let canvas = cc.find('Canvas');
canvas.on('touchstart', this.playAudio, this);
},
playAudio () {
this.audioSource.play();
}
});
微信自動音樂播放,并在引擎啟動之后,使用其他方式播放音頻的時(shí)候停止這個音頻的播放。
document.addEventListener('WeixinJSBridgeReady', function () {
cc.resources.load('audio/music_logo', cc.AudioClip, (err, audioClip) => {
var audioSource = this.addComponent(cc.AudioSource);
audioSource.clip = audioClip;
audioSource.play();
});
});
物理與碰撞系統(tǒng)
碰撞系統(tǒng)
Cocos Creator 內(nèi)置了一個簡單易用的碰撞檢測系統(tǒng),支持 圓形、矩形 以及 多邊形 相互間的碰撞檢測
點(diǎn)擊組件的edit
來修改碰撞盒的大小
分組:通過 菜單欄 - 項(xiàng)目 - 項(xiàng)目設(shè)置 :設(shè)立新的分組,在 分組列表 下面可以進(jìn)行 碰撞分組配對 表的管理
碰撞系統(tǒng)的腳本控制
當(dāng)一個碰撞組件被啟用時(shí),這個碰撞組件會被自動添加到碰撞檢測系統(tǒng)中,并搜索能與之進(jìn)行碰撞的其他已添加的碰撞組件來生成一個碰撞對。需要注意的是,一個節(jié)點(diǎn)上的碰撞組件,無論如何都是不會相互進(jìn)行碰撞檢測的。
獲取碰撞檢測系統(tǒng)
var manager = cc.director.getCollisionManager();
manager.enabled = true;
manager.enabledDebugDraw = true;
manager.enabledDrawBoundingBox = true;
碰撞系統(tǒng)回調(diào)
當(dāng)碰撞系統(tǒng)檢測到有碰撞產(chǎn)生時(shí),將會以回調(diào)的方式通知使用者,如果產(chǎn)生碰撞的碰撞組件依附的節(jié)點(diǎn)下掛的腳本中有實(shí)現(xiàn)以下函數(shù),則會自動調(diào)用以下函數(shù),并傳入相關(guān)的參數(shù)。
/*
* @param {Collider} other 產(chǎn)生碰撞的另一個碰撞組件
* @param {Collider} self 產(chǎn)生碰撞的自身的碰撞組件
*/
onCollisionEnter: function (other, self) {
console.log('on collision enter');
// 碰撞系統(tǒng)會計(jì)算出碰撞組件在世界坐標(biāo)系下的相關(guān)的值,并放到 world 這個屬性里面
var world = self.world;
// 碰撞組件的 aabb 碰撞框
var aabb = world.aabb;
// 節(jié)點(diǎn)碰撞前上一幀 aabb 碰撞框的位置
var preAabb = world.preAabb;
// 碰撞框的世界矩陣
var t = world.transform;
// 以下屬性為圓形碰撞組件特有屬性
var r = world.radius;
var p = world.position;
// 以下屬性為 矩形 和 多邊形 碰撞組件特有屬性
var ps = world.points;
},
onCollisionStay: function (other, self) {
console.log('on collision stay');
},
onCollisionExit: function (other, self) {
console.log('on collision exit');
}
碰撞組件
一個節(jié)點(diǎn)上可以掛多個碰撞組件,這些碰撞組件之間可以是不同類型的碰撞組件。
碰撞組件目前包括了 Polygon(多邊形),Circle(圓形),Box(矩形) 這幾種碰撞組件,這些組件都繼承自 Collider 組件,所以 Collider 組件的屬性它們也都享有。
物理系統(tǒng)
Cocos-creator 的默認(rèn)物理范例 這個范例里面有不少很不錯的代碼方法,包括切割、彈性、速度,引力等。
物理系統(tǒng)將 box2d 作為內(nèi)部物理系統(tǒng),并且隱藏了大部分 box2d 實(shí)現(xiàn)細(xì)節(jié)(比如創(chuàng)建剛體,同步剛體信息到節(jié)點(diǎn)中等)。 你可以通過物理系統(tǒng)訪問一些 box2d 常用的功能,比如點(diǎn)擊測試,射線測試,設(shè)置測試信息等。
開啟物理系統(tǒng)
cc.director.getPhysicsManager().enabled = true;
開啟繪制
cc.director.getPhysicsManager().debugDrawFlags = cc.PhysicsManager.DrawBits.e_aabbBit |
cc.PhysicsManager.DrawBits.e_pairBit |
cc.PhysicsManager.DrawBits.e_centerOfMassBit |
cc.PhysicsManager.DrawBits.e_jointBit |
cc.PhysicsManager.DrawBits.e_shapeBit
;
設(shè)置物理重力,默認(rèn)的重力加速度是 (0, -320) 世界單位/秒^2,按照上面描述的轉(zhuǎn)換規(guī)則,即 (0, -10) 米/秒^2
cc.director.getPhysicsManager().gravity = cc.v2();
cc.director.getPhysicsManager().gravity = cc.v2(0, -640);
物理刷新率/步長,可以進(jìn)行修改,或許類似FixedUpdate()
var manager = cc.director.getPhysicsManager();
// 開啟物理步長的設(shè)置
manager.enabledAccumulator = true;
// 物理步長,默認(rèn) FIXED_TIME_STEP 是 1/60
manager.FIXED_TIME_STEP = 1/30;
// 每次更新物理系統(tǒng)處理速度的迭代次數(shù),默認(rèn)為 10
manager.VELOCITY_ITERATIONS = 8;
// 每次更新物理系統(tǒng)處理位置的迭代次數(shù),默認(rèn)為 10
manager.POSITION_ITERATIONS = 8;
查詢物體
物理系統(tǒng)提供了幾個方法來高效快速地查找某個區(qū)域中有哪些物體,每種方法通過不同的方式來檢測物體,基本滿足游戲所需。
點(diǎn)測試
var collider = cc.director.getPhysicsManager().testPoint(point);
//矩形
var colliderList = cc.director.getPhysicsManager().testAABB(rect);
射線檢測
var results = cc.director.getPhysicsManager().rayCast(p1, p2, type);
for (var i = 0; i < results.length; i++) {
var result = results[i];
var collider = result.collider;
var point = result.point;
var normal = result.normal;
var fraction = result.fraction;
}
最后一個參數(shù)指定了檢測的類型:
cc.RayCastType.Any
檢測射線路徑上任意的碰撞體,一旦檢測到任何碰撞體,將立刻結(jié)束檢測其他的碰撞體,最快。
cc.RayCastType.Closest
檢測射線路徑上最近的碰撞體,這是射線檢測的默認(rèn)值,稍慢。
cc.RayCastType.All
檢測射線路徑上的所有碰撞體,檢測到的結(jié)果順序不是固定的。在這種檢測類型下,一個碰撞體可能會返回多個結(jié)果,這是因?yàn)?box2d 是通過檢測夾具(fixture)來進(jìn)行物體檢測的,而一個碰撞體中可能由多個夾具(fixture)組成的,慢。
cc.RayCastType.AllClosest
檢測射線路徑上所有碰撞體,但是會對返回值進(jìn)行刪選,只返回每一個碰撞體距離射線起始點(diǎn)最近的那個點(diǎn)的相關(guān)信息,最慢。
射線檢測的結(jié)果
射線檢測的結(jié)果包含了許多有用的信息,你可以根據(jù)實(shí)際情況來選擇如何使用這些信息。
collider
指定射線穿過的是哪一個碰撞體。
point
指定射線與穿過的碰撞體在哪一點(diǎn)相交。
normal
指定碰撞體在相交點(diǎn)的表面的法線向量。
fraction
指定相交點(diǎn)在射線上的分?jǐn)?shù)。
剛體 RigidBody
剛體是組成物理世界的基本對象,你可以將剛體想象成一個你不能看到(繪制)也不能摸到(碰撞)的帶有屬性的物體(沒有渲染也沒有碰撞)。
剛體屬性:
質(zhì)量 var mass = rigidbody.getMass();
剛體的質(zhì)量是通過 碰撞組件 的 密度 與 大小 自動計(jì)算得到的。當(dāng)你需要計(jì)算物體應(yīng)該受到多大的力時(shí)可能需要使用到這個屬性。
移動速度 rigidbody.linearVelocity = velocity;
移動速度衰減系數(shù),模擬空氣摩擦力 rigidbody.linearDamping = damping;
剛體上某個點(diǎn)的移動速度,
可以傳入一個 cc.Vec2 對象作為第二個參數(shù)來接收返回值,
剛體的get方法都提供了 out 參數(shù)來接收函數(shù)返回值。
var velocity = cc.v2();
rigidbody.getLinearVelocityFromWorldPoint(worldPoint, velocity);
旋轉(zhuǎn)速度:rigidbody.angularVelocity = velocity;
旋轉(zhuǎn)衰減速度:var velocity = rigidbody.angularDamping;
旋轉(zhuǎn)固定:rigidbody.fixedRotation = true;
剛體碰撞監(jiān)聽 rigidbody.enabledContactListener = true;
box2d 中只有旋轉(zhuǎn)和位移,并沒有縮放,所以如果設(shè)置節(jié)點(diǎn)的縮放屬性時(shí),會重新構(gòu)建這個剛體依賴的全部碰撞體。一個有效避免這種情況發(fā)生的方式是將渲染的節(jié)點(diǎn)作為剛體節(jié)點(diǎn)的子節(jié)點(diǎn),縮放只對這個渲染節(jié)點(diǎn)作縮放,盡量避免對剛體節(jié)點(diǎn)進(jìn)行直接縮放。
每個物理時(shí)間步之后會把所有剛體信息同步到對應(yīng)節(jié)點(diǎn)上去,而處于性能考慮,節(jié)點(diǎn)的信息只有在用戶對節(jié)點(diǎn)相關(guān)屬性進(jìn)行顯示設(shè)置時(shí)才會同步到剛體上,并且剛體只會監(jiān)視他所在的節(jié)點(diǎn),即如果修改了節(jié)點(diǎn)的父節(jié)點(diǎn)的旋轉(zhuǎn)位移是不會同步這些信息的。
剛體類型:
cc.RigidBodyType.Static
靜態(tài)剛體,零質(zhì)量,零速度,即不會受到重力或速度影響,但是可以設(shè)置他的位置來進(jìn)行移動。
cc.RigidBodyType.Dynamic
動態(tài)剛體,有質(zhì)量,可以設(shè)置速度,會受到重力影響。
cc.RigidBodyType.Kinematic
運(yùn)動剛體,零質(zhì)量,可以設(shè)置速度,不會受到重力的影響,但是可以設(shè)置速度來進(jìn)行移動。
cc.RigidBodyType.Animated
動畫剛體,Animated 是從 Kinematic 類型衍生出來的,一般的剛體類型修改 旋轉(zhuǎn) 或 位移 屬性時(shí),都是直接設(shè)置的屬性,而 Animated 會根據(jù)當(dāng)前旋轉(zhuǎn)或位移屬性,與目標(biāo)旋轉(zhuǎn)或位移屬性計(jì)算出所需的速度,并且賦值到對應(yīng)的移動或旋轉(zhuǎn)速度上。添加 Animated 類型主要是防止對剛體做動畫時(shí)可能出現(xiàn)的奇怪現(xiàn)象,例如穿透。
剛體方法
獲取或轉(zhuǎn)換旋轉(zhuǎn)位移屬性,使用這些 API 來獲取世界坐標(biāo)系下的旋轉(zhuǎn)位移會比通過節(jié)點(diǎn)來獲取相關(guān)屬性更快,因?yàn)楣?jié)點(diǎn)中還需要通過矩陣運(yùn)算來得到結(jié)果,而這些 api 是直接得到結(jié)果的。
獲取世界坐標(biāo)
// 直接獲取返回值
var out = rigidbody.getWorldPosition();
// 或者通過參數(shù)來接收返回值
out = cc.v2();
rigidbody.getWorldPosition(out);
獲取世界旋轉(zhuǎn)值 var rotation = rigidbody.getWorldRotation();
世界坐標(biāo)轉(zhuǎn)換到局部坐標(biāo) var localPoint = rigidbody.getLocalPoint(worldPoint);
局部轉(zhuǎn)世界坐標(biāo)var worldPoint = rigidbody.getWorldPoint(localPoint);
向量轉(zhuǎn)換var worldVector = rigidbody.getWorldVector(localVector);
向量轉(zhuǎn)換var localVector = rigidbody.getLocalVector(worldVector);
獲取剛體質(zhì)心
當(dāng)對一個剛體進(jìn)行力的施加時(shí),一般會選擇剛體的質(zhì)心作為施加力的作用點(diǎn),這樣能保證力不會影響到旋轉(zhuǎn)值。
var localCenter = rigidbody.getLocalCenter();
和 var worldCenter = rigidbody.getWorldCenter();
力與沖量
移動一個物體有兩種方式,可以施加一個力或者沖量到這個物體上。力會隨著時(shí)間慢慢修改物體的速度,而沖量會立即修改物體的速度。 當(dāng)然你也可以直接修改物體的位置,只是這看起來不像真實(shí)的物理,你應(yīng)該盡量去使用力或者沖量來移動剛體,這會減少可能帶來的奇怪問題。
// 施加一個力到剛體上指定的點(diǎn)上,這個點(diǎn)是世界坐標(biāo)系下的一個點(diǎn)
rigidbody.applyForce(force, point);
// 或者直接施加力到剛體的質(zhì)心上
rigidbody.applyForceToCenter(force);
// 施加一個沖量到剛體上指定的點(diǎn)上,這個點(diǎn)是世界坐標(biāo)系下的一個點(diǎn)
rigidbody.applyLinearImpulse(impulse, point);
力與沖量也可以只對旋轉(zhuǎn)軸產(chǎn)生影響,這樣的力叫做扭矩。
// 施加扭矩到剛體上,因?yàn)橹挥绊懶D(zhuǎn)軸,所以不再需要指定一個點(diǎn)
rigidbody.applyTorque(torque);
// 施加旋轉(zhuǎn)軸上的沖量到剛體上
rigidbody.applyAngularImpulse(impulse);
有些時(shí)候需要獲取剛體在某一點(diǎn)上的速度時(shí),可以通過 getLinearVelocityFromWorldPoint
來獲取,比如當(dāng)物體碰撞到一個平臺時(shí),需要根據(jù)物體碰撞點(diǎn)的速度來判斷物體相對于平臺是從上方碰撞的還是下方碰撞的。
碰撞組件
物理碰撞組件屬性
sensor
- 指明碰撞體是否為傳感器類型,傳感器類型的碰撞體會產(chǎn)生碰撞回調(diào),但是不會發(fā)生物理碰撞效果。
density
- 碰撞體的密度,用于剛體的質(zhì)量計(jì)算
friction
- 碰撞體摩擦力,碰撞體接觸時(shí)的運(yùn)動會受到摩擦力影響
restitution
- 碰撞體的彈性系數(shù),指明碰撞體碰撞時(shí)是否會受到彈力影響
內(nèi)部細(xì)節(jié)
物理碰撞組件內(nèi)部是由 box2d 的 b2Fixture 組成的,由于 box2d 內(nèi)部的一些限制,一個多邊形物理碰撞組件可能會由多個 b2Fixture 組成。
當(dāng)多邊形物理碰撞組件的頂點(diǎn)組成的形狀為凹邊形時(shí),物理系統(tǒng)會自動將這些頂點(diǎn)分割為多個凸邊形。
當(dāng)多邊形物理碰撞組件的頂點(diǎn)數(shù)多于 b2.maxPolygonVertices
(一般為 8) 時(shí),物理系統(tǒng)會自動將這些頂點(diǎn)分割為多個凸邊形。
一般情況下這些細(xì)節(jié)是不需要關(guān)心的,但是當(dāng)使用射線檢測并且檢測類型為 cc.RayCastType.All
時(shí),一個碰撞體就可能會檢測到多個碰撞點(diǎn),原因即是檢測到了多個 b2Fixture。
碰撞回調(diào)
類似Unity的Oncollision,onTriggerEnter 這兩個處理的不一樣
需要先在rigidbody中開啟碰撞監(jiān)聽
rigidbody.enabledContactListener = true;
,才會有相應(yīng)的回調(diào)產(chǎn)生。回調(diào)中的信息在物理引擎都是以緩存的形式存在的,所以信息只有在這個回調(diào)中才是有用的,不要在你的腳本里直接緩存這些信息,但可以緩存這些信息的副本。
在回調(diào)中創(chuàng)建的物理物體,比如剛體,關(guān)節(jié)等,這些不會立刻就創(chuàng)建出 box2d 對應(yīng)的物體,會在整個物理系統(tǒng)更新完成后再進(jìn)行這些物體的創(chuàng)建。
定義回調(diào)函數(shù)
cc.Class({
extends: cc.Component,
// 只在兩個碰撞體開始接觸時(shí)被調(diào)用一次
onBeginContact: function (contact, selfCollider, otherCollider) {
},
// 只在兩個碰撞體結(jié)束接觸時(shí)被調(diào)用一次
onEndContact: function (contact, selfCollider, otherCollider) {
},
// 每次將要處理碰撞體接觸邏輯時(shí)被調(diào)用
onPreSolve: function (contact, selfCollider, otherCollider) {
},
// 每次處理完碰撞體接觸邏輯時(shí)被調(diào)用
onPostSolve: function (contact, selfCollider, otherCollider) {
}
});
回調(diào)的順序
當(dāng)兩個碰撞體相互覆蓋時(shí),box2d 默認(rèn)的行為是給每個碰撞體一個沖量去把它們分開,但是這個行為不一定能在一個時(shí)間步內(nèi)完成。 像這里顯示的一樣,示例中的碰撞體會在三個時(shí)間步內(nèi)相互覆蓋直到“反彈”完成并且它們相互分離。
在這個時(shí)間里我們可以定制我們想要的行為,onPreSolve 會在每次物理引擎處理碰撞前回調(diào),我們 可以在這個回調(diào)里修改碰撞信息,而 onPostSolve 會在處理完成這次碰撞后回調(diào),我們可以在這個回調(diào)中獲取到物理引擎計(jì)算出的碰撞的沖量信息。
下面給出的輸出信息能使我們更清楚回調(diào)的順序
...
Step
Step
BeginContact
PreSolve
PostSolve
Step
PreSolve
PostSolve
Step
PreSolve
PostSolve
Step
EndContact
Step
Step
...
回調(diào)的參數(shù) contact
回調(diào)的參數(shù)包含了所有的碰撞接觸信息,每個回調(diào)函數(shù)都提供了三個參數(shù):contact
、selfCollider
、otherCollider
。
selfCollider
和 otherCollider
很容易理解,如名字所示,selfCollider
指的是回調(diào)腳本的節(jié)點(diǎn)上的碰撞體,ohterCollider
指的是發(fā)生碰撞的另一個碰撞體。
最主要的信息都包含在 contact
中,這是一個 cc.PhysicsContact
類型的實(shí)例,可以在 api 文檔中找到相關(guān)的 API。contact
中比較常用的信息就是碰撞的位置和法向量,contact
內(nèi)部是按照剛體的本地坐標(biāo)來存儲信息的,而我們一般需要的是世界坐標(biāo)系下的信息,我們可以通過 contact.getWorldManifold
來獲取這些信息。
var worldManifold = contact.getWorldManifold();
var points = worldManifold.points;
var normal = worldManifold.normal;
points
碰撞點(diǎn)數(shù)組,它們不一定會精確的在碰撞體碰撞的地方上,如下圖所示(除非你將剛體設(shè)置為子彈類型,但是會比較耗性能),但實(shí)際上這些點(diǎn)在使用上一般都是夠用的。
normal
碰撞點(diǎn)上的法向量,由自身碰撞體指向?qū)Ψ脚鲎搀w,指明解決碰撞最快的方向。
碰撞法向量并不是碰撞體碰撞的角度,他只會指明可以解決兩個碰撞體相互覆蓋這一問題最短的方向。
如果你希望知道碰撞的真正的方向,可以使用下面的方式:
var vel1 = triangleBody.getLinearVelocityFromWorldPoint(worldManifold.points[0]);
var vel2 = squareBody.getLinearVelocityFromWorldPoint(worldManifold.points[0]);
var relativeVelocity = vel1.sub(vel2);
修改contact
//禁用
contact.disabled = true;
contact.disabledOnce = true;
在 onPreSolve
中修改 contact
的信息,因?yàn)?onPreSolve
是在物理引擎處理碰撞信息前回調(diào)的,所以對碰撞信息的修改會影響到后面的碰撞計(jì)算。
// 修改碰撞體間的摩擦力
contact.setFriction(friction);
// 修改碰撞體間的彈性系數(shù)
contact.setRestitution(restitution);
關(guān)節(jié)組件
物理系統(tǒng)包含了一系列用于鏈接兩個剛體的關(guān)節(jié)組件。關(guān)節(jié)組件可以用來模擬真實(shí)世界物體間的交互,比如鉸鏈,活塞,繩子,輪子,滑輪,機(jī)動車,鏈條等。
目前物理系統(tǒng)中提供了以下可用的關(guān)節(jié)組件:
Revolute Joint
- 旋轉(zhuǎn)關(guān)節(jié),可以看做一個鉸鏈或者釘,剛體會圍繞一個共同點(diǎn)來旋轉(zhuǎn)。
Distance Joint
- 距離關(guān)節(jié),關(guān)節(jié)兩端的剛體的錨點(diǎn)會保持在一個固定的距離。
Prismatic Joint
- 棱柱關(guān)節(jié),兩個剛體位置間的角度是固定的,它們只能在一個指定的軸上滑動。
Weld Joint
- 焊接關(guān)節(jié),根據(jù)兩個物體的初始角度將兩個物體上的兩個點(diǎn)綁定在一起。
Wheel Joint
- 輪子關(guān)節(jié),由 Revolute 和 Prismatic 組合成的關(guān)節(jié),用于模擬機(jī)動車車輪。
Rope Joint
- 繩子關(guān)節(jié),將關(guān)節(jié)兩端的剛體約束在一個最大范圍內(nèi)。
Motor Joint
- 馬達(dá)關(guān)節(jié),控制兩個剛體間的相對運(yùn)動。
屬性
connectedBody
- 關(guān)節(jié)鏈接的另一端的剛體
anchor
- 關(guān)節(jié)本端鏈接的剛體的錨點(diǎn)
connectedAnchor
- 關(guān)節(jié)另一端鏈接的剛體的錨點(diǎn)
collideConnected
- 關(guān)節(jié)兩端的剛體是否能夠互相碰撞