Unityで簡単な2D脱出ゲームを作ってウェブサイトで公開してみよう 〜画像とテキストの変更〜

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
自分では意外と使わないけど出番は多そうな機能

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

セットした画像やテキストをゲーム中に変える
インスペクターから操作する
スクリプトから操作する
画像オブジェクトやテキストオブジェクトを作成して画面上に配置するところまでは以前にやりましたが、そのセットした画像やテキストをゲーム中に変える方法を今回は試してみたいと思います。アクティブ状態を操作して別のオブジェクトを表示するのではなく、オブジェクトはそのままでインスペクターの内容を変更するというものです。

やり方としてはインスペクターから操作する方法とスクリプトから操作する方法の二種類があるので順番に見ていきましょう。

動画はこちらです。





インスペクターから操作する

まずはインスペクターから操作する方法です。クリックイベントに画像を変える処理を入れてみます。

前にインスペクターでオブジェクトのアクティブ状態を操作する方法を紹介したことがありましたが、やり方はあれとほぼ同じです。あの時はプルダウンから「GameObject → SetActive」の順に選択しましたが、今回は「Image → Sprite sprite」の順に選択します。

Sprite spriteって何かカーメン・カーメンみたいだな

SetActiveの時はチェックボックスの入力欄が出てきましたが、Sprite spriteの場合は画像の入力欄が出てくるので、変更したい画像をドラッグ&ドロップすればクリックでその画像に変わるイベントの完成です。

注意しなきゃいけないのは、この設定で変わるのは画像だけです。オブジェクトの大きさなどは変わりません。つまりサイズの違う画像、特に縦横の比率が違う画像に変更しようとすると、クリックで変化した時に縦横の比率がおかしな状態で表示されたりします。大きさも一緒に変える場合はスクリプトからの操作が必要になるので、ちょっと後回しにしましょう。先にテキストの変更をインスペクターで操作する方法を見てみます。

テキストもやり方は画像と一緒で、プルダウンから「Text → string text」を選択して、出てきた入力欄に変更したいテキストを入力するだけです。

String text

こっちもオブジェクトの大きさまでは変わらないので、長いテキストを入れるとはみ出てしまう可能性があります。スクリプトから操作すればオブジェクトのサイズも一緒に変えられますが、インスペクターから操作する場合ははみ出ても良いようにHorizontal OverflowやVertical OverflowをOverflowにしておいた方が良いでしょう。



スクリプトから操作する

スクリプトから操作する場合も考え方はインスペクターの場合と一緒です。まずは画像を変える処理をコードで書いてみましょう。

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

public class Sink : MonoBehaviour {
  public GameObject close;
  public Sprite open;

  public void ChangeImage() {
    close.GetComponent<Image>().sprite = open;
  }
}

これでインスペクターでSprite spriteに画像をセットした時と同じ動きを実現できます。ここではclose変数にセットしたオブジェクトの画像をopen変数にセットしたものに変えるという処理を行なっています。

忘れないようにしてほしいのは上から四行目の「using UnityEngine.UI」の一文。UI関連のオブジェクトをスクリプトで操作したい時はこれが必要になります。これを書き忘れるとエラーになっちゃうのでご注意。

「close.GetComponent<Image>()」はclose変数にセットしたオブジェクトのImageコンポーネントを取得するコードです。インスペクターでオブジェクトに追加したコンポーネントはこのGetComponent関数で取得できます。コンポーネント名はインスペクターを見れば分かります。

コンポーネントたち

上記のコードではこの中のImageコンポーネントを取ってきているわけですね。これでオブジェクトの画像や色をスクリプトで上書きできる状態になります。

public class Sink : MonoBehaviour {
  public GameObject close;
  public Sprite open;

  public void ChangeImage() {
    close.GetComponent<Image>().sprite = open;
    close.GetComponent<Image>().color = new Color(0.1f, 0.2f, 0.3f, 1);
  }
}

色はRGBAのそれぞれの値を0〜1の範囲で設定します。設定値に小数を使用する場合は「float」という小数型で値を入れないといけないので、数字の後ろにfをつけます。

同じ要領でRect Transformコンポーネントを取得すればオブジェクトの位置や大きさを変えることもできます。

public class Sink : MonoBehaviour {
  public GameObject close;
  public Sprite open;

  public void ChangeImage() {
    close.GetComponent<Image>().sprite = open;
    close.GetComponent<RectTransform>().sizeDelta = new Vector2(205, 145);
    close.GetComponent<RectTransform>().localPosition = new Vector2(167, -97);
    close.GetComponent<Image>().color = new Color(0.1f, 0.2f, 0.3f, 1);
  }
}

位置はオブジェクトを移動させる時にも出てきたlocalPositionにxとyの値を入れてやればオッケーです。positionでも良いです。

大きさは「sizeDelta」に値を入れます。位置と同じようにVector2を使って「Vector2(width, height)」で値を設定します。

テキストもやることは一緒です。GetComponentでTextコンポーネントを取得して値を上書きするだけです。

public class Bottle : MonoBehaviour {
  public GameObject name;

  public void Change() {
    name.GetComponent<Text>().text = "水の入ったペットボトル";
  }
}

改行コードも使えます。

public class Bottle : MonoBehaviour {
  public GameObject name;

  public void Change() {
    name.GetComponent<Text>().text = "水の入った\nペットボトル";
  }
}

これで「水の入った」「ペットボトル」の2行になります。

インスペクターで変更する際に改行つきのテキストをセットする方法が分からなかったので、もしかしたら改行が必要な場合はスクリプトからの変更が必須かもしれないです。すみませんがそこは調査不足……orz



自作のコンポーネントについて

ImageやRect Transform、Textコンポーネントと同様に、自作のスクリプトをオブジェクトに追加した場合もGetComponentで取得できます。

例えば以前に作ったDialogスクリプトやMouseスクリプトなどがセットされているオブジェクトを変数に入れれば、GetComponentでコンポーネントを取得してそのスクリプト内の関数を呼び出すことができます。

//Dialogスクリプト
public class Dialog : MonoBehaviour {
  public GameObject dialog;

  public void OpenDialog(GameObject detail) {
    dialog.SetActive(true);
    detail.SetActive(true);
  }
}

//Mouseスクリプト
public class Mouse : MonoBehaviour {
  public Texture2D cursor;

  pubilc void OnMouseEnter() {
    Cursor.SetCursor(cursor, Vector2.zero, CursorMode.ForceSoftware);
  }

  public void OnMouseExit() {
    Cursor.SetCursor(null, Vector2.zero, CursorMode.ForceSoftware);
  }
}

//Bottleスクリプト
public class Bottle : MonoBehaviour {
  public GameObject name;
  public GameObject hint;
  public GameObject mouse;
  public GameObject detail;

  public void Change() {
    name.GetComponent<Text>().text = "水の入ったペットボトル";
    hint.GetComponent<Dialog>.OpenDialog(detail);
    mouse.GetComponent<Mouse>.OnMouseExit();
  }
}

hintやmouse変数にはそれぞれDialogスクリプトやMouseスクリプトをセットしたオブジェクトが入っていると思ってください。これでBottleスクリプトのChange関数からOpenDialog関数やOnMouseExit関数を実行できます。

このように別のスクリプトの関数を使用する場合はスコープがpublicでないといけません。OpenDialogやOnMouseExit関数のスコープがprivateになっていると「その関数を呼び出す権限がないぞよ」というエラーが出ます。現実世界でも他人のプライベートにずかずか踏み込んでいくのは難しいですからね。親しい者どうしでも一定の遠慮は必要ですし、その辺の忖度はプログラミングの世界でも同じということですね。



GetComponentの省略

省略って言い方が適切か分からないのですが、コンポーネント名を型にした変数を作るとGetComponentを使わなくてもそのコンポーネントをスクリプトで使用できます。

public class Bottle : MonoBehaviour {
  public Text name;
  public Dialog hint;
  public Mouse mouse;
  public GameObject detail;

  public void Change() {
    name.text = "水の入ったペットボトル";
    hint.OpenDialog(detail);
    mouse.OnMouseExit();
  }
}

こんな感じです。ちょっとだけコードを書く量が減りますね。

この時、変数にセットするのはGetComponentの時と同じオブジェクトで大丈夫です。テキストオブジェクトをname変数にセットすれば自動的にnameの中身がそのテキストオブジェクトのTextコンポーネントになります。hintやmouseも同様で、それらのスクリプトがセットされているオブジェクトを変数にセットすればオッケーです。

ただしこっちの書き方だと変数で扱えるコンポーネントが一つに絞られるので、画像の時みたいに画像と大きさを変えたいみたいな時はそれぞれの変数を用意しないといけません。

//GetComponentを使う場合
public class Sink : MonoBehaviour {
  public GameObject close;
  public Sprite open;

  public void ChangeImage() {
    close.GetComponent<Image>().sprite = open;
    close.GetComponent<RectTransform>().sizeDelta = new Vector2(205, 145);
  }
}

//コンポーネント名を型にする場合
public class Sink : MonoBehaviour {
  public Image image;
  public RectTransform size;
  public Sprite open;

  public void ChangeImage() {
    image.sprite = open;
    size.sizeDelta = new Vector2(205, 145);
  }
}

どっちの方が良いんだと訊かれたら、正直どっちでも良いと思うので某沢尻さんのごとく「別に……」と答えるしかないですが、あまり変数を増やしたくないのであればGetComponentを使う方が良いかもしれませんね。

ちなみにスクリプトの中でGetComponentを使って変数にコンポーネントをセットする場合も変数の型はコンポーネント名にする必要があります。

public class Sink : MonoBehaviour {
  public GameObject close;
  public Sprite open;

  public void ChangeImage() {
    Image image = close.GetComponent<Image>();
    image.sprite = open;
    image.color = new Color(0.1f, 0.2f, 0.3f, 1);

    RectTransform rect = close.GetComponent<RectTransform>();
    rect.sizeDelta = new Vector2(205, 145);
    rect.localPosition = new Vector2(167, -97);
  }
}






個人的にはGetComponentとかコンポーネントを型にした変数を作ることは多々あるので、このやり方は遅かれ早かれ必要になるのかなと思います。特に自作のスクリプトの関数を他から呼び出すってのは、ゲームの規模が大きくなればなるほど使用頻度も増えるんではないかと……たぶん。

次回はアイテム一覧を画面内に作りたいと思います。本作ではその部分に等間隔配置の機能を使っているので、それの使い方を紹介するのがメインになると思います。

それでは次回もよろしくお願いしこるすきー。



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