link facebook2 twitter youtube

【Unity】初学者からプロへ!Unityの一番の上達法【チュートリアル1】

Unityはじめの一歩

Unity公式チュートリアルを実行してきます。

Unityは公式のドキュメントが充実しています。
さらに、チュートリアルも充実しており、Unity初学者には最適の教材とのことですので、
実際に作ってUnityを触ってきます!

Unityのチュートリアル

チュートリアルページ

Unityのチュートリアルは以下のページから参照できます。

https://unity3d.com/jp/learn/tutorials

Unityのチュートリアルは、実際にデモを作成する『プロジェクト』とTips的なコンテンツがジャンルごとにカテゴライズされている『トピック』で構成されています。

今回は、実際にデモを作成する『プロジェクト』を作成して、Unityのイロハと制作の流れを習得したいと思います。

玉転がし

初めての人は、まず、こちらから始めましょう。(初級)

と紹介されている【玉転がし】を作ってみましょう!!
こんな感じです!

gif

Setting up the Game

早速ゲームを作ってきましょう!
まずはプロジェクトの作成です!新規unityプロジェクトを作成するためにホームスクリーンを出します。

ホームスクリーン

まずプロジェクト名を作成します!
『Roll a ball』としましょう。

次にLocationとの項目にプロジェクトの保存先を指定します。
最後に3D2Dか指定します。今回の【玉転がし】は3Dのゲームなので3Dを選択しましょう!
これで新しいプロジェクトが作成できます!

さらのプロジェクト開いた画面

シーンを保存する

新しいプロジェクトを作成できたらシーンを保存しましょう!

その名の通り、シーンとは場面のことです。
RPGを思い浮かべてください。RPGには街のシーンがあったり、ダンジョンのシーンがあるなど複数の場面(シーン)からゲームが構成されています。このそれぞれがシーンという単位です。

Unityでは、GameObjectを任意の場所に配置したり、コンポーネントを適用したりして、ゲームを作成していきます。その場所の座標情報やコンポーネントのデータなどを保管する場所がシーンです。

今回作成する『玉転がし』では、玉を転がしてマリオにおけるコイン的なものをゲットするというこの場面をシーンとします。

チュートリアルの動画では、Assetsディレクトリにシーンを格納する専用のディレクトリ、_Scenesを作成して、その中にMiniGameを作成しています。

土台を作成する

玉転がしの土台となるオブジェクトを作成しましょう。
メニューバーのGameObject > 3D Object > Planeを選択し直方体を作成します。
HierarchyビューのCreateタブからも作成できます。

作成したGameObjectをリネームします。Groundとします。

GameObjectの位置を初期化する

作成されたGameObjectを初期値に移動させます。

Hierarchyビュー上のGroundが選択されている状態で、Inspectorパネルを表示させると、
Groundに関するプロパティが表示されます。

Transformの欄の右上に歯車マークがあります。
クリックし、メニューを展開してResetを押下します。

すると、Groundが(0, 0, 0)の位置に移動します。
これでゲームを作成する際に、値を操作しやすくなりました!

playerオブジェクト(ボール)を作成

土台を作成した手順と同じで、playerオブジェクトを作成しましょう。
球体を作成するのでSphereを選択します。
playerオブジェクトも場所を初期化しましょう。

InspectorパネルからTransformの欄の右上に歯車マーク > Resetを選択して戻しましょう!

土台にめり込んだような状態になりました。
これは、土台とボールのリセット位置が同じだからです。
現時点では、このままで構いません。

マテリアルを使って彩色

UnityではGameObjectにマテリアルを使って彩色します。
GameObjectの色のプロパティに色情報を持ったマテリアルを適用するイメージです。

テキスチャを使って彩色することも多いですが、今回は単にマテリアルを使って描画します。

まず、マテリアル用のディレクトリを作成しましょう!
シーン用のディレクトリ作成と同じ要領で、MaterialsディレクトリをAssetsディレクトリに作成してください!

  1. UnityのProjectビューの上部にあるCreateタブからForderを選択しディレクトリを作成します。
  2. Materialsにリネームしてください。
  3. Projectビュー上のMaterialsディレクトリが選択された状態から改めてCreateタブを押下し、Materialを選択、マテリアルを作成してください。マテリアルディレクトリが作成されます。

cap

  1. 作成されたマテリアルディレクトリをBackgroundに変更します。
  2. InspectorのMain Mapsの箇所のAlbedoの色を任意の色に変更します。
  3. Inspectorの下にあるプレビューウィンドウを表示させます。Backgroundとあるバーを上方にドラッグします。
  4. ProjectビューのBackgroundをSceneパネルの土台にドラッグします。するとマテリアルのが土台に適用されます。

土台の色の描画ができました。

ディレクショナルライト

Unityのプロジェクトを立ち上げると、自動的にライトが立ち上がります。
これをディレクショナルライトといいます。
ディレクショナルライトは太陽を模したライトです。

このディレクショナルライトを調整して、玉の影の加減を変えていきます。

  1. HierarchyビューからDirectional Lightを選択します
  2. InspectorのTransformのY axisを60にします。

いい感じの影に調整できました。

GameObjectを動かそう!

玉の動き方を考えてみましょう。

  1. 玉を土台の上を転がる
  2. 空中に浮かんで漂ってはいけない
  3. コイン的なものにぶつかったときはそれらを収集する

この条件を満たす必要があります。
そのためには、Physicsを使います。

Physicsは、Unityに内蔵されている物理エンジンです。

物理エンジンとはなんでしょうか?
簡単に言えば、複雑な物理計算を行ってくれるライブラリです。
ゲームを実装するには重力、摩擦係数など計算しないといけないことがたくさんあります。
それはとても難易度が高く、自前で実装しようとすると時間もスキル習得のコストも膨大になります。

Unityに内蔵されているPhysicsは物理エンジンのライブラリです。
これに重力などの計算を任せてしまえば、リアルな動きを実装でき、私たちはデザインやゲーム設計に時間と労力を割くことができます。
Unityを使うことによって初心者でも簡単にゲームをつくることができるという背景には、Physicsなど便利なライブラリがたくさん用意されているからなのですね。

Physicsは、RigidbodyコンポーネントColliderコンポーネントから構成されます。
簡単に言えばRigidbodyコンポーネントは『力の計算(重力や摩擦などの計算)』を担当し、 Colliderコンポーネントは『当たり判定』を担当します。

Rigidbody (リジッドボディ) はオブジェクトに物理挙動を可能にするためのメインコンポーネントです。リジッドボディを加えた瞬間から、オブジェクトは重力の影響を受けるようになります。1つ以上の Collider (コライダー) コンポーネントを加わえれば、オブジェクトは衝突の影響によって動くようになります。

Collider コンポーネントは、物理衝突のためのオブジェクト形状を定義します。

作っている玉転がしも、希望の挙動を実現するには、RigidbodyコンポーネントColliderコンポーネントの機能を使う必要があります。

まず、対象となるGameObject(玉)に、リジッドボディを効かせましょう。
ところでコンポーネントとはなんでしょうか?

コンポーネントというのはこれのことです。
cap

GameObjectとは、簡単に言えば箱です。入れ物です。
その中には、その箱の状態や情報などのプロパティを入れて、意図するオブジェクトに構成していきます。
コンポーネントは、プロパティを定義するために使います。
例えば、すべてのゲームオブジェクトには自動で Transform (トランスフォーム)コンポーネントが付いています。キャラクター、ライトなどすべてのゲームオブジェクトには位置情報や大きさの情報が必要だからです。
それらを変更するためにトランスフォームコンポーネントを使います。
それと同様に、Rigidbodyコンポーネントはリジッドボディのプロパティを設定するために使います。
GameObjectは、コンポーネントとスクリプトで動きや描画を定義していきます。

玉にリジッドボディ(力の計算)をアタッチする

Hierarchyビュー上の玉(Player)がハイライトされていることを確認して、メニューバーからComponent > Physics > Rigidbodyを選択してRigidbodyコンポーネントをPlayerにアタッチします。
すると、PlayerのInspectorにRigidbodyが追加されます。

玉を思い通りに動かそう

キーボードの操作と玉を連動させたいです。
そのために必要なのが、スクリプトです。

ゲームオブジェクトの動作はアタッチされた Component により制御されます。Unity ビルトインのコンポーネントは多目的で使用できるものの、カスタムのゲーム機能を実装するには十分でない場合が多いです。Unity により Script を使用してカスタムのコンポーネントを作成し、ゲーム中のイベント開始、時間の経過に伴ったコンポーネントプロパティーの修正、ユーザー入力操作への反応ができます。

Unityにもともと実装されているコンポーネントは多種多様で高機能ですが、希望する動きを実現する時に不十分である場合もあります。
その際、希望する挙動などを自前で実装する必要があります。それは、スクリプト(GameObjectに命令するための台本のようなもの)を記述してGameObjectにアタッチします。

Unityのスクリプトには、C#、Javascriptが一般的に使われます。
Unityユーザの大半がC#を使っているので、今回はC#を使います。

スクリプトを作成

まずスクリプトを作成します。
メニューバーから作成する方法など方法はたくさんありますが、今回は、作成とGameObjectへのアタッチを同時に行います。

  1. HierarchyビューからPlayer(玉)を選択します。
  2. Inspectorの下部にあるAdd Componentを押下します。
  3. ドロップダウンリストにあるNew Sceiptを選択します。
  4. ファイル名と言語を指定します。
  5. Create and Addを押下して作成完了です。

スクリプトを書こう

作成したスクリプトを編集します。
Unityでデフォルトで起動するエディターはMonoDevelopが内蔵されています。

作成したスクリプトファイルをダブルクリックするとMonoDevelopが立ち上がります。

cap

まず、今回の玉転がしでは、最初に書かれているStart関数Update関数は利用しないので削除します。

スクリプトに何を記述して何を実行させたいでしょうか?
まず、フレームごとにキーボードが押されたかチェックしたいです。そのキーボードの入力に対してGameObjectを動かします。
どのように、キーボードの入力を感知し、またそれを受けてGameObjectを更新するのでしょうか?

2つの選択肢があります。
UpdateFixedUpdateです。

Updateはフレームが更新される前に呼ばれます。
FixedUpdateはPhysicsの計算が行われる前に実行されます。

スクリプトを適用する玉は、Physicsを利用します。
なので、FixedUpdateを利用します。

では早速、FixedUpdateの中に処理を記述していきましょう。

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

public class PlayerController : MonoBehaviour {

	public float speed;

	private Rigidbody rb;

	void Start ()
	{
		rb = GetComponent<Rigidbody>();
	}

	void FixedUpdate() {
		float moveHorizontal = Input.GetAxis("Horizontal");
		float moveVertical = Input.GetAxis("Vertical");

		Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);

		rb.AddForce(movement * speed);
	}
}

まず、重要なのはInput.GetAxis("Horizontal")です。
Inputはキーボード操作に関するクラスです。GetAxisはそれに用意されているメソッドです。

引数に"Horizontal"を入れることで横方向の移動の実数を取得できます。

その値を変数、float moveHorizontalに格納しています。
縦方向の移動についてはInput.GetAxis("Horizontal")を使い、
moveVerticalに格納します。これらは後に、キーボードの操作に連動して玉を動かす時の値しとして使います。

private Rigidbody rb
とはなんでしょうか?

先程も言いましたが、GameObjectに物理法則を適用させるためには、Unityが用意している物理エンジンのライブラリを利用する必要があります。Physicsはその一つです。力の計算を適用するためにはRigidbodyを使います。そして先程、UnityのGUIから玉にリジッドボディコンポーネントを適用しました。
しかし、それだけでは不十分です。
このスクリプト(PlayerController)と玉に適用したリジッドボディコンポーネントを連動させないといけません。
まず、

private Rigidbody rb

という変数を用意します。
Rigidbodyクラスの変数rbです。
リジッドボディコンポーネントへ今記述しているスクリプトの参照を保持するために変数を用意します。

Start関数には以下の記述をします。

void Start ()
{
    rb = GetComponent<Rigidbody>();
}

Start関数は、スクリプトが実行された最初の1フレーム目に一回だけ実行される関数です。
この関数の中で、リジッドボディコンポーネントへスクリプトをアクセスするための記述を書き、先程作った変数rbに格納しています。

GetComponentというのは、他のコンポーネントへアクセスするために使います。
この例であれば、リジッドボディコンポーネントがあるかどうか判定して、もしリジッドボディコンポーネントあるのであれば、アクセスできるように参照させます。
これで、GUIから作成したリジッドボディコンポーネントに対して、PlayerControllerスクリプトから操作できるようになりました。

では、GameObject(玉)に対してスクリプトで記述した処理が効くようになったところで、実際に玉を動かす処理を書きましょう!

FixedUpdate関数には、

Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);

rb.AddForce(movement * speed);

とあります。
rbは、rbという変数名で参照されるリジッドボディコンポーネントを指しています。
このリジッドボディコンポーネントに対し、addForceを使います。

AddForceはリジッドボディに対して、力を加えます。
キーボードの操作の取得は、変数moveHorizontalと変数moveVerticalに保存してあります。
毎フレームのこの変数の値を玉に渡して動かします。その際に使うのがaddForceです。

addForceを使う時に、もう一つ知っておくと便利なものクラスがあります。
Vector3です。
vector3を端的に説明すれば、3つの小数を一つの箱に保持します。
3D空間の位置情報を示すのに適しているため、3D空間でものを動かす際によく用いられます。
vector3の引数は、(x, y, z)です。

変数moveHorizontalと変数moveVerticalをvector3クラスのインスタンスに入れ、その値をAddForceに渡すことでリジッドボディコンポーネントを持つ玉が動くという算段です。

変数movementはVector3クラスのインスタンスです。
movementに変数moveHorizontalと変数moveVerticalを代入しましょう!

そうすることで、変数movementはフレーム毎に変数moveHorizontalと変数moveVerticalの値がアップデートされます。

その3D位置情報をaddForceに渡して、動かします。

実際の画面で確かめてみる

Unityに戻り、ゲームを試してみましょう!
実行してわかるのですが、玉のスピードが遅いです。
AddForceに渡す値が小さいのが原因です。ですので、任意の係数を掛けていい感じのスピードになるように調整しましょう!

その係数が変数speedです。
この変数は実際にゲームを実行しながら値をいい感じに調整します。

ここで知っておきたい便利Tipsをご紹介します!

Unityで実行して玉を動かし実際の挙動をみて、monodevelopで値を調整する、という一連の作業を繰り返し行うのは、めんどくさいですね。
できれば、Unityの画面上で作業が完結できれば楽なのですが、、、

こういうときは、変数speedの前にアクセス修飾子publicを付けてください。
publicは『アクセス無制限』を意味します。
Unityにおいては、その値の操作をUnityの画面上で行えます。

Inspectorのスクリプトの箇所にpublicを指定した変数が表示されるようになります。

cap

これは便利!Unity上で、変数speedを調整出来ます!
speed変数を操作していい感じのスピードに調整してください!

カメラを調整しよう!

今の設定では、カメラは動きません。
これでは、玉が動いた時にカメラが捉えらなくなり、ゲームプレイヤーからすればプレイが困難になります。
玉とカメラを紐付けて、玉の動きに合わせてカメラを動かすことができれば常に玉を確認でき、快適にプレイできそうですね。

カメラの位置を変える

まずはカメラの位置を変えましょう。
Hierarchyビューからカメラを選択します。

  1. TrasformのPositionのYを10
  2. TrasformのRotationのXを45

に変更します。

カメラを玉の子要素にする

HierarchyビューのカメラをPlayerにドラッグアンドドロップします。
カメラがPlayerの子要素になりました。

これにより、玉が動くことで、その子要素のカメラも同じように動きます。
まるでCSSで、親は要素に適用された処理を子要素が継承するようなイメージですね。

カメラと玉の連動も完了したことですし、ゲームを実行して確かめてみましょう!

あれ?カメラが回転して、天地がぐるんぐるん回ってしまいます。。。

コイン的なのを収集できるようにする

玉とコインの衝突を検知できればそれをイベントとして収集することができそうです。
このゲームエリアには様々なGameObjectがあります。
地面・玉・壁・コイン的なの。
これらを識別して玉とコイン的なのとの衝突を正確に判定しないと駄目です。
玉が何かにぶつかったらぶつかったものは消滅するという動きだと、ゲームが始まった瞬間に地面がなくなってしまいます。
そのあたりの判定をスクリプトで記述していきましょう!

ここでTipsです。
Vector3など使い方を調べたい時、ホットキーを使って公式のリファレンスを参照していました。
違う方法でリファレンスを参照する方法を紹介します。

Unityの画面のInspectorを見てください。
ここには各コンポーネントが並べられています。
各コンポーネントのヘッダー箇所には、コンポーネントを便利に使うために機能を有しています。
右の方にある本のようなアイコンがあります。
これは当該コンポーネントのリファレンスへのリンクが参照されています。
ですので『このコンポーネントなんやねん?』となった場合は、その本をクリックして調べることができます。

また、この場合はスクリプトのリファレンスではなく、コンポーネントのリファレンスに飛びます。ご注意あれ。
「ちゃうねん。コンポーネントの情報ちゃうねん。スクリプトの情報やねん、ほしいの」という場合は、ページ上部の
SWITCH TO SCRIPTINGを押下することでコンポーネントからスクリプトのリファレンスに移動できます。

OnTriggerEnterを使おう

OnTriggerEnterを使います。
最初に想像してみてください。
勇敢な配管工が見事なアーチでジャンプを決めてコインを取る姿を。

このコードは、物理計算することなく、玉とコインの衝突を判定してくれます。
リファレンスにあるサンプルをコピーしてスクリプトに貼り付けます。
このサンプルコードでは希望の挙動を実現してくれないので、あとで書き換えます。

    void OnTriggerEnter(Collider other) {
        
    }

OnTriggerEnterは、Unityにより呼ばれて、あるGameObjectが最初に触れた瞬間にイベントが走ります。
引数はColliderとotherです。otherはこの場合Pick Upにしたいです。このスクリプトがアタッチされているGameObject(玉)とPick Upが触れた瞬間に関数の処理が走るようにすれば希望の挙動になります。
問題は、Pick Upのみをotherとして認識させることです。
もし床をotherとして認識させたらゲームが始まった瞬間に処理が走ってゲームが終わってしまいます。

Pick Uのみotherとして判定させるためにGameObjectのリファレンスを見てみましょう!
GameObjectをスクリプトで操作する時のアイテムがたくさんありますね。

ここでは、CompareTag,SetActiveを使いましょう!
Tagは文字列をGameObjectに紐付けることで他のTagとの違いを判定することができます。
CompareTagは文字列をGameObjectに紐付けることで、効率的に複数のTagを判定することができます。

Tagを使って、そのタグの値を判定する方法は、

if (gameObject.tag == "Some String Value")

で可能です。
しかし、これより簡単に判定する方法があります。それが、CompareTagです。
CompareTagはUnityにもともと実装されているコードです。

if (other.gameObject.CompareTag("Pick Up"))

これで判定します。

サンプルコードでは、Destroyメソッドを使って該当のGameObjectをシーンから削除する処理が書かれています。作っている玉転がしでは削除する必要はなく、非表示にするだけで大丈夫そうです。
表示非表示は、SetActiveでコードから操作できます。

gameObject.SetActive(false);

引数の論理値で表示非表示を切り替えることができます。

では、コイン的なのをタグ名Pick Upを付けてさっきのスクリプトで判定できるようにしましょう。
コイン的なの全部に反映させたいので、一括で反映したいので、Prefabにタグ名を付けます。

PrefabのPick Upがハイライトされている状態から、InspectorでTagの箇所を見ます。
ここからPick Upを選択したいです。

Add tagを押下してカスタムタグを作成します。
もちろん名前はPick Upです。

カスタムタグが作成できたら、Unityに戻って、PrefabのPick Upをハイライトさせ、さっきのタグの選択をPick Upにします。

これでインスタンス全部にタグPick Upを指定することができました。

ではプレイしてみましょう!

うーん、壁に当たった時のような反応になりますね。。。

検証する前にUnityの物理計算システムについて考察してみましょう。
Unityでは、2つの衝突の大きさを重ねることはできません。
2つもしくはそれ以上のオブジェクトが衝突する際、Unityはそのオブジェクトの速度や回転、形を衝突の条件として計算します。
その際に重要なのが、オブジェクトが静的なのか動的なのかということです。
静的コライダーとは、シーン上で動いてないものです。例えば壁や床です。
動的コライダーとは、シーン上で動いているものです。例えば玉や、車です。

静的なオブジェクトが衝突した際は、その衝突の影響は受けません。
しかし、動的コライダーである玉は衝突の影響を受けます。
コイン的なのと玉がぶつかった時、壁に玉がぶつかったような反応になりました。

静的コライダーに、動的コライダーをぶつけるとUnity物理エンジンは、その影響を与えます。
計算上は衝突の影響を受け、その影響を計測し続けているのですが、実際の挙動として衝突が起こったとはみなされません。

静的コライダーに動的コライダーをぶつけた場合、静的コライダーがぶつかったとして判定されるので、オブジェクトは動きません。

では、この場合、どうやって衝突を判定させればいいのでしょうか?
トリガーもしくはトリガーコライダーを使います。

トリガーもしくはトリガーコライダーを使うことで、OnTriggerイベントで衝突を検出することができます。
これとうまく使えば、例えばアドベンチャーゲームで、隠し扉を見つけた時に、ドアと主人公の衝突を検知し、ミニマップなどに『隠し部屋を見つけました』などと表示することができます。便利ですね。

では早速、トリガーに変更しましょう。
Pick UpをハイライトしてInspectorを見ます。
Box Collideris Triggerをチェック入れます。
それでトリガーに変更できます。
全部のPick Upに適用したいのでPrefabのPick Upのis Triggerを操作しましょう。

プレイモードに移動して実行してみましょう!

いい感じですね。
ただ、もう一つ考えないといけないことがあります。それは、UnityのPhysicsの最適化です。
物理エンジンの処理はとても容量を使います。なので、最適化して処理を軽くしましょう。

Unityでは、シーン上すべてのGameObjectの情報をキャッシュしています。
静的オブジェクトであれば、動くことはないので、フレーム毎に情報をキャッシュを削除してもよさそうです。
Pick Upの情報がキャッシュとして残されている理由は、回転しているからです。

Unityに、『これはフレーム毎に位置や情報を再計算する必要はないよ』と教えてあげましょう。
リジッドボディやコライダーが設定されているGameObjectは、動かされることを期待されています。
Pick Upはボックスコライダーはアタッチされていますが、リジッドボディはアタッチされていません。
この状態だと、フレーム毎に情報を再計算されます。
では、解決していきましょう!
PrefabのPick UpのInspectorの下部、Add ComponentからPhysics > Rigid Bodyを選択します。これで静的コライダーから動的オブジェクトに変更されました。

保存して実行してみましょう!
Pick Upが床から落ちました。。。
修正が必要ですね。

リジッドボディを適用したことで重力が発生し、また、トリガーにしたので床との衝突判定もなされず下に落ちたのです。
リジッドボディコンポーネントを見てみましょう。
ここには、重力を向こうにするパラメータがあります。Use Gravityというものです。
これは、対処療法的な処理です。確かに、Pick Upは下に落ちなくなりましたが、以前、その他の物理法則の影響を受けます。
Is Kinematicを選択します。
これでこのGameObjectはKinematic rigid bodyになりました。
Kinematic rigid bodyはどんな物理法則の影響も受けません。そしてTransformでアニメーションすることもできます。
このKinematic rigid bodyのおかげでis Triggerで衝突判定イベントを実行できますし、同時に物理法則に影響なくアニメートすることもできます。

おさらいです。
静的コライダーは動きません。壁や床のようなものです。
動的オブジェクトは動きます。そして通常、リジッドボディをアタッチすることで物理法則が働きますが、Kinematicリジッドボディを使うことで物理法則に関係がない動かし方が可能になります。

スコアと文字を表示しよう!

収集したコイン的なののスコアとテキストを表示させましょう!

スコアを保持するツールが必要です。あとスコアをカウントして更新するツールも必要になります。
では早速、玉にそれを実現するスクリプトを記述していきましょう!

玉のInspectorのスクリプトを開きましょう!

プライベート変数を宣言します。これは、スコアを保持する変数です。
スコアは正の整数値なのでint型を使い、countと命名します。

初期値はもちろん0です。収集するごとに1ずつインクリメントされます。
この変数はprivateです。Inspectorから操作せず、このスクリプトのみで操作するので、publicにはしません。

start関数はスクリプトが実行されたタイミングで一回のみ実行される関数です。
なので、この関数内に、初期値である0を代入します。

	void Start ()
	{
		rb = GetComponent<Rigidbody>();
		count = 0;
	}

Pick Upを回収できたら、先程作ったcountの数を一個増やしたいです
Pick Upの表示非表示は、OnTriggerEnter関数で管理していました。

Pick UpというタグがつけられたGameObjectにPlayerが衝突したら、Pick Upを非表示にするのでしたね。
非表示にするタイミングでカウントが増えることが望む挙動なので、ここにカウントアップするコードを書きます。

これでcountに回収した数をリアルタイムで管理することができました。

数を表示させよう!

では、countの値と、「現在の取得数は・・・」的なテキストを表示させましょう!
Unity's UI Toolsetを使います。

Hierarchyビューから、Create > UI > Textを選択します。

Canvasという親要素、Text・EventSystemという子要素が生成されました。
UI ToolsetのTextはCanvasの子要素でないと思ったとおりの挙動にならないので注意してください!

Textをリネームして「Count Text」にします。
でがカスタマイズしていきます!

デフォルトのテキストはちょっと暗いです。見にくいので白色に変えましょう!
InspectorのColorスロットから変更できます。

テキストの内容も変えましょう!
Count Textにします。

このテキスト、今、ゲームエリアの中心にあるので、邪魔ですね。
なぜ中心に表示されるかというと、親要素であるCanvasの中心に表示されるからです。
UI ToolsetのTextのInspectorのTransformの箇所、他のGameObjectのTransformとは内容が違います。
Rect Transformです。
これは、多目的に使用することができるUIシステムを実現するために特化されたものです。
アンカリングとポジショニングが含まれています。

左上に表示しましょう。
一番簡単に左上に移動する方法は、アンカーのプリセットメニューを使うことです。
Inspectorのプリセットボタンをクリックしてください。

ピボットしながらポジショニングしたいのでaltとshiftを押しながら、左上を選択してください。
すると、テキストが左上に移動します。

ただ、今のままでは、左上にピタピタに配置されています。
ちょっとスペースをもたせましょう!

InspectorからPos.x: 10 Pos.y: -10に設定しましょう!
いい感じですね。

では、回収したPick Upの数を表示するスクリプトを書きましょう!

PlayerControllerスクリプトに戻りましょう!
まず、スクリプトでUI ToolsetのTextを認識させる必要があります。

UI Toolsetを使いますというのを宣言し、このファイルにインポートします。
 ES6のimport的なことでしょうか。まあ、JSの方が後乗りですがね。。。

using UnityEngine.UI;

これを先頭に書きましょう。
まず、テキストのための変数を用意します。

public Text countText;

この変数に表示するテキストを格納します。
なので、これもStart関数の中で初期値を設定します。

	void Start ()
	{
		rb = GetComponent<Rigidbody>();
		count = 0;
		countText.text = "Count: " + count.ToString ();
	}

この記述は、count変数の初期化後に記述してください。
何かしらの値が設定されていないとテキストを表示出来ませんからね。

このテキストは、Pick Upを獲得したすぐに更新しないといけません。
では、その記述をどこに書いたらいいでしょうか?

もちろん、OnTriggerEnter関数の中ですね。
では記述します。

	void OnTriggerEnter(Collider other) {
		if (other.gameObject.CompareTag ("Pick Up"))
		{
			other.gameObject.SetActive(false);
			count = count + 1;
			countText.text = "Count: " + count.ToString ();
		}
	}

countText.text = "Count: " + count.ToString ();を二回書いてしまいました。
これは、DRYの法則に反します。

この処理を関数に独立させて、その関数を呼び出すことで同じ挙動を実現しましょう。
それがよりエレガントです。

	void SetCountText()	{
		countText.text = "Count: " + count.ToString ();
	}

そして

    countText.text = "Count: " + count.ToString ();

と書いていた箇所で

    SetCountText();

と呼び出せばエレガントになりました。

では、Unityに戻りましょう。
玉のInspectorには、先程追加したCount Textのスロットが追加されています。

このCount Textプロパティと、UI ToolsetのTextをアタッチさせたいですね。
ドラッグアンドドロップでUIをCount Textプロパティのスロットにドラッグアンドドロップして完了です。

すべてのPick Upを回収し終えた時に何かテキストを表示させたいですね。
先程と同じ手順で、UI ToolsetのUIのテキストを作成します。
自動的にさっき作ったCanvasの子要素になります。

これも文字色を白にしましょう。視認性がいいですからね。
文字サイズを大きくしましょう!24くらいですかね。
これもInspectorから可能です。

文字のアライメントを真ん中にしましょう!
Wordなどでおなじみの中央にするボタンがInspectorにあるのでそれを選択してください。
次にプレースホルダーの文字を変えましょう。この文字は後でスクリプトによって変更されるのですが、
とりあえずこのTextのUIがどれが識別しやすくするためにプレースホルダー文字を設定します。
Win Textにしましょう!

このテキストですが、中央すぎると見栄えが・・・
ちょっと上に表示したいかもです。
この場合も、InspectorからPos.y: 75に設定しましょう!
ちょっと上に移動していい感じです。

スクリプトに移動します。
今の状態では、UI ToolsetのUIのテキストはスクリプトから参照されていないので、操作することが出来ません。

なので、その処理をしましょう。

    public Text winText;

を作成します。
start関数に初期文字を設定します。

    winText.text = "";

今回は空にします。
空なのでゲームエリアには存在しますが、見えない状態です。

Pick Upの数は、12です。
なので、count変数の値が12になった時に、このテキストを更新すれば希望の挙動になります。

	void SetCountText()	{
		countText.text = "Count: " + count.ToString ();
		if (count >= 12) {
			winText.text = "You Win";
		}
	}

12個回収できれば、You Winと表示します。

ではUnityに戻りましょう。
このWin Textをスクリプトのテキストと連動させます。
PlayerのInspectorからドラッグアンドドロップで可能でしたね。

ではプレイしてみましょう!
うまく行きました!

cap

ゲームをビルドしよう!

Unityの便利なところに、様々なプラットフォームで実行できるように設計されています。
ビルドする前に、シーンを保存します。

まず、Build Settingsウィンドウを開きます。
メニューバーからFile > Build Settingsで表示できます。

Unityマークが右についているものは、現在選択されている設定です。
ウィンドウの右にはビルドの設定があります。

今回はスタンドアロン版でビルドするので、
Pc, Mac & Linux StandaloneでOKです。

ビルドするシーンを選ぶ

ウィンドウの上の、Add Currentボタンを押下してビルドしたいシーンを選ぶことができます。
もしくは、Projectビューから該当のシーンをドラッグアンドドロップすることでも選択できます。

ゲームに組み込みたいシーンが選択できれば準備万端。
Buildボタンを押下してビルドしましょう!

保存場所と名前

Buildボタンを押下すると、名前と保存先を聞かれます。
任意の名前、保存先を選択してください。
ちなみに、ビルド設定でマックにした場合、.appファイルとしてすべてのアセットがバンドルされます。
windowsではバンドルされず、ビルドされたアプリケーションと同じ階層にアセットが保存されます。

実行してみる!!

では、ビルドしたアプリケーションを実行してみましょう!
解像度の選択をしていざプレイ!

cap

作ったとおりの挙動がされていることがわかります!
完成です!お疲れ様でした!

まとめ

このチュートリアルでは、GameObjectの設置、位置の設定、コンポーネントの追加、独自の動きをスクリプトで実現しました。

ライト、カメラ、コライダー、トリガー、リジッドボディも触りました。
どれも簡単にさわったのみですが、Unityを使うにあたり必要な知識を広く網羅できました。

とても充実したチュートリアルでした。
次のチュートリアルも楽しみです。

© eggplant