Unityで簡単な2D脱出ゲームを作ってウェブサイトで公開してみよう 〜メッセージの表示と非表示〜

メッセージの表示と非表示

この記事を三行にまとめると

やることはいつものごとくアクティブ状態の操作なんですけどね
数秒後に自動で何らかの処理を走らせるにはどうしたら良いか
次回作もメッセージを吹き出しで出すならそういう風に作ろうと思います
今回はテキストメッセージの表示を実装します。脱出ゲームではよく特定の場所(画面の下部とか)にメッセージウインドウがあったりしますが、本作はその固定のスペースを確保するのに失敗したので、画面内に吹き出しでメッセージを出しています。でも考え方は固定の場所に出す場合と同じなので、参考になる部分はあるかと思います。まあ、やることはいつものごとくアクティブ状態の操作なんですけどね。なので今日の話のメインはメッセージを自動で消去する機能の方です。

動画はこちらです。





吹き出しの表示

冒頭の宣言通り、吹き出しの表示はアクティブ状態を操作するだけです。

public class Balloon : MonoBehaviour {
  public void ShowBalloon() {
    this.gameObject.SetActive(true);
  }
}

オブジェクトをアクティブにするだけだと新たに説明することが何もないんで、今回は少し書き方を変えてみました。

「this.gameObject」にはこのスクリプトをインスペクターでセットしたゲームオブジェクトが自動的に入ります。Balloonというオブジェクトにこのスクリプトをセットすればthis.gameObjectはそのBalloonオブジェクトになります。別にスクリプト名とオブジェクト名をそろえる必要はないです。今回はたまたまそういう例になったってだけで、Itemオブジェクトにセットすればthis.gameObjectはItemオブジェクトになるし、他の場合も同様。変数にセットする場合と内容的には一緒ですが、変数と違って中身が固定されるので、スクリプトをセットしたオブエジェクトと違うオブジェクトを扱いたい場合は変数を別途用意する必要があります。thisは省略しても良いです。「gameObject.SetActive(true)」だけでも同じ動きになります。

本作ではこのスクリプトを各吹き出しにセットしています。それでthis.gameObjectがそれぞれの吹き出しオブジェクトになるので、ShowBalloonを実行した時に吹き出しが表示されるというすんぽうです。



吹き出しの自動消去

本作では吹き出しは表示された3秒後に自動的に消えるようになっています。クリックしたら消えるとかでも良いとは思うんですが、プレイヤーはその仕様を知らないので必ずしもクリックしてくれるとは限らないということで、今回は自動消去を採用しました。でも改めて考えてみると、もし自分がプレイヤーだったらどうすりゃメッセージが消えるんだろうって思って、とりあえず吹き出しをクリックするかもね。逆にそれで消えない方がどうすりゃ消えるのか分からなくて困惑するかも。

まあ僕の落ち度に関しては置いといて、数秒後に自動で何らかの処理を走らせるにはどうしたら良いかという話をしましょう。

Unityには「Invoke」という時間差で関数を発動させることができる機能があります。これを使うことで3秒後に自動で吹き出しを消す処理を実現できます。

使い方はこんな感じです。

public class Balloon : MonoBehaviour {
  public void ShowBalloon() {
    this.gameObject.SetActive(true);
    Invoke("HideBalloon", 3.0f);
  }

  public void HideBalloon() {
    this.gameObject.SetActive(false);
  }
}

Invokeは時間差で発動させたい関数の名前とそれを何秒後に発動させるかを引数で指定します。上記の書き方だと「3秒後にHideBalloon関数を実行する」という内容になります。

これでShowBalloonが発動して吹き出しが表示された3秒後に自動で吹き出しが消えるようになります。

例えばゲーム画面の下部に固定のメッセージウインドウを用意した場合でも、そこのテキストオブジェクトに対してInvokeを使えば、一定時間が経つとウインドウからメッセージが消える処理を実装できます。それ以外でもプレイヤーの操作とは関係なしに一定時間が経った時に何らかの動きを発生させたい場合は、このInvokeを使うと良いでしょう。

ただしInvokeで関数を呼び出す場合は引数を渡すことができません。今回の場合で言うとHideBalloonに何らかの引数を渡すことができないので、非アクティブにしたいオブジェクトはthis.gameObjectなどを使って指定する必要があります。

ちなみに本作では使用していませんが、一定時間ごとに同じ関数を繰り返し呼び出すことができる「InvokeRepeating」という関数などもあります。使ったことがないんで詳細については省略。



Invokeのキャンセル

画面下部に固定のメッセージを出す場合、同一のオブジェクトに対してテキストを書き換える処理を行うことになると思います。つまり同じオブジェクトに対してInvokeを何度も重ねがけする形になります。この時、Invokeは一定時間が経過する前にもう一度呼び出されたら発動時間を延長してくれるわけではありません。重ねがけした分だけ何度も発動します。

ということは画面のいろんな場所をクリックして連続でメッセージを表示させた時、タイミングによってはメッセージを表示する処理とメッセージを消去する処理がほぼ同時に呼び出される可能性もあります。これは本作の吹き出しでも起こりうることです。

吹き出しを表示した3秒後には必ずHideBalloonが発動します。だから例えば同じオブジェクトに対してShowBalloonを連続で発生させた場合、HideBalloonも連続で発生することになるので、タイミングがかぶるとShowBalloonとHideBalloonが同時に発動してメッセージが表示された瞬間に消えるなんて動きにもなりかねません。動画の方で実践しているので、良かったらそっちも見てみてください(7:08あたりからです)

これを回避するには発動待ちになっているHideBalloonをキャンセルする処理が必要になります。

public class Balloon : MonoBehaviour {
  public void ShowBalloon() {
    this.gameObject.SetActive(true);
    CancelInvoke("HideBalloon");
    Invoke("HideBalloon", 3.0f);
  }

  public void HideBalloon() {
    this.gameObject.SetActive(false);
  }
}

Invokeの上に「CancelInvoke」という関数を追加しました。この引数に発動待ちになっている関数名を入れると発動をキャンセルできます。つまりShowBalloonが呼び出されて吹き出した表示された後、3秒以内にもう一回ShowBalloonを呼び出すと、現在発動待ちになっているHideBalloonがキャンセルされて吹き出しは消えなくなります。その後新たにInvokeを実行して、次の3秒後にHideBalloonを呼び出そうとします。

したがって3秒以内にShowBalloonを呼び出し続けている限り、一つ前のInvokeはキャンセルされ続けるので、HideBalloonが実行されるのは最後にShowBalloonを呼び出してから3秒後ということになります。これなら変なタイミングでメッセージが消えることはなくなる。

ちなみにCancelInvokeで何の関数も指定しない場合は、現在発動待ちになっている全てのInvokeがキャンセルされます。

public class Sample : MonoBehaviour {
  public void Activate() {
    Invoke("Func2", 3);
    Invoke("Func3", 3);
  }

  public void CancelOnce() {
    CancelInvoke("Func2");
  }

  public void CancelAll() {
    CancelInvoke();
  }
}

雑なサンプルですが、これだとCencelOnceを実行したらFunc2だけキャンセルされて、CancelAllを実行したらFunc2とFunc3の両方がキャンセルされる感じです。

InvokeRepeatingのキャンセルもこのCancelInvokeで対応できます。






さっきも言いましたけど、吹き出しがクリックで消えるのかいずれ勝手に消えるのかはプレイヤーには分からないので、こういう時はクリックでも消えるし3秒経っても消えるみたいな仕様にしておいた方が無難かもしれないですね。次回作もメッセージを吹き出しで出すならそういう風に作ろうと思います。ただ、これは前にも言ったかもしれないですけど、僕の作ったゲームは海外からのアクセスも少なくないみたいなので、できればテキストメッセージをほぼ出さずに済むようなゲームを作りたいとも思っています。

絵の見せ方がもっと上手くなればもう少しテキストを減らせると思うんですが、どうしても作ってる途中に「これ、説明つけ加えないと何を示した絵か分からなくね?」って思ってついテキストを加えちゃうのと、一箇所にテキストを入れちゃうと他の箇所にも入れとかないと不自然な気がしちゃって……気がつくとテキストが増えてんのよ。まあテキストがあっても意味分かんねーって言われることもありますけどね(泣)

ヒントは分かりやすすぎても難易度が下がっちゃうから、分かりやすすぎず分かりにくすぎずってのが理想だと思うんですが、その難易度が高いって話なのよね。プログラミングよりもそっちの方がよっぽど難しいっす。

次回はボタンオブジェクトの使い方を紹介します。ぶっちゃけ画像オブジェクトにクリックイベントをつければボタンの役割を果たせるから、とりあえず画像オブジェクトの使い方だけ知っておけば事足りるような気もするんですが……一応ね。

それじゃあ次回もよろしくお願いししのここじし。
 もしかしたら何か関連しているかも? 
 みんなからのコメント 
まだコメントはいただけてないみたい……