今回はUnityのゲームでワープポイントに接触したら他の場所にワープする機能を作成してみたいと思います。
今回作る機能としては、ワープするエリアに主人公が接触したらそのエリア内で所定の位置と角度に移動させ、そこでワープのパーティクルを表示します。
ワープするまでの時間を設定出来るようにし、時間が経過したら移動先にワープさせます。
移動先でもワープのパーティクルを表示する事にします。
完成させるサンプルは↑のような感じになります。
さっそく機能を作成していきましょう。
ワープのパーティクルを用意する
まずはワープする時に表示するパーティクルを用意します。
アセットストアでteleporterで検索し出てくる無料のパーティクルをインポートします。
インポートしたパーティクルのプレハブはAssets→m31_teleporter_FX→04_FX_prefabの中にあります。
サンプルの為に設定を少し変更します。
teleporterプレハブのParticle SystemのDurationを3にし、Loopingのチェックを外します。
teleporterプレハブはワープする時にインスタンス化しますが、再生が止まったら削除したいので、teleporterプレハブに以下のスクリプトを設定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | using UnityEngine; using System.Collections; public class DeleteParticle : MonoBehaviour { private ParticleSystem particle; void Start () { particle = GetComponent<ParticleSystem> (); } // Update is called once per frame void Update () { if (particle.isStopped) { Destroy (this.gameObject); } } } |
自身のゲームオブジェクトのParticleSystemコンポーネントを取得し、Updateメソッド内でisStoppedで止まったかどうかを判定し、止まっていたらゲームオブジェクトを削除します。
teleporterプレハブは以下のようになります。
これでワープ用のパーティクルプレハブが出来上がりました。
ワープする場所、ワープする先を作成する
そのエリアに入ったらワープする場所とワープ先を作っていきます。
サンプルの舞台は適当に作って頂いて・・・、ワープ元とワープ先を作っていきます。
↑のように緑の領域がワープ元で高い場所にある赤い領域がワープ先になります。
ワープ元の作成
ワープ元の領域を作っていきます。
サンプルの舞台を作成
まずはAssetsフォルダ内で右クリック→Create→Materialを選択し、名前をWarpとします。
WarpのAlbedoの右の色の選択部分をクリックし、色の調整とAを100にして不透明度を下げ領域が多少透けるようにします。
ヒエラルキー上にCubeを作成し名前をWarpにし、サイズの調整、向きの調整、Mesh RendererのMaterialsに作成したWarpを設定します。
またBox ColliderのIs Triggerにチェックを入れ物理的に当たらないようにしておきます。
ここで重要になるのがWarpゲームオブジェクトの位置と向きです。
今回はワープ元の領域に入ったらWarpの中心の位置と向きに向き直してからワープするようにする為、Warpゲームオブジェクトの中心が地面との接触点、向きがカメラの向きを向くようにしています。
これは領域に入ったら、カメラの方に向き直しながら中心位置に移動させるためです。
Warpゲームオブジェクトに取り付けるスクリプトの作成
次にWarpにWarpというスクリプトを作成し設定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | using UnityEngine; using System.Collections; public class Warp : MonoBehaviour { public Transform warpPoint; void OnTriggerEnter(Collider col) { if(col.tag == "Player") { // キャラクターの状態をワープ状態に変更 col.GetComponent <WarpChara>().SetState(WarpChara.WarpCharaState.GoToWarpPoint, transform, warpPoint); } } } |
publicでワープする先のゲームオブジェクトを指定出来るようにし、WarpにPlayerタグを持つキャラクターが入ってきたらキャラクターのWarpCharaスクリプトを取得し、
SetStateメソッドに、状態、WarpのTransform、ワープ先のTransformを渡して呼び出します。
WarpCharaスクリプトをまだ作成していないのでわかり辛いと思いますが、要はWarpの領域にキャラクターが入ったらキャラクターの状態を変更するメソッドを呼び出しているだけです。
ワープ先の作成
次はワープ先を作成していきます。
ワープ元と同じように新しくマテリアルを作成し、名前をWarpPointとします。
ヒエラルキー上にCubeを作成し名前をWarpAreaとし、先ほど作成したWarpPointマテリアルを設定します。
WarpAreaはワープ先なので特別な処理をせず、Box Colliderを使う必要がない為、右の歯車をクリックし、Remove Componentを選択します。
↑のような感じになります。
WarpAreaの中心はCubeの中心になっているのでWarpAreaをワープ先としてしまうとワープ先が少し地面から離れてしまいます。
そこでWarpAreaの子要素に空のゲームオブジェクトを作成し、名前をWarpPointとしてWarpAreaの地面部分に移動させます。
↑のようになります。
このWarpPointを先ほど作ったワープ元のWarpスクリプトのインスペクタのWarpPointに設定します。
キャラクタースクリプトの作成
キャラクター用スクリプトを作成していきます。
CharacterControllerやAnimator、移動させるスクリプト等は

の辺りを参照してください。
AnimatorではIdle状態とWalk状態を作成し、アニメーションパラメータにFloat型のSpeedが0.1より上だったらWalk、それより下だったらIdleへと遷移するように作成します。
スクリプトが長いので分割して説明していきます。
スクリプトに追記していってください。
設定部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | using UnityEngine; using System.Collections; public class WarpChara : MonoBehaviour { public enum WarpCharaState { Normal, GoToWarpPoint, }; private CharacterController characterController; private Animator animator; private Vector3 velocity = Vector3.zero; private WarpCharaState state; private Transform waitPoint; private Transform warpPoint; private InstantiateParticle instantiateParticle; // 歩く速さ public float walkSpeed; // ワープポイントでキャラクターを中央に移動させたり回転させたりするスピード public float goToWaitPointSpeed; } |
まずはキャラクターの状態を表す列挙型を宣言し、通常の移動出来る状態とワープ処理をしている状態を作成します。
ワープ処理状態の時はキャラクターの操作を出来ないようにします。
waitPointはワープ元の領域に入った時にその領域内の指定したポイントを入れます。
これはワープ元の中心の位置と角度が入り、そこに自動で移動するようにします。
warpPointはワープ先のTransformが入ります。
instantiateParticleはワープのパーティクルをインスタンス化する為のスクリプトでキャラクター自身に設定し、パーティクルをインスタンス化します。
InstantiateParticleは後で作成します。
goToWaitPointSpeedはwaitPoint(ワープ元の中心)に移動や回転する時のスピードをインスペクタで設定出来るようにしています。
Startメソッド
Startメソッドではコンポーネントの取得等を行っています。
1 2 3 4 5 6 7 8 9 | // Use this for initialization void Start () { characterController = GetComponent<CharacterController> (); animator = GetComponent <Animator> (); state = WarpCharaState.normal; instantiateParticle = GetComponent<InstantiateParticle> (); } |
自身のCharacterController、Animator、InstantiateParticleを取得したり、キャラクターの状態変数をノーマル状態にしています。
Updateメソッド
Updateメソッドではキャラクターの状態に応じた処理を行っています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // Update is called once per frame void Update () { if (state == WarpCharaState.normal) { if (characterController.isGrounded) { velocity = Vector3.zero; var input = new Vector3 (Input.GetAxis ("Horizontal"), 0f, Input.GetAxis ("Vertical")); if (input.magnitude > 0f) { transform.LookAt (transform.position + input); velocity += transform.forward * walkSpeed; animator.SetFloat ("Speed", 1f); } else { animator.SetFloat ("Speed", 0f); } } velocity.y += Physics.gravity.y * Time.deltaTime; characterController.Move (velocity * Time.deltaTime); } else if (state == WarpCharaState.goToWarpPoint) { GoToWarpWaitPoint (); } } |
キャラクターがノーマル状態の時はキャラクターの移動処理を行ってます。
キャラクターがワープ処理状態の時はGoToWarpWaitPointメソッドを呼び出しています。
GoToWarpWaitPointメソッド
GoToWarpWaitPointメソッドはwaitPointの位置と角度に徐々に移動させ、waitPointの位置と角度になったらパーティクルの表示と待機、ワープをさせます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | void GoToWarpWaitPoint() { if (Vector3.Distance(transform.position, waitPoint.position) > 0.1f || Quaternion.Angle(transform.rotation, waitPoint.rotation) >= 5f ) { animator.SetFloat("Speed", 1f); Debug.Log("移動中"); transform.position = Vector3.Lerp(transform.position, waitPoint.position, goToWaitPointSpeed * Time.deltaTime); transform.rotation = Quaternion.Lerp(transform.rotation, waitPoint.rotation, goToWaitPointSpeed * Time.deltaTime); } else if (transform.position != waitPoint.position //&& Quaternion.Angle(transform.rotation, waitPoint.rotation) < 5f ) { animator.SetFloat("Speed", 0f); Debug.Log("きっちり位置と角度を合わせる"); transform.position = waitPoint.position; transform.rotation = waitPoint.rotation; // ワープ用パーティクルの表示 instantiateParticle.InstantiateWarpParticle(transform.position); // 3秒後にワープ Invoke("Warp", 3f); } } |
Vector3.DistanceでキャラクターとwaitPointの位置が0.1より大きい時、またはキャラクターの角度とwaitPointの角度が5度以上ある時は位置と角度をwaitPointに近づけます。
else ifでキャラクターの位置とwaitPointの位置が違う場合は目的地に到着したと見なし実際のワープ処理を行います。
まずはSpeedを0にしWalkからIdleへと状態を遷移させます。
その後、キャラクターの位置と角度をwaitPointに完全に合わせる為、代入します。
その後instantiateParticleスクリプトのInstantiateWarpParticleに自身の位置を引数として渡してパーティクルをインスタンス化します。
Invokeを使って3秒後に自身のスクリプトにあるWarpメソッドを実行します。
teleporterのDurationを3にしたのはこの秒数と合わせる為です。
キャラクターがwaitPointに移動してからワープするまでの待機時間を変えたい場合はpublicフィールドで秒数を指定出来るようにして、teleporterのDurationも調整します。
Warpメソッド
Warpメソッドでは実際のキャラクターの移動や移動先にパーティクルを表示する処理を行ってます。
1 2 3 4 5 6 7 8 9 10 | void Warp() { transform.position = warpPoint.position; transform.rotation = warpPoint.rotation; // 移動先でワープパーティクルの表示 instantiateParticle.InstantiateWarpParticle(transform.position); SetState(WarpCharaState.Normal); } |
キャラクターの位置をwarpPointの位置と角度にします。
移動先でもワープパーティクルを表示する為InstantiateWarpParticleメソッドにキャラクターの位置を引数として渡しインスタンス化しています。
それが終わったらワープ処理が終わったのでSetStateメソッドでキャラクターをノーマル状態に戻しています。
SetStateメソッド
SetStateメソッドはキャラクターの状態を変更するメソッドです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public void SetState(WarpCharaState state, Transform waitPoint = null, Transform warpPoint = null) { this.state = state; this.waitPoint = waitPoint; this.warpPoint = warpPoint; // 状態に応じた処理 if (state == WarpCharaState.GoToWarpPoint) { velocity = Vector3.zero; characterController.enabled = false; } else if(state == WarpCharaState.Normal) { characterController.enabled = true; } } |
Warpゲームオブジェクトに設定したWarpスクリプトから呼び出された場合はwaitPointとwarpPointが渡されてきます。
先ほどのWarpメソッドが呼び出した場合は状態しか渡されない為、waitPointとwarpPointが渡されてきません。
そういった場合はnullが指定されるように引数であらかじめ設定しておきます。
受け取った引数をフィールド値にそれぞれ入れた後、状態がワープ処理状態に遷移する時はvelocityの値を初期化しています。
またCharacterControllerコンポーネントが有効である状態で、キャラクターの位置を直接変更する場合はCharacterControllerコンポーネントが邪魔をして位置を変更出来ない場合があるので、一旦CharacterControllerコンポーネントを無効化し、移動が完了したら有効にします。
ここら辺は

も参照してみてください。
パーティクルをインスタンス化するスクリプトの作成
パーティクルをインスタンス化するスクリプトはキャラクター自身に取り付けた方が都合がいいのでキャラクターに取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 | using UnityEngine; using System.Collections; public class InstantiateParticle : MonoBehaviour { public GameObject warpParticle; public void InstantiateWarpParticle(Vector3 pos) { Instantiate (warpParticle, pos, Quaternion.Euler(0f, 0f, 0f)); } } |
warpParticleに指定したゲームオブジェクトを受け取った引数の位置に作成します。
角度は全部0にしています。
WarpChara、InstantiateParticleスクリプトを取り付けたキャラクターのインスペクタは
↑のようになります。
ワープ機能を確認する
これで全ての機能の作成が終わりました。
MainCameraにFollowTargetスクリプトを設定し、キャラクターを追従するようにして確認してみます。
↑のようになりました。
もっと演出を加えるならばワープまでの待機時間にキャラクター自身を少しづつ透明にし、ワープ先ではだんだんと見えるようにする。
といった事をすると近未来感がありますね(^_^)v
単純なワープ機能を作るだけなら
1 2 3 | transform.position = waitPoint.position; |
という記述だけで終わってしまいますが・・・(^_^;)
その周りを肉付けする事でワープしている感じが出てきますね(^_^)v