Tank


リジッドボディを付与されたキャラの動かし方や弾丸の発射、カメラの追従、時間の計測などをやってみよう。

[作成手順]

地面の作成

1.CubeからFloorを作成する。transformは下図。

2.適当に表面材質を設定する。

カメラの設定

3.カメラのトランスフォームを下図のように設定する。

タンクの作成

砲塔が旋回しない自走砲タイプの戦車を作成する。

4.CreateEmptyからTankを作成する。配置場所はあとで調整するのでどこでも良い。大切なことはZ軸が前になるよう子要素をモデリングしていくことだ。
5.Tankの子要素といてBodyとTurretを作成する。transformは下図。
●Boby(Cube)

●Turret(Cylinder)


(親要素のZ軸(青)が前になるように子要素をモデリングする)
6.表面材質を適当に設定する。
7.親要素のみにRigidbodyを付与する。
8.親要素のtransformを以下のように設定する。

タンクを動かそう

9.TankController1.csを以下のように作成する。

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

public class TankController1 : MonoBehaviour {
	Rigidbody rb;
	void Start () {
		rb = GetComponent<Rigidbody> ();
	}

	void Update () {
		float x = Input.GetAxis ("Horizontal");
		float z = Input.GetAxis ("Vertical");
		Vector3 dir = new Vector3 (x, 0f, z);

		if (dir.magnitude > 0.1) {
			transform.LookAt (transform.position + dir);
			rb.velocity = transform.forward * 5.0f;
		}
		
	}
}

10.Tankにこのスクリプトを付与し実行してみよう。上下左右キーでタンクが動けば成功だ。
LookAtで入力方向を向かせタンクを前方(z軸)に進めている。右向きから左向きに一瞬で向きを変えるのがこのスクリプトの特徴。コミカルなゲームに向く動きだ。

11.先ほどの動きでも良いがもう一つ作ってみよう。先ほどのTankController1はリムーブコンポーネントしてTankController2.csを作成し付与しよう。

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

public class TankController2 : MonoBehaviour {

	void Update () {
		float rot = Input.GetAxis ("Horizontal");
		float acc = Input.GetAxis ("Vertical");
		transform.Rotate (0, rot, 0);
		transform.Translate (0, 0, acc*0.1f);
	}
}

12.実行してみよう。上下キーで前進後退、左右キーで車体の旋回を行う。自走砲にピッタリの動きだ。今回はこれで行こう。

砲弾の発射

13.砲身からぶっ放す砲弾を作成しよう。プリミティブ素材のCapsuleから作成したいのだがデフォルトでは正面(z軸)が違う方向を向いている。

14.こういった時には親要素を作り子要素をz軸を意識しながら入れる形で実現する。まずはCreateEmptyからBulletを作成する。(座標はどこでもよい)
15.その中に子要素としてCapsuleを作成する。transformは下図。

16.親要素を見てみよう。子要素を90度回転させることによって正面(z軸)の設定に成功している。Unityをやる時に必須のテクニックだ。

17.適当な表面素材を設定し、Rigidbodyを付与する。この際Rigidbodyは親要素のみにつけるのは言うまでもない。
18.すり抜け予防にRigidbodyのCollision DetectionをContinuous Dynamicに設定しておこう。

19.画面から見えなくなったときにDestroyするようにBulletController.csを以下のように作成し付与しよう。
(OnBecameInvisibleはMeshRendererが必須コンポーネントのため必須コンポーネントオプションを指定しておく
こうしておくと、もし付いていない場合には自動的に付与してくれる(すでについている時は外そうとするとerror))

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

[RequireComponent(typeof (MeshRenderer))]
public class BulletController : MonoBehaviour {
	void OnBecameInvisible(){
		Destroy (gameObject);
	}
}

19.ここまでやったら、これをプレファブ化してヒエラルキーからは削除しておこう。ここらで一旦シーンをセーブしておくと良いだろう。(Main)

発射ロジックの実装

20.TankController2.csを以下のように変更しよう。

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

public class TankController2 : MonoBehaviour {

	public GameObject prefab;

	void Update () {
		if (Input.GetButtonDown("Jump")) {
			StartCoroutine (Fire ());
		}
		float rot = Input.GetAxis ("Horizontal");
		float acc = Input.GetAxis ("Vertical");
		transform.Rotate (0, rot, 0);
		transform.Translate (0, 0, acc*0.1f);
	}
	IEnumerator Fire(){
		yield return new WaitForSeconds (0.1f);
		GameObject obj = (GameObject)Instantiate (
			prefab,
			transform.position+transform.forward*2.4f+transform.up*0.7f,
			Quaternion.LookRotation(transform.forward)
		);

		obj.GetComponent<Rigidbody> ().velocity = transform.forward * 80.0f;

	}
}

21.インスペクターからbulletプレファブを登録したら実行してみよう。スペースキーで砲弾が発射されれば成功だ。

ブロックの作成

砲弾でぶっ飛ぶブロックを作成しよう。吹っ飛ぶ気持ち良さを優先させるためここでは質量を軽くする。
22.CubeからBlockを作成し(座標は任意)、適当な表面材質を設定する。
23.Rigidbodyを付与し、Massは0.1
すり抜け予防のためCollision DetectionをContinuous Dynamicに設定しておこう
24.Blockというタグを作成し付与する
25.プレファブ化し、ヒエラルキーからは削除しておく。

Blockジェネレーターの作成

26.CreateEmptyからBlockGeneratorを作成し、BlockGenerator.csを以下のように作成し付与する。

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

public class BlockGenerator : MonoBehaviour {
	public GameObject prefab;

	void Start () {
		StartCoroutine (CreateBlock ());

	}

	IEnumerator CreateBlock(){
		for (int i = 0; i < 100; i++) {
			Instantiate (
				prefab,
				new Vector3(Random.Range(-10.0f,10.0f),Random.Range(3.0f,10.0f),Random.Range(0.0f,10.0f)),
				Quaternion.Euler(new Vector3(Random.Range(-5.0f,5.0f),Random.Range(-5.0f,5.0f),Random.Range(-5.0f,5.0f)))
			);
			yield return new WaitForSeconds (0.01f);

		}
	}
}

27.インスペクターからblockプレファブを登録し、実行してみよう。気持ちよく吹っ飛ばしてくれ。

IsKinematic

28.砲弾をぶっ放すだけでなく、堅牢なボディーも戦車の特徴だ。blockの山に突っ込んでみよう。

ああ!乗り上げて転倒してしまった。
28.TankのRigidbodyのIsKinematicにチェックをいれる。こうすることでスクリプトのみで移動し、外部から影響はうけない。これでどんな悪路でも安定して走破することができる。

カメラの設定

カメラをどのように使うかでゲームの性質ががらっと変わる。アクションゲームではかなり重要な部分だ。今回は手始めにカメラが戦車に追従する動きを入れてみよう。
29.CameraController.csを以下のように作成し、カメラにアタッチする。

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

public class CameraController : MonoBehaviour {
	public Transform tank;
	public float dist=3.5f;
	public float height=1.5f;

	void LateUpdate(){
		transform.position = tank.position + (-tank.forward * dist) + tank.up * height;
		transform.LookAt (tank.position);

	}
}

30.インスペクターからTankを登録し実行してみよう。カメラがTankを追尾するようなった。被写体からのオフセットを指定して被写体を写すというのが基本だ。これをUpdateメソッドのあとに実行するLateUpdateに記述する。

GameMananger

ただ、爽快に吹っ飛ばすだけでもいいが、一応終了条件みたいなものを設定しよう。今回は全部のblockを吹っ飛ばすまでの時間を競うものとする。
31.CreateEmptyからGameManagerを作成し、GameManager.csを以下のように作成し付与する。

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

public class GameManager : MonoBehaviour {
	public GUIStyle timeStyle;
	public GUIStyle msgStyle;
	private float clearTime;
	const int BLOCKCOUNT = 100;
	private int count=0;
	bool isGoal=false;

	void Update () {
		if (count == BLOCKCOUNT) {
			isGoal = true;
		} 
		if (!isGoal) {
			clearTime += Time.deltaTime;
		}
		
	}
	public int GetCount(){
		return count;
	}
	public void AddCount(){
		this.count++;
	}
	void OnGUI(){
		
		GUI.Label (new Rect (Screen.width-250,Screen.height-40,100,100), "Time:" + clearTime.ToString("0.000"), timeStyle);
		if (isGoal) {
			GUI.Label (new Rect (10,Screen.height-40,100,100), "Finish!!", msgStyle);

		}
	}
}

32.インスペクターからフォントの設定をしよう。今回はどちらもfont-size:40,color:白に設定したがお好みに合わせて調整してもらいたい。

吹き飛ばしたblockのカウント


33.ステージ全体を巨大なキューブで囲ってそこから飛び出した数をカウントすることにした。まずはCubeからCountZoneを作成し、transformを以下のように設定する。

34.TriggerなのでコライダーのIsTriggerにチェックを入れ、不要なMeshとMeshRendererはリムーブコンポーネントしておく。
35.CountZoneController.csを以下のように作成しCountZoneに付与する。

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

public class CountZoneController : MonoBehaviour {
	public GameManager gm;

	void OnTriggerExit(Collider coll){
		if (coll.gameObject.tag == "Block") {
			gm.AddCount ();
		}
	}
}

36.インスペクターにヒエラルキーにあるGameManagerをドラッグして登録する。

完成

以上で終了だ。細かいゲームバランスを調整してみてもらいたい。

[課題]
●今回blockの生成の際for文の中で100とハードコーディングしてしまっている。これをGameManagerから参照するように変えてみよう。
●パーティクルでエフェクトを加えてみよう。
●効果音を加えてみよう。

[完成版]
以下からダウンロード解凍後、assetsフォルダに入っているMain.unityをクリックすることでUnityが開く(Unity5.5.4)