ボール飛ばしゲーム②

Unity入門2

現在の確認

  • 現在は、ボールが転がっていくだけかと思います。
  • これを少しゲームっぽくしていきましょう。

完成イメージ

手元から別の球を打って、赤球を弾き飛ばす。

どんどん赤球が、転がってくる。

マウスクリックで、別の球が飛んでいく。

  • 一つのボールだけではなく、つぎつぎに落ちてくるボールを、別のボールで撃って弾き飛ばすようなゲームを作ります。

玉を複数出す

Prefabとは?

設計図のような物

大きさ:10

Sphere(玉)の作り方

色:赤

摩擦係数:0.5

Prefab(プレハブ)

  • まず、ボールを複数出す事からはじめましょう。
  • Unityでは、「Prefab(プレハブ)」という便利な機能がありますので、これを使います。
  • PrefabはUnityでは、非常に重要な機能ですので、しっかり理解しましょう。

Prefab(プレハブ)

ゲーム世界

Sphere①

Sphere③

Sphere②

Prefabを元にして、同じ玉を作りだす。

大きさ:10

Sphere(玉)の作り方

色:赤

摩擦係数:0.5

Prefab(プレハブ)

  • 以下はPrefab(プレハブ)のイメージです。
  • Prefab(設計図)を元にして、実際のボールを作り出します。

Prefab(プレハブ)

Prefabの場合

クラスの場合

クラス

オブジェクト①

オブジェクト①

Prefab

オブジェクト①

オブジェクト①

※補足

Prefab自体も、内部的にはクラスとオブジェクトで出来ています。

Prefabとは言わば、クラスをより抽象度の高いレイヤーで扱えるようにした機能と言えます

(つまりは、使いやすくしたということ)

  • ここまでの説明で、「設計図?それってクラスじゃね?」と思った人。ちゃんと覚えてますね!
  • たしかにPrefabはクラスと非常に似た機能です。ただ、ひとつ大きな違いがあります。
  • クラスの場合【クラスからオブジェクト(実体)】は作れましたが、【オブジェクトからクラス】は作れませんでした。(というかクラスがないとオブジェクトが作れないので当然です)
  • でも、Prefabの場合は【オブジェクト(実体)からPrefab】が作れます。
  • これは非常に重要な違いです。

Prefab(プレハブ)

ゲーム世界

Sphere

実際の”玉”を元にして、Prefab(プレハブ)を作ります。

大きさ:10

Sphere(玉)の作り方

色:赤

Prefab(プレハブ)

摩擦係数:0.5

  • では早速やっていきましょう。
  • Prefab(プレハブ)は、白紙の状態から作ることも出来ますが、今回はすでに見本となるSphere(スフィア)がありますので、それを元にPrefab(プレハブ)を作ります。

Prefab(プレハブ)

コッチにほり込む

  • 実際の作業です。
  • Hierarchy(ヒエラルキー)のSphere(スフィア)をProject(プロジェクト)ビューにマウスで移動させます。

Prefab(プレハブ)

これが作られたPrefabです。

球(Sphere)の「設計図」が作られたという事です。

  • これだけでPrefab(プレハブ)は完成です。
    • (この超絶ラクチンなところがUnityの長所ですね)

  • Material(マテリアル)の[Sphere]と紛らわしいですが、小さい方がPrefabです。

Prefab(プレハブ)

Prefabというのがわかるように、末尾に「Pre」を追加しました。

※ほんとは何でも良いですが、ここではこのルールでいきます。

  • 今のままだと、名前がわかりにくいので変更しておきましょう。

Prefab(プレハブ)

名前がPrefabと同じになっています。

Delete(デリート)を選択

  • Prefab(プレハブ)があれば、元のSphere(スフィア)は必要なくなりますので、削除しておきます。

Prefab(プレハブ)

Prefabからオブジェクトを作りたい時は、

Project(プロジェクト)SpherePreHierarchy(ヒエラルキー)ビューにマウスで移動させるだけです。

  • もし、もう一度復活させたい時は、先ほどのPrefabから作ってしまえば良いので安心です。試しに復活させて見ましょう。
  • Prefabから実際の球を作る方法は、以下にようにします。

Prefab(プレハブ)

  • 何度も移動させれば、何個でも作る事が可能です。
  • この状態で、ゲームを再生してみるとわかりますが、複数の玉が同じ場所に、”同時に”出てきます。
  • 座標までコピーされるので当然ですね。
  • スクラッチいうと「複製」をしまくった状態ですね。
  • これだと困るので、今増やしたオブジェクトはすべて削除してください。

Prefab(プレハブ)

​ちなみにUnityでは、以下のプログラミング言語が使えます。

  1. C#(シーシャープ)
  2. Java Script(ジャバスクリプト)
  3. Boo(ブー)※ python ⇛今のバージョンでは使えない

この内、どのプログラミング言語を使うかは自由ですが、現在、企業などのUnity開発では、C#が主流になっています。

また、C#以外はUnity独自ルールの部分が多く、一言でいうと「クセが強い」のでC#で進めていきます。

  • さて、目的を再確認しておきましょう。
  • 今やりたい事は、「最初から同時に」玉を出す事ではなく、ではなく「1つずつ、自動的に」の玉を作り出す事です。
  • その命令をさせるためには、「Script(スクリプト)」と呼ばれる、命令ファイルを作る必要があり、プログラミング言語を使う必要があります。
  • ここでやっとC#を使ったプログラミングに入ります。(今まではただの準備です)

玉を生み出すScript作成

名前を変更しておきましょう。

ここではSphereCreate(スフィアクリエイト)にしておきます。

※ファイル名によって、スクリプトの中身(コード)も変わります。

間違いの元なので、今は同じファイル名にしておいてください。

  • ProjectビューのCreateの[C#Script]をクリックしてScriptファイルを作ってください。

玉を生み出すScript作成

  • 追加されたファイルをダブルクリックしてみましょう。
  • MonoDevelop(モノデベロップ)]というエディタが開くと思います。
  • MonoDevelopUnityの、デフォルトのテキストエディタで、ココに入力してプログラミングしていく事になります。
  • ※なんか最近、Windowsだと[Visual Studio(ビジュアルスタジオ)]が開くようなので、画像と少し違うかも知れませんが、気にせず進めてください。基本一緒です。

玉を生み出すScript作成

using UnityEngine;
using System.Collections;

public class SphereCreate : MonoBehaviour {

	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
	
	}
}
  • では中身です。
  • 最初から以下の状態になっていると思います。
  • このコードは「たぶん必要になるやろ」という部分をUnityが最初から書いてくれているだけです。
  • UnityでのC#プログラミングは、基本的に、この用意してくれているコードに追加して記述する事になります。

玉を生み出すScript作成

using UnityEngine;
using System.Collections;
  • では、用意されたコードを軽く見ていきましょう。
  • [using〜]は、【名前空間を短縮するため】の書き方でしたね。
  • 上の名前空間[UnityEngine](ユニティエンジン)には、Unityで用意してくれてるクラスなどが入っています。
  • 下の[System.Collections](システムコレクションズ)に関しては、C#で最初から用意されてるクラスが入っています。
  • このように最初から用意されているクラス(組み込みクラス)にもUnityが用意しているクラスと、プログラミング言語C#)が用意しているクラスとがあります。
  • この違いは、UnityでC#を使う場合は気にしなくても良いですが、Unity以外でC#を使う際は、当然Unityが用意しているクラスは使えませんので注意が必要です。

玉を生み出すScript作成

public class SphereCreate : MonoBehaviour {
    //省略
}
public class SphereCreate : MonoBehaviour {
    //省略
}
  • usingの下にはクラス定義がされていると思います。
  • Unityの場合、「このクラス名は、スクリプトファイルと同じ名前になります」※重要
  • ファイルを作った時に、Unityが勝手にクラス名ファイル名を同じ名前にしてくれるので、通常は気にしなくても良いのですが、「後でファイル名を変えたりしてしまうと動かなくなります」※めちゃ重要
  • 「やってしまう可能性が高い」+「この事を知らないと、気づくのが非常に難しい」ので注意です。
  • さて、上の部分は今までにない書き方ですね。
  • これは継承(けいしょう)という「オブジェクト指向」の重要な概念です。
  • 継承(けいしょう)という言葉の通り、別のクラスから【メンバ(メソッドやフィールド)を引き継いで使えるようにする仕組み】です。

継承

public class SphereCreate : MonoBehaviour {
    //省略
}
public class SphereCreate : MonoBehaviour {
    //省略
}

子クラス

親クラス

サブクラス

スーパークラス

言い方は色々

(親と子が一番わかりやすいかな?と思う)

MonoBehaviour

(モノ・ビヘイビア)

SphereCreate

(スフィア・クリエイト)

  • 継承関係の2つのクラスの内、元になるクラス(:の右側、上ではMonoBehaviour)の事を親クラスまたはスーパークラスと言います。
  • 新しく作られるクラス(:の左側、上ではSphereCreate)の事を、子クラスまたはサブクラスと言います。
  • 継承は、色々な部分に影響を及ぼす重要な概念です。(今はサラッと進みますが奥が深いです)
  • 今回の例では、Unityが用意しているMonoBehaviourという親クラスを継承し、新しくSphereCreateを作ると言う事です。
  • MonoBehaviourを継承しているおかげで、Unity独自の機能が使えるようになります。

玉を生み出すScript作成

	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
	
	}

Start

メソッド

Update

メソッド

  • 次からが実際に入力する部分です。
  • void Start()][void Update() ]のメソッドという部分に注目してください。
  • Start(スタート)メソッドは【ゲームが開始した時に"1度だけ"】したい処理を書きます。
  • Update(アップデート)メソッドは、【プレイ中、"ずっと"】したい処理(正確には1フレーム事に)を書きます。
  • スクラッチで言うと下の感じです。(カンタンですね)

ちょっと脱線フレームとは?

パラパラ漫画と仕組みは同じ!

  • 一応「フレーム」について説明したいと思います。
  • ゲームだけでなく、映画・動画など全ての映像は、結局は「複数の画像が高速で切り替わっているだけ」な事は知っているかと思いますが、その時の1枚1枚の画像が1フレームです。
  • また、1秒間に表示されるフレーム数の単位をfpsと言います。たとえば1秒間に30フレーム切り替わるのであれば30fpsです。
  • 切り替える速度は、その映像を表示させる(データから画像を生成している)機械の規格によってかわります。例えば映画は1秒間に24フレーム(24fps)、現在のテレビは基本60フレーム(60fps)などです。このデータから画像を生成する事”を「レンダリング」と言います。
  • Unityの場合は、◯◯fpsと決まっているわけではありません。「処理に余裕がある」ならフレームは早くなりますし、余裕がないなら遅くなります。(「処理に余裕がある」かどうかは、「プログラム的な処理の量」と、「パソコンおよびディスプレイの処理能力」との2つの要因に影響されます)
  • つまり【Updateメソッド】は、毎フレームごとに処理されるので、「毎回同じタイミングではない」のです。
  • とはいえ細かい差なので、普段は特に気にしないで良いですが、「正確な時間で行いたい処理」の場合は、問題になってきます。この問題を回避する方法は後ほど詳しくやります。

玉を生み出すScript作成

using UnityEngine;
using System.Collections;

public class SphereCreate : MonoBehaviour {

	void Update () {
	
	}
}
  • さて、スクリプトの話に戻ります。
  • 今回のスクリプトでは、「連続して玉を生み出したい」ので、Updateメソッドの方に記述していきます。
  • Startメソッドは不要なので消しちゃいましょう。
  • こんな感じになりました。
  • //から始まる、英語のコメントも消してますが、別に消さなくても良いです。
    • てか実際は、Startメソッドも、そのままで良いのですが、わかりやすさのために、不要な場合は消しながら進みたいと思います。(今後も不要なメソッドは、説明なく消します)
  • この先は、実際にエディタに書き込みながら進んで行きます。

玉を生み出すScript作成

using UnityEngine;
using System.Collections;

public class SphereCreate : MonoBehaviour {
        float time;
	void Update () {
		time += 0.01f;
		if(time >= 3.0f){
                    //ココに②オブジェクトを作る処理を書く
                    time = 0;
                }
	}
}
  • まずは必要な事を整理しましょう。
  • 連続して玉を生み出す」のに必要なものと言えば2点です。
    • ①特定のタイミング(間隔)で、
    • ②SpherePreのプレハブからオブジェクト(インスタンス)を作る。
    • ③上の処理を繰り返す。
  • 「繰り返す」の部分はUpdateメソッドがやってくれるので、①と②だけを考えれば良いですね
  • 色々な書き方があると思いますが、今回は以下の感じのプログラムにします。
  • 追加した赤枠のコードが①の【特定のタイミング(間隔)】を指定している処理です。
    • 次のページで解説します。

玉を生み出すScript作成

using UnityEngine;
using System.Collections;

public class SphereCreate : MonoBehaviour {
        float time;
	void Update () {
		time += 0.01f;
		if(time >= 3.0f){
                    //ココに②オブジェクトを作る処理を書く
                    time = 0;
                }
	}
}
  • まず最初にUpdateメソッドの外で、変数timeを作っています。
  • その後、Updateメソッドの中の【time += 0.01f】とし、1フレームごとにtimeが0.01増えていくようにしています。
  • そして、timeを増やす事にif文で条件判定をし、もしtimeが3.0以上になった時に②オブジェクトを生み出す処理を実行すれば良い感じです。(今は省略しています)
  • オブジェクトを生み出した後は、一旦timeを0に戻しています。
  • これで、①特定のタイミング(間隔)を指定する部分の処理はとりあえずOKです。

玉を生み出すScript作成

using UnityEngine;
using System.Collections;

public class SphereCreate : MonoBehaviour {
        public GameObject pre;
        float time;
	void Update () {
		time += 0.01f;
		if(time >= 3.0f){
                    Instantiate(pre);
                    time = 0;
                }
	}
}
  • ②「Prefabからオブジェクトを作る」コードが上の赤枠です。
  • まず、Updateメソッドの外で、[GameObject] (ゲームオブジェクト)型の変数preを作っています。
  • 【GameObject】型とは、Unity独自の型で、その名の通りゲームのオブジェクトである事を示す型です。※変数名の【pre】はprefabを短くしただけ。
  • Updateメソッドの中では、[Instantiate(pre)]というメソッドを呼び出しています。
  • このメソッドもUnityだけのメソッドで【()内のPrefabからインスタンス(オブジェクト)を作る】というメソッドです。
    • つまり今回の場合は「変数preを元にして、オブジェクトを作る」という意味になります。
  • この変数preは、後で中身(Prefab)を代入します。

玉を生み出すScript作成

  • 出来上がったスクリプトですが、今のままでは何にも動きません。
  • スクリプトは何かしらのオブジェクトにひっつけないといけないのです。
  • 今回のスクリプトは誰がやっても良いような処理なので、そこら辺のオブジェクト(カベや坂など)にひっつけちゃっても良いのですが、後で「この処理ダレがやっているんだ?」となりますので、やめときましょう。
  • 代わりに空のオブジェクトを作成し、そいつにスクリプトを動かしておいてもらいましょう。
  • Create】を押し、【Create Empty】(クリエイトエンプティ:空(エンプティ)のオブジェクト)を選んでください。
  • 最初は名前が【GameObject】になっていると思いますので、【SphereScr】に変えといてください。
    • ※scrはscript(スクリプト)の略です。

玉を生み出すScript作成

  • スクリプトの貼り付け方ですが、これは単純にドラッグ&ドロップで引っ付きます。
  • このようにオブジェクトにスクリプトやRigidbodyなどを引っ付ける事を【アタッチ】と言います。
  • 引っ付いた後は、inspector(インスペクター)ビューにComponent(コンポーネント:部品)として表示されてます。※画像の赤枠部分
  • この中で注目したいのが、【Pre】という項目です。
    • ※画像の青枠
  • そう、さっきスクリプト内で作った変数preです。
  • 変数preは、ただ宣言しただけなので中身は空でしたね。
  • その事が、【None】(ナン:なにもない)で表されています。
  • もちろん、このままだと設計図(Prefab)がない状態なので、オブジェクトは出来ません。
    • ここに【SpherePre】を設定するわけです。
  • 設定の仕方はカンタンで、Noneと書かれた枠に、【SpherePre】をドラッグ&ドロップするだけです。
    • 次ページにつづく

玉を生み出すScript作成

  • こうなればOKです。
  • もうコレで動くはずですので、ゲームスタートしてみてください。(数秒ごとに玉が出れば成功です)

玉を生み出すScript作成

public GameObject pre;
Instantiate(pre);

[Instantiate(pre)]というメソッドは、()内のインスタンス(今回はpre)を作る、というメソッドでしたね。

  • 大事なところなので、もう一度おさらいです。
  • スクリプトを見直しておきましょう。
  • 上のコードでは空だった変数ですが、inspectorビューから代入してあげることで、preには【PrefabPre】(作ったプレハブ)が入った事になりましたね。
  • そのため、下のコードでオブジェクトが作れるようになるのです。
  • なんとなくUnityでのプログラミングの一連の流れは掴みましたでしょうか?
  • すべてのプログラミングに言える事ですが、プログラムを理解するためには、まず【処理の流れ】を気にしてください。
  • そして、ぼんやりと全体を見るのではなく、一つ一つの処理の塊(パーツ)を意識してみれば、コードからプログラムの全体像が理解できるようになります。

玉を生み出すScript作成

public GameObject pre;
float time;
  • また、今回のPrefabのよう【inspectorビューからスクリプトの変数に代入する】事はUnityでは非常によく使います。
    • 今回はGameobject型でしたが、他の型でも使えます。
  • ただし「public変数」しかinspectorビューには表示されません。
  • つまりは変数の頭に「public」が付いている必要があるのです。
  • 上のコードでも、time変数にはpublicがついていない(つまりはプライベート)なので、inspectorビューに表示されず、publicが付いてるpre変数だけが表示されていましたね。
  • 当然time変数publicを足せばinspectorビューに表示され、スクリプトの外から値を代入する事が出来ます。

玉を生み出すScript作成

  • さて、ここからこのスクリプトに少し手を加えながら、もう少し理解を深めましょう。
  • 今は玉の出てくるタイミングは、大体4秒前後かなと思います。
    • ※Unityのエディタ環境だと、大体60−80fpsぐらいだからです。
  • 1フレームで0.01増えるようにスクリプトを設定していましたよね?なので、それが3.0になる(つまり300回実行される)のに、60fpsだと5秒、80fpsだと3.45秒かかります。※単純な割り算(300/ fps)です。
  • では実際に、現在のfpsを見てみましょう。ゲームを再生してください。
  • 画面右上の[stats](スタッツ:統計)を押すとウインドウが開きます。
  • そこの、Graphics(グラフィックス)の項目に、fpsがリアルタイムに表示されています。

玉を生み出すScript作成

time += 0.01f;
time += Time.deltaTime;

フレーム

フレーム

フレーム

フレーム

Time.deltaTime

Time.deltaTime

Time.deltaTime

つまり【Time.deltaTime】は、フレームが終わるごとに、毎回違う値が入る

  • で、この「fpsによって玉が出る時間が変わってしまう」問題を解決しておきたいと思います。
  • やる事自体はカンタンです。SphereScrスクリプトを開いてください。
  • 下のコードを変更すると、【3秒ごとに生成される】処理になります。
  • この【Time.deltaTime】(タイム.デルタタイム)メソッドは、【前のフレームの開始時間から】→【今のフレームの開始時間まで】の時間が常にカウントされています。
  • なんだかややこしいので下の図を見てください。

玉を生み出すScript作成

フレーム

フレーム

フレーム

フレーム

フレーム

フレーム

フレーム

フレーム

30fps

60fps

フレーム

フレーム

フレーム

1秒

スタート

Time.deltaTime]をUpdateメソッド内で足していくと、

60fpsの場合は60回(60フレーム)足さないと1にならないが、

30fpsの場合は30回(30フレーム)足すだけで1になる、

つまり1秒になるタイミングは同じになります。

逆の言い方をすると、今回の作ったtime変数は1秒で、ちょうど1増える事になります。

そのため、3になった時(玉が出る時)は必ず3秒間隔になります。

  • つまりは【Time.deltaTime】は、fpsごとの差を吸収してくれるので、30fpsで動こうと、60fpsだろうと【同じ間隔(時間)】で実行する事が出来るのです。

玉を生み出すScript作成

public float time;
  • Time.deltaTime】の動きを、目で見てみると理解しやすいかと思うので、Inspectorビューから見れるようにしてみましょう。
  • 単純に変数timepublicにするだけです。
  • publicになったので、inspectorビューに表示されているはずです。実際にゲームを動かして値の動きを見てみましょう。([Time.deltaTime]はややこしいので使いかたが理解していればOKです)

確認問題①

  • 現在は3秒ごとにオブジェクトが作られていますが、これを以下の条件を満たすプログラムに変更してください。
    • 条件①:すでにあるスクリプト「SphereCreate」を実際に変更してください。
    • 条件②:現在、インスタンスが作られる間隔として、3.0に指定しているものを変数にしてください。(変数名は自由です)
    • 条件③:変数の値は、inspectorビューから変更出来るようにしてください。
    • 条件④:スクリプトが修正出来たら、inspectorビューから値を与え、5秒ごとにオブジェクトが生成されるようにしてください。

※この問題を終わらせないと、次以降のプログラムが出来ませんので、必ず終わらせ、そして答え合わせをしてから進んでください。

玉を撃つスクリプト

手元から別の球を打って、赤球を弾き飛ばす。

どんどん赤球が、転がってくる。

マウスクリックで、別の球が飛んでいく。

  • 次は一番の山場の「玉を撃つ」処理を実装しましょう。
  • まずイメージを再度確認しておきます。(赤球とありますが、別に色は何でも良いです)

玉を撃つスクリプト

  • まず、転がってくる玉とは、別の玉を用意しなければいけませんね。
  • ちょっと見直したりしながら、サクッ作ってください。(見直すならココらへん
    • 別にキレイな玉である必要もないので、形や色も自由に決めましょう。
    • ※ただしRigidbodyや摩擦係数(まさつけいすう)は必ず設定してください。
  • 出来上がったら、プレハブ化し、元のオブジェクトは消しておいてください。
  • この時、プレハブの名前はBulletPre(ブレット:弾丸のこと)としといてください。
  • また、今転がっている玉も変更しても結構です。
    • (マト的に小さいので大きめにしといた方が良いかも)
  • 最低限ちゃんと転がるようにはしておきましょう。
    • 次のページに参考の玉の画像をのせていますので、迷ったら同じようにしておいてください。

玉を撃つスクリプト

撃つ玉

転がる玉

  • ココでは以下の2つの玉で進めます。(摩擦係数は[friction]を使いまわしてます)

玉を撃つスクリプト

  • ここで、一つだけ新しい要素を足します。Tag(タグ)という要素です。
  • これは、HTMLのclassみたいなもので、カンタンに言うとオブジェクトに付ける印です。
  • 【複数のオブジェクトに同じタグ】をつける事も出来ますし【1つのオブジェクトに複数のタグ】をつけることもできます。
  • さっき作った玉【撃つ玉】と、前からある玉【転がる玉】を後で区別する処理が必要になるので、【転がる玉】の方だけタグをつけておきたいと思います。
    • タグで分けるのは非常にカンタンなので、よく使われます。
  • では、実際の作業ですが、【転がる玉】のinspectorビューの上の[Tag]欄を見てください。[Untagged](タグがない)になっているといますが、これをクリックして[Add tag]を選択してください。※続きは次のページ

玉を撃つスクリプト

  • こんな画面になっていると思うので、+を押してタグを追加します。タグ名は「scoreUp」(スコアアップ)にしています。
  • 名前を付けたら、もう一度【転がる玉】のプレハブをクリックして、Tag欄をクリックして見ましょう。
  • 今作ったタグが追加されていると思いますので、それを選択してください。
  • これでタグの設定は終わりです。
    • ※今はタグを付けただけなので、もちろん何も変化はありません。

玉を撃つスクリプト

  • 続いては 【玉を撃つ】という処理ですね。これを細かく分割して考えると、
    • マウスをクリックした時に、
    • プレハブ[BulletPre]からインスタンスを作り、
    • 画面手前の座標から、
    • マウスをクリックした場所に、
    • 特定の力で飛ばす。
    • ⑥(上記の処理を繰り返す)
  • ​こんな感じでしょうか。
  • もちろん「繰り返す」の部分はUpdateメソッドに任せて、上の①−⑤だけを考えます。
  • この部分は一番難しく、新しい概念も出てきますので、気合入れていきましょう。
  • では、新しいスクリプトファイルを作ってください。
  • 名前はBulletCreate(ブレット・クリエイト:弾丸作る)にしてください。(作り方はここらへん
  • 今回もStartメソッドは使いませんので、下の状態から初めます。

玉を撃つスクリプト

  • まずは①[マウスをクリックした時に] の処理です。
  • if文の条件部分(Input.GetMouseButtonDown(0))がポイントです。
  • Input.GetMouseButtonDown(0)](インプット.ゲットマウスボタンダウン)という名前通り、「ボタンが押されたか?」を判定するメソッドです。
  • 引数の"0"は左クリックを表しています。
    • つまり左のボタンが押されたか?」を判定していますね。
  • ちなみに右クリックは"1"、真ん中クリック(ないマウスもある)は"2"になります。

玉を撃つスクリプト

  • ちなみに、今回は使いませんが先ほどの[Input.GetMouseButtonDown()]に似ているメソッドとして、下記があります。

    • GetMouseButtonUp()(ゲットマウスボタンアップ):ボタンが離れたか?

    • GetMouseButton(ゲットマウスボタン):ボタンが押されているか?(現在進行系)

  • ​​この三種はそれぞれ、条件に当てはまる時にtrueの値を返します。

  • ​「ボタンが押されたか?」と「押されているか?」の違いは単純に「ボタンが押されたか?」は押した瞬間だけを判定するのに対し、押されているか?」は押されている状態を、ずっと判定する時に使います。

  • 【押しっぱなしで玉を出し続けたい場合は、[GetMouseButton]】にするって感じです。

玉を撃つスクリプト

public class SphereCreate : MonoBehaviour {
        public GameObject pre;
	void Update () {
	    if(Input.GetMouseButtonDown(0)){
                GameObject bullet = Instantiate(pre);
	    }
	}
}
  • 次に②[プレハブ[BulletPre]からインスタンスを生成する]の処理です。
  • これは前回のスクリプトとほぼ同じです。
  • まず、変数preを宣言しています。
    • 後でinspectorビューから、BulletPreを代入するための変数です。
  • 下の赤線の右側Instantiate(pre)]は、プレハブからインスタンスを作るメソッドでしたね。
    • 今回は、その生成したインスタンスを、そのまま変数bullet(ブレット)に代入しています。
  • 前回は、ただ重力に任せて転がっていくだけでしたので、【ただ作って終わり】でしたが、今回は生成したインスタンスの生成場所方向動きなどを設定しなければいけません。
    • そのために一旦変数に代入しています。
  • 次のページにて、その処理を設定します。(次からややこしいぞ)

玉を撃つスクリプト

ゲーム世界(Scene)

ゲーム画面

画面の手前はココ

※if文以外の場所は省略してます。

  • 次は、③[画面手前の座標から]を設定する部分になります。
  • このコードを理解するためには、まず【画面手前の座標から】という意味を理解しなければいけません。
  • Unityの場合、ゲーム画面は【カメラオブジェクト(Main Camera】が映している目線でしたよね?
    • つまり【画面手前の座標から】とは【カメラオブジェクト自体の座標から】という意味になります。※次に続く。
if(Input.GetMouseButtonDown(0)){
    GameObject bullet = Instantiate(pre);
    bullet.transform.parent = transform;
    bullet.transform.localPosition = new Vector3(0, 0, 0);
}

玉を撃つスクリプト

  • 改めてコードを確認しましょう。
  • 赤枠の[bullet.transform.parent = transform;]は【bullettransformの子どもにする】という意味になります。
  • bullet】とは【撃つ玉】のことですね。
  • transform】とは、【このスクリプトをアタッチしたオブジェクト】のことを指しています。
  • 後でMainCameraオブジェクトに、このスクリプトを貼り付けるので、【transform=MainCamera】という意味になります。
    • つまりはbullet(子)transform(親)】の関係が生まれます。
      • これは下のコードに関連します。※次のページへ。
if(Input.GetMouseButtonDown(0)){
    GameObject bullet = Instantiate(pre);
    bullet.transform.parent = transform;
    bullet.transform.localPosition = new Vector3(0, 0, 0);
}

玉を撃つスクリプト

if(Input.GetMouseButtonDown(0)){
    GameObject bullet = Instantiate(pre);
    bullet.transform.parent = transform;
    bullet.transform.localPosition = new Vector3(0, 0, 0);
}
  • 次は【bullet.transform.localPosition = new Vector3(0, 0, 0) 】の部分です。
  • この内【transform.localPosition】(トランスフォーム,ローカルポジション)のメソッドは、【親オブジェクトから見た相対座標】を表しています。
  • つまり今回の場合は【カメラオブジェクトからの相対座標】ですね。
    • 相対座標とは、相対パスと同じ意味で【親からの距離】のことです。
  • そして、その相対座標に[=new Vector3(0, 0, 0) ]というのを代入しています。
  • この[Vector3](ベクター3)は、座標などを指定するクラスで、[(0, 0, 0) ]というのは【x座標0,y座標0,z座標0】という意味で【0距離】を表しています。
  • こうする事で、カメラからの距離は0、つまりは【同じ座標にしろ】という意味になります。
  • new】が付いているのは、Vector3(0, 0, 0) ただのクラス(正確には構造体というもの)のため、そのままだと代入が出来ないからです。
    • new】してインスタンスとして作成すると代入が出来るようになります。
  • このように相対座標を設定することで、もしカメラが動いたり、後でカメラの場所を変更したとしても、問題なく動作するプログラムになります。

玉を撃つスクリプト

  • 以上のことをまとめると、
    • 下のコードの赤枠部分は「transform(今回はカメラになる予定)の子になります!」という処理で、
    • 青枠部分は「親と同じ座標にいきます!」という処理になるということですね。
  • スクラッチで言うと、下の感じです。※雰囲気ですが
if(Input.GetMouseButtonDown(0)){
    GameObject bullet = Instantiate(pre);
    bullet.transform.parent = transform;
    bullet.transform.localPosition = new Vector3(0, 0, 0);
}

玉を撃つスクリプト

2Dを

3Dに

yは◯◯

xは◯◯

zは??

クリックした座標はココ!!

クリックした先

はドコ?

ゲーム世界

  • 次に【④マウスをクリックした場所に(飛ばす)】ですが、方向を決めるためには、まずマウスを【クリックした座標(ゲーム内の3D座標)を取得】しなければいけません。
  • ただし、プレイヤーがクリックするのはディスプレイ画面なので、当然平面です(つまり2D)
  • この2D座標にz軸を足し、3D空間の座標に置き換えなければいけません。
  • これは非常に厄介な処理になります。
  • これを簡単に行うための仕組みとして、Ray(レイ:光線)というものがあります。
  • これは「クリックした座標に対して、カメラからまっすぐに見えない光線(レイ)を飛ばし、その光線が他のオブジェクトとぶつかった場所の座標を取得する」という仕組みです。
  • 次のページで詳細を確認してください。

玉を撃つスクリプト

Rayを飛ばすイメージ

クリックした座標へ、カメラから光線を飛ばす!!

ゲーム世界

クリックした座標

その光線がそのまま飛んでいった先で、オブジェクトとぶつかった座標をゲット!!!

  • このような方式で、2D座標から3D座標を取得します。
    • この3D座標の事を「ワールド座標」とも言います。
  • もちろんこのRayは内部的な処理の話なので、実際に光線は見えません。
  • この処理によって「クリックしたワールド座標」がわかれば【方向】や【距離】などのデータも取得できるようになります。

玉を撃つスクリプト

if(Input.GetMouseButtonDown(0)){
    GameObject bullet = Instantiate(pre);
    bullet.transform.parent = transform;
    bullet.transform.localPosition = new Vector3(0, 0, 0);
    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    Vector3 dir = ray.direction;
}
  • 仕組みを理解したら、実際の使いかたです。
  • [Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);](カメラ.メイン.スクリーンポイントトゥレイ.(インプット.マウスポジション))がカメラから【Rayを飛ばして、取得したデータ】をRay型の変数rayに代入しているコードです。
  • Rayを飛ばして、取得したデータ】とは、具体的に言うとorigin(オリジン:座標)とdirection(ダイレクション:方向)のデータです。
  • そして、その下のコード[Vector3 dir = ray.direction]で、Vector3型の変数dirに[ray.direction](レイ.ディレクション:方向)代入しています。
  • つまり、この時点でクリックした場所への方向データを、dir変数に取り出す事ができたということです。
  • (今回は方向(direction)だけがあれば良いのでoriginはムシしてます)

玉を撃つスクリプト

public class BulletCreate : MonoBehaviour {
    public float power;
    public GameObject pre;
    void Update () {
        if(Input.GetMouseButtonDown(0)){
            GameObject bullet = Instantiate(pre);
            bullet.transform.parent = transform;
            bullet.transform.localPosition = new Vector3(0, 0, 0);
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            Vector3 dir = ray.direction;
            bullet.GetComponent<Rigidbody>().velocity = dir * power;
        }
    }
}
  • やっとラスト【⑤特定の力で飛ばす】の処理です。
  • まずはUpdateメソッドの外で、変数powerを宣言しています。
  • [bullet.GetComponent<Rigidbody>().velocity = dir * power;]の左側[bullet.GetComponent<Rigidbody>().velocity](ブレット.ゲットコンポーネント.<リジッドボディ>.ベロシティ)とは【撃つ玉】の、Rigidbodyコンポーネントの【velocity(ベロシティ)】というプロパティの事で、これは【速度】を指定するプロパティです。
  • これに[dir * power](方向*大きさ)を代入しています。
    • こうする事で変数power分で、飛ばす力(大きさ)が調節できます。次に続く。

ベクトル

  • 先ほどから出てきている【方向】などの処理をちゃんと理解するには、【ベクトル】という概念が必要になります。
    • たぶん中学?高校?で習う数学・物理学用語で、【向き大きさ(長さ)を持った量】のことです。
      • (日常会話では「キミの考え方は、ボクとはベクトルが違う」みたいに方向性という意味でベクトルを使うことが多いですね)
  • Unityでの「ベクトル」は、当然ながら物理学的な意味でのベクトルですが、その意味をそれほど理解しなくても使えるように、便利なクラス・メソッドがたくさん用意されています。
    • その代表的なのが[Vector3](ベクター3)です。
  • そもそも「ベクター」とは「ベクトル」同じ意味です。(ドイツ語がベクターで、英語がベクトル。たぶん)
    • だから[Vector3]を(ベクトル3)と読んでもOKです。
      • ​(普通はみんなベクター3と読みますが)​​

動かしてみよう

  • すべて出来たら、このスクリプトをアタッチして動かします。
    • ※どのオブジェクトにアタッチ(貼り付け)すれば良いかは、今までの説明読んでいればわかりますよね?
  • アタッチしたら最後にinspectorビューから変数の値を指定しましょう。
  • 指定するのは、GameObject型のPreと、float型のPowerですね。
  • Preにはプレハブの【BulletPre】をほり込んで、Powerは50という値を与えましょう。
  • 出来たら実際に試してください。
    • (Powerの値は色々ためして、好みの値でOKです)
  • 撃つ玉を、転がる玉に当てると飛ばす事が出来るはずです。
  • これで、だいぶゲーム的になってきましたね。
using UnityEngine;
using System.Collections;

public class Destroy : MonoBehaviour {
	void OnBecameInvisible(){
		Destroy(gameObject);
	}
}

玉を消す

  • 次は作ったオブジェクトを消す処理です。
  • 作ったオブジェクトをそのままにしておくと、画面から見えていなくても残ったままになり、無駄に動作が重くなってします。(スクラッチで言うとクローンを削除しないとと重くなるのと同じ)
  • ​消えるタイミングは、【画面から見えなくなったら消える】ことにしたいと思います。
  • これは非常に簡単です。新しいスクリプトファイルを作り、以下のように書いてください。
  • ファイル名はDestroy(デストロイ)でもしておいてください。
  • これで完成です。簡単ですね。
  • [OnBecameInvisible()](オンビケイムインビジブル)というメソッドは【カメラに映らなくなった時】に動くメソッドで、カメラに映らなくなった時】に勝手に呼び出されます。
    • Updateメソッドに入れてなくても。
  • その中の[Destroy()](デストロイ)メソッドは、その名の通り【破壊する】メソッドで、(gameObject)と引数に記入する事で、このスクリプトがアタッチされているオブジェクト自体を破壊します。

OnBecameInvisible()で受け取って、

Destroy()で破壊

玉を消す

  • 今までと違う点としては、StartメソッドもUpdateメソッドも、両方とも使ってませんね。
  • この[OnBecameInvisible()](オンビケイムインビジブルみたいに、【◯◯の時に呼び出される】タイプのメソッドは、Startメソッドや、Updateメソッドに入れてはいけません。
  • スクラッチで言うなら、下の形のブロックが繰り返しの中に入れられないのと同じです。
  • つまりUnityの処理的に、カメラ外に行くと下記のような信号を送っていると考えられます。
  • それを、[OnBecameInvisible()]で受け取ってるわけですね。

玉を消す

撃つ玉の方は、MainCameraの子オブジェクトなので、画像のように、一段下にあります。

転がる玉は、そのまま一番上の階層に表示されてます。

それぞれの玉オブジェクトが、画面から消えた後に減っていく事を確認してください。

ちなみにDestroy(gameObject, 5.0)]のように、2個めの引数としてfloat型の値を指定すると、[5秒後に消す]など時間を指定して消す事もできます。

  • 後は、このスクリプトを消したい2つの玉に引っ付けます。
  • ただ、この玉のオブジェクトは、ゲームスタートしてから作られるので、今はHierarchyビューにはないはずです。
  • このような場合はPrefab自体に引っ付ければOKです。
  • そうしておくだけで【消すスクリプトが引っ付いたオブジェクト】が作られるわけですね。
  • 実際に試してください。
    • ※【カメラに映らなくなった】という判定は、実際に見えなくなってから少し後ぐらいになると思います。

UI(ユーアイ)とは【ユーザーインターフェイス】の略で、ゲームやWEB業界などでは、非常に良く使われる言葉です。

ユーザー(使う人)とゲーム(またはWEBサイト等)の間で、情報をやりとりするものの事を言いますが、単純にゲームなどの【操作画面の事】だと思えば良いです。

どんなに内容が面白いゲームでも、「UIがクソ」であればユーザーは【面白い】とは感じません。

UIは非常に重要なので、覚えておきましょう。

得点を作る

  • 次はゲームに欠かせない【得点】の要素を導入しましょう。ステップとしては以下の2つになります。
    • 画面に得点を表示する。
    • 玉をうまく飛ばせたら、得点をプラスする。
  • ​​まずは①ですが、今のところ得点など文字を表示できそうな場所がありませんよね?その場所を作る作業からです。
  • Hierarchy(ヒエラルキー)ビューの、Create(クリエイト)から、【UI(ユーアイ)→Text(テキスト)】を選択しましょう。

得点を作る

  • 作成すると下のような感じで、[Canvas](キャンバス)と、その中に[Text](テキスト)が作成されます。※[EventSystem](イベントシステム)というのも勝手に出来ますが、これはUIの制御用のオブジェクトで、特に気にしなくても良いです。(もちろん消してはダメ)
  • [Canvas](キャンバス)が、文字などを配置できる空間で、そこにテキストを並べるイメージです。
  • では、このテキスト名を変更し、Score(スコア)にしておきましょう。※右クリック→Renameです。
  • 今度はテキストの領域(りょういき)を広くします。
  • Scoreのinspector)ビューから
    • Widthを200、
    • Heightを50
      • にしましょう。

文字の内容

文字の大きさ

文字の色

現在の画面

得点を作る

  • 次に文字自体の大きさも大きくし、内容も「スコア」に変え、ついでに色も変えましょう。(数値は自由にしてください)
  • inspectorビューの【Text(script)】の中を変更します。

得点を作る

  • 次に位置を整えるのですが、まずはHierarchyビューから[Canvas]を押してください。
  • 今はこんな感じに見えているかと思います。
  • 大きな白い枠が、Canvas(キャンバス)で、ど真ん中にテキスト(スコア)があると思います。
  • このCanvas(白い枠)は、【ゲーム時の画面枠】を表しています。
  • つまり、このままゲームをスタートすると、画面中央のスコアが表示されます。(試してみましょう)
  • 普通のオブジェクトとは違って【ワールド座標(3D座標)】とは無関係な配置なので注意してください。

得点を作る

  • では、スコアは【ゲーム画面の左上】に配置したいので、【キャンバス(白い枠)の左上】に移動させましょう。普通にマウスで移動させればOKです。(四角の部分を掴んで移動させる)
  • こんな感じです。
  • 実際にスタートして見てみましょう。
    • Unityの画面表示を小さくしていると見え方も違うので、大画面にしてから見てみましょう。
using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class Score : MonoBehaviour {
	public static int score;
	Text text;
	void Start () {
	}
	void Update () {
	}
}

得点を作る

  • 今のままだと、文字が表示されているだけですので、実際にスコアが増えていくようにします。
  • もちろんスクリプトが必要になりますので、新しくスクリプトファイルを作成しましょう。
  • 名前はScoreにしておいてください。
  • 最初に[Using UnityEngine.UI]をつけておきます。
    • これは、Unityで使うUI系のクラスがまとめられている名前空間です。
  • 次の2つの変数(フィールド)を宣言しています。上の変数scoreには、[static](スタティック)が付いています。
    • つまり変数scoreは【Scoreクラス自体】に所属するフィールドですね。(見返しはココ
  • 下の変数はTextクラスのtextを宣言しています。Text型(クラス)はUnity独自クラスです。
    • UIのパーツとしてのTextなので、String型(文字列)とは違うので気をつけましょう。
using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class Score : MonoBehaviour {
	public static int score;
	Text text;
	void Start () {
		text = GetComponent<Text>();
	}
	void Update () {
		text.text = "Score : " + score.ToString();
	}
}

得点を作る

  • 次は処理です。
  • 今回はStartメソッドも、Updateメソッドも両方とも使っていますね。
  • Startメソッドの方で、変数textに[GetComponent<Text>()]を代入しています。
  • これは、inspectorビューの【Text(Script)】の部分をすべて代入するということです。
    • 右画像の部分です。(次ページに続く)
using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class Score : MonoBehaviour {
	public static int score;
	Text text;
	void Start () {
		text = GetComponent<Text>();
	}
	void Update () {
		text.text = "Score : " + score.ToString();
	}
}

得点を作る

  • text.text】とは、つまりinspectorビューの【Text(Script)】の内の、text部分(スコアの部分)の事です。
  • それに右側の["Score : " + score.ToString();]を代入しているわけですが、["Score : "]は単純に文字列です。
  • [score.ToString()]はScore変数の値(int型)をString型に変換(キャスト)しています。
    • それを+で足して、text.textに代入しています。(つまり表示される文字が変わる)
  • 出来たらゲームオブジェクトのScoreにこのスプリクトを貼り付け、実際に動かしてみましょう。※Score:0と表示されればOK
  • 続いてUpdateメソッドのコードを見ましょう。

得点を作る

  • こんな感じです。
  • inspectorビューに書いたスコアが、スクリプトによってScoreに変わっていますね。
  • これがスタートするとtext.textに代入されて上書きされているという事です。
  • 表示名は自由なので、好きなものに変えておきましょう。
    • ※当然スクリプトを変えないといけません。
    • ※Unityのウインドウの大きさで見え方も違うので、大画面にしてから見てみましょう。

撃つ玉

撃たれる玉

目には見えない、透明なカベオブジェクトを用意

カベにぶつかれば得点

ぶつからない(ちゃんと飛ばせてない)なら得点なし

得点を作る

  • さてスコアの表示が出来たところ、次はスコアが加算する方法です。(今のままではずっと0なので)
  • もちろんスクリプトが必要なわけですが、先に加算するタイミングを考えたいと思います。
  • 単純に考えたら、【玉に当たったら】ですが、今回はカスッた程度では点数にならないようにしたいので、【ちゃんとぶっ飛ばせたら】点数が増えるようにします。
  • では、【ちゃんとぶっ飛ばせたら】の判定ですが、以下のようしたいと思います。

得点を作る

  • という事で、まずは目に見えないカベを用意します。
  • 今回は元々ある画面の奥のカベを編集して作りたいと思います。
    • (名前を同じにしていればWall3)
  • 大きさや、場所は下の画像の感じにしましょう。
  • こんな感じになりました。(あえて隙間をあけて配置しています)
  • このカベの姿を消し、玉が素通りする(跳ね返らない)ようにすれば【当たり判定だけ】の存在になるという事です。

どっちでもOK

得点を作る

  • カベを見えなくするのはカンタンです。
  • inspectorビューの[Mesh Renderer](メッシュレンダラ)のチェックマークを外すか、または削除しちゃいましょう。
  • 削除は右クリック[Remove Component](リムーブコンポーネント)です。
  • こんな感じで透明になりましたね。
  • でも、実はまだカベとしての役割だけは残っています。(玉が跳ね返るという事)
  • 試してみましょう。

Collider(コライダー)とは?

コライダーはオブジェクトの「当たり判定」の事で、Unityで新しく作ったオブジェクトに関しては最初から設定してくれています。

(今まで設定しなかったのはそのため)

Unity外で作られたキャラクターなどを持ってきて使う場合などは、このコライダーを設定しなければいけません。

詳しくは、また別の機会にやります。

得点を作る

  • 跳ね返るなど、他のオブジェクトに干渉しない、つまりはムシされる状態にするためには、[Is Trigger](イズトリガー)にチェックをつける必要があります。
  • is Trigger]は、inspectorビューの[BoxCollider](ボックスコライダー)という場所にあります。
  • ちなみにトリガーとは「(ピストルの)引き金」の事で、日本語でいうと【きっかけ】と同じ意味です。
  • つまり、このカベは【きっかけ】だけの存在になったということです。
public class Hit : MonoBehaviour {
	void OnTriggerEnter ( Collider co  ){

	}
}

得点を作る

  • さて、この【透明な元カベ】が出来ましたが、ただあるだけでは意味がありませんね。
  • ここからスクリプトで、この【透明な元カベ】に[撃たれる玉(転がる玉)]が当たった時に点数を増やす処理を作ります。([撃つ玉]は当ってもムシする)
  • では、新しいスクリプトを用意しましょう。名前はHit(ヒット)にでもしておきましょう。
  • これが中身です。
  • 今回はStartメソッドとUpdateメソッドは使いません。変わりに[OnTriggerEnter ()](オントリガーエンター)というメソッドを使っています。
  • この[OnTriggerEnter ()](オントリガーエンター)は、トリガー(透明な元カベのこと)に別オブジェクトのコランダー(当たり判定)と接触した時に呼び出されるメソッドです。
    • (前にやった[カメラから消えたら呼び出されるメソッド]みたいな感じです)
  • 引数には(Collider co)というのが付いています。
  • これはそのまんまでColliderクラス(型)のco引数という事だけです。(引数名coは何でも良い)
  • この引数には【当たってきたオブジェクト】が自動的に入れられます。

得点を作る

public class Hit : MonoBehaviour {
	void OnTriggerEnter ( Collider co  ){
		if(co.gameObject.tag == "scoreUp"){
			Score.score++;
		}
	}
}
  • 次は中身です。
  • OnTriggerEnter()メソッドが呼び出されたら(何かがぶつかってきたら)、if文で条件判定をしています。
  • その時の条件がtrueの時だけ、[Score.score]を++(インクリメント)で1上げる処理です。
  • 条件式【(co.gameObject.tag == "scoreUp")】では、ぶつかったオブジェクトのタグを調べていますね。
  • つまりタグに「scoreUp」が設定されていれば、そのオブジェクトは撃たれる玉(転がる玉)]なので、スコアを上げる処理(Score.score++)を実行するという意味になります。
    • ※変数Scoreはクラス変数なので、【Score.score】という書き方で呼び出せますね。
  • 完成したらHitスクリプトが動くように【トリガーオブジェクト(透明のアレ)】にひっつけましょう。(アタッチ)
  • ここまで出来たら、ゲームが完成しています。ぜひ遊んでみましょう。

ボール飛ばしゲーム② Unity入門1_2

By kinocode

ボール飛ばしゲーム② Unity入門1_2

  • 3,672