Art-7: アニメーション仕様
基本方針
手続き的アニメーション(Procedural Animation)
Rift Survivors のキャラクター・敵・ボスは スキンメッシュ不使用・ボーン不使用。 各パーツ(Object3D / Mesh)を階層に配置し、毎フレーム 回転・位置・スケール を 関数(sin / cos / 線形補間)で直接書き換えることでアニメーションさせる。
なぜ手続き的なのか
| 観点 | 骨+スキンメッシュ | 手続き的(本作品の方式) |
|---|---|---|
| ロードサイズ | .glb / .gltf が膨らむ(アニメクリップ込み) | ジオメトリのみで済む(コード上で姿勢計算) |
| Vibe Jam 2026 要件 (< 2MB) | 厳しい | 有利 |
| 見た目 | 滑らか(現代的) | カクカク(PS1・FF7 ワールドマップ的) |
| 実装工数 | DCC ツール連携が必要 | Three.js だけで完結 |
| バリエーション | クリップ追加が重い | 数値パラメータで自在に差分生成 |
PS1 時代の「カクカク感」は仕様
1997 年の PlayStation はキャラの手足が 1/10 秒ごとにスナップ するような低頻度更新で アニメーションしていた。本作品はこれを 意図的な表現 として採用する。 バグではなく、ノスタルジーとリズム感を生むデザイン要素。
- 一部パーツの更新レートを 10 fps 相当 に量子化(Frame-Step)
- 胴体・頭は 60 fps で滑らかに、腕・脚・武器は 10 fps でスナップ、というハイブリッド運用も可
パーツ構成のおさらい
詳細は Art-3: キャラクターデザイン 参照。
class CharacterRig extends THREE.Object3D {
head: THREE.Mesh; // 頭
body: THREE.Mesh; // 胴体(回転の親にはしない)
armL: THREE.Mesh; // 左腕(肩ピボット)
armR: THREE.Mesh; // 右腕(肩ピボット)
legL: THREE.Mesh; // 左脚(股ピボット)
legR: THREE.Mesh; // 右脚(股ピボット)
weapon: THREE.Object3D; // 武器(右手の子に配置 = 腕と連動)
// 追加: アニメーション状態
state: AnimState = 'idle';
stateTime: number = 0; // 現ステートの経過秒
blendT: number = 0; // 遷移中の補間係数 0→1
}重要: 各パーツの geometry.translate() で回転ピボットをパーツの端に寄せること。 これにより「肩を中心に腕が回る」「股を中心に脚が振れる」動きが成立する。
// 腕ジオメトリの生成例(肩が原点になるよう平行移動)
const armGeo = new THREE.BoxGeometry(0.2, 0.6, 0.2);
armGeo.translate(0, -0.3, 0); // ピボットが肩になるプレイヤーアニメーション一覧
ステート概要
| ステート | 周期 / 長さ | ループ | 優先度 | 備考 |
|---|---|---|---|---|
idle | 2.0 s | ✅ | 1 | 立ち止まり中の微振動 |
walk | 0.6 s | ✅ | 2 | 通常移動 |
dash | 0.4 s | ✅ | 3 | ダッシュ中(ダッシュスキルとは別) |
attack1 | 0.18 s | ❌ | 5 | コンボ 1 段目 |
attack2 | 0.18 s | ❌ | 5 | コンボ 2 段目 |
attack3 | 0.22 s | ❌ | 5 | コンボ 3 段目 |
attack4 | 0.30 s | ❌ | 6 | コンボ 4 段目フィニッシュ |
skillCast | 0.40 s(タメ) + 0.25 s(解放) | ❌ | 7 | スキル詠唱 |
hitReact | 0.20 s | ❌ | 8 | 被ダメージ |
dodgeRoll | 0.40 s | ❌ | 9 | 回避(I-Frame 0.3s と同期) |
death | 1.50 s | ❌ | 10 | 死亡 |
優先度: 大きい数値が勝つ(death > hitReact > dodgeRoll > 攻撃 > 移動 > idle)。
Idle(待機)
コンセプト: 息をするような上下の揺れ+頭と腕の小さな揺動。停止中であることをプレイヤーに伝える。
| パラメータ | 値 |
|---|---|
| 周期 | 2.0 秒 |
| 上下幅(body) | ±0.03 units |
| 腕の前後揺れ | ±0.04 rad |
| 頭のわずかな首振り | ±0.02 rad(Y 軸、0.25x 周期) |
updateIdle(dt: number) {
this.stateTime += dt;
const t = this.stateTime;
const w = (Math.PI * 2) / 2.0; // 角速度: 1 周 2 秒
this.body.position.y = 0.0 + Math.sin(t * w) * 0.03;
this.head.position.y = 0.7 + Math.sin(t * w) * 0.03;
this.armL.rotation.x = Math.sin(t * w) * 0.04;
this.armR.rotation.x = -Math.sin(t * w) * 0.04;
this.head.rotation.y = Math.sin(t * w * 0.25) * 0.02;
// 脚は動かさない
this.legL.rotation.x = 0;
this.legR.rotation.x = 0;
}Walk(歩行)
コンセプト: 手足を sin カーブで逆位相に振る、標準的な手続き歩行。
| パラメータ | 値 |
|---|---|
| 周期 | 0.6 秒(約 1.67 Hz) |
| 腕の振り幅 | ±0.5 rad |
| 脚の振り幅 | ±0.4 rad |
| 体の上下バウンス | ±0.04 units(2x 周期) |
updateWalk(dt: number) {
this.stateTime += dt;
const w = (Math.PI * 2) / 0.6;
const s = Math.sin(this.stateTime * w);
this.armL.rotation.x = s * 0.5;
this.armR.rotation.x = -s * 0.5;
this.legL.rotation.x = -s * 0.4;
this.legR.rotation.x = s * 0.4;
// 体のバウンス(脚の接地に同期: 2 倍周波数で上下)
this.body.position.y = Math.abs(Math.sin(this.stateTime * w)) * 0.04;
this.head.position.y = 0.7 + Math.abs(Math.sin(this.stateTime * w)) * 0.04;
}Dash(ダッシュ移動)
コンセプト: Walk を速く、さらにキャラを進行方向に傾ける。
| パラメータ | 値 |
|---|---|
| 周期 | 0.4 秒 |
| 腕の振り幅 | ±0.7 rad |
| 脚の振り幅 | ±0.6 rad |
| 前傾角度 | 0.15 rad(本体 Object3D の rotation.x) |
updateDash(dt: number) {
this.stateTime += dt;
const w = (Math.PI * 2) / 0.4;
const s = Math.sin(this.stateTime * w);
this.armL.rotation.x = s * 0.7;
this.armR.rotation.x = -s * 0.7;
this.legL.rotation.x = -s * 0.6;
this.legR.rotation.x = s * 0.6;
// 前傾(親の Object3D を倒す)
this.rotation.x = 0.15;
}Attack Combo 1〜4(通常攻撃コンボ)
戦闘システム詳細: 戦闘システム の「通常攻撃」参照。
各段の総尺は短く、武器を持つ腕(利き腕 = armR) の回転で振りを表現する。 ヒットストップ(30〜80ms)は 外部タイムスケールで 0 倍 にするので、 アニメ側は単純に time を進めるだけでよい(global timescale をかければ自動で止まる)。
| 段 | 総尺 | 振りかぶり | インパクト | 戻し | 主パーツ | 方向 |
|---|---|---|---|---|---|---|
| 1 | 0.18 s | 0.05 s | 0.05 s | 0.08 s | armR | 右→左 横振り |
| 2 | 0.18 s | 0.05 s | 0.05 s | 0.08 s | armR | 左→右 横振り(1 の逆) |
| 3 | 0.22 s | 0.07 s | 0.05 s | 0.10 s | armR + body | 上→下 縦振り(体をひねる) |
| 4 | 0.30 s | 0.10 s | 0.08 s | 0.12 s | armR + armL + body | 両手フィニッシュ(回転斬り) |
フレーム換算表(60 fps)
| 段 | 総尺 | 振りかぶり | インパクト | 戻し |
|---|---|---|---|---|
| 1 | 11 F | 3 F | 3 F | 5 F |
| 2 | 11 F | 3 F | 3 F | 5 F |
| 3 | 13 F | 4 F | 3 F | 6 F |
| 4 | 18 F | 6 F | 5 F | 7 F |
// 共通: フェーズ内 0→1 の正規化時刻
function phasePct(elapsed: number, start: number, len: number): number {
return THREE.MathUtils.clamp((elapsed - start) / len, 0, 1);
}
updateAttack1(dt: number) {
this.stateTime += dt;
const t = this.stateTime;
// 0.00-0.05 振りかぶり: armR を +1.8rad へ
// 0.05-0.10 インパクト: armR を -1.2rad へ一気に
// 0.10-0.18 戻し: 0 rad へ
if (t < 0.05) {
this.armR.rotation.z = THREE.MathUtils.lerp(0, 1.8, phasePct(t, 0, 0.05));
} else if (t < 0.10) {
this.armR.rotation.z = THREE.MathUtils.lerp(1.8, -1.2, phasePct(t, 0.05, 0.05));
} else {
this.armR.rotation.z = THREE.MathUtils.lerp(-1.2, 0, phasePct(t, 0.10, 0.08));
}
// コンボ終了判定
if (t >= 0.18) this.transitionTo(this.combo.nextState);
}Skill Cast(スキル詠唱)
コンセプト: 両腕を掲げて一瞬ためる → リリース。スキル1/2/R 共通のひな型。 アルティメット(R)のみ 0.50 s の無敵演出に合わせて全体を 1.25x に引き伸ばす。
| フェーズ | 時間 | 動き |
|---|---|---|
| 構え | 0.00 - 0.10 s | 両腕を前下方へ下ろす (rotation.x = 0.3) |
| タメ | 0.10 - 0.40 s | 両腕を頭上へ (rotation.x = -2.4)、body わずかに後傾 |
| ポーズ | 0.40 - 0.50 s | 静止(武器発光ピーク、カメラのズーム演出ポイント) |
| 解放 | 0.50 - 0.65 s | 両腕を前方へ振り下ろし、エフェクト放出 |
updateSkillCast(dt: number) {
this.stateTime += dt;
const t = this.stateTime;
if (t < 0.10) {
const p = t / 0.10;
this.armL.rotation.x = THREE.MathUtils.lerp(0, 0.3, p);
this.armR.rotation.x = THREE.MathUtils.lerp(0, 0.3, p);
} else if (t < 0.40) {
const p = (t - 0.10) / 0.30;
this.armL.rotation.x = THREE.MathUtils.lerp(0.3, -2.4, p);
this.armR.rotation.x = THREE.MathUtils.lerp(0.3, -2.4, p);
this.rotation.x = THREE.MathUtils.lerp(0, -0.1, p); // 後傾
} else if (t < 0.50) {
// ポーズ: 何もしない(次フェーズまで保持)
} else if (t < 0.65) {
const p = (t - 0.50) / 0.15;
this.armL.rotation.x = THREE.MathUtils.lerp(-2.4, 1.2, p);
this.armR.rotation.x = THREE.MathUtils.lerp(-2.4, 1.2, p);
this.rotation.x = THREE.MathUtils.lerp(-0.1, 0, p);
} else {
this.transitionTo('idle');
}
}Hit React(被弾)
コンセプト: 一瞬だけ体を後ろにのけぞらせ、赤フラッシュ をマテリアルカラーで表現。 I-Frame 0.5s は別仕様(戦闘システム)、アニメは 0.2s のみ。
| パラメータ | 値 |
|---|---|
| 総尺 | 0.20 秒 |
| のけぞり角 | rotation.x = -0.25 rad |
| 赤フラッシュ | 全 Mesh の material.emissive を #ff0033 に、強度 1.0 → 0 で線形減衰 |
| 減衰カーブ | 0.00 s でピーク、0.12 s でゼロ |
updateHitReact(dt: number) {
this.stateTime += dt;
const t = this.stateTime;
// のけぞり(のけぞり→戻し)
if (t < 0.08) {
this.rotation.x = THREE.MathUtils.lerp(0, -0.25, t / 0.08);
} else {
this.rotation.x = THREE.MathUtils.lerp(-0.25, 0, (t - 0.08) / 0.12);
}
// 赤フラッシュ
const flash = Math.max(0, 1 - t / 0.12);
this.forEachMesh(m => m.material.emissive.setHex(0xff0033).multiplyScalar(flash));
if (t >= 0.20) this.transitionTo('idle');
}Dodge Roll(回避)
コンセプト: 進行方向に一回転。I-Frame と完全に同期させる(戦闘システム)。
| パラメータ | 値 |
|---|---|
| 総尺 | 0.40 秒(ダッシュ本体 0.2 + 後隙 0.1 + 立て直し 0.1) |
| 回転 | 本体 Object3D の rotation.x を 0 → −2π へ線形 |
| I-Frame | 0.0 - 0.3 s(無敵) |
| 移動距離 | 5.0 units(0.2 s で移動、後半は惰性) |
| 残像エフェクト | 毎 0.03 s に body/head のスナップショットを opacity 0.4 → 0 でフェード |
updateDodgeRoll(dt: number) {
this.stateTime += dt;
const t = this.stateTime;
const p = Math.min(t / 0.40, 1);
this.rotation.x = -Math.PI * 2 * p; // 1 回転
// 残像トレイル(0.03 s 刻み)
if (Math.floor(t / 0.03) !== Math.floor((t - dt) / 0.03)) {
this.spawnAfterImage();
}
if (t >= 0.40) {
this.rotation.x = 0;
this.transitionTo('idle');
}
}Death(死亡)
コンセプト: 前のめりに倒れ、地面に接地後フェードアウト。
| パラメータ | 値 |
|---|---|
| 総尺 | 1.50 秒 |
| 倒れ込み | 0.00 - 0.50 s(rotation.x 0 → π/2) |
| 接地後ブレ | 0.50 - 0.70 s(頭だけ ±0.05 rad で 2 回バウンド) |
| フェードアウト | 0.70 - 1.50 s(material.opacity 1 → 0) |
敵アニメーション
敵の基本ポリゴン構成は head / body / legs(まとめて1メッシュ可) / 武器か突起 程度で軽量化する。 スウォームは胴体だけの 1 パーツ構成でも良い。
スウォーム型(グリッチビット)
| パラメータ | 値 |
|---|---|
| 移動 | 浮遊スウェイ: position.y = baseY + sin(t * 8) * 0.05 |
| 回転 | 常時 rotation.y += 4.0 * dt(高速スピン) |
| 攻撃 | 接触ダメージのみ。独立アニメなし |
| 死亡 | 0.15 s でスケール 1→0、グリッチ風破片 4 個飛散 |
意図: 大量に画面を占拠するノイズ感を演出。
updateSwarmIdle(dt: number) {
this.body.position.y = this.baseY + Math.sin(this.time * 8) * 0.05;
this.body.rotation.y += 4.0 * dt;
}メレー型(データドローン)
| ステート | 尺 | 内容 |
|---|---|---|
idle | 1.2 s ループ | 軽いホバー(y ±0.08) |
chase | walk 0.5 s ループ | 脚を左右に振る |
attack | 0.35 s | 片腕を上 → 振り下ろし(0.1 s タメ + 0.10 s 打撃 + 0.15 s 戻し) |
stagger | 0.25 s | 後ろにのけぞり+赤フラッシュ |
death | 0.50 s | 前のめり倒れ + フェード |
レンジ型(ネオンスナイパー)
| ステート | 尺 | 内容 |
|---|---|---|
idle / chase | 0.5 s ループ | 歩行モーション共通 |
aim | 0.3 s | 武器を構える(armR.rotation.x = −π/2 へ 0.3 s で遷移)、頭がプレイヤーを追尾 |
fire | 0.15 s | 武器が反動で 0.08 s 後傾 → 0.07 s で戻る |
kite(後退) | 歩行の逆再生 | time を負の速度で進めるだけで後退歩行になる |
チャージ型(リフトチャージャー)
| ステート | 尺 | 内容 |
|---|---|---|
telegraph(予兆) | 1.5 s | その場で足踏み(脚 rotation.x を ±0.3 rad, 5 Hz)、体を前傾 0.2 rad、赤いオーラ膨張 |
charge(突進) | 0.8 s | 前傾 0.35 rad 固定、残像は出さず フレームステップ 10 fps に量子化 して PS1 感を強調 |
recoil(衝突後) | 0.4 s | 後ずさり、ヨロヨロ(rotation.z ±0.15 rad で揺動) |
ボスアニメーション(マルチフェーズ)
ボスは HP 閾値でフェーズが切り替わり、モーションセットが変わる。
フェーズ構成の例
| フェーズ | HP | モーションセット | テンポ |
|---|---|---|---|
| P1 | 100〜70% | 標準近接 3 種 + 遠隔 1 種 | 通常 |
| P2 | 70〜35% | 範囲攻撃追加、移動速度 1.15x | 速い |
| P3 | 35〜0% | 全攻撃の隙短縮、怒り状態 | さらに速い |
フェーズ遷移アニメ(Phase Transition)
フェーズが切り替わる瞬間の 0.8 秒の専用モーション。 プレイヤーにフェーズ変化を明確に伝え、戦闘テンポを区切る。
| 時間 | 内容 |
|---|---|
| 0.00 - 0.20 s | 両腕を真横に広げ、rotation.z = ±π/2。体を軽く沈める |
| 0.20 - 0.50 s | 全身赤フラッシュ(emissive)、周囲に衝撃波リング放出 |
| 0.50 - 0.80 s | 構え直し、新フェーズ idle へ遷移 |
被弾判定の仕様(gameplay/enemies.md:235 準拠)
この演出中は ボス側に 0.5 秒の硬直 + DEF=0 の被弾弱点状態 が発生する(docs/gameplay/enemies.md:235, 267, 286 参照)。 プレイヤーは無敵にはならず、他の敵・環境ダメージは通常通り被弾する。演出 0.00〜0.50 s 区間(硬直)がプレイヤーの反撃ウィンドウとなる。
ボス攻撃パターン例(近接)
| 攻撃 | 予兆 | 発生 | 戻し | 総尺 | 備考 |
|---|---|---|---|---|---|
| 横薙ぎ | 0.4 s | 0.15 s | 0.4 s | 0.95 s | 左から右 |
| 叩きつけ | 0.6 s | 0.10 s | 0.5 s | 1.20 s | 着弾点に AoE |
| 突進 | 0.8 s | 0.5 s | 0.6 s | 1.90 s | 直線、壁で停止 |
| 咆哮(P2+) | 0.3 s | 0.6 s | 0.3 s | 1.20 s | 全方位ノックバック |
予兆の重要性
予兆フェーズはボスの emissive を攻撃方向に沿って段階的に強める(0 → 1)。 プレイヤーはこの色変化を見て回避する。手続き的演出なのでテクスチャ追加ゼロ。
環境アニメーション
環境のアニメはパーティクルと Instanced シェーダで処理する。 1 オブジェクトごとに update() を回さず、共有 uniform uTime で全体を一括駆動する。
ネオン看板の明滅(Flicker)
// フラグメントシェーダで明滅。ジッター強めに
float flicker(float t, float seed) {
float base = 0.85;
float n = fract(sin(dot(vec2(seed, floor(t * 12.0)), vec2(12.9898, 78.233))) * 43758.5453);
return base + step(0.9, n) * 0.3 - step(0.97, n) * 0.5; // 稀に消える
}| パラメータ | 値 |
|---|---|
| 平均明度 | 0.85 |
| 点滅周波数 | 12 Hz(カクつかせる) |
| ドロップアウト確率 | 3%(一瞬真っ暗) |
データストリーム流(Data Stream Flow)
床・壁に走るシアン色のライン。
| パラメータ | 値 |
|---|---|
| 流速 | 2.0 units/秒 |
| シェーダ | uv.x + uTime * 2.0 を sin に通してグロー |
| 方向 | ダンジョンの廊下方向(InstancedMesh の instance 属性で方向指定) |
ポータル回転
| パラメータ | 値 |
|---|---|
| 回転軸 | Y |
| 角速度 | 1.2 rad/秒(リング内側)、−0.8 rad/秒(リング外側、逆回転) |
| スケール脈動 | 1 + sin(t * 2) * 0.05 |
| 呼び込み演出 | プレイヤー接近時に 0.3 s で 1.3x スケール拡大、半透明波紋放出 |
背景パーティクルドリフト
| パラメータ | 値 |
|---|---|
| 粒子数 | デスクトップ 400 / モバイル 150 |
| 速度 | 0.3 units/秒(上方向) |
| 寿命 | 4〜8 秒(個体ごとにランダム) |
| 更新 | GPU 側で position.y += uTime * 0.3 相当を計算(Points + カスタムシェーダ) |
実装アプローチ(Three.js)
階層とピボット
PlayerCharacter (Object3D) ← ここが世界座標で動く
├─ head (Mesh)
├─ body (Mesh)
├─ armL (Mesh, ピボット=肩)
├─ armR (Mesh, ピボット=肩)
│ └─ weapon (Object3D) ← 腕の子。腕が回ると武器も一緒に回る
├─ legL (Mesh, ピボット=股)
└─ legR (Mesh, ピボット=股)更新ループの雛形
class CharacterRig extends THREE.Object3D {
update(dt: number) {
this.stateTime += dt;
// 1. ベースステートを更新
switch (this.state) {
case 'idle': this.updateIdle(dt); break;
case 'walk': this.updateWalk(dt); break;
case 'dash': this.updateDash(dt); break;
case 'attack1': this.updateAttack1(dt); break;
// ...
}
// 2. ブレンド中なら前ステートと補間
if (this.blending) this.applyBlend(dt);
// 3. PS1 風フレームステップ(オプション)
if (this.ps1Mode) this.snapToLowFps(10);
}
transitionTo(next: AnimState, blendTime = 0.08) {
this.prevSnapshot = this.snapshotPose();
this.state = next;
this.stateTime = 0;
this.blending = true;
this.blendT = 0;
this.blendDuration = blendTime;
}
}ブレンド(遷移補間)
ほとんどの遷移は 0.08 秒の線形補間 で十分。攻撃→被弾のような急な割り込みは 即時切り替え(blendTime = 0) で PS1 的なスパンッとした切れ味を出す。
applyBlend(dt: number) {
this.blendT += dt / this.blendDuration;
const k = Math.min(this.blendT, 1);
// 現ステートが計算した「目標ポーズ」と prevSnapshot を k で補間
for (const part of this.parts) {
part.rotation.x = THREE.MathUtils.lerp(this.prevSnapshot[part.name].rx, part.rotation.x, k);
// ... 他軸・位置も同様
}
if (k >= 1) this.blending = false;
}| 遷移元 → 遷移先 | ブレンド時間 | 備考 |
|---|---|---|
| idle ↔ walk | 0.08 s | スムーズ |
| walk → dash | 0.05 s | 俊敏感 |
| * → attack1 | 0.00 s | スパンッと切り替え(PS1 感) |
| attackN → attackN+1 | 0.00 s | コンボは即接続 |
| * → hitReact | 0.00 s | 割り込み |
| * → dodgeRoll | 0.00 s | 割り込み |
| * → death | 0.10 s | 少し余韻 |
PS1 風フレームステップ(Frame-Step)
// 一部パーツだけ 10 fps に量子化して「カクカク感」を出す
snapToLowFps(fps: number) {
const step = 1 / fps; // 0.1 s
const snapped = Math.floor(this.stateTime / step) * step;
// snapped を使って改めてポーズ計算(各パーツの手足のみ)
const s = Math.sin(snapped * ((Math.PI * 2) / 0.6));
this.armL.rotation.x = s * 0.5;
this.armR.rotation.x = -s * 0.5;
this.legL.rotation.x = -s * 0.4;
this.legR.rotation.x = s * 0.4;
// 胴体・頭は 60 fps のまま(ハイブリッド)
}対象: armL / armR / legL / legR / weapon 除外: body / head(スムーズに動いた方が酔わない)
モバイル最適化
画面外敵の更新ステップダウン
カメラ外は手抜きして良い
トップダウン斜めカメラで 画面外 にいる敵は、位置追従(AI)だけ動かし、 アニメーション(手足の sin 計算)はサボる。
| 条件 | 更新頻度 |
|---|---|
| 画面内 + カメラから 15 units 以内 | 毎フレーム(60 fps) |
| 画面内 + カメラから 15 units 超 | 2 フレームに 1 回(30 fps) |
| 画面外(frustum culling) | 6 フレームに 1 回(10 fps)、姿勢は固定可 |
// シーンマネージャーの更新ループ内
enemies.forEach(e => {
e.ai.update(dt); // AI は必ず動かす
if (!frustum.containsPoint(e.position)) {
e.animationTick = (e.animationTick + 1) % 6;
if (e.animationTick !== 0) return; // スキップ
e.rig.update(dt * 6); // まとめて 6 倍 dt で進める
} else {
e.rig.update(dt);
}
});その他のモバイル配慮
| 項目 | デスクトップ | モバイル |
|---|---|---|
| PS1 フレームステップ | オフ(好みで) | デフォルト ON(軽量化にも寄与) |
| ブレンド | 有効 | ON のままで OK(sin 計算の方が重い) |
| 残像エフェクト生成頻度 | 0.03 s | 0.06 s |
| パーティクル数 | 400 | 150 |
| 敵リグの手足パーツ数 | フル | スウォームは 1 メッシュに統合 |
アニメーション優先度と割り込みルール
キャンセルルール:
- 回避 (
dodgeRoll) は 攻撃中でも発動可能(プレイヤー救済) - 被弾 (
hitReact) は 攻撃中・スキル中は発生しない(I-Frame とは別のスーパーアーマー扱い)- 例外: ボス攻撃はスーパーアーマーを貫通
- 死亡 (
death) は 全てに優先
作業スコープのまとめ
| 項目 | v1 | v2 以降 |
|---|---|---|
| プレイヤー基本 9 モーション | ✅ | — |
| コンボ 1〜4 | ✅ | — |
| 敵 4 タイプ基本モーション | ✅ | — |
| ボス基本パターン + フェーズ遷移 | ✅ | — |
| 環境手続きアニメ | ✅ | — |
| PS1 フレームステップ | ✅(オプション) | — |
| モバイル最適化 | ✅ | — |
| 残像エフェクト | ✅(簡易) | 高度な残像(メッシュ補間) |
| リアクション細分化(ダウン、受け身など) | ❌ | ✅ |
参照
- Art-1: スタイルガイド — PS1 風の視覚方針
- Art-3: キャラクターデザイン — パーツ構成
- 戦闘システム — コンボ・I-Frame 仕様
- 敵・ボス — 敵 AI ステート
- カットイン演出 — ポータル入場時の別ルート演出