本編に入らずに、本編と同様の動作・システムを行えるテストプレイシーンを追加しました。
本編でも設定はいじれますが、関係ないところでゆっくり自分好みに設定してもらうためです。
背景などは、本編の使いまわしにしようと軽く考えていました。
しかし、この際だから(次作でも必要になるかもしれない、と)別途に用意してみました。
完成が遠のくばかりになってしまうけれども、ちょっとしたおまけみたいになったので良しとします。
背景3D
新聞部の部室
久しぶりのBlockbenchです。
新聞部の「部室」で、本編の「調理室」の半分の面積にしました。

さり気なく、過去作の部室の3D化となっています。


雑多な感じで、ごちゃついてるのが好みだったので、床にも資料などをばらまきました。
実装画面を見てみると、カメラが床を映すシーンがまったくありませんでした(´・ω・`)
いずれはカメラ(視線)操作の実装も良いかもしれませんね。

ドットポスター

Aiに生成してもらった架空ポスターを縮めて、
自作で使っているカラーパレットの色・色数に変換しました。
色数が違う画像だと違和感が出てしまって馴染まないなと、常々思っていたので、変換はとても便利でした。
想定していたほど、面倒でも難しくもなかったので、お薦めです。
やり方は、画像色の変換法・指定カラーパレット で詳しく解説しています。
Aiは、架空ポスターも適当に作ってくれるので、ありがたし。

Unity内実装

やはり光と影が加わると、映えますね。
調理室より、立体物や影は多いかもしれません…
ポスターやホワイトボード、散らばった紙面などは、直接書き込んでいるので、一枚のSpriteとなっています。
調理室の方は、地味に、すべて分けてしまっています。
調整を考えてのことだと思いますが、いっしょにしてしまって良い気がしますね。
(書いてて、今回のポスター位置の調整などに時間を取られたことを思い出す…ケースバイケース、かな…)
3Dモデル配置時に、床や机上に吸着させたい場合、
「Vキー」を押しながら、モデルの角をホバーさせ、狙いの角でクリック&ドラッグで吸着できます。
ノベルパート
テストルーム版
Cut進行のノベルパートです。
本編同様の動作・設定変更可能になっています。

Cut進行のループ処理

テストプレイシーンは、任意で終わってもらうために、ループさせる必要があります。
この「ループ処理」は本編にはない挙動なので、新たに構築しなければなりませんでした。
・Cut進行をちゃんと再構成しないといけない
・ロード暗幕との連携
この2つの問題を解決します。
■ 実装概要
ノベルパートのCut006(選択肢等)からCut001へ戻るループ処理を、
「フェードアウトを挟んだシーンリロード」と「遷移先Cut(Cut001)での暗幕解除」の責任分離により構築。
状態リセットの担保:
シーンリロードを行うことで、メモリ上の単発変数および SAV_ 変数(フラグ)を自動的に初期化し、カメラやオブジェクトの状態が残留する問題を排除。
ロード画面(暗幕)の安全制御:
ループ処理側(Sys_SceneLoopManager)で暗幕の全自動解除を行わず、遷移先の Cut_001 がアクティブ化したタイミングで Cmd_HideFadeCanvas が直接暗幕を非表示化するため、DOTweenの競合やフェードの消え残りバグが原理的に発生しない。
■ 新規・改修スクリプト一覧
1. Sys_CutManager.cs(改修)
// 通常開始時に指定Cutから始めるための静的変数を追加
public static int TargetStartCutIndex = 0;
void Start()
{
if (!WillLoadFromSave)
{
// 指定カットから開始(通常は0、ループ時はTargetStartCutIndex)
StartCut(TargetStartCutIndex);
TargetStartCutIndex = 0; // 実行後初期化
}
WillLoadFromSave = false;
}
2. Sys_SceneLoopManager.cs(新規作成)
// 暗幕フェードアウトとシーン再読み込みのみを担当
public class Sys_SceneLoopManager : MonoBehaviour
3. Cmd_HideFadeCanvas.cs(新規作成)
ループ後の暗幕の削除に必要。
「ループ時の暗幕消去」と「ロード時の遡り再生」を両立させるため、
Cmd_HideFadeCanvas.cs に 「ロード中なら何もしない」 という1行ガードを追加。
// ★【重要】セーブ&ロードの遡り処理中なら、フェードインを発火させずにスキップ
if (Sys_SaveManager.IsLoading) return;
■ Hierarchy・運用ルール
1. SceneLoop_Manager の配置
シーン内 System_set の直下に GameObject を作成し、Sys_SceneLoopManager.cs をアタッチ。
2. 選択肢ボタンの割り当て
ループ用UIボタンの OnClick イベントに Sys_SceneLoopManager.JumpToCutWithReload(1) をアサイン。
(Cut001へ戻す場合は引数 1)

3. Cmd_HideFadeCanvas.cs のアタッチ
Hierarchyの Cut_001 オブジェクト(または配下の演出オブジェクト)にアタッチ。
(引数 2 の場合は、Cut_002 内のオブジェクトになる)
探索パート
テストルーム版
どうせなら部室を回ってもらおうと「探索パート」も追加してみました。
特にゲーム性はありませんが、動作確認(チュートリアル)用ということで。
※メモチェックはあるので、とにかく調べましょう。

改善★ VCamの切り替え
シネマシーンカメラを一時固定する機能を使った場合、デフォルトカメラに戻るときに間が空く挙動がありました。
その改善を行いました。

本編の探索パートでも起きているであろう問題なので、気がついてよかったです。
詳しくは、改善★0911 Cinemachineカメラ切り替え挙動の改善
スプライトの発光
モニターやチカチカライトを発光させたいなぁと、前から思っていたので、この機にやってみました。

ポストエフェクトの「Bloom」を使えば良さそうなのですが、ピンポイントでスプライトに適用する方法が分からなかったので、ただの「ぼかし画像」での力業をしました。
軽めになるので、これもまた良しとしておきます。

普通のアニメスクリプトで「モニター走査線」「緑ランプ」をアニメーションさせ、
その裏に「ぼかした画像」を配置し、アルファ値アニメスクリプトで点滅させています。
2つの別々のアニメスクリプトを使っているので、同期できるかが問題です。
今回のために、アニメスクリプト(Cmd_Normal_Anime.cs)を適応修正させました。
■ 今回の改修・新規作成の概要
独立したスプライト切替アニメーションと透明度アニメーションを「軽量・堅牢・絶対ズレない」 仕様で構築。
Cmd_Normal_Anime.cs(既存修正):
コルーチン(StartCoroutine / WaitForSeconds)処理を廃止し、Update() ベースの絶対時間計算処理へ刷新。
Cmd_Alpha_Anime.cs(新規作成):
Cmd_Normal_Anime と完全に同一のタイマー・再生構造を持った、透明度(Alpha)切り替え用スクリプト。
■ 実装の技術的要点(改善点)
① GC Alloc(ガベージ)の完全ゼロ化
旧仕様の課題:
コルーチン内で yield return new WaitForSeconds(…) を毎回生成していたため、
コマ切り替えのたびに GC アロケーションが発生していました。
新仕様:
Update() 内で Time.deltaTime を積算(elapsedTime)して処理する方式に変更。
ヒープ割り当てがゼロになり、ロースペック PC でのスパイク(プチフリーズ)を物理的に防ぎます。
② 絶対時間計算による「ズレ」の完全防止
算術計算:
int calculatedFrame = Mathf.FloorToInt(elapsedTime / duration);
同期の仕組み:
両スクリプトが同一の frameDuration(コマ間隔秒数)で同時に有効化(OnEnable / Play)された場合、
経過時間から直接コマ番号を算出します。
堅牢性:
ロースペック環境でフレームレートが低下(FPSドロップ)しても、復帰時に数学的に同じコマ番号を指すため、
長時間再生してもスプライトとアルファがズレることがありません。
■ 運用ルール・設定時の注意点
スクリプトを個別に動かすか、両方を組み合わせて動かすかに応じて以下のルールを遵守。
【ルール1】スプライトと透明度を同期させたい場合
- frameDuration(コマ間隔)を同じ値に設定する
例: スプライト側が 0.1 なら、アルファ側も 0.1 に合わせます。 - 両方のスクリプトを同時に開始させる
基本的に同じ GameObject にアタッチし、playOnAwake = true のまま有効化(OnEnable)すれば自動的に完全同期します。 - 外部から操作(Play / Pause / Stop 等)する場合は両方に呼び出す
スクリプト間は相互に非依存(疎結合)です。
C# スクリプト等から一時停止させる場合は、両方のコンポーネントに対して Pause() を実行してください。
【ルール2】コマ数(リスト要素数)が異なる場合の挙動
- スプライト数 ≠ アルファ指定数 でも動作可能
- インデックス計算に剰余(% totalFrames)を用いているため、
例えば「スプライト4枚(Loop)」に対して「アルファ2値 [1.0, 0.5](Loop)」を設定した場合でも、
エラーを出さず周期的にループします。
【ルール3】SpriteRenderer.color の上書き注意
- 他のダメージ演出スクリプトや Tween ライブラリ等で spriteRenderer.color(特に RGB 値や Alpha 値)を直接書き換えると、Cmd_Alpha_Anime の適用値と衝突する可能性があります。
- 透明度制御は原則 Cmd_Alpha_Anime に集約する。

■ コンポーネント仕様一覧(共通)
どちらのスクリプトも外部インターフェース(API)および Inspector の使い勝手を統一しています。
Inspector 設定項目
- frameDuration : 1コマあたりの表示時間(秒)
- loop
:ループ再生するか(false の場合は最終コマで停止) - playOnAwake : 有効化(OnEnable)時に自動再生するか
公開 API(他スクリプトからの操作)
| メソッド / プロパティ | 機能 |
|---|---|
| Play() | 最初(0コマ目)からアニメーションを再生 |
| Pause() | 現在のコマで一時停止 |
| Resume() | 一時停止を解除して再開 |
| TogglePause() | ポーズ状態を反転(ポーズ中なら再開、再生中ならポーズ) |
| Stop() | 停止して再生時間を初期化 |
| IsPlaying | 再生中かどうかを取得(bool) |
| IsPaused | ポーズ中かどうかを取得(bool) |
スプライトのビルボード化
カメラの移動・回転に合わせて2Dスプライトの向きを追従(ビルボード化)させてみようと思いました。
3Dモデルに2Dスプライトを合わせていると、横から見た場合にペラペラになってしまい、見栄えが悪くなります。
3つのカップに適用し、常にカメラ目線になってもらいました。
カメラ追従する様が、なかなか楽しい(*´ω`*)
スクリプト自体も、1枚で出来たので、難しいということはなさそうです。
しかし、
自作の場合、移動でのカメラ回転はあるにせよ、その後は正面固定なので、あまり恩恵を受けられないことが発覚。
動画のように、常にぐりぐりとカメラを回す操作の場合は、あると良さそうです。
もしくは、正面ではなく、45度視点になる場合とかにも効きそうです。
カメラワークとの兼ね合いで、必要になったときに実装ということにしておきましょう。
今回の実装は、
カメラの向きに追従(ビルボード化)2Dスプライト【URP】 で詳しく解説してます。
実機での挙動
本編の後に制作しているだけあって、作り込み的に本編より良くなってしまった気がしないでもないです。
発光やビルボードなど、やってみたいことも試せたので、楽しかったです。
ほとんどの人が試運転せずに本編にいくと思うと、コスト的に本当に必要だったかな、と思ってしまいますが、
むしろ、このパートだけで【体験版】にするということも…笑
いつも通り、ちょっと遠回りをしてしまいましたが、
あとの目立った実装は、「タイトル画面」「エクストラモード」の二つになりました。
コントローラー対応などのシステム面はまだまだ残っていますが、頂上はチラチラ見えてきています。

コメント