Unity3Dで3D脱出ゲームの作り方、パート3(カメラの移動、カードキーの取得、一枚ドア、二枚ドア、リフトの設置)

Unity3Dで3D脱出ゲームの作り方、パート3です。
がんばれ、ゴールはあと少し・・・。

カメラの移動

Unity: Stealth – Camera Movement



プレイヤーがちょうどよく収まる感じにシーンビューを調整します。
camera_mainを選択してGameObject → Align With Viewを選択します。
これでcamera_mainのPositionやRotationが調整されます。

camera_mainを選択してCameraMovementという名前でC#スクリプトを作成して編集します。
using UnityEngine;
using System.Collections;

public class CameraMovement : MonoBehaviour {
	public float smooth = 1.5f;

	private Transform player;
	private Vector3 relCameraPos;
	private float relCameraPosMag;
	private Vector3 newPos;

	void Awake(){
		player = GameObject.FindGameObjectWithTag (Tags.player).transform;
		relCameraPos = transform.position - player.position;
		relCameraPosMag = relCameraPos.magnitude - 0.5f;
	}

	void FixedUpdate(){
		Vector3 standardPos = player.position + relCameraPos;
		Vector3 abovePos = player.position + Vector3.up * relCameraPosMag;
		Vector3[] checkPoints = new Vector3[5];
		checkPoints [0] = standardPos;
		checkPoints [1] = Vector3.Lerp (standardPos, abovePos, 0.25f);
		checkPoints [2] = Vector3.Lerp (standardPos, abovePos, 0.5f);
		checkPoints [3] = Vector3.Lerp (standardPos, abovePos, 0.75f);
		checkPoints [4] = abovePos;

		for(int i = 0; i < checkPoints.Length; i++){
			if(ViewingPosCheck(checkPoints[i])){
				break;
			}
		}

		transform.position = Vector3.Lerp (transform.position, newPos, smooth * Time.deltaTime);
	}

	bool ViewingPosCheck(Vector3 checkPos){
		RaycastHit hit;

		if(Physics.Raycast (checkPos, player.position - checkPos, out hit, relCameraPosMag)){
			if(hit.transform != player){
				return false;
			}
		}

		newPos = checkPos;
		return true;
	}

	void SmoothLookAt(){
		Vector3 relPlayerPosition = player.position - transform.position;
		Quaternion lookAtRotation = Quaternion.LookRotation (relPlayerPosition, Vector3.up);
		transform.rotation = Quaternion.Lerp (transform.rotation, lookAtRotation, smooth * Time.deltaTime);
	}
}

カードキーの取得

Unity: Stealth – The Key



char_ethanオブジェクトを選択してPlayerInventoryという名前でC#スクリプトを作成して編集します。
using UnityEngine;
using System.Collections;

public class PlayerInventory : MonoBehaviour {
	public bool hasKey;
}

Modelsのprop_keycardをシーンビューにドロップしてPosition X:-22 Y:0.4 Z:32に変更します。

AssetsのAnimatorsを選択してAnimator Controllerを作成してKeycardAnimatorという名前に変更します。
KeycardAnimatorを選択したまま、Modelsのprop_keycardを展開してそこにあるSpinをAnimatorビューにドロップします。
AnimatorビューのSpinを選択してFoot IKにチェックを入れます。
prop_keycardオブジェクトを選択してKeycardAnimatorをprop_keycardオブジェクトのAnimator Controllerにアタッチします。
続けてSphere Colliderを追加してRadius 0.92、Is Triggerにチェックを入れます。
続けてLightを追加してRange 4.5、Intensity 2.5、Colorを水色に変更します。

prop_keycardオブジェクトを選択してKeyPickupという名前でC#スクリプトを作成して編集します。
using UnityEngine;
using System.Collections;

public class KeyPickup : MonoBehaviour {
	public AudioClip keyGrab;

	private GameObject player;
	private PlayerInventory playerInventory;

	void Awake(){
		player = GameObject.FindGameObjectWithTag (Tags.player);
		playerInventory = player.GetComponent<PlayerInventory>();
	}

	void OnTriggerEnter(Collider other){
		if(other.gameObject == player){
			AudioSource.PlayClipAtPoint(keyGrab, transform.position);
			playerInventory.hasKey = true;
			Destroy (gameObject);
		}
	}
}

prop_keycardオブジェクトのKey Pickup(Script)のKey GrabにAudioのkeycard_pickupをアタッチします。
prop_keycardオブジェクトのAnimatorのApply Root Motionのチェックをはずします。
prop_keycardオブジェクトを展開してprop_keycard_cardを選択してMesh RendererのUse Light Probesにチェックを入れます。
最後にprop_keycardオブジェクトをPrefabsにドロップします。

自動扉(ドア1枚)

Unity: Stealth – Single Doors



AssetsのModelsのdoor_generic_slideをシーンビューにドロップして、Position X:-6 Y:0 Z:7、Rotation Y:90に変更します。
door_generic_slideオブジェクトを展開してdoor_generic_slide_panelオブジェクトを選択してUse Light Probesにチェックを入れます。
door_generic_slideオブジェクトを選択してSphere Colliderを追加してCenter Y:1、Radius 3にします。
door_generic_slide_panelオブジェクトを選択してBox Colliderを追加します。

AssetsのAnimatorsを選択してAnimator Controllerを作成してSingleDoorAnimatorという名前に変更します。
SingleDoorAnimatorを選択してAnimatorビューを選択して、ParametersにBoolのOpenを追加します。
AssetsのModelsのdoor_generic_slideを展開してClosedとOpenをAnimatorビューにドロップします。
ClosedからOpenへTransitionを作成してConditionsをOpen trueにします。
OpenからClosedへTransitionを作成してConditionsをOpen falseにします。
Transitionを150%に引き伸ばします。(説明するの面倒なので動画を見よう!)

door_generic_slideオブジェクトを選択してSingleDoorAnimatorをdoor_generic_slideオブジェクトのAnimator ControllerにアタッチしてApply Root Motionのチェックをはずします。

door_generic_slide_panelオブジェクトを選択してRigidbodyを追加してUse Gravityのチェックをはずして、Is Kinematicにチェックをいれます。

door_generic_slideオブジェクトを選択してAudio Sourceを追加してDoorAnimationという名前でC#スクリプトを追加して編集します。
using UnityEngine;
using System.Collections;

public class DoorAnimation : MonoBehaviour {
	public bool requireKey;
	public AudioClip doorSwishClip;
	public AudioClip accessDeniedClip;

	private Animator anim;
	private HashIDs hash;
	private GameObject player;
	private PlayerInventory playerInventory;
	private int count;

	void Awake(){
		anim = GetComponent<Animator>();
		hash = GameObject.FindGameObjectWithTag(Tags.gameController).GetComponent<HashIDs>();
		player = GameObject.FindGameObjectWithTag (Tags.player);
		playerInventory = player.GetComponent<PlayerInventory>();
	}

	void OnTriggerEnter(Collider other){
		if (other.gameObject == player) {
			if (requireKey) {
				if (playerInventory.hasKey) {
					count++;
				}
				else {
					audio.clip = accessDeniedClip;
					audio.Play ();
				}
			}
			else {
				count++;
			}
		}
		else if(other.gameObject.tag == Tags.enemy){
			if(other is CapsuleCollider){
				count++;
			}
		}
	}

	void OnTriggerExit(Collider other){
		if(other.gameObject == player || (other.gameObject.tag == Tags.enemy && other is CapsuleCollider)){
			count = Mathf.Max (0, count-1);
		}
	}

	void Update(){
		anim.SetBool (hash.openBool, count > 0);

		if(anim.IsInTransition(0) && !audio.isPlaying){
			audio.clip = doorSwishClip;
			audio.Play ();
		}
	}
}

door_generic_slideオブジェクトのDoor Animation(Script)のDoor Swish ClipにAudioのdoor_open、Access Denied ClipにAudioのdoor_accessDeniedをアタッチします。door_generic_slideオブジェクトのSphere ColliderのIs Triggerのチェックを入れます。

door_generic_slideオブジェクトをPrefabsにドロップします。
door_generic_slideオブジェクトを2つ複製して、
1つをPosition X:-15.9 Y:0 Z:7、もう1つをPosition X:-7.9 Y:0 Z:37にします。

自動ドア(2枚)

Unity: Stealth – Player Setup



AssetsのModelsのdoor_exit_outerをシーンビューにドロップしてPosition X:-22 Y:0 Z:46、Rotation Y:270にします。
door_exit_outerオブジェクトを展開して子要素のUse Light Probesにチェックを入れます。

door_exit_outerオブジェクトを選択してSphere Colliderを追加してIs Triggerにチェックをいれ、Center X:-1 Y:1 Z:0、Radius 2にします。
door_exit_outerオブジェクトの子要素を選択してBox Colliderを追加します。

AssetsのAnimatorsのSingleDoorAnimatorを複製して、複製してできたのをDoubleDoorAnimatorという名前に変更します。
DoubleDoorAnimatorを選択してAnimatorビューを選択します。
Openを選択してModelsのdoor_exit_outerを展開してdoor_exitOuter_openedをOpenのInspectorのMotionにドロップします。
Closedを選択してModelsのdoor_exit_outerを展開してdoor_exitOuter_closedをClosedのInspectorのMotionにドロップします。

door_exit_outerオブジェクトを選択してDoubleDoorAnimatorをdoor_exit_outerオブジェクトのAnimator ControllerにアタッチしてApply Root Motionのチェックをはずします。

door_exit_outerオブジェクトの2つの子要素を選択してRigidbodyを追加してUse Gravityのチェックをはずし、Is Kinematicのチェックをいれます。

DoorAnimationスクリプトをdoor_exit_outerオブジェクトへドロップします。
door_exit_outerオブジェクトのDoor Animation(Script)のDoor Swish ClipにAudioのdoor_openを設定し、Access Denied ClipにAudioのdoor_accessDeniedを設定します。Require Keyにチェックを入れます。

door_exit_outerオブジェクトをPrefabsにドロップします。

LiftDoorsTrackingと言う名前でC#スクリプトを作成して編集します。
using UnityEngine;
using System.Collections;

public class LiftDoorsTracking : MonoBehaviour {
	public float doorSpeed = 7f;

	private Transform leftOuterDoor;
	private Transform rightOuterDoor;
	private Transform leftInnerDoor;
	private Transform rightInnerDoor;
	private float leftClosedPosX;
	private float rightClosedPosX;

	void Awake(){
		leftOuterDoor = GameObject.Find ("door_exitOuter_left_001").transform;
		rightOuterDoor = GameObject.Find ("door_exitOuter_right_001").transform;
		leftInnerDoor = GameObject.Find ("door_exitInner_left_001").transform;
		rightInnerDoor = GameObject.Find ("door_exitInner_right_001").transform;

		leftClosedPosX = leftInnerDoor.position.x;
		rightClosedPosX = rightInnerDoor.position.x;
	}

	void MoveDoors(float newLeftXTarget, float newRightXTarget){
		float newX = Mathf.Lerp (leftInnerDoor.position.x, newLeftXTarget, doorSpeed * Time.deltaTime);
		leftInnerDoor.position = new Vector3 (newX, leftInnerDoor.position.y, leftInnerDoor.position.z);

		newX = Mathf.Lerp (rightInnerDoor.position.x, newRightXTarget, doorSpeed * Time.deltaTime);
		rightInnerDoor.position = new Vector3 (newX, rightInnerDoor.position.y, rightInnerDoor.position.z);
	}

	public void DoorFollowing(){
		MoveDoors (leftOuterDoor.position.x, rightOuterDoor.position.x);
	}

	public void CLoseDoors(){
		MoveDoors (leftClosedPosX, rightClosedPosX);
	}
}

リフトの設置

Unity: Stealth – The Lift



AssetsのModelsのprop_lift_exitをシーンビューにドロップしてPosition X:-21.85 Y:1.5 Z:48、Rotation Y:270にする。
prop_lift_exitオブジェクトを展開してdoor_exit_innerの2つの子要素を選択してprop_lift_exitオブジェクトの残りの3つの子要素を選択してUse Light Probesにチェックを入れる。

prop_lift_exitオブジェクトに空のGameObjectを作成してprop_lift_colliderという名前に変更します。
prop_lift_colliderオブジェクトを選択してMesh Colliderを追加してMeshにprop_lift_exit_collision_001を選択します。

prop_lift_exitオブジェクトを選択してBox Colliderを追加してIs Triggerにチェックをいれ、Center X:0 Y:-1.1 Z:0、Size X:3.3 Y:0.5 Z:3.5にします。
続いてRigidbodyを追加してUse Gravityのチェックをはずして、Is Kinematicにチェックをいれます。
続いてAudio Sourceを追加してPlay On AwakeのチェックをはずしてLoopにチェックを入れてAudio ClipにAudioのlift_raiseを設定します。

prop_lift_exitオブジェクトにLiftTriggerという名前でC#スクリプトを追加して編集します。
using UnityEngine;
using System.Collections;

public class LiftTrigger : MonoBehaviour {
	public float timeToDoorsClose = 2f;
	public float timeToLiftStart = 3f;
	public float timeToEndLevel = 6f;
	public float liftSpeed = 3f;

	private GameObject player;
	private Animator playerAnim;
	private HashIDs hash;
	private CameraMovement camMovement;
	private LiftDoorsTracking liftDoorsTracking;
	private bool playerInLift;
	private float timer;

	void Awake(){
		player = GameObject.FindGameObjectWithTag (Tags.player);
		playerAnim = player.GetComponent<Animator>();
		hash = GameObject.FindGameObjectWithTag(Tags.gameController).GetComponent<HashIDs>();
		camMovement = Camera.main.gameObject.GetComponent<CameraMovement>();
		liftDoorsTracking = GetComponent<LiftDoorsTracking>();
	}

	void OnTriggerEnter(Collider other){
		if(other.gameObject == player){
			playerInLift = true;
		}
	}

	void OnTriggerExit(Collider other){
		if(other.gameObject == player){
			playerInLift = false;
			timer = 0f;
		}
	}

	void Update(){
		if (playerInLift)
			LiftActivation ();

		if (timer < timeToDoorsClose) {
			liftDoorsTracking.DoorFollowing ();
		}
		else {
			liftDoorsTracking.CloseDoors();
		}
	}

	void LiftActivation(){
		timer += Time.deltaTime;

		if(timer >= timeToLiftStart){
			playerAnim.SetFloat(hash.speedFloat, 0f);
			camMovement.enabled = false;
			player.transform.parent = transform;

			transform.Translate(Vector3.up * liftSpeed * Time.deltaTime);

			if(!audio.isPlaying){
				audio.Play ();
			}
		}
	}
}

prop_lift_exitオブジェクトをPrefabsにドロップします。

Playボタンを押して実行する。

・・・

リフトの内側のドアが開かない。
開かないけど素通りできる。

NullReferenceException: Object referece not set to an instance of an object


わかった。

LiftDoorsTrackingスクリプトをprop_lift_exitオブジェクトにアタッチするとエラーがでなくなる。

あとLiftDoorsTrackingスクリプトでdoor_exit_…の名前が違うのでそれも直す。
leftOuterDoor = GameObject.Find ("door_exit_outer_left_001").transform;
rightOuterDoor = GameObject.Find ("door_exit_outer_right_001").transform;
leftInnerDoor = GameObject.Find ("door_exit_inner_left_001").transform;
rightInnerDoor = GameObject.Find ("door_exit_inner_right_001").transform;

次がラストパートです!

コメントを残す

メールアドレスが公開されることはありません。