Unityで簡単な2D脱出ゲームを作ってウェブサイトで公開してみよう 〜アクティブを制す〜

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
アクティブ状態の操作

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

ゲーム作りのキモとでも言うべきアクティブ状態の操作
激怒したメロスが助走つけて殴りに来るだろうしね
「プログラミング=難解」という呪縛からも脱出しちゃいましょう
全てのオブジェクトには「アクティブ」と「非アクティブ」の2つの状態があります。アクティブというのはゲーム画面上に表示されていて、クリックなどのイベントが作動する状態のことです。反対に非アクティブは非表示の状態で、クリックなどのイベントも作動しません。

ゲームの作り方にもよるでしょうが、僕はこのアクティブと非アクティブの切り替えは超重要だと思っています。もうね、これができるようになれば脱出ゲームは9割できたも同然ってくらい大事です。何せこいつを使えばゲーム内のほとんどの動きを実装できますからね。本作でもドアの開閉やアイテムの取得、使用、視点移動、ヒント画面の表示、水道の水を出したり暖炉の火を消したり椅子を置いたり吹き出しメッセージを出したり消したり、それらの動きは全てアクティブ状態の切り替えで行なっています。

というわけで今日はそのゲーム作りの一番のキモとでも言うべきアクティブ状態の操作をやってみましょう。

動画はこちらです。





スクリプトが不要なアクティブ操作

アクティブの操作はプログラミングが必要な場合とそうじゃない場合があるので、まずはスクリプトを書かなくても操作できる方法から見ていきましょう。

本作の例で言うと、流し台の右下にある扉をクリックした時に扉が開いたり閉じたりする動きはコードを書かなくても実装できます。

ひらけごまの合言葉すら不要

まずは閉じた扉の画像オブジェクト(Close)にクリックイベントをつけます。クリックイベントをつけるためには「Add Component → Event → Event Trigger」の流れでEvent Triggerコンポーネントを追加します。

Event Trigger

コンポーネントを追加すると「Add New Event Type」というボタンが出てくるので、その中から「PointerClick」を選択。

PointerClick

これでオブジェクトにクリックイベントがつくので、「+」ボタンを押してクリック時に発動する動きを設定する入力欄を追加します。

入力欄って言い方で合ってるのかな?

イベント設定の手順を簡単に言うと「操作対象のオブジェクトをドラッグ&ドロップして操作したい関数をセット」になります。入力欄の中の「None(Object)」となっているところにオブジェクトを持ってきて「No Function」と出ているプルダウンから関数を選ぶという流れです。

今回の場合だと「開いた扉の画像オブエジェクトをドラッグ&ドロップしてSetActiveをセット」となります。SetActiveというのはオブジェクトのアクティブ状態を操作する関数です。

実際に上記の設定を行うとこんな感じになります。

SetActiveセット完了

「Open」というオブジェクトがセットされてプルダウンのところが「GameObjejct.SetActive」となっているのが分かると思います。画像が小さくて見えんって場合はクリックして拡大してみてください。Openというのは開いた扉の画像オブジェクトです。

Openの横にチェックボックスが出ていますが、ここにチェックを入れると、クリックした時に対象のオブジェクトがアクティブになります。チェックをはずすとクリックした時に対象のオブエジェクトが非アクティブになります。今回はOpenをアクティブにしたいのでチェックを入れています。

これで閉じた扉(Close)をクリックした時に開いた扉(Open)がアクティブになります。

同じ要領でOpenオブジェクトにOpenを非アクティブにするイベントを追加すれば、扉をクリックするたびに開いたり閉じたりの動きが交互に作動します。



なぜこれで扉が開閉するのか?

厳密に言うならば上記の操作で行なったのは「扉を開閉する処理」ではなく、あくまでも「画像オブジェクトのアクティブ状態を切り替える」です。だからこの方法を用いれば必ず扉の開閉の動きを実装できるというわけではありません。

僕の場合、扉や蓋のように開けると閉じるの二つの動きがあるところは、基本的に開いた状態の画像と閉じた状態の画像を二つ用意して、開いた画像のアクティブ状態を切り替えて開閉の動きを見せるやり方を採用しています。

前にも言いましたがUIオブジェクトを二つ以上重ねた時はヒエラルキーで下にある方が手前にきます。だから閉じた画像オブエジェクトの手前に開いた画像オブジェクトを重ねておけば、開いた画像がアクティブになった時に閉じた画像は奥に隠れます。またUIオブジェクトは奥に隠れるとクリックが効かなくなるので、開いた画像がアクティブになれば閉じた画像はそのままにしておいても自動的にクリックできない状態になります。個人的には開閉の動きに関してはこのやり方が簡単かなあと思っています。

ハードルがあるとすれば、開いた状態と閉じた状態の両方の画像素材が見つからない場合がある点ですかね。どちらか片方はあるんだけど、同じ扉のイラストで両方がそろっていない時って結構あるのよ。あとは箱の蓋が開いた状態のイラストとかね。でもそれさえ見つかればUnity側での労力はだいぶ少ないと思います。

もちろんこの方法がベストかどうかは分かりません。このやり方だと画像を入れ替えるだけなので、例えば「箱が開くアニメーションをつけたい」みたいな場合には対応できません。僕はそこまでの技術はないのでこのやり方で十分です。

これは半分言い訳ですけど、僕の中ではゲーム作りはまだまだ練習段階なので、凝った動きだったりグラフィックのキレイさにこだわるよりは、とりあえずゴールまでプレイできるものを公開する方を優先させても良いのかなあと。僕は完璧主義者なところがあるんで、以前は小説なんかでも誰に読ませても恥ずかしくないクオリティのものが作れるまで世には出さないなんて考えてた時期もあったんですが、でもそんなこと思ってるといつまで経っても作品が完成しないんですよね。そもそも誰に読ませても恥ずかしくないクオリティってどの程度のレベルだよって話ですしね。もしそれが「こ、これは……太宰治の再来か!?」と言われるレベルだとしたら、一生かかっても無理だっつー話っすわ。「俺の方が治より文才あるっしょ」とか調子こいて言った日にゃ、激怒したメロスが助走つけて殴りに来るだろうしね。

だからあまりそういうことを考えるのは止めました。動画もそうですけど、どうせ最初は素人なんだからクオリティも評価も低くて当然だし、クオリティが上がるの待ってたら先に俺自身が天に上がっちまうわってことで、何でも良いから作ったら出そうと、それくらいの気持ちでも良いんじゃないでしょうか、たぶん。

すみません。だいぶどうでもいい話をしてしまいました。本編に戻りましょう。



スクリプトを書いてアクティブ操作

今度はスクリプトを書いてアクティブ状態を切り替えてみましょう。

Unityのスクリプトは「C#」というプログラミング言語を使用します。C#やプログラミングを基礎から話すと日がまた昇り繰り返しても終わらないくらい長くなってしまうので、このコードはこういう意味を持ってるんですよ〜くらいの感じで行きたいと思います。

スクリプトファイルも画像やフォントなどと同様にプロジェクトウインドウで管理します。プロジェクトウインドウのCreateボタンを押すか右クリックでメニューを開くと「C# Script」というのがあるので、それで新規にファイルを作成します。名前は適当で大丈夫です。

ファイルを開くとこんなコードが書いてあります。

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

public class Tap : MonoBehaviour {
  void Start () {

  }

  void Update () {

  }
}

classの横に「Tap」と書いてありますが、これはファイルを作成した時につけた名前がそのまま反映されます。classが何かって話をするとやっぱり長くなってしまうので、ここではこのクラスの中にいろいろなスクリプトを書いていくんだということだけ覚えておきましょう。

「Start」と「Update」はそれぞれ関数と呼ばれるものです。クリック時などに作動する動きはこの関数の中に作っていきます。ただし今回はStartもUpdateも必要ないので削除しちゃって良いです。

ここでは動画の中でもやっている通り、蛇口をクリックした時に水道の水を出したり止めたりする動きを関数の中に作ります。

そのためにまずクラスの中に変数を一つ作成します。

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

public class Tap : MonoBehaviour {
  public GameObject water;
}

「water」という変数を作りました。変数とは数学の方程式でいうxとかyと同じで、何らかの値を代入するものです。代入するものは数字や文字だったりゲームオブジェクトだったりとさまざまです。今回はwaterに水道の水オブジェクトを代入します。waterの横に「GameObject」と書いてありますが、これはこの変数にゲームオブジェクトを入れるぞという宣言のようなものです。

C#の場合、変数を作る時はその変数にどんな値を入れるかを定義しなければなりません。例えば整数を入れるならintとか、日本語を入れるならstringとか。こういうのを「型宣言」と言います。どういう値を入れる時にどんな型が必要になるかはその都度見ていきましょう。

変数は指定された型の値しか入れることができないので、今回の場合、water変数に数字とか文字を入れることはできません。逆に整数型で定義した変数などにゲームオブジェクトを入れることもできません。



続いて関数を作ります。関数名も基本的には何でも良いです。使っちゃダメな名前もなくはないんですが今は気にしなくても良いでしょう。別に「Akuma」と名付けても裁判になったりはしないから大丈夫。今回はアクティブ状態を切り替えるという意味を込めて「Switch」とします。

関数の中はこんな感じです。

public class Tap : MonoBehaviour {
  public GameObject water;

  public void Switch() {
    if(water.activeSelf) {
      water.SetActive(false);
    } else {
      water.SetActive(true);
    }
  }
}

内容を簡単に説明すると「water変数にセットしたオブジェクトがアクティブなら非アクティブに、非アクティブならアクティブにする」という処理です。

「water.activeSelf」というのがオブジェクトの現在のアクティブ状態です。アクティブなら「true」、非アクティブなら「false」という値が入っています。water変数には水道の水オブジェクトを入れるので、水がアクティブならtrue、非アクティブならfalseになるということですね。

「SetActive」は先ほどのドアの開閉の時に出てきたSetActiveと同じで、オブジェクトのアクティブ状態を操作するものです。「water.SetActive(false)」は水オブジェクトを非アクティブに、「water.SetActive(true)」は水オブジェクトをアクティブにします。

ifというのはプログラミングの世界では極めてよく使われる「if文」という構文で、条件によって処理を分岐させる機能を持っています。ifの名の通り「もしも〜だったら〜」みたいなことですね。

基本的にif文は条件が真だったら上の処理、偽だったら下の処理を行います。

if(条件) {
  条件が真の時の処理
} else {
  条件が偽の時の処理
}

今回の場合だとwater.ActiveSelfの中身が条件となります。つまり「水オブジェクトはアクティブな状態である」という条件に対し、その通りであれば上の処理、そうでなければ下の処理を行うことになります。

if(水オブジェクトがアクティブである) {
  水オブジェクトを非アクティブにする
} else {
  水オブジェクトをアクティブにする
}

言葉にするとこんな感じですかね。

このスクリプトをオブジェクトにセットすればインスペクターからSwitch関数を呼び出せるようになります。

スクリプトを追加するにはファイルをプロジェクトからインスペクターにドラッグ&ドロップすればオーケーです。スクリプトを追加するとwater変数の入力欄が出てくるので、セットしたいオブジェクト(ここでは水)をドラッグ&ドロップします。

スクリプトをセット

そして扉の開閉の時と同じようにクリックイベントのところにオブジェクトをセットします。ただしこの時セットするのは操作対象のオブジェクト(水オブジェクト)ではなく、スクリプトを追加したオブジェクトです。動画では蛇口オブジェクト(Tap)にスクリプトを追加しているので、Tapオブジェクトをドラッグ&ドロップします。すると関数一覧のプルダウンの中に追加したTapスクリプトが出てくるので、そこからSwitch関数を選択することができるようになります。

関数をセット

これでクリック時にSwitch関数が発動するようになります。最初は水オブジェクトが非アクティブなのでactiveSelfはfalseです。なのでif文の下の処理が動いて水オブジェクトがアクティブになります。その状態でもう一度クリックすると今度はactiveSelfがtrueに変わっているので、if文の上の処理が動いて水が非アクティブになります。あとはその繰り返しです。






ふー、ちょっと長くなっちゃいましたね。もうちょっと簡潔に話せれば良かったんですが、スクリプトのコードが入ってくるとどうしてもね……。

ちなみに今後もちょいちょいスクリプトを書く作業が発生します。でも大丈夫。プログラミングなんてそんな難しいもんじゃないっす。確かに僕は仕事でも十年くらいコード書いてますから多少プログラミングに慣れてるってのはありますけど、それを差し引いても本作程度のちょっとしたゲームを作るくらいなら、高校受験なんかよりもはるかに簡単です。だからもしも「脱出ゲーム作ってみたいけどプログラミングってやったことないしなあ……どうせ(ハードルが)お高いんでしょう?」と思っている人がいるなら、この機会に「プログラミング=難解」という呪縛からも脱出しちゃいましょう。僕が本シリーズを通じてその呪縛を解き放つお手伝いをします。

ただ、僕の動画を見てそれでもやっぱりプログラミングって難しいと思ったなら、それはプログラミングが難しいんじゃなくて僕の説明が下手で分かりやすく伝えられてないだけだから、その場合は……ごめん。

次回は室内の移動をアクティブ操作で行いますが、今回よりもコードの量はちょい多めです。でも何とか分かりにくくならないように頑張りますんでよろしくお願いしーしゃーぷ。



本シリーズの記事の一覧はこちら
Unityで簡単な2D脱出ゲームを作ってウェブサイトで公開してみよう 〜エピローグ〜
 もしかしたら何か関連しているかも? 
 質問や感想などお気軽にコメントしてください