原カバンは鞄のお店ではありません。

Unityを使ったゲーム制作のあれこれを綴っていきます。

【Unity】Action型を利用してInvokeをラムダ式で記述する

寒波襲来

天気予報の「午前中は晴れますが午後からは景色が一変します」というアナウンスを余り信じていなかったけれど午後になると天気予報通りに景色が一変して天気予報のお姉さんに心の中で土下座した皆さんこんにちは。窓の外が一気に真っ白になって、あっという間に雪が積もっていきました。雪国以外では数センチの積雪でも交通機関は麻痺するので、こういう強烈な寒波は台風と同じレベルの脅威ですね。

 

忘れられたゲーム

エルデンリングが発売されてもう少しで一年経ちます。このゲームはそのクオリティもさることながら、ボリュームも一般のオープンワールドゲームを凌駕するほど膨大だったため、発売されてから数か月間はゲーム関連の各種雑誌やYoutube等々はエルデンリング一色となっていました。
この為、その間に発売されたゲームは例え出来が良いものでも余り話題に上らず、そのまま忘れられていく運命となったものが数多くありました。
忘れられた都市 - The Forgotten City』もそうした不幸な運命を辿ったゲームの一つだと言えます。

 

store.steampowered.com

 

元ネタはスカイリムの有名なMODであまりに好評だった為、独立したゲームとしてリメイク仕直したというこのゲーム。
ジャンルは一人称視点のアドベンチャーゲームで、探索や謎解き、キャラクターとの会話がメインですが、一部アクションゲームの要素もあります。

古代ローマ時代の小さな都市に迷い込んだ現代人が、なんとか元の時代へ戻る方法を探そうとしますが、その都市は「黄金律」と呼ばれる恐ろしい規律によって縛られた閉鎖都市で、その呪縛を解かない限りに現代へは戻ることができない、主人公はこの「黄金律」の規律を侵さずに、その呪縛の謎へ迫っていく・・・といったストーリー。

このミステリーに対する謎解きに加えて、「Outer Wilds」や「Twelve Minutes」のようなタイムリープの要素も加わって非常にやりごたえのあるゲームとなっています。
マルチエンディング形式で世界的な脚本家組合賞も受賞したストーリーは本当に秀逸です。
大団円の真エンディングはご都合主義ながら全ての謎が解け、スッキリと晴れやかな気持ちになると思います。初見の人は、先ずは何も見ずに遊んで欲しいゲームです。

 

Action型

ゲームに限らず色々なシステム開発で、とあるクラスの処理終了を待ってから処理を行いたい、というケースはよく直面しますが、UnityではそういうケースではDelegateを使用することが多いです。

Delegateは一言でいうと「メソッドを代入できる」型です。

「メソッドを代入できる」とは正確には関数が保存されているアドレスを指すポインタを保持する、という意味で、要はこれを使えばコールバック(Callback)という仕組みが実現できるようになります。

 

例えば下の図のようなカウントタイマーがあり、その終了によって右側の文字盤の文字を変更したい場合、

カウントタイマー側のクラスにdelegate型の変数を定義します。

public class CountDownTimer : MonoBehaviour
{
   public delegate void OnCompleteDelegate();	// delegate 型の宣言
   public OnCompleteDelegate CompleteHandler;	// delegate 型の変数を宣言

   public void RegistDelegateFunc(OnCompleteDelegate argCall)
   {
       CompleteHandler += argCall;		// メソッドを代入
   }

   private void Complete_CountDown()
   {
       CompleteHandler?.Inovke();	// コールバックを実行
   }
}

 

文字盤側のクラスではCountDownTimerクラスのRegistDelegateFuncにてカウント終了のコールバックを受け取る関数を指定することで、カウントタイマーの終了時に文字盤の文字変更の処理を行うことができます。

public class WordBoard : MonoBehaviour
{
    public CountDownTimer _timerCtl;
    
    void Start()
    {
        // コールバックを指定する
        _timerCtl.RegistDelegateFunc(()=>CallBack_Timer());
    }
    
    public void CallBack_Timer()
    {
        // 文字盤の文字を変える
    }
}

 

上記の仕組みを実装して動作させた結果が以下の動画となります。

Action型(System.Action)とはこのDelegate型から派生した型の一つになります。

 

Action型の使い方

Action型(System.Action)はDelegate型から派生した型であるため、Delegate型と同様の使い方で使用しますが、Action型はDelegate型とは違い、型宣言を行う必要がありません。型の定義と変数宣言を一度に行うことができます。
なので、先ほどのCountDownTimerクラスのソースはAction型を使うと以下のように変更する事が出来ます。

public Action OnCompleteAction; // Action型の変数宣言

public void RegistActionFunc(Action argCall)
{
    OnCompleteAction += argCall;		// メソッドを代入
}

private void Complete_CountDown()
{
	OnCompleteAction?.Inovke();
}

 

最初の二行が一行になっていることがわかると思います。型宣言がないため、Actionでは戻り値を定義できません引数はAction<string, int, ....>のように<> の中に型を列挙することで指定することができます。

 

Action型の仕様を利用する

例えば先程のカウントタイマーと文字盤のシステムで

上の動画のようにタイマーの文字盤が変わると「少し遅れて」背景の色を変更する、という処理を行いたい場合、通常はカウントタイマーの文字盤が変わったタイミングでBackgroundColorを変える処理をInvokeを使って呼び出します。

public class CountDownTimer : MonoBehaviour
{
    List<Color> _skyboxColorList;    // 背景色のリスト
    int _colorIndex = 0;                 // リストのIndex

    // 文字盤が変わったときに呼ばれる関数
    public void  ChangedTimer()
    {
        // BackgroundColorを変える関数を0.2秒後に呼び出す
        this.Invoke("ChangeSkyColor", 0.2f);
    }
    
    // BackgroundColorを変える
    private void ChangeSkyColor()
    {
         _colorIndex++;
         Camera.main.backgroundColor = _skyboxColorList[_colorIndex];
    }
}

 

Invokeは実行する関数を関数名(string型)で指定するので、必ず関数定義が必要となり、その為、2,3行程度の短い処理でも関数化する必要がありました。

前述のようにAction型では変数宣言のみで使用することができるため、この仕様を利用すると「Actionデリゲート → ラムダ式」のように間接的な関係を作って関数名指定の箇所をラムダ式で記述することができます

this.Invoke(new Action(() => {
    _colorIndex++;
    Camera.main.backgroundColor = _skyboxColorList[ColorIndex];

}).Method.Name, 0.2f);

 

まぁ、コーディングの作業量が減る程度の効果しかありませんがTipsとして知っておけば自慢できる・・・かも。

◇プライバシーポリシー

●個人情報の利用目的

当ブログでは、メールでのお問い合わせ、メールマガジンへの登録などの際に、名前(ハンドルネーム)、メールアドレス等の個人情報をご登録いただく場合がございます。

これらの個人情報は質問に対する回答や必要な情報を電子メールなどをでご連絡する場合に利用させていただくものであり、個人情報をご提供いただく際の目的以外では利用いたしません。

●個人情報の第三者への開示

当サイトでは、個人情報は適切に管理し、以下に該当する場合を除いて第三者に開示することはありません。

・本人のご了解がある場合
・法令等への協力のため、開示が必要となる場合

個人情報の開示、訂正、追加、削除、利用停止
ご本人からの個人データの開示、訂正、追加、削除、利用停止のご希望の場合には、ご本人であることを確認させていただいた上、速やかに対応させていただきます。

アクセス解析ツールについて

当サイトでは、Googleによるアクセス解析ツール「Googleアナリティクス」を利用しています。

このGoogleアナリティクスはトラフィックデータの収集のためにCookieを使用しています。このトラフィックデータは匿名で収集されており、個人を特定するものではありません。
この機能はCookieを無効にすることで収集を拒否することが出来ますので、お使いのブラウザの設定をご確認ください。

●免責事項

当サイトからリンクやバナーなどによって他のサイトに移動された場合、移動先サイトで提供される情報、サービス等について一切の責任を負いません。

当サイトのコンテンツ・情報につきまして、可能な限り正確な情報を掲載するよう努めておりますが、誤情報が入り込んだり、情報が古くなっていることもございます。

当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。

●プライバシーポリシーの変更について

当サイトは、個人情報に関して適用される日本の法令を遵守するとともに、本ポリシーの内容を適宜見直しその改善に努めます。

修正された最新のプライバシーポリシーは常に本ページにて開示されます。