概要
EndlessBookを使って、無限にめくり続けられる本を作る。
ページは表示中の左・右ページと非表示中の左・右ページとして用意する。
環境
Unity 2021.3.1f1
EndlessBook 1.9.1
デモシーンの用意
MainCameraとLightのほかに、最小限コントローラー(Demo02.sceneではDemo)と、Book、Page Viewsという名のゲームオブジェクトが必要。各基本構成はDemo02.scene参照。
とりあえずDemo02.sceneを開いて改造する方法について要点を示す。
Page Viewsの作成
4つのPage Viewを作る。PageView_05を複製して作成。
PageView_00、PageView_01, PageView_02, PageView_03という名前とする。(Demo02.csからオブジェクト名の名前検索あり)。
PageView_00, 01, 02, 03は離して配置する。
各PageViewは内部カメラで移る範囲に何らかのゲームオブジェクトを使って判別可とする。また各Camera下に適切なPagebackgroundL/Rオブジェクトを置き、Cameraが映す範囲に合わせて調整する。
各PageViewに対応したRenderer Textureを生成し、また新規Materialを4つ用意し、各マテリアルのInspector->Materials->Albetoに対応させるRender Textureをドラッグアンドドロップして設定しておく。
できたPageViewはそれぞれDemo(Controller)のPageViewsのElementとして登録しておく。
Book オブジェクトの設定
BookオブジェクトはPrefabで提供されている。Assets\EndlessBook\Book\Prefabs\Book.prefab
ヒエラルキーにD&Dし、右クリックからPrefab->Unpackにより展開する。(Unpackが必要ない場合もある?)
Prefabの最上位オブジェクト「Book」のコンポーネントにExtendEndlessBookコンポーネント(下記)をAdd Componentする。
Assets\EndlessBook\Book\Editor\BookInspectorを開き、
OnInspectorGUI()をすべてコメントアウトする。これにより、Book.prefabに標準設定されたEndlessBookコンポーネントの設定値が表示されるようになる。
ExtendEndlessBookコンポーネントのすべての初期値をEndlessBookコンポーネントと合わせる。
Page Viewの作成で作ったMaterialをInspector上でremainder material listにドラッグアンドドロップして設定する。
各スクリプト
Demo02.cs
宣言
public EndlessBook book;
を削除
public ExtendEndlessBook book;
public float stateAnimationTime = 1f;
public EndlessBook.PageTurnTimeTypeEnum turnTimeType = EndlessBook.PageTurnTimeTypeEnum.TotalTurnTime;
public float turnTime = 1f;
を追加。
Update()
private void Update()
{
bool changeState = false;
EndlessBook.StateEnum newState = default(EndlessBook.StateEnum);
// change the state of the book
if (Input.GetKeyDown(KeyCode.Z)) { changeState = true; newState = EndlessBook.StateEnum.ClosedFront; }
else if (Input.GetKeyDown(KeyCode.X)) { changeState = true; newState = EndlessBook.StateEnum.OpenFront; }
else if (Input.GetKeyDown(KeyCode.C)) { changeState = true; newState = EndlessBook.StateEnum.OpenMiddle; }
else if (Input.GetKeyDown(KeyCode.V)) { changeState = true; newState = EndlessBook.StateEnum.OpenBack; }
else if (Input.GetKeyDown(KeyCode.B)) { changeState = true; newState = EndlessBook.StateEnum.ClosedBack; }
if (changeState)
{
book.SetState(newState, animationTime: stateAnimationTime, onCompleted: OnBookStateChanged);
}
bool turnToPage = false;
int newPageNumber = 0;
if (Input.GetKeyDown(KeyCode.A)) { turnToPage = true; newPageNumber = 1; }
else if (Input.GetKeyDown(KeyCode.S)) { turnToPage = true; newPageNumber = 2; }
else if (Input.GetKeyDown(KeyCode.D)) { turnToPage = true; newPageNumber = 3; }
else if (Input.GetKeyDown(KeyCode.F)) { turnToPage = true; newPageNumber = 4; }
else if (Input.GetKeyDown(KeyCode.G)) { turnToPage = true; newPageNumber = 5; }
if (Input.GetKeyDown(KeyCode.Q)) { turnToPage = true;newPageNumber = book.CurrentPageNumber - 1;}
else if (Input.GetKeyDown(KeyCode.W)) { turnToPage = true;newPageNumber = book.CurrentPageNumber + 1;}
if (turnToPage)
{
book.TurnToPage(newPageNumber, turnTimeType, turnTime,
openTime: stateAnimationTime,
//onCompleted: OnBookTurnToPageCompleted,
onPageTurnStart: OnPageTurnStart,
onPageTurnEnd: OnPageTurnEnd
);
}
}
を追加。
GetPageView(int pageNumber)
protected virtual PageView GetPageView(int pageNumber)
{
return pageViews.Where(x => x.name == string.Format("PageView_{0}",
(pageNumber == 0 ? "Front"
: (pageNumber == 999 ? "Back"
: pageNumber.ToString("00"))))).FirstOrDefault();
}
を、
protected virtual PageView GetPageView(int pageNumber)
{
return pageViews.Where(x => x.name == string.Format("PageView_{0}",
(pageNumber == 0 ? "Front"
: (pageNumber == 999 ? "Back"
: (pageNumber % 4).ToString("00")
)
))).FirstOrDefault();
}
に書き換え。
ExtendEndlessBook.cs (EndlessBookクラスのラッパークラス)の作成
using echo17.EndlessBook;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ExtendEndlessBook : EndlessBook
{
[SerializeField]
protected List reminderMaterialList = new List();
protected override Material GetPageMaterial(int pageNumber)
{
return reminderMaterialList[pageNumber % 4];
}
public override void TurnToPage(int pageNumber, PageTurnTimeTypeEnum turnType, float time,
float openTime = 1f,
StateChangedDelegate onCompleted = null,
PageTurnDelegate onPageTurnStart = null,
PageTurnDelegate onPageTurnEnd = null
)
{
// only do this call if not already turning pages, changing state, and the not already in the page group specified
if (isTurningPages || isChangingState || IsDraggingPage) return;
if (currentState == StateEnum.OpenMiddle && IsInCurrentPageGroup(pageNumber))
{
if (pageNumber == currentPageNumber) return;
SetPageNumber(pageNumber);
return;
}
if (pageNumber < 1)
{
LogInvalidPageNumber();
return;
}
// set up the turn to page data to be used internally
turnToPage = new TurnToPageData()
{
pageNumber = pageNumber,
turnTimeType = turnType,
time = time,
onCompleted = onCompleted,
onPageTurnStart = onPageTurnStart,
onPageTurnEnd = onPageTurnEnd
};
// if the state is not OpenMiddle, first set to that state
if (currentState != StateEnum.OpenMiddle)
{
SetState(StateEnum.OpenMiddle, openTime, (IsInCurrentPageGroup(pageNumber) ? onCompleted : TurnToPageInternal));
// exit and wait for the state set to finish
return;
}
// already in the current page group, so set the page number and materials
if (IsInCurrentPageGroup(pageNumber))
{
SetPageNumber(pageNumber);
return;
}
// call the internal page turn method.
// this is also called when the state change is completed if the current state is not OpenMiddle.
TurnToPageInternal(StateEnum.OpenMiddle, StateEnum.OpenMiddle, currentPageNumber);
}
public override PageData GetPageData(int pageNumber)
{
if (pageNumber < 1)
{
LogInvalidPageNumber();
return PageData.Default();
}
return pageData[pageNumber - 1];
}
public override void SetPageData(int pageNumber, PageData data)
{
if (pageNumber < 1)
{
LogInvalidPageNumber();
return;
}
// set the data
pageData[pageNumber - 1] = data;
// if the page is currently being displayed,
// update the book's materials
if (IsInCurrentPageGroup(pageNumber))
{
SetPageNumber(currentPageNumber);
}
}
public override void RemovePageData(int pageNumber)
{
if (pageNumber < 1) { LogInvalidPageNumber(); return; } // remove the data pageData.RemoveAt(pageNumber - 1); // if the page is currently being displayed, // update the book's materials if (currentPageNumber > pageData.Count)
{
SetPageNumber(currentPageNumber - 1);
}
}
public override void MovePageData(int pageNumber, int direction)
{
direction = Math.Sign(direction);
if (pageNumber < 1)
{
LogInvalidPageNumber();
return;
}
if (direction == 0 || (pageNumber == 1 && direction == -1) || (pageNumber == LastPageNumber && direction == 1)) return;
var data = pageData[pageNumber - 1 + direction];
pageData[pageNumber - 1 + direction] = pageData[pageNumber - 1];
pageData[pageNumber - 1] = data;
SetPageNumber(CurrentPageNumber);
}
}
コードを見ると長く見えるが、実際にやっていることは、
①ページで読み込むマテリアルをページ数%4で割ったものに固定する
②インスペクターで与えたpagesの最大値を超えた場合、エラーを吐くのではなくスルーさせる
処理の対応のみ。
以上。
“Endless”という名前なので、初めからこのパターンのデモも用意しておいてほしかった。