9月, 2015 のアーカイブ

もう当分書かないと思っていた宴ネタ再びです。

今回は宴で作ったゲームにUnityの標準イメージエフェクトを使う方法を紹介します。

 

blg16
こんな感じの画面を作ります

 

[注意事項]

今回は宴事態のスクリプトを弄るので、あまり推奨出来る方法ではありません。その内正式にサポートされるみたいなので、それを待った方が懸命かもしれません。

 

[実装]

では実装に入ります。今回は画面全体をセピア調にするエフェクトを追加します。

まず、イメージエフェクトのスタンダードアセットを追加します。

 

blg17Assets > Import Package > Effects でインポートしてください。

 

インポートが出来たら、宴で使われているカメラ(UICamera)にセピア用のスクリプトをアタッチしてください。

blg18

 

セピア調のスクリプトをアタッチしている間は常にエフェクトが出ていますので、取りあえずはOffにしておきます。

これでイメージエフェクトの準備が出来ましたので、次は宴から呼び出す処理を追加します。

宴のリファレンスにあるSeneMessageコマンドを使う を参考にしていますので、そちらも参照してください。

まず、宴からメッセージを受け取るクラスを作るのですが、その前に受け取るコマンド一覧を変数として保持しているクラスを作成します。

 

using UnityEngine;

public static class CommandDefine
{
public const string LOG = "Log";
public const string SEPIA = "Sepia";
public const string LOAD = "Load";
public const string RESET = "Reset";
}

 

これは単純にスクリプト内に文字列の直打ちは減らしたいから宣言しています。List<String>等を宣言し、インスペクターから足していくとかでも大丈夫です。

次に、実際にメッセージを受け取るクラスを作成します。これは上記のリファレンスを参考にしています。

 

using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using Utage;

public class UtageMessageReciever
: MonoBehaviour
{
public AdvEngine Engine { get { return this.m_engine ?? (this.m_engine = FindObjectOfType<AdvEngine>()); } }

[SerializeField]
AdvEngine m_engine;

/// summary;
/// イメージエフェクトがあるカメラ
/// summary;
[SerializeField]
Camera m_camera;

/// summary;
/// セピア調フラグの変数名
/// summary
[SerializeField]
string m_sepiaVariableName = "IsSepia";

void Awake()
{
}

//SendMessageコマンドが実行されたタイミング
void OnDoCommand(AdvCommandSendMessage command)
{
switch (command.Name)
{
case CommandDefine.SEPIA:
OnSepia(command);
break;
default:
Debug.Log("Unknown Message:" + command.Name);
break;
}
}

//SendMessageコマンドの処理待ちタイミング
void OnWait(AdvCommandSendMessage command)
{
switch (command.Name)
{
default:
command.IsWait = false;
break;
}
}

private void OnSepia(AdvCommandSendMessage command)
{
// セピアのon/off設定
bool isEnabled = bool.Parse(command.Arg2);
m_camera.GetComponent<UnityStandardAssets.ImageEffects.SepiaTone>().enabled = isEnabled;

// 変数設定
Engine.Param.TrySetParameter(m_sepiaVariableName, isEnabled);
}

 

このクラスではSendMessageでSepiaのコマンドが送られてきたらon/offを設定し、それを変数に保存しています。

m_sepiaVariableNameがセピア調フラグの変数名指定なので、これと同じ名前のフラグを宴のパラメーターに追加します。

今回は「IsSepia」を追加しています。

 

blg19フラグ追加

 

クラスや変数の設定が出来ましたので、新たにゲームオブジェクトを作り、先ほどのスクリプトをアタッチし、変数を設定します。

blg20AdvEngineへの参照も追加してください

 

そして、リファレンスにある通り、AdvEngineのAdvScenarioPlayerコンポーネントのSendMessageTargetに上記で作ったオブジェクトを指定してください。

 

blg21

 

これで準備出来ましたので、実際にシナリオ(エクセル)から呼び出してみます。

 

blg22

 

これでフラグがTrueの間は画面が変更されるはずです。

 

blg16

 

ここで終わり・・・と言いたい所なのですが、現状は二つ問題が有ります。

一つ目はエフェクトの途中でタイトル画面画面に戻るとエフェクトが残ったままになります。

もう一つは途中でセーブし、ロードするとエフェクトが設定されていません。

 

まずはタイトルに戻る時の対応を入れます。

UtageMessageRecieverクラスの中に状態を全てリセットする為のメソッドを追加します。今の所はカメラの処理のみです。

 

    private void Reset()
    {
        m_camera.GetComponent<UnityStandardAssets.ImageEffects.SepiaTone>().enabled = false;
    }

 

そして、宴の中にあるシナリオを終了する処理の中にこれを呼び出す処理を追加します。

AdvScenarioPlayer内のEndScenario()メソッドの最後にメッセージを送る対象(今回は上の方でUtageMessageRecieverのゲームオブジェクトに設定したやつです)に”Reset”のメッセージを送ります。

(現在のバージョンだとAdvScenarioPlayer.csの131行目からです)

 

        ///<summary>
        /// シナリオ終了
        /// </summary>
        public void EndScenario()
        {
            Engine.Clear();
            SendMessageTarget.SendMessage(CommandDefine.RESET);
            isEndScenario = true;
        }

 

“Reset”はCommandDefineの中に足してありますので、そこから指定しています。

これで終了時にリセット処理が呼ばれカメラのリセットが呼ばれます。他の処理を追加した時にも同じ所で纏めて処理は出来ると思います。

 

次はセーブ/ロード処理です。

まずUtageMessageRecieverにロード用メソッドを追加します。

 

    private void Load()
    {
        object isSepia = false;
        if (!Engine.Param.TryGetParameter(m_sepiaVariableName, out isSepia))
        {
            Debug.Log("Param NOT found!");
            return;
        }
        
        m_camera.GetComponent<UnityStandardAssets.ImageEffects.SepiaTone>().enabled = (bool)isSepia;
    }

 

これは変数一覧からフラグを獲得し、見つかった時はその値をカメラに適応させています。

次はシナリオ終了時と同じ様に、ロード時にメッセージを送る処理を追加します。

今回はAdvEngine内のLoadSaveData()メソッドの最後に追加しました

(AdvEngine.csの420行目ぐらい)

 

        ///<summary>
        /// セーブデータのロード
        /// </summary>
        /// <param name="saveData">ロードするセーブデータ</param>
        void LoadSaveData(AdvSaveData saveData)
        {
            Clear();
            saveData.LoadGameData(this);

            //古いセーブデータかを設定しておく
            ScenarioPlayer.IsOldVersion = (saveData.FileVersion <= AdvSaveData.Version2);

            StartScenario(saveData.CurrentSenarioLabel, saveData.CurrentPage, saveData.CurrentGallerySceneLabel);

            // ロード処理
            ScenarioPlayer.SendMessageTarget.SendMessage(CommandDefine.LOAD);
        }

 

一番下にある一行です。今回も”Load”はCommandDefineに追加してあります。

気が付いた方が居るか解りませんが、実はセピア調のフラグ設定はここの部分の為に追加してあります。

ただ、順番的に後で説明するのが面倒だったので、順番を無視して最初に説明を入れてました、すみません・・・

 

これでロード時にもセピア調の設定が呼ばれるはずです。

今回の解説は以上です。

注意事項でも説明した通り、宴の内部を弄っているのであまり推奨出来ません。

主な理由は宴本体がアップデートされた時に差異が出来て入れ直す必要が有ったり、やり過ぎるとそもそもアップデートがしにくくなる事があるので、内部の変更は最小限に収めたい所です。

 

宴ネタは流石にもう当分は無いと思う・・・

 

前回のノベじゃむ記事に引き続き、宴ネタです。

ジャム中とその後にゲームを遊んでみた時に宴のデフォルト環境で気になって変更を加えて見た所を紹介します。

 

[テキストが全部表示された後に出るカーソルを動かしたい]

blg1 - コピー(この白い逆三角形のアイコンです)

 

これは唯一ジャム中に実装した超どうでも良い機能です。

上記の画像にあるアイコンが止ったままだと何か動かしたい衝動に駆られました。

幽霊の住む家を作った時もこのアイコン移動を入れてた影響な気もします。

 

実装ですが、まず宴で生成したシーン内のアイコンを探し当てます

blg6

 

“IconRoot”の下にある”IconBrPage”と言うゲームオブジェクトが目標のアイコンです。

今回はジャムで実装した方法をそのまま載せます。まず、動かす処理を実装します。

アイコンを動かすにはiTweenの機能を使用します。iTween自体は宴に入っているので、新規に追加する必要はありません。

iTweenでやる事は指定した移動量を移動し、移動が終わったら今度は逆方向に移動を開始する、を繰り返します。

 

using UnityEngine;
using System.Collections.Generic;
using Utage;

public class IconMover 
    : MonoBehaviour 
{
    [SerializeField]
    Vector3 m_moveAmount; // 移動量

    [SerializeField]
    float m_duration; // 移動時間
    RectTransform m_transform;

    void Awake() 
    {
        m_transform = GetComponent<RectTransform>();
    }

    private void StartMove()
    {
        Hashtable table = new Hashtable();
        table.Add("time", m_duration);
        table.Add("position", m_transform.position + m_moveAmount);
        table.Add("easetype", iTween.EaseType.easeInOutCubic);
        table.Add("oncomplete", "OnTweenEnd");
        iTween.MoveTo(gameObject, table); 
    }

    private void OnTweenEnd()
    {
        m_moveAmount *= -1.0f;
        StartMove();
    }
}

 

Start()でなくAwake()を使用しているのは、ゲームオブジェクトのactiveが頻繁にtrue/falseを行き来するからです。

これで後はAwake()時にStartMove()を呼べば良さそうですが、このアイコンはテキストの一番を後ろに移動するようになっている為、直ぐに移動すると開始位置がずれてオカシナ移動を行います。

なので回避方法としてAwake()から数フレーム(今回は10を指定しました)待ち、その後に移動を開始しました。

面倒なのでクラス全部乗せます

 

using UnityEngine;
using System.Collections.Generic;
using Utage;

public class IconMover 
    : MonoBehaviour 
{
    [SerializeField]
    Vector3 m_moveAmount; // 移動量
    Vector3 m_startPos;

    [SerializeField]
    float m_duration; // 移動時間
    RectTransform m_transform;
    
    [SerializeField]
    int m_startFrame;
    int m_frameCount;

    void Awake() 
    {
        m_transform = GetComponent<RectTransform>();
        m_frameCount = 0;
    }

    void OnEnable()
    {
        m_frameCount = 0;
    }

    void OnDisable()
    {
        m_frameCount = 0;
        iTween.Stop(gameObject);
        m_transform.position = m_startPos;
    }

    private void StartMove()
    {
        Hashtable table = new Hashtable();
        table.Add("time", m_duration);
        table.Add("position", m_transform.position + m_moveAmount);
        table.Add("easetype", iTween.EaseType.easeInOutCubic);
        table.Add("oncomplete", "OnTweenEnd");
        iTween.MoveTo(gameObject, table); 
    }

    private void OnTweenEnd()
    {
        m_moveAmount *= -1.0f;
        StartMove();
    }

    void Update()
    {
        if (m_frameCount < m_startFrame)
        {
            m_frameCount++;
            return;
        }

        if (m_frameCount == m_startFrame)
        {
            m_startPos = m_transform.position;
            StartMove();
            m_frameCount++;
        }
    }
}

 

ちなみに、ゲームオブジェクトが非アクティブになった時はOnDisable()でiTweenの移動を停止し、位置を移動開始前の位置に戻しています。

このスクリプトを”IconBrPage”の親の”IconRoot”にアタッチし、移動量と開始待ちフレーム数を指定したら動かせます。

・・・やったら解ると思いますが、上下に動くだけのどうでも良い機能に長い説明をしました。

 

[Auto再生中な事が一目で解る様にしたい]

これはジャム中に思っても何故か機能とも直そうともしなかった機能です。(やれば簡単だったのでやればよかった・・・)

実装が楽だったと言う理由で、今回はAuto再生中とおまけでSkip中は文字が赤くなるように弄ってみました。

blg7Autoの部分が赤くなっています

さっそく実装を紹介します。今回はAutoボタンとSkipボタン両方に適応させる為、ボタンの色を変える為の基礎クラスを作成します。

やる事はAwake時と選択時に現在の設定(onかoff)によって文字の色を変えるだけです。この基礎クラスにおいては今の状態を確認する処理をabstractメソッドにし、各ボタン用クラスで別途確認するようにしています。

 

またまた面倒なので全乗せ


using UnityEngine;
using System.Collections;
using Utage;

public abstract class IGameButtonExtension 
    : MonoBehaviour 
{
    public AdvEngine Engine { get { return this.m_engine ?? (this.m_engine = FindObjectOfType<AdvEngine>()); } }

    [SerializeField]
    AdvEngine m_engine;

    [SerializeField]
    protected Color m_normalColour = Color.white;

    [SerializeField]
    protected Color m_onColour = Color.red;

    [SerializeField]
    protected UnityEngine.UI.Text m_text;

    void Awake()
    {
        SetupColour();
    }

    public void OnTap()
    {
        SetupColour();
    }

    protected abstract bool IsTurnedOn();

    protected virtual void SetupColour()
    {
        if (IsTurnedOn())
        {
            m_text.color = m_onColour;
        }
        else
        {
            m_text.color = m_normalColour;
        }
    }
}

 

このスクリプトはボタン自体に設定しますので、文字のゲームオブジェクトのコンポーネントへの参照も入れる様にしています。オブジェクト自体は子供として有るので(GetChild()でも可能です)。

 

次にこれを各ボタン用に継承したクラスを作成します。ゲームの設定はAdvEngineのConfigの中に入っていますので、そこを参照しています。

 

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Utage;

public class AutoButtonExtension 
    : IGameButtonExtension 
{
    protected override bool IsTurnedOn()
    {
        return Engine.Config.IsAutoBrPage;
    }
}

 

これで準備出来ましたので、Autoのボタンにアタッチします。アタッチした後はAdvEnginとボタンの子オブジェクトへの参照を設定し、ボタンのクリック時の処理(OnClick)に新しいイベントを追加し、上記で実装したOnTap()を指定してください。

 

blg8

 

これでAutoは出来ましたので、次はSkipボタンを実装します。

 

using UnityEngine;
using System.Collections;

public class SkipButtonExtension
    : IGameButtonExtension
{
    protected override bool IsTurnedOn()
    {
        return Engine.Config.IsSkip;
    }
}

 

そしてこれもアタッチして、Autoと同じ設定をします。

blg9

これで実装完了です。ボタンを押せば色が変わる様になります。

 

[Skip, Auto等のボタンの表示をon/off切り替えたい]

宴のゲームはデフォルトでボタンが全部表示されており、正直Autoで読み進めて居る時は邪魔に感じます。

なので、押すと表示/非表示を切り替えられるボタンを追加してみました。

blg10「Log」ボタンの隣に「Menu」ボタンが追加されました

 

blg11押したら消えてスッキリ!

実装自体は単純で、現在の表示状態を保持し(開始時はOn)、ボタンを押した時は設定を切り替え、それに合わせてボタンのオブジェクトをアクティブ/非アクティブにしているだけです。

今回はボタンの親への参照を保持し、各ボタンの設定を切り替えています。

using UnityEngine;
using System.Collections;

public class MenuEnableButton 
    : MonoBehaviour 
{
    [SerializeField]
    RectTransform m_menuParent; // 各ボタンを保持している親オブジェクト

    bool m_isEnabled = true;

    public void OnTap()
    {
        m_isEnabled = !m_isEnabled;
        m_menuParent.gameObject.SetActive(m_isEnabled);

        int childCount = m_menuParent.childCount;
        for ( int i = 0; i < childCount; ++i )
        {
            m_menuParent.GetChild(i).gameObject.SetActive( m_isEnabled );
        }
    }
}

 

スクリプトの準備が出来ましたので、ボタンの準備をします。 ボタンは取り合えずLogボタンをCtrtl+Dで複製し、名前と文字を変えます。

その後にスクリプトアタッチし、Autoボタンの時にも行ったOnClickへのイベント指定処理を行います。ここで気をつけるのは、Logボタンで既に一つ入っていますが残ったままだと押した時にログが出ますので、ここの中身を空にしてください。

 

blg12

設定が多いですが、これで終わりです。

 

[選択肢の位置を動かしたい]

選択肢の位置がデフォルトでは一番上から順番に配置されるので、自分たちのゲームでは調度キャラの顔に被ってました。

なので、位置を取り合えず調整出来る方法を紹介します。

 

blg13彩夏ちゃんのかわいい顔が見えないでござるぅぅぅ!!!

 

blg14見える、見えるぞ!

 

正式な方法があるのかも知れませんが、今回やったのは、インスペクター上で開始位置とボタン毎のスペースを変更しました。

 

blg15デフォルト値

「Space」がボタン毎のスペース、「Padding to Top」が最初のボタンから画面の一番上までのスペースを表していると思われます。

なので、今回はSpaceを40, Padding to Top を 160にしました。

ただ、これは選択肢全体に影響を与える為、ちゃんとしたゲームを作る場合は他の方法を探した方が安全な気がします・・・

 

以上です。宴ネタはまた何かあったら書きます。

ノベじゃむで提出したexeをこのバージョンに差し替えたい・・・