ゲーム開発者が補足するUnity公式チュートリアル~Roll-a-ball編~

Unity は優れたゲーム開発ツールですが、Unity 公式チュートリアル内容は英語で、しかもゲーム開発者には馴染みが深くとも業界初心者には難解な部分が結構あります。そこで、チュートリアルを噛み砕いて少しでも分かりやすく解説を追加してみようと思います。自分はプログラマですが Unity は初心者なので、この記事によって自身の復習としたいです。

この記事は以下の環境で開発しています。
Windows10
Visual Studio Community 2015
Unity 5.5.0f3

同じ環境を構築する場合は、Visual Studio をインストールした後にUnityをインストールするべきです。そうすることで、Unityインストール時に、Visual Studio に Unity 用プロジェクト設定が追加されると思います。

では、さっそく解説していきます。
内容は全翻訳ではなく、あくまで補足という形になりますのでご注意ください。

1. Environment and Player

01. Setting up the Game

00:39 新規プロジェクト作成

5.5では新規プロジェクトの表示画面がかなり変わっていたので念のため補足しておきます。

チュートリアルの表示

Unity 5.5の表示(2段階)

Enable Unity Analytics は「ゲームがどれくらい遊ばれているか」を解析できる機能です。
デフォルトONになっていますが気にせずこのままでも大丈夫です。

指定した Location (保存場所)に、指定した Project name のフォルダーが作られてゲームプロジェクトが保存されます。

Organization は Unity に登録したアカウントを使います。
逆に言えば、アカウントを登録しないと Unity を使うことはできません。

Unity 初回インストール時にアカウント登録とライセンス登録とがあります。
無料の Personal ライセンス登録の選択肢は上から以下のようになっています。

The company or organization I represent earned more than $100,000 in gross revenue in the previous fiscal year.
私が代表する会社または組織は、前年度の総収益が10万ドル以上でした。

The company or organization I represent earned less than $100,000 in gross revenue in the previous fiscal year.
私が代表する会社または組織は、前年度の総収益が10万ドル未満でした。

I don’t use Unity in a professional capacity.
私はプロの立場でUnityを使用しません。

10万ドル以上の収益がある場合は Personal を使うことができません。
個人開発者であれば、ほとんどの人が2つ目の選択肢を選ぶことになるでしょう。
3つ目の選択肢は、いわゆる学生、アカデミーでの利用を意味します。

ライセンスの選択画面

01:03 シーンの保存

シーンを保存する解説で「Scenes」とする場面がありますが、これは別にアンダースコア(←これのこと)を付ける必要はありません。
解説者は、「アンダースコアを付けるとフォルダーの並びで先頭に来るから」と言っている通り、Scenes フォルダ並びだけの問題なので、付けないと不味いというわけではありません。

_Scenes

01:49 3D Object のリネーム

3D Object を作成して名前を変更する場面、Windows10 ではシングルクリックで少し待つか、F2キーを押して変更します。
F2キーはファイル名の変更や、Excelの入力内容の変更など、Windows 上の様々な場面で押すキーなので変更キーとして覚えておくといいでしょう。

名前の変更

02. Moving the Player

01:13 コンポーネントの並び

Sphere Collider (球体の当たり判定)を Rigidbody (物理演算エンジン)の下に並び替える場面がありますが、これは Unity のシステムがコンポーネントを上から順に実行していることを解説しています。

これは、Script (プログラム)を実行するときに、頭の片隅に入れておかなければいけない規則で、例えば、Script 内で当たり判定を使う場合、Collider コンポーネントをその Script よりも手前(上)に配置しなければ使うことができません。

Script が思うように動かない場合、この並びが間違っていることがあります。Script コンポーネント同士でも順序によって結果が変わるので気を付けなければなりません。

コンポーネントは上から順に実行される

03:53 Script のプログラミング

Unity は物を動かしたりするために、Script コンポーネントを使います。
この Script は、Javascript 言語か、C# 言語によってプログラミングされます。

Javascript

Internet Explore、Microsoft Edge、Google Chrome、Firefox などインターネットブラウザなどで主に利用されているプログラミング言語

プログラムされた内容をそのまま逐次実行する動的言語と呼ばれる言語で、処理速度が遅い反面、書いた内容を即実行できるお手軽さがあります。

C#

「Java は著作権問題があるから自分たちで新たに言語を作る」という感じで誕生した Microsoft が C 言語を元に、Java 言語を参考にして作ったプログラミング言語

プログラムされた内容は、コンパイルという機械語に変換作業が行われるので、即実行はできない反面、処理速度は速いという面があります。

特別な理由が無い限り、C# で Script をプログラムすることをおすすめします。
Javascript は処理速度よりも、Visual Studio などのプログラミング開発ツールで「コード補完」が難しいという難点があります。

コード補完

Visual Studio などプログラミングを支援するツール上で、プログラムを書き込んでいるときに、漢字変換のようにコンピューターが入力したい文字を予測して、候補を表示してくれる機能です。

下の図では、「Ga」とだけ入力したときに、その下に「GameObject」「gameObject」と候補と、その簡易解説が左に表示されています。これがコード補完機能です。
これが出来るのと出来ないのとでは、プログラミング速度や楽さが全然違ってきます。
ちなみに、これでリターンを押すと、「Ga」は「GameObject」に自動変換され入力されます。

Visual Studio でのコード補完

ついでに、この上の図のコード内容について解説をします。
Unity を使いたいユーザーの大半は、このプログラムコードが意味不明で拒絶反応を起こしてしまうのではないかと思いますので、この解説で意味をイメージしてもらえれば幸いです。

ネームスペース(名前空間)

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

一番上に、「using」に続いて何か英単語が並んでいますが、これは「ネームスペース(namespace)を利用します」という宣言です。
ネームスペースとは、プログラムファイルの集合体につけた総称で、「using UnityEngine」で言えば、UnityEngine という名のプログラム群です。

ネームスペースのイメージ
UnityEngine

ネームスペースの中にいるプログラムたちのイメージ

UnityEngine というプログラム達が実際どこにいるかという設定は、Visual Studio ではプロジェクトの参照というところで見ることができます。
Unity から Script を開けば、自動的にネームスペースやコンパイル設定などを行った状態のプロジェクトが開くので、細かい設定は気にしなくても大丈夫です。

UnityEngine のネームスペースのファイル設定など
D:\Unity\Roll a Ball\Library\UnityAssemblies\UnityEngine.dll

ちなみに、Visual Studio のコード補完の図では、「using System.Collection」と「using System.Collection.Generic」が灰色になっていますが、これはこれらのライブラリを現在このプログラム内で使っていないので、「必要ないですよ」と色で表現してくれています。

このようなプログラム全体を解析して、不要な所であったり、エラーだと思われる部分を表示してくれるのは、Visual Studio などのプログラミング開発ツールの強みです。

クラス(class)

public class Test : MonoBehaviour {

}

この部分は「{」から「}」までの部分が、「Test」という名前の、参照可能なクラス(public class)であることを示しています。

クラスについては、オブジェクト指向というプログラミングの独特な考え方などを理解しないといけない難解な概念なので、ここでは詳細を省きます。
このクラスが、プログラムファイルひとつひとつの単位みたいなものです。
このクラスがたくさん集まって、ひとつのネームスペースになります。

「: MonoBehaviour」は「MonoBehaviour というクラスを継承しています」という宣言です。
継承についてもオブジェクト指向なので、砕いて説明すると「MonoBehaviour が持っている技を使えるようにします」という感じです。

MonoBehaviour クラスを継承する Test クラスのイメージ

ちなみに、宣言とは誰にしているのかというと、相手はコンピューターです。
「MonoBehaviour 使います」とコードで書くことで、Visual Studio が MonoBehaviour のプログラムをコンパイルしたり、コード補完を支援できるようになったりします。

メソッド(関数=ファンクション)とコメント

	// Use this for initialization
	void Start () {

	}

	// Update is called once per frame
	void Update () {

	}

クラスの中身についての解説です。
「void Start ()」はメソッドです。
クラスがプログラムファイルの単位だとすれば、メソッドはファイル内にある個々のプログラム本体と言えます。
「()」が付くことでメソッドであることを宣言しています。
また、Start メソッドは MonoBehaviour クラスにあるメソッドで、それを使っています。

「//」が付いている行はコメントです。コメントはプログラム対象から除外されます。
「// Use this for initialization」は「void Start ()」に対してのコメントで、「この下のプログラムは初期化のために使います」と説明書きがされています。

Visual Studio でコメントにしたい所は、Ctrl + K、Ctrl + C と2回入力、逆にコメントを解除したい行は、Ctrl + K、Ctrl + U と2回入力します。

余談
Visual Studio がイケてないと思う理由の1つに、このショートカットキーを2回入力させる問題があります。
他のイケてる開発ツールとかだと、Ctrl + / でコメントにして、コメントになっているところで Ctrl + / とすればコメントを解除できるといった感じで、Microsoft は、もっとスマートにショートカットキーを設定するべきだと思います。

メソッドの入力(引数)

この()の間には、メソッドの入力値が設定できます。
Start メソッドのカッコ内に何も書いてないので入力値は無いということになります。
例えば、整数値(int)の入力(inputという名前)がある場合は、以下のように記述します。

void Start (int input) {
}

メソッドの出力(返値)

void は、このメソッドの出力値が void である、つまり空であることを宣言しています。
int Start () であれば、int の出力値、つまり整数の出力値が、
float Start () であれば、float の出力値、つまり小数点の出力値が、
といった感じになります。

void でなければ、Start メソッド内に出力値を設定する「return」を加える必要があります。

int Start () {
	return 0;
}

float Start () {
	return 0.0f;
}

といった感じです。
ちなみにセミコロン(;)は、プログラムのひとつの処理を区別するために文末に付けます。
文章の句点みたいなものです。

余談
Java 言語では同じ内容を以下のように記述します。
「using」が「import」、「:」が「extends」に変わっただけです。
C# が Java によく似ているのが分かると思います。

import System.Collections;
import System.Collections.Generic;
import UnityEngine;

public class Test extends MonoBehaviour {

	// Use this for initialization
	void Start () {

	}

	// Update is called once per frame
	void Update () {

	}
}

長くなりましたが、プログラミングコードの意味不明な部分について、少しは理解できたでしょうか。プログラムは、短い文字や記号とルールによって、非常に深い意味をコードに持たせています。これは入力量を減らすためにやっていることで、覚えるまでが大変です。

04:26 Script のライフサイクル

動画で PlayerController という Script を作成し、その編集を行う場面で、Update と FixedUpdate という2つのメソッドが作成されますが、これについて Unity とどんな関係性があるのかを解説します。

そのためには、まず Unity がどういった流れで動いているかを知る必要があります。
下の図は、その流れの一部、Script のライフサイクルを表したものです。

Script のライフサイクル(Script Lifecycle Flowchart)

これは Unity の Script コンポーネント単体の処理の流れです。
実際ゲームを動かすとき Unity は、シーンの持つ各オブジェクト>オブジェクトの持つ各コンポーネント、といった感じに関連するものを順番に処理していきます。

例えば PlayerController は、Player オブジェクトの処理の順番が回ってきて、コンポーネントを順番に、Transform、Sphere (Mesh Filter)、Mesh Renderer、Rigidbody、Sphere Collider、と実行されて行って、そして PlayerController (Script) で上の図のような処理が実行されます。
それが終わったら、他のオブジェクト、そのオブジェクトのコンポーネント、といった感じに流れて行って、全部のオブジェクトが終わったら、また最初からやり直します。

このループを繰り返すことで、実際のゲーム画面が動いているわけです。
この1つのループのことを、1フレームと言います。

さて、このライフサイクルは MonoBehaviour クラスの中に既にプログラムされています。
PlayerController は MonoBehaviour を継承して作成されます。
そうすると、PlayerController は上の図をプログラムしなくても言いわけです。
Update と FixedUpdate しかプログラムしなくても、実際は見えないところで上の図のプログラムが実行されているわけです。

そして、PlayerController に Update と FixedUpdate メソッドを書くことで、MonoBehaviourの Update、FixedUpdate が実行されたときに、一緒に PlayerController の物も追加で実行されるというわけです。

ちなみに、Start は Script で最初の1度だけ実行され、FixedUpdate は物理演算が実行されたときに呼び出され、Update は Script が実行されたときに毎回実行されます。
そのため、動画内では Update を消して FixedUpdate を使うという形になります。

この Script のライフサイクルは、他のゲームエンジンなどでも一般的な処理の流れです。
ほとんどのゲームプログラムは、入力検知、動作処理、描画を1フレームとして無限ループによって成り立っていることを覚えておくといいでしょう。

05:03 クラス、メソッドのヘルプの呼び出し

Input というクラスが、一体どういった構成なのか知りたいときには、APIと呼ばれる、プログラムの構成情報に特化した書式のヘルプを呼び出します。

Visual Studio では検索したい単語を選択状態にしてから、 Ctrl + Alt + M 、Ctrl + H と2回ショートカットキーを入力します。

動画内ではブラウザで表示

Visual Studio ではタブ内に表示

Description は、このクラスがどういった構成がされているかという大まかな概要が書かれています。
「こんな感じで他のプログラムで利用するといいよ」といったサンプルプログラムも載っていたりします。

ちなみに、Input の Description には「キーボードやマウスなどのデバイス入力情報を扱っているクラスです」みたいな感じで書かれています。

Static Variables は変数、つまり他のプログラムで使えるデータ一覧です。
例えば、Input クラスはacceleration という変数を持っていて、acceleration にはジャイロからの入力情報が入っています。

他のプログラムから Input.acceleration と呼び出せば、他のプログラムでジャイロセンサーのデータが取得できるというわけです。
そういった、他のプログラムから利用できるデータの一覧情報みたいなものが並んでいます。

Static Functions は関数、つまり他のプログラムで使えるメソッドの一覧になります。変数はデータを持っていて、メソッドは特定の処理を実行します。

例えば、Input.GetKey(“up”) というGetKey メソッドの処理は、キーボードの上キーが押されたかどうかを調べる処理を実行します。
GetKey の出力値は boolean、つまり真か偽、押されたか押されてないかを出力します。

ちなみに、変数とメソッドを判別できるようにするため、変数は単語の先頭を小文字、クラスやメソッドは単語の先頭を大文字にしないといけないという C# プログラムのルールです。

12:50 変数と Vector3 の補足

動画内で PlayerController のプログラムが完成していますが、これについて補足しておきます。

PlayerController が Player を動かすプログラム

変数の型

using UnityEngine;
using System.Collections;

public class PlayerController : MonoBehaviour {

    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);
    }
}

まず、「private Rigidbody rb;」について、「rb」は変数です。

変数はデータを持っておけます。
データに関しては数値だけでなく、クラスも持つことできます。
この「持っておく」で一体どこにデータを置いておくのかというと、コンピューターのメモリになります。パソコンのスペック表に出てくる CPU や メモリ(RAM)のそれです。

ゲームを遊ぶためにメモリが必要というのは、このようにプログラムの中で変数が使われて、変数のデータ保持のためにメモリが必要になるからです。

private は外部から非公開(非参照)であることを宣言しています。
このクラスでしか使わない変数であるとあえて宣言することで、メモリを節約したりできます。
オブジェクト指向的には、参照できてしまうと外部からデータ内容を変更できてしまうので、それを防止するために非参照にするという意味もあります。

Rigidbody rb は rb が Rigidbody クラスのデータだけしか持ちませんという意味があります。
変数は予めどんな種類のデータを持つのかをコンピューターに宣言しなくてはいけません。
これは「変数の型」という C# におけるルールなので覚えておきましょう。
Rigidbody 型の変数 rb という感じに言います。

ちなみに、プログラム言語によっては、この変数の型を宣言しなくても良かったり、何でも持てますよ型みたいな型を宣言できる言語もあります。

変数の初期化

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

Start の中身を見ていきましょう。

先ほどの「private Rigidbody rb」というのは、あくまで宣言だけでデータがありません。
ならば、どうやってデータを追加するのかというとイコール(=)で追加します。

イコールの左側に変数、右側にデータを書くことで、変数にデータを入力できます。
ここでは Component クラスが持つ、GetComponent () というメソッドを使っています。
Component クラスは using UnityEngine で宣言したネームスペース中のクラスです。

このメソッドは、この Script コンポーネント(Component)が参照できる他のコンポーネント、この場合で言えば、Player オブジェクトに設定されている コンポーネントを呼び出しています。

「<Rigidbody>」の部分はジェネリクス(ジェネリック)と呼ばれる、メソッドの () の前に <> を付けることで、メソッドの出力値の型を呼び出す側が自由に指定できるようにするためのもので、Rigidbody 型のデータをメソッドに指定しています。

同じ意味で、以下のように書くこともできます。
こうやって書くのが長くて面倒だから、ジェネリクスみたいな書き方が生まれたようなものです。

    void Start ()
    {
        rb = GetComponent("Rigidbody") as Rigidbody;
    }

Start メソッドの中に、変数のデータ入力を書いておくことで、この Script が実行された最初のタイミングで、変数の中に Rigidbody のデータが初期化されます。

こうすることで、他の FixedUpdate で変数 rb を使っても、常に最新のデータが設定されているので、データが空で問題が起きるということがありませんし、Start は一度しか実行されませんので無駄な処理をしないという意味で、このメソッドの中で変数の初期化を行っています。

Vector3の概念

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

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

        rb.AddForce(movement);
    }

FixedUpdate の中身を見ていきましょう。

「float moveHorizontal」と「float moveVertical」の行は、float 型の変数の初期化を行っています。ここでキーボードの上下左右の入力量を変数に設定しているわけです。

「Vector3 movement」の行も、Vector3 型の変数の初期化を行っています。
Vector3 とは三次元空間の X 軸Y 軸Z軸それぞれの座標を指定することで、三次元の方向量を表したものです。
これは Unity の Scene 表示での3色で方向表示されているあれと同じ意味です。
Transform コンポーネントの X、Y、Z の値もこの Vector3 のことです。

Unity の3D表示

コンストラクタ

「new Vector3(moveHorizontal, 0.0f, moveVertical)」の部分で、new とありますが、これは new を付けることで、これはコンストラクタですよと宣言しています。

コンストラクタとはクラスデータ(インスタンス)を新しく作成するメソッドを意味していて、入力値を指定することで、データを設定したクラスデータを作ることができます。

「new Vector3()」とすれば、空の Vector3 インスタンスを作成できます。
「新しく作成する」というのがポイントで、違うデータ設定がされたクラスを複数使うためにこの仕組みがあります。

「rb.AddForce(movement)」は、宣言されていた Rigidbody 型の変数 rb、そしてその rb に設定された Rigidbody クラスの AddForce メソッドが実行されています。

入力値は Vector3 型の変数 movement、それには Vector3 インスタンスが設定されています。この方向値を AddForce は受け取って、その方向に物理演算を実行します。

出力値は void なので何も出力されません(出力されませんが、処理は実行されます)。

14:33 パラメーターの設定と Script との連携

Unity の特筆すべき機能に、Script で書いた内容が Unity と連携するというのがあります。
この機能が「デザイナーやディレクターが直接ゲーム開発をコントロールできる」と話題になりました。

動画内で PlayerController (Script コンポーネント)に、Speed というパラメーターが追加されていて値を入力することができるようになっています。

これは PlayerController 内に、speed という名前の変数を用意したからです。

public class PlayerController : MonoBehaviour {

    public float speed;
}

Unity は 参照可能(public)な変数を自動的に見つけて、コンポーネントの項目として表示し、編集することができるようにします。

Script に public float speed を追加することで
Speed という項目が追加されている

また、Unity 上で設定した値は、プログラム実行時、自動的に PlayerController に伝わり、 PlayerController に Speed が設定された状態でプログラムが動きます。
これは素晴らしいシステムだと思います。

2. Camera and Play Area

01. Moving the Camera

04:27 カメラを移動させるタイミング

Main Camera を Player に追従させようとするために、初期位置の Main Camera と Player の位置関係を offset に持たせて、Player が移動したら Player の移動後の位置 + offset に Main Camera が移動するように Script を作成する所です。

using UnityEngine;
using System.Collections;

public class CameraController : MonoBehaviour {

    public GameObject player;

    private Vector3 offset;

	// Use this for initialization
	void Start () {
            offset = transform.position - player.transform.position;
	}

	// Update is called once per frame
	void LateUpdate () {
            transform.position = player.transform.position + offset;
	}
}

なぜ Update ではなく LateUpdate を使っているのかというと、Script のライフサイクルの図を思い出してください。図では、以下のように並んでいるはずです。

Update
Internal animation update
LateUpdate

これは「Internal animation update」が内部的アニメーション更新、つまり、Update は移動前に実行され、LateUpdate は移動後に毎フレーム実行されるということです。
Main Camera の移動は Player が移動した後に行いたいので、Update だとダメだと言うことになります。

02. Setting up the Play Area

特筆すべきことはないと思います。オブジェクトの複製は Ctrl + D です。

3. Collecting, Scoring and Building the game

01. Creating Collectable Objects

04:00 クラスの乗算と1秒間を正確に知るための方法

浮遊したキューブを自動的に回転させるために、Rotator という Script を作成します。

using UnityEngine;
using System.Collections;

public class Rotator : MonoBehaviour {

	// Update is called once per frame
	void Update () {
            transform.Rotate(new Vector3(15, 30, 45) * Time.deltaTime);
	}
}

演算子のオーバーロード

まず、何気に説明無しに使用されている「new Vector3(15, 30, 45) * Time.deltaTime」、Vector3 型と float型の乗算、これは演算子のオーバーロードという特殊なことをしています。

乗算は数値同士でしか行えませんが、クラスに乗算する方法があります。
それは「クラスに演算子( * など)が使われたときに、クラス内部でどういう計算するか」をクラスでプログラムすることで可能になります。

この場合で言えば、Vector3 クラスの中に、おそらく以下のような特殊なメソッドが用意されているはずです。(実際に Unity のソースコードを見たわけでないので間違っているかも)

public class Vector3 {

    public static Vector3 operator *(Vector3 a, float d)
    {
        a.x = a.x * d;
        a.y = a.y * d;
        a.z = a.z * d;
        return a;
    }
}

このクラスに対して、演算子「*」が使われたときに、このメソッドがオーバーロードされて、通常の四則演算ではなくこのメソッドの内容が適用されます。

入力値は、
a が計算式の左辺、つまり先ほどの例で言えば「new Vector3(15, 30, 45) 」、
d が計算式の右辺、つまり「Time.deltaTime」が割り当てられます。

そして出力値は、
左辺と右辺をプログラムされたように計算して、a を出力します。

こういったプログラムがあるからこそ、Vector3 型と float 型の乗算が行うことができるわけです。
ちなみに、演算子のオーバーロードは C#、C++、Ruby などでしかできない結構特殊な方法です。

1フレームあたりの秒数

キューブを回転させるために毎フレーム実行される Update を使っています。
では1フレームは何秒なのでしょうか。それは、このゲームが実行される環境によります。

シューティングゲームの用語に「60FPS」みたいなものがありますが、あれはこのフレームと関連があります。シューティングゲームのジャンルのひとつである FPS = first person shooting と同じ略称なのでややこしいですが、60FPS とは「60 frame per second」の略です。

つまり、直訳すると「1秒間に60フレーム」という単語になります。
「60FPS だとぬるぬる動く!」というのは、1秒間に Update が60回実行されているため、ゲームキャラクターの移動などがスムーズに動いているということになります。
これがパソコンが重かったりで 5FPS などだと、1秒間に5回しか移動しないため、キャラクターの移動がカクカクするわけです。

このように、フレームはパソコンの環境や状態によって常に実行される回数が変動しているため、時間をフレーム内で計測するのが困難になります。

そこで、Time.deltaTime があります。この deltaTime は1フレームが何秒で実行されたかをデータとして持っています。

60FPS ならば、deltaTime は 1/60 = 0.01667 という値になっていて、1秒間に60回 Update されるので、Update ごとに 0.01667 すれば、1秒後に1になるわけです。
30FPS ならば、deltaTime は 1/30 = 0.03333 という値になっていて、1秒間に30回 Update されるので、Update ごとに 0.03333 すれば、1秒後に1になるわけです。

このように、ぬるぬる動く状態だろうと、カクカク動く状態だろうと、フレームの状態に合わせて deltaTime の値が1秒間に合わせて変動することで、一定の時間間隔(秒速)を求めることができます。

この辺りのフレームと実時間の関係は難解なので、きっちり理解するには少し追加で勉強が必要かもしれません。

難解な場合、今は Time.deltaTime を乗算することで、1秒間に動かす量(秒速)を求められるというのを暗記するだけでいいと思います。

02. Collecting the Pick Up Objects

07:53 if による条件分岐プログラミング

どんなプログラミング言語でも、ほぼ必ず出てくる件によってプログラムを分岐させる「if 文」というのがあります。

if (true か false を出力するプログラム)
{
    この{}内は true なら実行し、false なら実行されない
}

このように、「()」内の値が true (真)か false (偽)かを判定して、プログラムを分岐させることができます。

if 文はプログラミングの基本的な部分で必ず出てきます。
プログラミング言語によって、書き方が微妙にことなったりしますが、分岐させるところはどの言語も同じなので覚えておくといいと思います。

では、実際の PlayerController の実装を見てみましょう。

    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag("Pick Up"))
        {
            other.gameObject.SetActive(false);
        }
    }

「other.gameObject.CompareTag(“Pick Up”)」が true か false かで分岐させています。

この場合、gameObject のCompareTag メソッドは、Tag が Pick Up という文字列ならば true、違うなら false を出力します。
つまり、ぶつかった物体(other)のゲームオブジェクトのタグが「Pick Up」かどうかを判定しているわけです。

この CompareTag の出力結果が true なら、SetActive (false) を実行して、そのゲームオブジェクトのアクティブフラグを Off にします(true なら On)。
そして Off になることで、その物体は非表示になるというプログラムになるわけです。

逆に CompareTag の出力結果が false なら、{}内のプログラムは飛ばされるので、物体のアクティブフラグは On のまま(初期状態は On)というわけです。

12:53 衝突フラグ

PlayerController に衝突時に実行する OnTriggerEnter メソッドを作りましたが、これは衝突した物体のうち片方(あるいは両方)の「Is Trigger」フラグが On でないと実行されません。
そして Is Trigger が On のときは、衝突した後に反発せずそのまますり抜けるようになります。

もし、すり抜けさせず、その上で衝突時に実行したい場合は、OnCollisionEnter を使いましょう。ただし、これを使うと逆に Is Trigger が On の物体と衝突しても何も起こらなくなります。

微妙にややこしいですが、この違いは覚えておくと後々困らないと思います。

Is Trigger にチェックを入れると
衝突時にすり抜けて OnTriggerEnter が実行される

15:17 Rigidbody による Collider 最適化

Pick Up に Rigidbody コンポーネントを付ける理由として、Collider の処理速度に影響するので付けますといった感じの説明がされています。

これは動かすオブジェクトには、Rigidbody を付けないと処理が重くなるということを意味しています。

オブジェクトを動かすと Collider の当たり判定を再計算しなければいけませんが、Rigidbody が付いていない物を動かしてしまうとシーン全体のオブジェクトが再計算されてしまいます。これはUnityがあえてそうしています。

逆にオブジェクトに Rigidbody が付いていれば、動かしたときに再計算されるのは、そのオブジェクトと影響する周りのオブジェクトだけになります。なので処理が軽くなるというわけです。

Use Gravity が On だと Pick Up は床を抜けて落ちていきます。
これは Pick Up の Is Trigger が On のためです。
ちなみに Is Trigger が Off のときは、床にコテンと落ちて回転の影響でズリズリ移動します。

床をすり抜けないようにするために、Is Kinematic を On にします。
これによって、Rigidbody による移動(Transform)をさせないようにします。

Collider の負荷軽減のため Rigidbody を加える

03. Displaying the Score and Text

07:41 文字列の連結と ToString メソッド

文字列は「+」演算子によって、文字をつなげることができます。
これも実は String クラスの演算子のオーバーロードになります。

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

ToString は Int32 クラスのメソッドです。
「int count;」は整数値を宣言していましたが、これは「Int32 count;」という Int32 型のクラスを宣言していることと同じです。
この辺りは少しややこしいので細かい説明は省略しますが、ようは数値や文字列もクラスであってメソッドを持っているということです。

04. Buiding the Game

特筆すべきことはないかと思います。
Unity は様々な環境に合わせてビルドすることができます。

終わりに

ものすごく長文になってしまいましたが、主にプログラミングの知識について補足できたのではないかと思います。

この補足量から考えると、Unity と言えど全くのプログラミング初心者には難しい代物だと言えるでしょう。

チュートリアルの最初も最初で難しすぎて挫折する人も多いかと思います。
その助けに少しでもこの記事が役に立てれば幸いです。


Comments

コメントを残す