「woman1-e」の「Animation」の横にある「○に・」アイコンをクリックして表示される「Select AnimationClip」から「Walk」を選択しておく(図8)。
「Plane」の上空に位置する「woman2-e」の「Animation」は「Idle」を指定する。これはキャラクターが休憩している状態だ。
このアプリを実行するためには、最初に「スペース」キーを押して、戦う相手を出現させる必要がある。この処理は、前々回記事で解説した方法を一部利用した形になる。
まず、Unityメニューの「GameObject」→「Create Empty」と選択する。空の「GameObject」がHierarchy内に追加される。この「GameObject」を選択し、表示されるInspectorから「Add Component」をクリックする。
「New Script」を選択し「Name」に「AddObject」、「Language」に「C Sharp」を指定して、「Create and Add」をクリックする。Inspector内にスクリプトが表示されるので、マウスでダブルクリックする。Visual Studioのエディターが起動するので、リスト1のコードを記述する。
public Transform myObj; private GameObject obj2; void Start () { obj2 = GameObject.Find("woman2-e"); } void Update () { if(Input.GetKey(KeyCode.Space)) { obj2.SetActive(true); myObj.transform.position = new Vector3(0, 0.1f, 0); } }
まず、Transform型のpublicな変数「myObj」を宣言し(1行目)、次に、GameObject型のprivateな変数「obj2」を宣言する(2行目)。「myObj」変数はpublicにしているため、Inspector内のプロパティに表示されるようになる。
Start関数内でFind関数を使って「woman2-e」にアクセスし、変数「obj2」で参照する(4行目)。Update関数内では、8行目で、「スペース」キーが押されたときにどうするかを記述している。
9〜12行目で、SetActive関数に「true」を指定して、「woman2-e」を表示させている。表示させる位置は「Vector3」で指定した位置になる。「Vector3」はX、Y、Zの値を持つベクトルで、位置情報を表す。この場合は、X=0、Y=0.1、Z=0の位置に「woman2-e」が出現する。
JavaScriptのコードも記載しておく、コードの解説はC#と同じだ。JavaScriptで書きたい場合は、連載第5回のコラム「スクリプトエディタの切り替え」を参照されたい。
public var myObj:Transform; private var obj2:GameObject; function Start () { obj2=GameObject.Find("woman2-e"); } function Update () { if(Input.GetKey(KeyCode.Space)){ obj2.SetActive(true); myObj.transform.position=Vector3(0,0.1,0); } }
では、ビルドして「GameObject」のInspectorを表示させてみよう。
図9のように、「My Obj」というプロパティが追加されている。横にある「○に・」アイコンをクリックして、「Select Transform」から「woman2-e」を選択する(図9)。
「woman1-e」のAnimationには「Walk」と指定しているので、実行すると、同じ位置で一瞬、歩行してすぐ停止する(動画1)。
これでは具合が悪いので、矢印キーで左右に方向転換し、ずっと歩き続けるスクリプトを書く必要がある。
また、これまでの連載で作ってきたサンプルはAnimator Controllerを使っていたので、衝突時にどのようなアニメーションをさせるかのスクリプトを書く必要がなかった。しかし、Animator Controllerを使わない場合はそうはいかない。細かいスクリプトの記述が必要となる。
「woman1-e」を選択して表示されるInspectorから、今までの手順に従って「CharacterAction」というスクリプトを追加して、リスト2のコードを記述する。
private GameObject obj1; private GameObject obj2; private bool flag; void Start() { flag = false; obj1 = GameObject.Find("woman1-e"); obj2 = GameObject.Find("woman2-e"); } void Update() { if (flag == false) { obj1.GetComponent<Animation>().Play("Walk"); } if (Input.GetKey("down")) { obj1.transform.position += transform.forward * 0.01f; } if (Input.GetKey("left")) { obj1.transform.Rotate(0, 2, 0); } if (Input.GetKey("right")) { obj1.transform.Rotate(0, -2, 0); } } private IEnumerator OnCollisionEnter(Collision col) { if (col.gameObject.tag == "Woman2") { flag = true; obj1.GetComponent<Animation>().Play("Skill"); obj2.GetComponent<Animation>().Play("Skill1"); yield return new WaitForSeconds(3); obj1.GetComponent<Animation>().Play("Skill_move"); yield return new WaitForSeconds(2); obj2.GetComponent<Animation>().Play("Dead2"); yield return new WaitForSeconds(2); obj2.GetComponent<Animation>().Stop(); yield return new WaitForSeconds(1); obj2.SetActive(false); yield return new WaitForSeconds(1); obj1.GetComponent<Animation>().Play("Walk"); } }
まず、「obj1」と「obj2」というGameObject変数を宣言する(1・2行目)。また「bool」型の変数「flag」も宣言しておく(3行目)。
5〜10行目のStart関数内では、変数「flag」を「false」で初期化しておく(7行目)。また、Find関数で「woman1-e」と「woman2-e」にアクセスして、変数「obj1」と「obj2」で参照しておく(8・9行目)。
Update関数内では以下の処理を行う。
変数「flag」が「false」であった場合は、「GetComponent<Animation>().Play」で、図8で設定した「Walk」アニメーションを実行する(14〜17行目)。これは女剣士が歩いているアニメーションだ。
GetKey関数内では、キーボードの「down(↓)」「left(←)」「right(→)」キーが押された場合のそれぞれの処理を記述している。
19〜22行目では、「down(↓)」キーが押された場合の処理をしている。前方向に進ませる、スピードは「0.01」としている。この値を大きくすると歩く速さというより進む速さが大きくなる。あまり大きくし過ぎると、歩くテンポと地面の関係が不自然になるので注意してほしい。
次は「left(←)」キーが押された場合の処理だ。「Rotate」を使って「Y」軸の値を「2」に指定している(24〜27行目)。
「right(→)」キーが押された場合は、「Rotate」を使って「Y」軸の値を「-2」と指定している。「left」キーが押された場合とは反対方向に向く(29〜32行目)。
なおAnimator Controllerを使わない場合は、スムーズに動かすためには、「down」キーを押しながら、同時に「right」や「left」キーを押す必要がある。単独で「right」や「left」キーを押すと、その場で回転だけをする動作になる。
次は、女剣士同志が接触した場合の処理で、OnCollisionEnter関数内に処理を書く(35行目)。前回も触れたが、「Character Controller」を使わない衝突判定にはOnCollisionEnter関数を使うのだ。
引数になっているCollisionのオブジェクト「col」には接触情報が入っている(35行目)。
また、OnCollisionEnter関数の戻り値は、「IEnumerator」型となっている(35行目)。
これは「コルーチン」から呼び出す場合に記述する方法だ。「yield return new WaitForSeconds(3)」といった処理が、IEnumeratorの中でしか使用できないから使っている(42、45、48、51、55行目)。
以下は、ゲームオブジェクトの「tag」が「Woman2」であった場合、つまり「woman2-e」と接触した場合(37行目)の処理だ。
「flag」を「true」で初期化し、「woman1-e」のアニメーションを「Skill」とし、「woman2-e」のアニメーションを「Skill1」とする(39〜41行目)。
「Skill1」のアニメーションを、3秒後に「Skill1_move」に変化させ(42〜44行目)、2秒後に「woman2-e」のアニメーションを「Dead2」として、「死ぬ」アニメーションを実行する(45〜47行目)。その2秒後にアニメーションを停止する(48〜50行目)。
「woman1-e」の接触する相手が「Woman2」ではなかった場合は、「woman1-e」の女剣士は歩き続けるはずだったのだが、コルーチンのOnCollisionEnter関数からは、Update関数が実行できず、そこで終わってしまう。「woman2-e」が死んで消えた後、「woman1-e」は一瞬歩き始めるが、すぐに停止する。この点はご了承願いたい。
ただし、再度「スペース」キーを押すと女剣士が現れ、キーボードの「↓」キーを押すと、再度決闘が始まる。これは、永遠に繰り返すことができるが、歩き続けることはできない。歩き続ける処理については、各自が考えてみてほしい。
JavaScriptのコードも記載しておく、コードの解説はほとんどC#と同じだが、JavaScriptコルーチンとIEnumeratorは使っていない。
private var obj1:GameObject; private var obj2:GameObject; private var flag:boolean; function Start () { flag=false; obj1=GameObject.Find("woman1-e"); obj2=GameObject.Find("woman2-e"); } function Update () { if(flag==false){ obj1.GetComponent(Animation).Play("Walk"); } if (Input.GetKey("down")) { obj1.transform.position += transform.forward * 0.01f; } if (Input.GetKey("left")) { obj1.transform.Rotate(0, 2, 0); } if (Input.GetKey ("right")) { obj1.transform.Rotate(0, -2, 0); } } function OnCollisionEnter(col:Collision){ if(col.gameObject.tag=="Woman2"){ flag=true; obj1.GetComponent(Animation).Play("Skill"); obj2.GetComponent(Animation).Play("Skill1"); yield WaitForSeconds(3); obj1.GetComponent(Animation).Play("Skill1_move"); yield WaitForSeconds(2); obj2.GetComponent(Animation).Play("Dead2"); yield WaitForSeconds(1); obj2.GetComponent(Animation).Stop(); yield WaitForSeconds(1); obj2.SetActive(false); flag=false; }else{ flag=false; obj1.GetComponent(Animation).Play("Walk"); } }
Copyright © ITmedia, Inc. All Rights Reserved.