mcpField
S
scenekit-product-stages

scenekit-product-stages

github.com/dembskyデザイン・UI11437 回インストール11 週間前に更新
リポジトリ

scenekit-product-stages について

PropMotionは、SwiftUIアプリで単一の3D製品オブジェクトをSceneKitでアニメーション表示するための実践的なパターンをまとめたスキルです。演出付きの主役オブジェクトを作るアプリ開発者向けに、ステージ、動き、照明、影、描画上の注意点を解説します。

主な機能

  1. 01透過SceneKitステージの構成
  2. 02コーディネーター管理のシーングラフ
  3. 03キーフレームによる動作演出
  4. 04照明と影の手法
  5. 05描画上の問題への対処

活用シーン

  1. 01SwiftUI画面へのステージ付き3D製品オブジェクトの追加
  2. 02登場、退場、投げる動きの作成
  3. 03SceneKitの影やアニメーション問題の調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dembsky/propmotion --skill scenekit-product-stages

インストール後、Claude Code で/scenekit-product-stagesを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
baked-animation.md
camera-choreography.md
deformable-surfaces.md
first-frame-and-warmup.md
fog-and-mist-sheets.md
gestures-and-haptics.md
hdr-environments.md
instancing-and-swarms.md
mesh-surgery.md
modeling-actors.md
motion-from-reference.md
multi-actor-physics.md
physics-without-engine.md
relief-actors.md
rendering-contract.md
sequencing-stages.md
shadows-and-lights.md
stage-recipe.md

詳細

PropMotion

SwiftUI アプリで、1つの3Dオブジェクトに動きをつけます。特定のよくある用途に向けた、本番環境向けのパターンです。透明なSceneKitステージ上で、洗練されたプロダクトアクターを演出します。登場、退場、投げる動き、影、触覚フィードバック、そして何日もの時間を奪う見落としがちな落とし穴を扱います。

SceneKitを使うべき場面

このフレームワークの立ち位置を正直に把握しましょう。

  • SceneKitはメンテナンスモードです。3D要件が重い新しいアプリ(アセットパイプライン、USD/USDZ、AR、大規模なワールド)では、RealityKitを優先してください。
  • SceneKitは、SwiftUIアプリ内に装飾的な3Dアクターを表示する最速の手段であり続けています。透明なSCNViewはあらゆるSwiftUIレイアウトに重ねて合成でき、ジオメトリシェーダーモディファイアーは1つのMSL文字列で済み、CoreAnimationとの相互運用も成熟しており、ここで紹介するすべての仕組みは、セッションのセットアップなしで通常のUIKitビュー上で動作します。
  • 3D要素が、動きを振り付ける単一の主役オブジェクトであれば、このスキルのレシピをそのまま適用できます。完全なインタラクティブワールドの場合は、いったん立ち止まり、まずRealityKitを検討してください。

ステージの基本レシピ

ステージは、透明なSCNView、シーングラフを管理する@MainActorのコーディネーター、立っている人の目の高さに置いたカメラ、3灯のライティングに専用のシャドウライトを加えた構成、接地感を出すぼかし、そして影を受ける面で構成されます。アクターの動きは、ベイク済みのキーフレームアニメーションで表現します。

struct ProductStage: UIViewRepresentable {
    let item: Item

    // The key must change ONLY when the scene must visibly change.
    private var stateKey: String { "\(item.id)" }

    func makeCoordinator() -> Coordinator { Coordinator() }

    func makeUIView(context: Context) -> SCNView {
        let view = SCNView()
        view.backgroundColor = .clear   // the stage composites over SwiftUI
        view.isOpaque = false
        view.antialiasingMode = .multisampling4X
        view.scene = context.coordinator.buildScene()
        view.pointOfView = context.coordinator.cameraNode
        context.coordinator.install(item)
        context.coordinator.markState(stateKey)
        // First-frame warm-up, two-key ignition: compile shaders off
        // the critical path, park the actor offstage, and gate the first
        // entrance on BOTH a minimum delay and prepare's completion.
        context.coordinator.parkOffstage()
        if let scene = view.scene {
            view.prepare([scene]) { _ in
                DispatchQueue.main.async { context.coordinator.markPipelinesWarm() }
            }
        }
        context.coordinator.scheduleFirstEntrance()
        return view
    }

    func updateUIView(_ view: SCNView, context: Context) {
        // State-key diffing: SwiftUI re-renders must never replay entrances.
        guard context.coordinator.stateKey != stateKey else { return }
        context.coordinator.markState(stateKey)
        context.coordinator.transition(to: item)
    }
}

各要素の構築順序は次のとおりです。

  1. ビューを透明にするフラグ(backgroundColor = .clear、isOpaque = false)、MSAA 4x。背景はホスト画面が提供し、シーン自体に背景の箱はありません。
  2. コーディネーターがノード階層を管理します。移動、リフト、ヨー、スピンなど、動きの要素ごとにノードを分けることで、独立したアニメーションが同じ変換を奪い合うことはありません。
  3. カメラ:projectionDirection = .horizontalにすると、アクターのサイズがステージの幅に合わせて変わります。少し下向きに傾けた高めの位置からの視点は、立っている観察者の視線に近くなります。カメラは固定してください。動くカメラは不自然に見えます。
  4. ライト:暖色のキーライト、弱い寒色のフィルライト、強いリムライト、明るさの基準値としてのアンビエントライトに加え、アクターの後方上方から照射し、見せたい造形を損なわない独立した影用ライトを配置します。
  5. 接地面:アクターの下に柔らかな暗いグラデーション平面(接地感を出すぼかし)を置き、実際の影を受けるための不可視の.shadowOnlyキャッチャー平面を配置します。
  6. 反射素材には写真ではなく、小さなプログラム生成の環境マップを使います。カメラの後ろ側は暗く保ちます。
  7. 振り付けはすべてCAKeyframeAnimationにベイクし、世代トークンで保護します。これにより、新しい動きが入ると保留中の処理はすべて置き換えられます。

コードを含む詳細:references/stage-recipe.md

時間を最も奪う落とし穴

落とし穴対策
MSAA が有効だと、コンソールエラーがゼロなのにディファードシャドウがまったく描画されませんshadowMode = .forward と .shadowOnly の受け皿を使います。影が表示されない場合は、まずシーンに目で見えるグレーのランバート床を置いてデバッグします。
新しい SCNView の最初のフレームで、登場アニメーションの最中に Metal パイプラインがコンパイルされますバックグラウンドで prepare([scene]) を実行し、アクターを舞台裏に置いて、最初の登場を遅らせます。1 フレーム目にアニメーションを開始してはいけません。
fillMode = .forwards と isRemovedOnCompletion = false の組み合わせは表示状態を固定し、削除はオブジェクトごとに必要ですノード、すべての子ジオメトリ、ライト、実行中のアクションを一括処理する一元的な clearAnimations を用意し、すべてのエントリーポイントから呼び出します。
正面から見た本物の金属が、グレーまたは黒い穴のように描画されます正面から見た鏡には、カメラの背後にある環境ゾーンが映り込みます。承認済みの塗装ベースはそのままにし、独自の反射マップを持つ薄い加算レイヤーを追加します。
castsShadow を切り替えると、ぼやけた半影が 1 フレームで突然現れます。また、前方方向シャドウでは shadowBias とライトの categoryBitMask が無視されますcastsShadow は常にオンにしておき、動きに同期させて shadowColor のアルファ値をアニメーションさせます。
固定のウォームアップ遅延では低温状態のデバイスや Simulator で依然としてカクつきます。また、パーティクルエフェクトは初回フレームで独自のパイプラインをコンパイルし、発火時にちょうどフレーム落ちします2 つの条件で点火します。最小遅延と prepare の完了ハンドラーの両方を満たしてから登場させます。実際のエフェクトと同じフラグを使い、不透明度ゼロのバーストでパーティクルパイプラインをウォームアップします。
デフォルトの UIGraphicsImageRenderer の形式は画面スケールを引き継ぐため、生成されるテクスチャのピクセル数が 9 倍に膨らみます。キー入力ごとの刻印のように、テクスチャをリアルタイムで再描画する場合は致命的ですレンダラー形式のスケールを 1 に固定し、キャンバスのサイズをアクターの画面上の投影サイズに合わせます。複数の入力による再ターゲット処理は、1 回の更新パスにつき 1 回の描画にまとめます。
写真から実物の動きを再現すると、確信に満ちたリグが横方向では破綻し、修正するたびに別の誤りが見つかります静止画に記録されるのは姿勢であり、経路や仕組みではありません。終端姿勢を合わせても軌道は特定できません。経路をモデル化します。直接姿勢を制御できるキャリブレーション用リグを用意し、所有者が実物に合わせてキーフレームを作成します。
キーフレーム付きの動きがキーフレームのたびに周期的にカクつき、描画やタイミングの修正をすべて行っても直りません急な動きは曲線に焼き込まれています。不均等な間隔のキーフレームに一様な Catmull-Rom 基底を使うと、各ノットで速度が不連続になります。距離ベースの位相上で、区間の長さを考慮した Hermite 接線(または自然三次スプライン)を使って補間し、数値による連続性チェックを通過した場合のみ有効にします。
大きなモデルから切り出したサブメッシュが、エラーなしにオブジェクト全体として計測されます切り出しで変更されたのはインデックスバッファだけで、頂点バッファには元の全頂点が残っています。サブメッシュのインデックスが参照する頂点だけを計測します。
正方形の画像を scene.lightingEnvironment に割り当てても、何も通知されず無視されます。反射はゼロで、すべての鏡面マテリアルが黒く描画され、コンソール出力もありません環境マップは認識されるキューブマップ形式で作成します。最も簡単なのは 2:1 の球面キャンバス(1024x512)です。正方形の球面マップを受け付けるのは material.reflective だけです。
シェーダークロックだけでアニメーションするシーンが 1 フレーム描画された後に止まり、数秒離して撮った 2 枚のスクリーンショットがピクセル単位で完全に一致しますオンデマンドの描画ループはシェーダー時間を認識できません。rendersContinuously = true を設定し、目視ではなくピクセル差分で動きを確認します。
シェーダー時間パターンの速度ダイヤルをスナップするとパターンが瞬間移動します。ダイヤルを補間するとストリームが目に見えて加速し、速度を落とすと逆流します位相は speed * absoluteTime ではなく、速度の積分でなければなりません。レンダラーデリゲートでクロックを蓄積し、速度を目標値に向けてイーズさせ、クロックは前進のみとします。
ジオメトリモディファイアーで揺らした半透明のシートに、折り目のシルエット沿いの明るい細線が現れます。かすめ角でのフェードを帯状にすると、細線が残るか、まっすぐな暗い縞が描かれますモディファイアーが動かすのは頂点であり、法線ではありません。波の解析的な傾きに応じて法線を傾け、さらにアルファ値を厚さ補正 (1+k)*facing/(facing+k) でスケーリングします。滑らかで、接線方向ではゼロになり、正面向きのフォグには影響しません。
手描きの環境マップで照らしたクロームが、おもちゃの金属のように描画されます。境界がはっきりした紙のように白いハイライトが 1 つだけ現れ、反射はポスタリゼーションされたグレーの帯になります手描きマップの上限は 1.0 なので、ロールオフできるダイナミックレンジがありません。カメラで wantsHDR を有効にし、FILE URL として渡した写真の .hdr HDRI を使います(UIImage で再エンコードすると、HDR が気付かないうちに LDR にクランプされます)。
lightingEnvironment には向きを制御する機能がなく、パノラマの正面にあるランプが反射のど真ん中に大きな塊として映ります代わりにカメラリグを回転させます。アクターが対称で床が放射状なら、フレーミングは同じまま反射の配置だけが変わります。画面空間のジェスチャーは、pointOfView を介して軸を持ち上げ、リグを考慮して構築します。
白い誘電体に完全な clearCoat を適用すると、真珠が白い芯を持つクロームになり、2 つの仕上げが同じ見た目になりますclearCoat は鏡面レイヤーです。真珠には粗さ約 0.2、clearCoat 約 0.3〜0.4 が適しています。銀ではなく、クリーム色の上に光沢を重ねます。
シェーダーで変形したアクターへのタップが、膨らんだ部分だけ正確に外れます。hitTest が変形前のメッシュしか見ていませんアクターに大きめの不可視コライダー(colorBufferWriteMask = [])を付け、.all でヒットテストしてから、ノード名でフィルタリングします。
同一になるはずのフリーズクロックのスナップショットが、見た目には違いがないのにゼロ以外の差分を示しますアダプティブ露出によって、シーンの履歴に応じて同じ瞬間が異なって描画されています。wantsExposureAdaptation = false にして、露出を手動で固定します。
パーティクルの破片がいつまでも滑り続けるか、まったく静止しません。摩擦を調整するたびに悪化しますparticleFriction は物理的な直感とは逆です。1.0 では滑り続け、0.0 では貼り付きます。最後の跳ねの後に粒子を止めるには、低い値(約 0.25)が適切です。
機構が実行時に作成した面(切断面や、倒れた部品の下面など)がほぼ真っ黒に描画される一方、アクターのほかの部分は正常に見えます視線軸にほぼ平行な面では、キーライトやシャドウの太陽光がかすめるように当たります。シーンに実際のアンビエントフロアを設け、作成時の姿勢だけでなく、機構が取り得るあらゆる向きでライティングを評価します。
エラーもシーンもなく、デバッグするものが何もない、空のステージが表示されます64 ビットのハッシュ演算後にナローイング初期化子(Int32(...))を使うと実行時にトラップが発生し、非同期タスク内ではクラッシュが見えません。truncatingIfNeeded を使って対象の幅でハッシュ演算を行い、シーンのロジックをデバッグする前にシステムのクラッシュレポートを確認します。

Reference map

  • references/stage-recipe.md:ステージ全体、ビューのセットアップ、コーディネーター、ノード階層、カメラ、照明リグ、接触ブロブとキャッチャー、プログラムによる環境マップとテクスチャ、ステージ上のインパクトパーティクル、ステージアクターのシェーダーモディファイア(リニア空間のユニフォームの落とし穴)、ステージの SwiftUI アイデンティティの維持(再マウントと更新ストームの落とし穴)。
  • references/first-frame-and-warmup.md:初回描画時に発生する Metal パイプラインのコンパイルによる引っかかりと、その解決策 2 つ。遅延した登場演出によるウォームアップ(prepare の完了を条件とする 2 キー点火方式へのアップグレード)、またはシーンをウォームな状態でマウントしたままターゲットを変更する方法。パーティクルパイプラインのウォームアップ、サインポストとデバイス上の引っかかりトレースによる解決策の検証。
  • references/shadows-and-lights.md:気づきにくい影の落とし穴すべて、信頼性の高い forward と shadowOnly の組み合わせ、影の表示状態のアニメーション、適切な照明予算、正面から見るメタル。
  • references/baked-animation.md:タイマーよりベイク済みキーフレームが優れている理由、ベイク前に複数フェーズの動きを設計するためのキューシート、シームの分類(C1 と接触インパルス)、重みの設計、高密度サンプリング、後処理の記録管理、生成トークン、アニメーション中のノードの奪取、滑らずに転がす方法、床面の経路に沿って転がす方法(操舵、スクリーンスペースでの演出、デバッグトレイル)、トランスフォーム以外のチャンネル(モーファーの重み、レンズ値、シェーダーユニフォーム)、オーサリング素材としてのスプリング、1 つのプロパティにつき 1 つの所有者。
  • references/motion-from-reference.md:実物の動きを再現する方法。静止画と動画から分かることと分からないこと、仕組みではなく軌道をモデル化する方法、キャリブレーションリグ(オーナーがアクターのポーズを決めてキーフレームを保存)、距離によるパラメーター化と手動で保存したポーズの区間重み付き補間、一様な Catmull-Rom の落とし穴、数値的な連続性の判定基準、現在のフェーズから抜け出せるシームレスなコサイン状態ループ、トリミングしたサブメッシュの計測。
  • references/physics-without-engine.md:手作業で実装した固定ステップ積分器をキーフレームにベイクする方法、単なる数値として表す壁と床、接触駆動の触覚フィードバック、縁を支点にした転倒、振り付けされたシーンで SCNPhysics より優れている理由。
  • references/mesh-surgery.md:実行時にアクターを切断する方法。単純な配列として扱うメッシュ、カメラ越しに切断面へ投影したスワイプ、三角形をクリップして 2 つの密閉された半分に分割する方法、キャップを簡単に作れる凸性の根拠、1 つの放射状アートワークに使える平面キャップ UV、どちらの断片が落下するかを判断する閉メッシュの体積と重心の積分、任意の断片に対応する支持点落下積分器、押し続けて狙いを定め、離したときに確定する操作、再スライス後も維持されるスクリプト切断。
  • references/relief-actors.md:CPU ハイトフィールドから作る打ち出し加工のレリーフオブジェクト。高さとクラスマップを同時に描画する方法(1 つのメッシュに複数の仕上げを適用)、放物線状の特徴プロファイル、シルエットに対する 3 つの同時保証、フィールドのよりぼかしたコピーから法線を求める方法、平面鏡を正面かつ静止状態で評価する方法、初期化時のハッシュ値を狭める落とし穴、オフラインレンダリングを目指して磨き込む前にラスタライズの上限を定めること。
  • references/gestures-and-haptics.md:ヒットテストなしでパンしてつかむ方法、トラックボール(画面軸をカメラ越しにワールド空間へ移し、作成済みの動きにクォータニオンを合成し、ステージクロックでフリックの慣性を制御し、タップとパンを共存させる)、つかんでいる間のソフトクランプ、離したときの速度、インパクト時の触覚フィードバック、操作中の指があってもスクリプト演出を維持する方法(回数に上限のある再試行、トークン付きポーリング、非表示アクターに対するジェスチャーガード、奪取時にホールド契約を終了)、分類体系としての Reduce Motion、非表示のステージへの VoiceOver アクセス、SwiftUI ジェスチャーとの共存。
  • references/sequencing-stages.md:複数のステージを 1 つの映画のように演出する方法。ビートをデータとして扱うマスタークロック、壁時計によるずれの落とし穴、退出演出が終わってから動きの途中で切り替える方法、ウォームアップのため次のステージを事前にマウントする方法、切り替え後も残るステージ(残留する煙)、ユーザー操作に応じたタイムラインの再基準化、録画のリード、フレーム単位での切り替え検証。
  • references/modeling-actors.md:プリミティブから説得力のある主役オブジェクトを作る方法。目分量で調整する前に実世界の比率を使うこと、くぼんだ面に環状形状を使うこと、チューブとトーラスを組み合わせたシルエット、放射状パターンの視認性、凹面に対するインスタンスごとの傾き、開いた隙間、形状として表現するレリーフの特徴(塗装はしない)、暗いステージ上のサテンメタルのアルベド、ファイルとして届くアクターの表示制御。
  • references/camera-choreography.md:演者としてのカメラ。オービットリグ、ベイク済みの披露フライト、ドリー移動に追従するフォーカス、慣性とホーム復帰を備えたドラッグオービット、接地感を出す SCNFloor の反射、ダウンライトの下でのマットな演出、トラッキングリグの制約(アクターには決して取り付けない理由)。
  • references/instancing-and-swarms.md:一度に数十個のアクターを扱う方法。群れ全体で 1 つのジオメトリを使う方法、物理演算ではなくスロットベースで積み重ねる方法、着地点から逆算して放物線を求める方法、タンブルから静止ポーズへのブレンド、シード付き乱数、スケジュールによる時間差の設定、破壊バースト(エンジンパーティクルがベイクに勝る唯一の場面と、それに伴う決定性の例外)、予算に関する注意事項。
  • references/fog-and-mist-sheets.md:連続する蒸気の流れ(換気口の空気、蒸気、ミスト)を 1 枚の半透明シートとして表現する方法。ジオメトリモディファイアによる波とフラグメントモディファイアによる霧、ドメインワープしたノイズと縞模様、マッハバンドを防ぐ五次フェード、ジッターを加えたエンベロープ、方向を示す下流側の明るさ、速度ダイヤルを変えても維持される統合位相クロック、折り目のシルエットに対する波で傾けた法線と厚みの補正、カメラ相対のダイヤルエンベロープ、プロファイル相関による流れの方向の計測。
  • references/multi-actor-physics.md:ベイク済みシミュレーションで複数のアクターを扱う方法。1 つのクロック上の N 個の状態ベクトル、ペアごとの衝突(分離してから、接近中であれば速度を交換)、ワールド全体を停止してつかむ方法、指が選んだアクターのヒットテスト、スクワッシュと触覚フィードバック用のアクターごとの接触リスト。
  • references/hdr-environments.md:説得力のあるメタル。カートゥーン調のハイライトを生む LDR の限界、ファイル URL を使った実際の .hdr HDRI(UIImage のクランプの落とし穴)、1.0 を超えるブルームしきい値と wantsHDR、スナップショットと露出適応の違い、環境を回転できないためカメラリグを回転させる方法、クリアコートと暗色仕上げに関する注意事項、暗い家具が必要な照明テーマ、平面鏡専用の環境(有限個のガウスカードを使い、球面全体を囲むリングは使わない)、クローム上の 3 種類の線の分類、CC0 素材の入手とバンドルへの同梱。
  • references/deformable-surfaces.md:呼吸する閉曲面。単位方向に対する fbm 変位、液体と岩を分けるオクターブ数の予算、有限差分による法線の再構成(これがないと鏡面仕上げは破綻します)、移動する前線を持つ float4 ユニフォームスロットとしてのタップ波紋、メッシュ密度とリング波長の関係、大きくした不可視のタップコライダー、アニメーション可能な KVC ダイヤルとしての振幅。
  • references/rendering-contract.md:決定論的なステージ。シェーダーユニフォーム、ノードのポーズ、ジェスチャーの慣性を一元管理する、注入可能な dt 積分クロック、フリーズ時の起動フックと過去時刻を指定したイベント、決定性チェックリスト(露出適応、壁時計、ポーズとしての Reduce Motion)、検証キット(動きの差分、ビット単位で同一なフリーズ時のペア、フリーズクロックでのインタラクション差分)、合成 CGEvent ジェスチャーとその落とし穴(古いウィンドウフレーム、介在する人間)。

レビューチェックリスト

ステージをリリースする前に、次の点を確認してください。

  • SCNView の backgroundColor = .clear および isOpaque = false を設定します。ステージは、目に見える枠や継ぎ目なしで合成されます。
  • ビューの最初のフレームではアニメーションを開始しません。prepare を実行し、最初の入場を遅らせるか、シーンをマウントしたままウォーム状態に保ちます。
  • シャドウには .shadowOnly のキャッチャーを使ったフォワードモードを使用します。遅延シャドウ、shadowBias、ライトの categoryBitMask による制御には依存しません。
  • アルファテクスチャ付きの平面(ラベル、デカール)では castsShadow = false を設定します。設定しないと、四角い影ができます。
  • シャドウの表示状態を変えるときは、shadowColor のアルファ値を徐々に変化させます。実行時に castsShadow を切り替えてはいけません。
  • シーンでテクスチャの色を忠実に再現する必要がある場合、平面に対して、環境光と入射角の余弦を掛けた指向性ライトの合計がニュートラル(1000)になるようにします。ライトグレーが白飛びしてはいけません。
  • lightingEnvironment(または background)の画像には、認識可能なキューブマップレイアウト(2:1 の球面、6:1 または 1:6 のストリップ、あるいは6枚の画像)を使用します。正方形の画像はそこで無視され、反射マテリアルは黒くレンダリングされます。
  • すべての非同期処理の継続部分(遅延した入場、スケジュールされたハプティクス、連鎖するトランジション)は世代トークンをキャプチャし、処理を行う前に再確認します。
  • アニメーション中のノードを引き継ぐコードは、removeAllAnimations を呼び出す前に .presentation の値をスナップショットしてモデルに反映します。
  • ベイク済みアニメーションでは、addAnimation の前にモデルを最終ポーズに設定します。クリーンアップ処理なしで fillMode = .forwards に依存してはいけません。
  • 複数フェーズの動きには、ポーズ関数の上に、フェーズ境界の構造体と一致するキューシートのコメントを記述します。フェーズとキューの時刻はその構造体だけに置き、サンプルループやスケジュールされたハプティクスにリテラル値を置いてはいけません。すべての継ぎ目を分類します。デザイン上の継ぎ目は C1、インパルスの継ぎ目は接触イベント時のみ発生し、それぞれキューを鳴らします。
  • 転がるオブジェクトの回転は、距離を半径で割った値に連動させます。滑らせてはいけません。
  • 実物から動きを取り込む場合、キャリブレーションリグ(直接ポーズ制御、保存したポーズ、所有者の視点)を通じてキーフレームとして作成します。写真から推測した仕組みで動きを作成してはいけません。
  • 手作業で作成したキーフレームは、ポーズ空間の距離でパラメーター化し、スパンの重みを反映した接線(または自然三次スプライン)で補間します。ベイクの前に、高密度サンプリングで数値的な最大加速度ステップのチェックに合格させます。
  • ループ状態では、状態の実際のスイープ範囲全体にわたる余弦関数で位相を制御します(開始位置と終了位置が同じで、反転時の速度はゼロ)。エンジンで繰り返し、終了時はアニメーションクロックから現在の位相を読み取り、残りの移動距離に応じた時間で同じ経路を戻ります。
  • フレームに入る、またはフレームから出る経路のイーズでは、表示範囲の両端で弧長に対する速度をゼロ以外に保ちます。静止中のアクターに経路を追加する場合、最初の接線をアクターの回転軸に正確に合わせます(アクターの長さによって、最初のフレームの向きの急変が増幅されます)。
  • 実際に触れている指にぶつかる可能性のあるスクリプト化された動きには、回数に上限を設けたトークン検証付きの再試行を使います。何も通知しないガードの後ろで一度だけ実行してはいけません。すべてのジェスチャー開始処理では、トークンを更新する前に、アクターが非表示または未マウントでないことを確認します。
  • 動きの不具合が報告されたら、調整を始める前に、経路を約4pt幅の平坦なリボンとして描画します(ベイクで使用した点を使い、ライティングなし、深度テストなし)。形状が承認されたらリボンを削除します。
  • 複数シーンのシーケンスでは、静的パブリッシャーからの実時間差分でスクリプトクロックを進めます。次のステージは、直前のステージがフレーム外に出てから切り替え、オフステージで待機中にあらかじめマウントします。
  • ハプティクスはアプリのハプティクス設定に従わせます。ジャイロ視差やシェイクなど、動きに連動する追加効果は「視差効果を減らす」設定に従わせます。
  • 60 Hz の処理(モーションコールバック、フレームごとの更新)は、ビューがウィンドウから外れたら停止し、再アタッチ時に再開します。
  • updateUIView では状態キーを比較します。SwiftUI の再レンダリングだけでは何も再生されないようにします。
  • ステージの SwiftUI ID は安定させます。条件付きで表示してはいけません(不透明度で非表示にします)。representable にクロージャーパラメーターを付けず、.id は意図的に再マウントする場合にのみ使用します。
  • VoiceOver でステージを利用できるようにします。ラベルと値を持つアクセシビリティ要素を1つ設け、ジェスチャーは調整可能なアクションまたは名前付きアクションとしても提供します。「視差効果を減らす」が有効な場合、ループする環境モーションは遅くするのではなく削除します。
  • テクスチャは GPU のテクスチャサイズ上限(最近の iPhone では16384 px、旧世代の GPU では8192 px)を大幅に下回るサイズに抑えます。テクスチャキャッシュがある場合、ステージが完全に終了したときに解放します。
  • 生成テクスチャはフォーマット倍率1でレンダリングし、アクターの画面上の投影サイズに合わせます。リアルタイムで再ターゲットされるテクスチャは、更新パスごとに1回のレンダリングに集約します。
  • 最初の入場は固定の遅延だけに頼らず、prepare の完了ハンドラーを条件とします。パーティクルパイプラインは、最初の実際のフレームの前に(実際のエフェクトと一致するフラグを使った不透明度ゼロのバーストで)ウォームアップします。
  • 衝突時のパーティクルはアルファブレンドし、距離順にソートし、各リグ固有のフラットカラーでライティングなしにします。エミッター形状をアクターと交差させてはいけません。
  • ハプティクスジェネレーターは準備し、スタイルごとに再利用します。スケジュールされたクロージャーの中で直接作成してはいけません。
  • シェーダークロックだけでアニメーションするステージでは rendersContinuously を設定します。動きは2つのキャプチャのピクセル差分で検証し、方向や速度に関する判断は目視ではなく、フレーム間のプロファイル相関で確認します。
  • 速度で制御されるシェーダーパターンは、累積クロック(速度の積分)で進めます。speed * absoluteTime は使いません。速度は目標値に向けてイーズさせ、クロックは前方向にのみ進めます。
  • 変形するシートの視点角度によるフェードでは、波によって傾いた法線を読み取ります(モディファイアーでは位置だけでなく _geometry.normal も更新します)。折り目のシルエットは、床値を持つ帯状のソフトステップではなく、厚みの補正で処理します。
  • 各モーションチャンネルがそれぞれのエンベロープを持ちます。あるチャンネル(波)の静止や減衰が、別のチャンネル(スイングの曲げ)が依存するランプに影響してはいけません。
  • 移植したエフェクトのダイヤル値は、まず画面空間で新しいカメラ向けに再調整します。エンベロープと連動する定数はすべて、細部を調整する前に、新しい可視区間に基づいて再導出します。
  • 鏡面仕上げのアクターは、ファイル URL として渡す写真品質の .hdr 環境でライティングし、カメラで wantsHDR を有効にして、1.0を超えるしきい値でブルームを適用します。手描きの LDR 画像をクロームやゴールドのマテリアルに使用してはいけません。
  • シェーダーで変形するジオメトリでは、変形に基づいて _geometry.normal を再構築します(複合変位では有限差分を使用します)。タップ判定にはベースメッシュではなく、目に見えない大きめのコライダーを使います。
  • シェーダーのユニフォーム、ノードのポーズ、ジェスチャーの慣性など、すべてのモーションソースは、フリーズ起動フックを備えた1つの dt 積分クロックに従わせます。露出適応はオフにします。起動間でフリーズしたスクリーンショットがビット単位で一致することで決定性を証明し、フリーズしたクロックでの差分がアクターの範囲内に収まることでインタラクションを検証します。モーションをエンジンのパーティクルシミュレーションに委ねるステージは、コメントで適用除外を明記し、動きのピクセル差分で正しさを証明します。
  • ステージの仕組み上発生しうるすべての向き(切断面、ひっくり返った裏面、回転途中のポーズ)でライティングを検証します。作成時の静止ポーズだけで検証してはいけません。
  • 生成テクスチャ内の線画(ストライプ、帯、輪郭線)は、塗りつぶしたベクターパスで描画します。行ごとの長方形を積み重ねて描画してはいけません。どの解像度でも、行を積み重ねると階段状のエッジになります。

コメント