任意のチャプターのみロードし、最小限のリソースだけダウンロード


概要

従来のチャプター分割の基本的な方法と、その問題点

宴では、ダウンロードコンテンツとして後からシナリオを追加可能にするために、チャプター単位でシナリオを分割する機能があります。
章ごとにシナリオを分ける
章ごとにダウンロードする場合

デフォルトでは、チャプター0から追加していく前提の形になっているため、「任意のチャプターだけダウンロードして再生する」といったことがしづらい状態でした。
また、ダウンロードする際も必要なものだけダウンロードするといことがしづらい状況でした。
宴3.5.8では機能を拡張しそういったケースにも対応できるようにしました。以下にその手法をまとめます。

サンプル

サンプルは「Utage/SampleOthers/SampleChapter0AndOther」以下にあります。

チャプター0番に共通設定・共通リソースを設定し、その他のチャプターを個別でダウンロードできる構成にする

シナリオをチャプターごとに分ける

章ごとにシナリオを分けるのやり方に従って、シナリオをチャプターごとに分けます。
各チャプターはフォルダで分けると管理しやすくなると思います。その際にチャプターの名前や各エクセルファイルの名前を重複させないようにしてください。
また、各チャプターフォルダ以下のエクセルファイルは複数置くことも可能です。

チャプター0で共通リソースや共通設定をする

チャプター0ではシナリオを記述せず、プロジェクト全体で共通となるリソースやマクロ、パラメーターなどの共通設定のみをします。
特にパラメーターに関しては、チャプター0だけに置いたほうが良いです。
チャプターの追加ダウンロードによってパラメータの定義が変動してしまうと、セーブデータの扱いが難しくなるためです。

チャプター1~でシナリオを記述する

チャプター1~の、各チャプターで各シナリオを記述してください。

基本的には、「リソース定義はチャプター0にだけまとめてしまう」というほうがシンプルで楽です。
単一のチャプターにだけしか使わないリソースであっても、チャプター0に置いて問題ありません。チャプター0フォルダ以下には複数のエクセルファイルを置けますので、作業分担も可能です。
製作の都合上で各チャプターにリソース定義を記述したい場合、「そのチャプターでのみ使う追加リソース」があれば、必要に応じてTextureシートやCharacterシートなどを追加して設定します。
複数のチャプターで同じリソース定義が重複しているとエラーになりますので、複数チャプターで共通となるリソースはすべてチャプター0にだけ設定してください。
作業分担の関係上などでリソース定義を各チャプターで分散して重複させる必要がある場合、各リソースのラベル名にチャプター番号を付けるなどの命名規則をしてラベル名だけは変えるようにしてください。

サーバーからダウンロードするための設定

基本的な部分はサーバーからダウンロードと同じです。URLの扱いなど細かい意味はそちらを読んでください。

アセットバンドルの作成

アセットバンドル作成時にリソースコンバーターの設定で「Separate Chapter」にチェックを入れてください。

シーンの設定

AdvEngineStarterの設定

通常のサーバーからのダウンロード設定と同じように「StargeType」を「Server」、「Scenarios」を「None」、「ServerURL」にアセットバンドルを置くURLを設定します。
さらに「Use Chapter」の「チェックをOn」にして、「Chapter Names」は”チャプター0のチャプター名”を設定します。

これで、AdvEngineStarterのロードの呼び出しと同時に、チャプター0がロードされて初期化されます。

AdvEngineの設定

AdvEnigne>DataManagerの「Is Background Donwload」のチェックをオフにします。
これをオフにすることで、チャプター0で定義したリソースを自動的にダウンロードすることを防ぎます。

各チャプターのダウンロードとロードのサンプル

各チャプターをダウンロードするためのサンプルコードは Utage\SampleOthers\SampleChapter0AndOther\SampleChapter0.cs にあります、
このページの最下段にも記述しておきます。
ポイントとなるのは、 this.Engine.DataManager.DownloadAllFileUsed(); を使うことで、
現在ロード済みのチャプターのシナリオ内で使われているリソースのみダウンロードしている点です。
チャプター0にリソース定義があっても、ロード済みのチャプター内のシナリオ内で使われいない場合は、そのリソースはダウンロードされません。

ファイルサイズの表示

サンプルコードには、ファイルサイズをダウンロード前に表示するためのコードも載せてあります。
ただし、これはデフォルトの状態では機能しません。
宴が使っているUnity公式のアセットバンドルのマニフェストファイルには、ファイルサイズの情報がないためです。
独自に作成したアセットバンドルリストを登録する を参考に、自作のファイルリストをもとにアセットバンドルのサイズをあらかじめ設定してください。

また、ファイルサイズが取得できるのはあくまで「チャプターのシナリオをロードした後」です。
シナリオのファイルサイズそのものを表示したり、シナリオをロードする前に必要となるリソースのファイルサイズを取得することはできません。
シナリオのみであれば、比較的軽いサイズであるため、起動時などにダウンロードを終わらせておくのが良いかと思います。

注意点

この手法を使う場合は、いくつか注意する点があります。

エンティティで使用するリソースが変わる場合は対応しきれない

この手法はシナリオを解析して必要となるファイルのみダウンロードしているため、シナリオの実行中に必要となるファイルが変化するケースでは正常に動作しません。
つまり、エンティティ機能を使って、表示キャラクターなどを動的に変化させている場合です。
シナリオ再生前に未ダウンロードであってもシナリオ実行中に自動的にダウンロードをすることはするのですが、「事前にリソースをダウンロード」ということはできません。

CGギャラリーやシーン回想などのサムネイル用の画像が扱えなくなる

この手法はシナリオで直接扱うリソース以外はダウンロードしません。
そのため、シーン回想などのサムネイル画像はダウンロードできなくなります。
チャプター分割は、主にノベルではなく会話シーンとして宴を使う場合での需要が多かったため、不要と思われる機能まではサポートできていません。

各チャプターのダウンロードのサンプルコード

以下、チャプターのダウンロードするためのサンプルコードです。
Utage\SampleOthers\SampleChapter0AndOther\SampleChapter0.cs にあります。

// UTAGE: Unity Text Adventure Game Engine (c) Ryohei Tokimura
using UnityEngine;
using Utage;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// チャプター0で全共通設定(共通リソースやマクロや、パラメーター設定)を定義し、
/// チャプター1~は個別にロードする場合のサンプル
/// </summary>
[AddComponentMenu("Utage/ADV/Examples/Chapter0")]
public class SampleChapter0 : MonoBehaviour
{

	/// <summary>ADVエンジン</summary>
	public AdvEngine Engine { get { return this.engine ?? (this.engine = FindObjectOfType<AdvEngine>()); } }
	[SerializeField]
	protected AdvEngine engine;

	public UtageUguiTitle title;
	public UtageUguiLoadWait loadWait;
	public UtageUguiMainGame mainGame;
	public List<string> chapterUrlList = new List<string>();
	public List<string> startLabel = new List<string>();

	void Start()
	{
	}


	public void OnClickDownLoadChpater(int chapterIndex)
	{
		StartCoroutine(LoadChaptersAsync(chapterIndex));
	}

	IEnumerator LoadChaptersAsync(int chapterIndex)
	{
		string url = chapterUrlList[chapterIndex];
		//もう設定済みならロードしない
		if (!this.Engine.ExitsChapter(url))
		{
			yield return this.Engine.LoadChapterAsync(url);
		}
		//設定データを反映
		this.Engine.GraphicManager.Remake(this.Engine.DataManager.SettingDataManager.LayerSetting);


		//DL前にファイルサイズを取得する場合・・・
		int sumSize = 0;	//合計ファイルサイズ
		//ファイルのセット
		var fileSet = this.Engine.DataManager.GetAllFileSet();
		foreach (var file in fileSet)
		{
			AssetFileBase assetFile = file as AssetFileBase;
			if (assetFile==null)
			{
				Debug.LogError("Not Support Type");
				continue;
			}
			else
			{
				//DL済みかチェック
				if (!assetFile.CheckCacheOrLocal())
				{
					//未DLのファイルのサイズを加算
					sumSize += assetFile.FileInfo.AssetBundleInfo.Size;

					//!注
					//AssetBundleInfo.Sizeは、デフォルトでは0.
					//宴が使っている、Unity公式のアセットバンドルマニフェストにはファイルサイズの情報がないため、
					//SampleCustomAssetBundleLoad.csを参考に
					//事前に独自で、アセットバンドルのサイズを設定しておく必要がある。
				}
			}
		}
		Debug.Log("DownLoad Size = 0");

		//シナリオ内で使用するファイルのみロード
		//Textureシートなどで定義だけされているものはDLしない
		this.Engine.DataManager.DownloadAllFileUsed();

		//ロード待ちのための画面遷移
		title.Close();
		loadWait.OpenOnChapter();
		loadWait.onClose.RemoveAllListeners();
		loadWait.onClose.AddListener(
			() =>
			{
				mainGame.Open();

				this.Engine.JumpScenario(startLabel[chapterIndex]);
			}
			);
	}
}