Cocos Creator 2.4x 筆記 - 從Unity到Cocos [4]

動畫系統(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)自有的 positionrotation 等屬性,也包含了組件 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)物理范例 這個范例里面有不少很不錯的代碼方法,包括切割、彈性、速度,引力等。

Box2D

物理系統(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ù)。

image

剛體 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 這兩個處理的不一樣

  1. 需要先在rigidbody中開啟碰撞監(jiān)聽 rigidbody.enabledContactListener = true;,才會有相應(yīng)的回調(diào)產(chǎn)生。

  2. 回調(diào)中的信息在物理引擎都是以緩存的形式存在的,所以信息只有在這個回調(diào)中才是有用的,不要在你的腳本里直接緩存這些信息,但可以緩存這些信息的副本。

  3. 在回調(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)相互覆蓋直到“反彈”完成并且它們相互分離。

image.png

在這個時(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ù):contactselfCollider、otherCollider。

selfColliderotherCollider 很容易理解,如名字所示,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é)兩端的剛體是否能夠互相碰撞

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內(nèi)容