停格動畫影片 (Stop-motion video)
使用此技能製作的影片,動作就像配合歌曲拍攝的停格動畫。每個姿勢(pose)維持一個八分音符的長度,然後所有事物一起改變。頁面在小節線處進入。每次點擊聲都落在鼓點上,重大的變化落在重大的大鼓聲上。幀率刻意保持在低位。當某個動作看起來不流暢時,會更好地調整其姿勢間距;節拍感絕不會被平滑掉。
這些規則源自製作一部 47 秒的宣傳影片,該影片配合一首 169.825 BPM 的鼓打貝斯 (Drum and Bass) 曲目,經過數百輪筆記反覆調整而成。影片以 30fps 運行,每個姿勢維持一個八分音符,即每秒 5.66 個姿勢,每個姿勢佔用 5 或 6 幀。此處的數值即為該影片的設定,而 live.md 中的則是那些在網頁上配合節拍器播放影片場景的數值。當某條規則可推廣應用於其他情況時,文字中會特別說明。
詞彙表
- 姿勢 (Pose):一個靜止的畫面,對應歌曲的一個八分音符。程式碼中稱之為一個「拍」(
STEP_MS,useBeat)。 - 歌曲拍 (Song beat):BPM 所計數的拍子,即一個四分音符。它等於兩個姿勢的長度,並會得到一個節拍器點擊聲。
- 小節 (Bar):四個歌曲拍,即八個姿勢。在鼓打貝斯中,大鼓落在姿勢 0,小鼓落在姿勢 2 和 6(從 0 開始計數)。
- 敲擊 (Hit):歌曲中的一個鼓聲。重大大鼓 (Big kick) 是指開啟一個樂句的大鼓(在該影片的曲目中為每隔一小節出現一次)。
- 創作毫秒 (Authored ms):程式碼中的時間單位。它們是以 200ms 的姿勢為基礎撰寫,並以
SPEED倍速播放 (SPEED = TEMPO / 150)。 - 時間戳記 (Timestamps) 的格式為 m:ss.ff,其中 ff 是幀數計數。這是 Remotion Studio 顯示時間的方式,也是筆記送達時的格式。
操作姿態
- 刻意保持低幀率。 絕不能為了讓動作流暢而平滑掉節拍。相反地,應該更好地調整動作的姿勢間距。
- 所有事物都維持一個姿勢的長度然後一起改變,轉場也不例外。沒有東西有自己的時鐘:嵌入的動畫或滾動頁面也必須跟隨姿勢的步伐。打字是唯一的例外:它比節拍更快,就像真實打字一樣,從其姿勢開始。
- 歌曲就是時鐘。 在分鏡之前先測量它。將頁面放在小節線上,並將動作安排在敲擊點上。
- 整部影片共用一個時鐘。 根據影片的幀數來計數姿勢,絕不根據某個場景自身的幀數。
- 以整個小節為單位移動。 以整個小節為單位來改變頁面長度,或者在下一個小節線之前將姿勢補回到別處。否則其後的所有內容都會與歌曲脫節。
- 沒有無意義的姿勢。 一旦某個頁面上的動作完成,只需維持足夠觀眾理解的長度即可。
- 少數幾種變化類型、同一方向、無淡入淡出。
- 透過測量來檢查。 使用每姿勢一張靜止圖、在渲染音訊中測量時間、以及使用節拍器剪輯。不要用眼睛判斷同步性。
工具:預設為 Remotion
預設提議使用 Remotion 來製作影片,除非專案已經以其他方式製作影片。在分鏡之前說明理由,並且只有在使用者要求其他工具時才更改。若是在網頁上,則改為即時播放 (live.md);若在動畫工具中,規則仍然適用,但輔助工具不適用。
- 為什麼它合適。 每一幀都是一個根據幀數計算的 React 組件,所以一個姿勢能精準落在它所在的任何幀上,並且每次渲染結果都相同。渲染過程會跨多個分頁、不按順序繪製幀,這只有由幀驅動的影片才能承受。
assets/remotion/中的輔助工具是為它編寫的,其 Studio 顯示時間的格式 m:ss.ff 也正是筆記送達的格式。 - 設定方式。
npx create-video@latest並選擇空白範本。將remotion和所有@remotion/*套件鎖定在同一個精確版本(該影片使用 4.0.532),並將 TypeScript 保持在 5.x 版本 (clock.md §7)。一個合成 (composition),1920×1080 解析度,30fps。將beat.ts和transitions.tsx複製到src/目錄下,並設定TEMPO。 - 配置方式。 在
remotion.config.ts中:JPEG 幀品質設為 95(預設值 80 會使純色背景和細字體的邊緣變得粗糙),BT.709 色彩空間(讓播放器顯示與瀏覽器相同的顏色),並開啟覆蓋寫入。 - 預覽與渲染。 使用
npx remotion studio進行工作;使用npx remotion render <Comp> out/<name>.mp4渲染 1080p 影片,加上--scale=2參數可輸出 4K;使用--frames=<first>-<last>或npx remotion still <Comp> --frame=<n>來檢查某個片段或某個姿勢。
決策順序
從上到下進行。每個步驟都會限制下一步。
- 提議工具。 Remotion,除非專案已經以其他方式製作影片;然後在執行其他任何操作之前,按照上述方式進行設定。
- 測量歌曲。 從歌曲的起音 (onsets) 找出節奏 (tempo),並確認該節奏網格在你使用的片段內保持一致。標記出小節和樂句、鼓聲消失和回來的地方、以及重大大鼓的位置。將曲目片段精確地裁剪到樣本起點,即樂句第一個大鼓前的一個頭髮絲(23ms)處。參見 song.md。
- 設定時鐘。 一個姿勢就是歌曲的一個八分音符。以 200ms 的姿勢配合
SPEED = TEMPO / 150進行創作,以 30fps 渲染,並根據整部影片的幀數來計數姿勢。參見 clock.md。 - 將頁面放在小節線上。 以姿勢為單位撰寫分鏡表。每個頁面包含整數個姿勢,並從一個小節線開始。在開始製作任何動畫之前,先將歌曲的事件映射到姿勢上。
- 安排敲擊點。 程式碼以每八分音符 2 到 4 個字元的不均勻區塊輸入;標題和按住退格鍵的動作會比其姿勢的節拍更快。點擊聲和爆裂聲放在小鼓上;著陸和新頁面放在強拍上;重大變化放在重大大鼓上。建構過程在音樂安靜的段落進行,而著陸則在鼓聲回來時發生。參見 hits.md。
- 間隔姿勢。 為每個姿勢選擇曲線。一個動作的第一個姿勢就是它的第一步,每個姿勢都讓動作有明顯的移動。事物是「著陸」而不是「爬行」。參見 poses.md。
- 更換頁面。 使用節拍上的硬切、地面的剪切、像紙張一樣覆蓋的頁面、攝影機進入某種顏色、從內部填充、穿過形狀的開口、或像素塊。絕不使用淡入淡出。參見 hits.md。
- 設定字體。 字體間距隨尺寸增大而收緊,行距也隨之收緊。每個詞在每個姿勢下都必須與其背景達到至少 4.5:1 的對比度。標題是打字輸入的,而非淡入顯示。參見 type.md。
- 製作音效。 每個點擊聲都落在歌曲本身的敲擊點上,並對齊到樣本;所有點擊聲都烘焙到一個與歌曲同時開始的音軌中。在點擊聲響起時將音樂音量降低,並保留一個節拍器剪輯用於檢查。參見 sound.md。
- 決定何時跳過一個節拍。 段落中斷 (Drop)、降低音量 (Duck)、保持 (Hold)、對齊 (Snap)、剪掉整個小節、在一個歌曲拍上結束。參見 skipping.md。
- 檢查。 製作一張連續劇照 (contact sheet),測量同步性,並觀看節拍器剪輯。以映射到姿勢的時間戳記形式記錄筆記。參見 checking.md。
- 在瀏覽器中即時播放? 同樣的規則,但配合節拍器:一個基於頁面時間的時鐘、畫面比聲音提前 25ms、聲音提前在音訊時鐘上排程、並且在沒有歌曲時每次變化都伴隨一個點擊聲。參見 live.md。
規則一覽
時鐘
| 項目 | 規則 |
|---|---|
| 幀率 | 30fps,刻意為之。一個流暢的 60fps 變體可以共用分鏡表 (clock.md §8) |
| 姿勢 | 歌曲的八分音符:在 169.825 BPM 下為 176.65ms,每秒 5.66 個,在 30fps 下佔 5 或 6 幀 |
| 創作 | 所有時間皆以 200ms 姿勢為基礎的 ms 表示;SPEED = TEMPO / 150;ms() 轉換為幀數,seconds() 用於嵌入式時鐘 |
姿勢 n 的第一幀 | ceil(n × 每姿勢幀數),基於整部影片的幀數 (beatFrame) |
| 頁面 | 包含整數個姿勢,從一個小節線開始(姿勢號可被 8 整除) |
| 更改長度 | 以整個小節為單位,或者在下一個小節線之前將姿勢補回 |
| Studio 拖曳 | 拖曳過的音軌會寫入一個 from 偏移量,導致畫面與聲音錯位:請移除它 |
間距 (Spacing)
| 移動類型 | 曲線 | 姿勢分佈百分比 |
|---|---|---|
| 進出(鏡頭移動、掃過、底線) | easeInOutSine | 在六個姿勢上分佈為 7, 18, 25, 25, 18, 7% |
| 從畫面外進入 | ease-out | 否則它緩慢的起始部分會在視野外浪費掉 |
| 頁面覆蓋另一頁面 | easeOutSine | 在一個三姿勢的轉場中分佈為 38, 70, 92% |
| 縮放、線條變粗、開口變大 | 每次增長相同倍數 | 縮放時,目標沿直線朝向中心移動 |
| 非少量姿勢 | 三次方 in-out(3, 12, 35, 35, 12, 3:中間躍進);強 ease-out(著陸在它的第一個姿勢:作為三姿勢轉場為 78, 97, 100%,作為使用 moved() 的移動為 88, 99, 100%) |
節拍上的事物
| 項目 | 規則 |
|---|---|
| 點擊聲 | 按壓一個姿勢,落在大鼓或小鼓上;游標在最後一次點擊之後的那個姿勢消失 |
| 游標 | 打字時為實心,之後以兩個姿勢開啟、兩個姿勢關閉的方式閃爍 |
| 旋轉器 | 每個姿勢最多旋轉四分之一圈;超過半圈會讓人感覺是反轉 |
| 滾動標記 | 以其行進距離除以半徑的角度旋轉,最終直立停止,使用 easeOutSine 曲線 |
| 計數器 | 每個姿勢顯示一個數字,使用強 ease-out、表格數字 (tabular figures),整數值停留兩個姿勢 |
| 標題 | 從其姿勢開始,比節拍更快地打出:每三個幀打出一個單詞;長行每 25ms(創作時間)打出一個字母 |
| 程式碼 | 以每個姿勢 2 到 4 個字元的不均勻區塊輸入 |
| 按住退格鍵 | 一次連續動作,每 25ms(創作時間)刪除一個字母,換行符號也是 |
| 鏡頭升起 | 升起,在頂部死寂維持兩個姿勢,然後下降 |
| 從畫面外出現 | 在移動完成之前不被繪製 |
| 手動放置 | 最多偏移該姿勢移動量的十分之一,一旦停止則完全不偏移;最終定位在幾像素範圍內向下 |
敲擊點 (Hits)
| 在歌曲中 | 在畫面中 |
|---|---|
| 八分音符 | 程式碼以區塊輸入,移動的步驟 |
| 小鼓 | 點擊聲,爆裂聲 |
| 強拍 | 著陸,新頁面,頁面的第一個動作 |
| 重大大鼓 | 重大變化:頁面著陸,最後一頁完整展示,最終點擊聲 |
| 鼓聲消失 | 建構過程 |
| 鼓聲回來 | 著陸 |
點擊聲必須在節拍上。轉場可以偏離節拍。
音效 (Sound)
| 項目 | 規則 |
|---|---|
| 點擊聲放置位置 | 對齊到樣本,落在歌曲的敲擊點上(該影片的敲擊點比其姿勢晚了 25ms) |
| 點擊聲檔案 | 在 tick 聲前有 40ms 的靜默,這樣裁剪到樣本時才不會吃掉它 |
| 點擊聲音軌 | 所有點擊聲烘焙到一個與歌曲同時開始的音軌中 |
| 音量降低 (Ducking) | 在重要的點擊聲周圍完全靜音;在其他點擊聲下,音量降為 45% 持續 160ms;淡入淡出時間為 10ms |
| 節拍器 | 在每個歌曲拍的敲擊點上播放一個點擊聲,小節的第一拍音調更高,音樂音量設為 20%;絕不包含在最終匯出中 |
字體 (Type)
| 尺寸 | 字間距 (Tracking) | 行距 (Leading) |
|---|---|---|
| 小於 20px | 0;等寬大寫字母 +2% | 1.4–1.5 |
| 20px 及以上 | −1% | 約 1.4 |
| 32px 及以上 | −2% | 約 1.25 |
| 56px 及以上 | −3% | 1.1–1.2;兩行顯示標題可降至 0.9 |
每一個詞在每個姿勢下都必須至少達到 4.5:1 的背景對比度。預設使用同一種字體家族(範例中使用 Geist 和 Geist Mono),黑底白字,如同觸覺設計(tactile-design)的做法;該影片本身則是用一種展示字體搭配其文字字體,並將頁面設定在純色背景上。計數器使用表格數字 (tabular figures)。標題是打字輸入的,絕不淡入顯示。
即時,在瀏覽器中 (Live, in a browser)
| 項目 | 規則 |
|---|---|
| 時鐘 | beat = anchorBeat + elapsed s × BPM / 60 × 2 基於 performance.now();在節奏變化時重新錨定到當前時間;每個場景都是 beats 的純函數 |
| 畫面 | 讀取 beats 的時間提前 25ms;每個場景在動畫幀回調內提交 (flushSync) |
| 聲音 | 每 25ms 一個計時器,在音訊時鐘上排程接下來的 120ms (getOutputTimestamp());時間視窗以音訊時間測量,並加上輸出延遲 |
| 可視範圍 | 僅有螢幕上顯示的內容進行動畫;範例中螢幕上顯示最多的內容就是聽到的內容 |
| 開始 | 在載入時嘗試,然後在第一次按下或按鍵時再次嘗試;啟動聲音的按鍵不會靜音(600ms);記住靜音狀態 |
| 無歌曲 | 每次變化都伴隨一個點擊聲:移動可以踩在八分音符上,但必須在點擊聲上開始和結束;移除該點擊聲本身的節拍器點擊 |
| 節拍器 | 一個安靜的木質敲擊聲(880Hz 持續 50ms,小節第一拍為 1320Hz),沒有音樂時不發出重擊聲,全部通過一個限制器 (limiter) |
| 暫停 | 在最接近的點擊聲處暫停:播放到該點擊聲為止,或者在 160ms 內逐漸恢復 |
| 減少動畫 (Reduced motion) | 一個能展示概念的靜止姿勢,僅在按下聲音按鈕後才移動 |
| 字體 | 單詞對比度 4.5:1,有意義的標記 3:1,縮放場景的最小單詞渲染後為 9px 或更大 |
何時跳過一個節拍 (When to skip a beat)
網格是嚴格的,因此打破它是個有意識的決定。以下的每一種破格方式在 skipping.md 中都有其原因。
- 在重要的點擊聲周圍讓音樂靜音。 從點擊聲前 50ms 到下一個小節線完全靜音,帶有 10ms 的淡入淡出。這個點擊聲被單獨聽到,節拍隨著後續內容回來。
- 在其他點擊聲下降低音樂音量。 將音樂音量降低到 45% 持續 160ms,讓點擊聲的重擊有空間。
- 在下一件事開始前,單獨維持一個結果 一個歌曲拍,例如複製後的打勾勾動畫。
- 在升起頂端維持兩個相同的姿勢。
- 在整數上維持兩個姿勢 然後再繼續。
- 將落在節拍之間的筆記對齊 到最接近的重拍。
- 剪掉整個小節, 即使筆記要求更少。說明這樣做的代價。
- 在一個歌曲拍上結束。 最後一頁維持約 2 秒,音樂在最後一個姿勢上淡出。否則在動作結束後立即結束。
絕不允許出貨 (Never ship)
- 為了掩蓋間距不良的移動而將動作平滑化以脫離節拍
- 場景從自己的第一幀開始計數姿勢
- 沒有在旁邊寫下理由就從非小節線開始的頁面
- 不是整個小節且未在下一個小節線前補回的長度變化
- 合成樹中遺留的 Studio
from偏移量 - 不在大鼓或小鼓上的點擊聲,或者點擊聲是放在某個幀上而非對齊到樣本
- 短點擊聲從自己的幀開始而非使用一個烘焙好的音軌
- 一個移動的第一個姿勢就是它離開的位置,或者在最後幾像素中緩慢爬行
- 從畫面外出現的東西在移動之前就被繪製出來
- 旋轉器或輪子每個姿勢旋轉超過半圈
- 淡入淡出,或在飽和背景之間交叉淡入淡出
- 背景變化快於每秒三次閃爍
- 在任何姿勢下,任何字詞與其背景的對比度低於 4.5:1
- [ ...(翻譯截斷)]