アセット EndlessBookを使って無限にめくれる本を作る

 

概要

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”という名前なので、初めからこのパターンのデモも用意しておいてほしかった。