連載
» 2017年03月13日 05時00分 公開

ゲーム開発初心者のためのUnity入門(14):Unity 5.5、5.6の新機能&Animator Controllerを使わないアニメーション制御 (2/3)

[薬師寺国安,PROJECT KySS]

「Animation」の設定

 「woman1-e」の「Animation」の横にある「○に・」アイコンをクリックして表示される「Select AnimationClip」から「Walk」を選択しておく(図8)。

図8 「woman1-e」の「Animation」に「Walk」を指定する

 「Plane」の上空に位置する「woman2-e」の「Animation」は「Idle」を指定する。これはキャラクターが休憩している状態だ。

「スペース」キーで3Dキャラを表示させるスクリプト

 このアプリを実行するためには、最初に「スペース」キーを押して、戦う相手を出現させる必要がある。この処理は、前々回記事で解説した方法を一部利用した形になる。

 まず、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);
    }
}
リスト1 「スペース」キーで「woman2-e」を表示させる処理(AddObject.cs)

 まず、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のコード

 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);
  }
}
リスト1 「スペース」キーで「woman2-e」を表示させる処理(AddObjectJS.js)

いったんビルドして実行

 では、ビルドして「GameObject」のInspectorを表示させてみよう。

 図9のように、「My Obj」というプロパティが追加されている。横にある「○に・」アイコンをクリックして、「Select Transform」から「woman2-e」を選択する(図9)。

図9 「My Obj」プロパティに「woman2-e」を指定する

 「woman1-e」のAnimationには「Walk」と指定しているので、実行すると、同じ位置で一瞬、歩行してすぐ停止する(動画1)。

 これでは具合が悪いので、矢印キーで左右に方向転換し、ずっと歩き続けるスクリプトを書く必要がある。

 また、これまでの連載で作ってきたサンプルはAnimator Controllerを使っていたので、衝突時にどのようなアニメーションをさせるかのスクリプトを書く必要がなかった。しかし、Animator Controllerを使わない場合はそうはいかない。細かいスクリプトの記述が必要となる。

スクリプトで3Dキャラにどんなアニメーションをさせるかを決める

 「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");
        }
    }
リスト2 矢印キーでキャラクターが歩き、別なキャラクターと接触すると決闘をする処理(CharacterAction.cs)

 まず、「obj1」と「obj2」というGameObject変数を宣言する(1・2行目)。また「bool」型の変数「flag」も宣言しておく(3行目)。

 5〜10行目のStart関数内では、変数「flag」を「false」で初期化しておく(7行目)。また、Find関数で「woman1-e」と「woman2-e」にアクセスして、変数「obj1」と「obj2」で参照しておく(8・9行目)。

 Update関数内では以下の処理を行う。

アニメーションをスクリプトから呼び出すAnimation.Play関数

 変数「flag」が「false」であった場合は、「GetComponent<Animation>().Play」で、図8で設定した「Walk」アニメーションを実行する(14〜17行目)。これは女剣士が歩いているアニメーションだ。

Animator Controllerを使わないキーボード操作時の処理

 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」キーを押すと、その場で回転だけをする動作になる。

「Character Controller」を使わない場合の接触時の処理はOnCollisionEnter関数

 次は、女剣士同志が接触した場合の処理で、OnCollisionEnter関数内に処理を書く(35行目)。前回も触れたが、「Character Controller」を使わない衝突判定にはOnCollisionEnter関数を使うのだ。

 引数になっているCollisionのオブジェクト「col」には接触情報が入っている(35行目)。

コルーチンとIEnumerator

 また、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のコード

 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");
  }
}
リスト2 矢印キーでキャラクターが歩き、別なキャラクターと接触すると決闘をする処理(CharacterActionJS.js)

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。