Cocos 動效

在 Cocos 給節點做動畫效果,有很多系統提供的實現方式:

1、動畫系統 Animation
2、動作系統 action
3、緩動系統 cc.tween

動畫系統 Animation 可以在 CocosCreator 手動編輯生成,不用寫代碼,但是隻適用於製作一些不太複雜的場景,而且,如果UI效果要大調,那推倒重做可能好過修修補補,在原基礎上調整效果,有時特別苦逼;動作系統 action 就能用代碼的方式做動效,調起來也方便,還可以複用,但是 action 代碼寫起來一大堆,代碼多了也不好理解,不好維護;緩動系統 cc.tween 在動作系統 action 的基礎上做了一層 API 封裝,提供了鏈式創建的方法,所以 cc.tween 會比 cc.Action 更加簡潔易用,cc.tween 可以對任何對象進行操作,並且可以對對象的任意屬性進行緩動。本文就是基於 Cocos 官網文檔所作的緩動系統 cc.tween 的使用記錄。

一、基本使用

1、鏈式 API

cc.tween 的每一個 API 都會在內部生成一個 action,並將這個 action 添加到內部隊列中,在 API 調用完後會再返回自身實例,這樣就可以通過鏈式調用的方式來組織代碼。cc.tween 在調用 start 時會將之前生成的 action 隊列重新組合生成一個 cc.sequence 隊列,所以 cc.tween 的鏈式結構是依次執行每一個 API 的,也就是會執行完一個 API 再執行下一個 API。

cc.tween(this.node)
    // 0s 時,node 的 scale 還是 1
    .to(1, { scale: 2 })
    // 1s 時,執行完第一個 action,scale 爲 2
    .to(1, { scale: 3 })
    // 2s 時,執行完第二個 action,scale 爲 3
    .start()
    // 調用 start 開始執行 cc.tween

2、設置緩動屬性

cc.tween 提供了兩個設置屬性的 API:
to:對屬性進行絕對值計算,最終的運行結果即是設置的屬性值
by:對屬性進行相對值計算,最終的運行結果是設置的屬性值加上開始運行時節點的屬性值

cc.tween(node)
  .to(1, {scale: 2})      // node.scale === 2
  .by(1, {scale: 2})      // node.scale === 4 (2+2)
  .by(1, {scale: 1})      // node.scale === 5
  .to(1, {scale: 2})      // node.scale === 2
  .start()

同時執行多個屬性

cc.tween(this.node)
    // 同時對 scale, position, rotation 三個屬性緩動
    .to(1, { scale: 2, position: cc.v2(100, 100), rotation: 90 })
    .start()

可用屬性

opacity、scale、scaleX、scaleY、angle(代替 rotation,爲逆時針方向)、position、等等等等

3、回調

cc.tween(this.node)
    .to(2, { angle: -90})
    .to(1, { scale: 2})
    // 當前面的動作都執行完畢後纔會調用這個回調函數
    .call(() => { cc.log('This is a callback') })
    .start()

4、延遲執行

cc.tween(this.node)
    // 延遲 1s
    .delay(1)
    .to(1, { scale: 2 })
    // 再延遲 1s
    .delay(1)
    .to(1, { scale: 3 })
    .start()

5、重複執行

repeat / repeatForever 函數會將前一個 action 作爲作用對象。但是如果有參數提供了其他的 action 或者 tween,則 repeat / repeatForever 函數會將傳入的 action 或者 tween 作爲作用對象。

cc.tween(this.node)
    .by(1, { scale: 1 })
    // 對前一個 by 重複執行 10次
    .repeat(10)
    // 最後 node.scale === 11
    .start()

// 也可以這樣用
cc.tween(this.node)
    .repeat(10,
        cc.tween().by(1, { scale: 1 })
    )
    .start()

// 一直重複執行下去
cc.tween(this.node)
    .by(1, { scale: 1 })
    .repeatForever()
    .start()

二、中級使用

1、並行執行緩動

cc.tween 在鏈式執行時是按照 sequence 的方式來執行的,但是在編寫複雜緩動的時候可能會需要同時並行執行多個隊列,cc.tween 提供了 parallel 接口來滿足這個需求。

let t = cc.tween;
t(this.node)
    // 同時執行兩個 cc.tween
    .parallel(
        t().to(1, { scale: 2 }),
        t().to(2, { position: cc.v2(100, 100) })
    )
    .call(() => {
        console.log('All tweens finished.')
    })
    .start()

2、easing

你可以使用 easing 來使緩動更生動,cc.tween 針對不同的情況提供了多種使用方式。

// 傳入 easing 名字,直接使用內置 easing 函數
cc.tween().to(1, { scale: 2 }, { easing: 'sineOutIn'})

// 使用自定義 easing 函數
cc.tween().to(1, { scale: 2 }, { easing: t => t*t; })

// 只對單個屬性使用 easing 函數
// value 必須與 easing 或者 progress 配合使用
cc.tween().to(1, { scale: 2, position: { value: cc.v3(100, 100, 100), easing: 'sineOutIn' } })

Easing 類型說明可參考 API 文檔

三、中高級使用

1、自定義 progress

相對於 easing,自定義 progress 函數可以更自由的控制緩動的過程。

// 對所有屬性自定義 progress
cc.tween().to(1, { scale: 2, rotation: 90 }, {
  progress: (start, end, current, ratio) => {
    return start + (end - start) * ratio;
  }
})

// 對單個屬性自定義 progress
cc.tween().to(1, {
  scale: 2,
  position: {
    value: cc.v3(),
    progress: (start, end, current, t) => {
      // value 是被操作屬性(如此處的 progress)的設定的最終結果值
      // 注意,傳入的屬性爲 cc.Vec3,所以需要使用 Vec3.lerp 進行插值計算
      return start.lerp(end, t, current);
    }
  }
})

例一:果凍動效
有一天,UI 的大兄弟 BIA 給我一段代碼,要我實現果凍動效:

freq = 4;
decay = 3;
n = 0;
if (numKeys > 0){
  n = nearestKey(time).index;
  if (key(n).time > time) n--;
}
if (n > 0){
  t = time - key(n).time;
  amp = velocityAtTime(key(n).time - .001);
  w = freq*Math.PI*2;
  value + amp*(Math.sin(t*w)/Math.exp(decay*t)/w);
}else
  value

作爲一個程序員,UI 都給代碼了,不上不行啊,但是代碼拆開看得懂,合起來就沒頭緒了,明顯跟咱的 style 有區別,最後查資料,發現是 AE 導出的代碼:

【AE表達式】萬能彈性表達式: https://www.jianshu.com/p/1189e7bc8187

一頓閱讀理解,提取中心思想後,發現關鍵只有這一句代碼:

value + amp*(Math.sin(t*w)/Math.exp(decay*t)/w);

上唄:

// 果凍動效
let freq = 4;
let decay = 3;
let w = freq * Math.PI * 2;
cc.tween(this.targetNode)
    .repeatForever(
       cc.tween()
            .to(0.4, { scaleX: 1.07, scaleY: 0.93 }, { easing: 'sineOut' })
            .to(0.2, { scaleX: 1, scaleY: 1 }, { easing: 'sineOut' })
            .to(1, {
                scaleX: 1,
                scaleY: {
                    value: 1,
                    progress: (start, end, current, ratio) => {
                        return end + end * (Math.sin(ratio * w) / Math.exp(decay * ratio) / w);
                    }
                }
            })
            .delay(0.66)
    )
   .start();

參考:
1、使用緩動系統:https://docs.cocos.com/creator/manual/zh/scripting/tween.html
2、【AE表達式】萬能彈性表達式: https://www.jianshu.com/p/1189e7bc8187

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章