Unity入門8

Space Shooter⑤

(スペースシューター)

  • 前回までで【敵】がまっすぐ下りてくるところまで出来ました。
  • この【敵】を動きをサンプルゲームのようにしたいと思います。
  • サンプルゲームは以下の動きです。※プレイヤーは消しています。
  • よく見ると、それぞれがランダムで複雑な動きしているのがわかるでしょうか?

状況確認

敵の動き

  • では、早速作っていきます。
  • 今ついている【Mover】は使わないので外しましょう。【Remove Component】ですね。
  • 新しいスクリプトを作成してください。名前は【Evasive】(イブイシブ:回避)としておきます。
    • scriptフォルダの中に作るのを忘れないでください。
      • 次へ。

敵の動き

  • まずは変数を用意しましょう。
  • たくさんありますね。それだけ設定項目が多い、つまり複雑な動きということです。
  • 変数の横のコメントは、本来はいりませんが、書いておいた方が後々ラクです。

敵の動き

  • 次に、Start()メソッドです。以下のように実装してください。
  • メソッドの中の1行目、【rb = GetComponent<Rigidbody>();】は前にもやった通り、コンポーネントを何度も探さないように、変数に保存して、処理を軽くしています。
  • 2行目の【rb.velocity = new Vector3(0,0,1) * speed;】は【Mover】ファイルと同じで、z座標(上から下)に対しての速度を設定しています。つまり、下への動きの部分です。
  • 3行目の【StartCoroutine();】(スタートコルーチン)メソッドは【コルーチン】と呼ばれる特殊な処理を開始するメソッドで、引数の【Evade()】(イベイドゥ:避ける)がコルーチンの名前です。
    • コルーチンも普通のメソッドと同じように、名前は自由です。Evade()】にしているだけです。
    • ただ、コルーチンは普通のメソッドとはかなり違う動きをします。
    • まずは、ざっとコルーチンに関して学んでおきましょう。次へ。

コルーチン

  • 【コルーチン】と呼ばれる特殊なメソッドの動きですが、一言で言うと【一定時間ごとに、実行したい処理をカンタンに書ける】ものです。
    • つまり「処理1」→「ちょっと待つ」→「処理2」→ちょっと待つ」→「処理3」のようなコードが書きやすいと言うことです。
    • 一番のポイントが、「ちょっと待つ」の部分を柔軟に設定できる点です。
    • コルーチンを使いこなすと、【イケてるコード】になりますので、ぜひ覚えましょう。
  • まず基本的な書き方です。以下のコードをEvasive】ファイルの追加してください。
  • IEnumerator】(アイニューメレーター)の部分が【コルーチン】の宣言部分です。
  • 言い方を変えれば【IEnumerator型のメソッド】が【コルーチン】と考えてOKです。※Unityの場合。
    • 次へ。

コルーチン

  • 続いて、以下のようにしてみましょう。
  • while()】(ホワイル)は繰返しでしたね。引数がtrueの間くり返すので、(true)になっているということは無限ループです。
    • Unityの場合はストップボタンで外から処理を止めれるので、このように書いても大丈夫です。
  • この繰返しの中の【yield】(イールド)が、ポイントです。
    • これは【①処理を一旦戻して(ループを抜けて)】から【②待ち時間が終わったら、このyieldの下から処理を始める】という動作をします。
    • return】が戻り値の指定でしたね。【null】ということは「戻り値はない」ということです。
  • Debug.Log()】はコンソールに出力するメソッドでしたね。("あ")なので、”あ”が表示される事になります。
    • これで、一度試してみましょう。※コンソール画面に注目。

コルーチン

  • 以下のようになると思います。※右側の数字が実行された回数です。
  • 現時点のコードでは【1フレーム】ごとに実行されるコルーチンになっています。
  • これを【1秒ごと】に実行されるコードに変えてみます。
  • これにはUnityでは便利なメソッドがあるので、それを使います。【yield】の戻り値を以下のように変えてください。
  • これで試してみると、1秒ごとに実行されるようになると思います。

コルーチン

  • また、コルーチンは基本的にメソッドなので、引数も使えます。
  • この場合は、当然呼び出す時も引数を指定しなければなりません。
  • 上記のようにすると、【1から10までだけ】表示させることもできます。

コルーチン

  • さらに発展させましょう。
  • Evasive】ファイルに、以下のコードも追加してください。
  • この状態で、動かしてみると、【コルーチンの外だよ◯】が大量に出力されると思います。
  • その下の方、大体【"コルーチンの外だよ51"か、"52"】辺りで、【1】が表示されていると思います。次へ。

コルーチン

  • コルーチンを使えるようになるためには、先ほどの動きを理解しなければいけません。
  • まず、コルーチン(Evade())は【yield return new WaitForSeconds (1);】の部分で【1秒待つ】としていますね?
    • 実は、その間は、処理全体が止まるわけではなく、【コルーチンだけが1秒止まる】のです。
  • そのコルーチンが止まっている1秒間で【FixedUpdate ()】メソッドが【コルーチンの外だよ◯】を表示し続けます。
    • ※デフォルトでは1秒間に50回くり返されます。誤差が出るのは1秒のカウントが少しズレがあるからです。
  • そして、1秒後にコルーチンは【yield return new WaitForSeconds (1);】の下、つまり【num++;】から処理を開始して、【Debug.Log(num);】で数字を表示させます。
    • ​以上をループさせていますね。
  • 現在はコルーチンは10をカウントしたら終わるようにはしていますが、【while(true)】に戻せば、ゲームを終了するまで、カウントします。
  • なんとなく雰囲気を掴んだでしょうか?
  • 次は、もう一歩すすめたコルーチンの使い方です。

コルーチン

  • コルーチンに下記を足してみましょう。
  • どんな結果になるかわかりますか?
  • 試してみるとわかりますが、今度は【1】や【2】などの数字の間に【”やぁ!”】が出力されるはずです。※2回目以降の【”やぁ!”】は、まとめられて表示されます。
  • このように【yield】は、【処理を抜けた場所】を記憶しており、待ち時間が終わったら、その抜けた場所(【yield】の場所)の下から、処理を再開させます。
    • これがコルーチンの重要なポイントです。

コルーチン

  • コルーチンに関して、だいぶ理解が出来たんじゃないかと思います。
  • コルーチンはUnityだけではなく、色々なプログラムで良く使われますので、ちゃんと理解しておきましょう。
  • 尚、コルーチンは、今やったこと以外にも、特定の条件でストップさせたり、コルーチンの中にコルーチンをネストしたり、他にも色々な使い方ができます。
    • また必要な際にやりたいと思います。

コルーチン

  • さて、スペースシューターの敵の動きに戻ります。
  • ぶっちゃけコルーチンさえ理解すれば、後はカンタンです。
  • 現在【Evasive】ファイルは色々テストした後の状態だと思うので、それぞれのメソッドを以下のようにしてください。変数は、前に設定したままです。
    • ※色々試した内容は消すよりも、コメントアウトしておいた方が、後で確認し直せるので便利です。

敵の左右移動の到着地点

  • 現時点では、コルーチンに【yield return new WaitForSeconds (1);】(1秒待つ)となっています。ここは【敵が出現してから、左右に動き始めるまでの待ち時間】です。
  • さらに追加して、以下のようにループを追加してください。
  • targetPoint】は【左右移動の到着地点】用の変数でしたね。
  • Random.Range (1, 3)】はもうお馴染みの乱数ですね(1,3)なので、1か2の数字になります。
  • その横の【Mathf.Sign ()】は初登場です。
    • これは【正負を返す】メソッドで、マイナスをつけて【-Mathf.Sign ()】とすることで、【マイナスはプラスに、プラスはマイナスに】変換できます。
    • つまり引数の【rb.position.x】(つまり今のx座標)がマイナスならプラスに、プラスならマイナスに変換し、それをRandom.Range (1, 3)】と掛け合わせて、到着地点にしています。
    • こうすることで、必ず【今いるx座標の反対側の座標】に到着地点が設定されます。

敵の左右移動の到着地点

  • 次に、ループの中に以下のように追加してください。
  • yield return new WaitForSeconds (1));】(1秒待つ)が2行と、それに挟まれて、【targetPoint】(左右移動の到着地点)に0が代入されていますね。
    • これは、到着地点を削除しているということです。(左右移動をやめるということ)
  • つまり、このループの中の動きは【到着地点を決める】、【1秒待つ】、【到着地点を削除する】、1秒待つ】が繰り返されることになります。
  • ここで使われるコルーチンはこれが全部です。言い方を変えれば、到着地点を決めたり、削除したりするコルーチン】を作っているわけです。
  • 後は、このコルーチンで設定される到着地点】に【実際に移動する】コードを書けば良いのです。

敵の左右移動

  • 次は、【FixedUpdate】メソッドにコードを追加して、左右移動を作っていきます。
  • まずは以下のようにしてください。
  • 1行目の【Vector3 position】は、お馴染みの座標を入れる型の変数に、今の座標を入れています。
  • 2行目の、新登場のメソッド【Mathf.MoveTowards】は、【開始位置から、目標の位置まで、一定の速度で動く】ということ、指定できるメソッドです。
    • 第1引数に、開始位置
    • 第2引数に、目標位置
    • 第3引数に、速度(加速度)
      • をそれぞれ指定します。
    • これを1行目で作った変数のx座標に代入しています。
  • 3行目の【rb.position = position】で現在の座標に代入しています。こうすることでMathf.MoveTowards】で指定したように動きます。
    • ​※【smoothing】を、0.05ぐらいに設定して、試してみましょう。

敵の左右移動

  • ちゃんと動いたとは思います。これが基本的なMathf.MoveTowards】の使い方です。
  • 後は、左右に移動した際に、敵が傾くようにしたいですが、今の動かし方は【poition】を動かしているので、【移動】と【傾き】を一致させるのが、うまくいきません。
  • そのため、Mathf.MoveTowards】の使い方を少し変更します。まずは下のようにしてください。
  • Mathf.MoveTowardsを【velocity】(加速度)に対して使っています。
  • この状態で動かしてください。先ほどと同じ動きになると思います。
    • 次へ。

敵の左右の傾き

  • 後は、傾きを追加するだけです。
  • これは何度も出てきていますが、velocity】の増減に合わせて、傾きも変化させているコードですね。
  • これで試してください。良い感じになるはずです。
    • ※tiltの値は50ぐらいでいいかと思います。
  • 実は敵の動きには、まだ少し問題点が残っています。
  • それは【画面外に飛んでいってしまう可能性がある】ということです。
  • 試しに下のように設定してみてください。

敵の左右の傾き

  • 下の感じになりましたよね?
  • これだと問題ですので、直しておきましょう。

敵の左右の傾き

試してみよう

  • 下の動画のように、どんな設定値にしても、敵が画面外に飛んでいかないように設定してください。
    • ヒント:プレイヤーの動きでやっているのと同じです。ただし、画面の下からは画面外に行けるようにすること。

残りの変数の使い道

  • これで敵の動きは完成しました。のですが、そういえばまだ使っていない変数がありますね。
  • 以下の4つの変数です。
  • この変数達も正しく使ってあげることで、もっと柔軟に値を変更することが出来ますね。
  • また、現在は【コルーチンでの待ち時間】はすべて1になっているので、ランダム時間で待つようにしてあげましょう。
  • その際に、上記の変数も適切に使ってあげればすべて使いきれそうです。次へ。

試してみよう

  • まだ使っていない変数を正しい場所に使ってあげてください。同時に、コルーチンの待ち時間もランダムに変更してください。
    • ヒント:【Vector2】型の変数は、
      • yield return new WaitForSeconds (Random.Range (startWait.x, startWait.y))
        • のように使うことが出来ます。
  • ※完成したら必ず見せてください。
  • 尚、インスペクタビューでの設定値はお好みで良いですが、例では以下の数値に進めます。
    • ※敵の出現位置は後でランダムに設定します。今は真ん中からでOKです。

敵のPrefab化

  • これで、敵の動きが完成しました。
  • では、この敵もPrefab化して、ドンドン出てくるようにしちゃいましょう。
    • 次へ。

試してみよう

  • 以下の動画のように敵をPrefab化して、どんどん出てくるようにしてください。※Prefab化したら、元のオブジェクトは削除してください。
    • ヒント:岩と同じように処理すればOKです。※つまり3つの岩と、敵の4種類の内どれかがランダムで、定期的に出現します。

試してみよう

  • 敵がカメラ外に消えたら、削除されるようにしてください。
    • ヒント:z軸が-70以上ぐらいで、カメラ外の判定になります。※カメラの位置によりますが。

試してみよう

  • 【敵とプレイヤー】にぶつかったら、プレイヤーが爆発して消えるようにと、【敵とプレイヤーの弾】がぶつかったら、敵が爆発して消えるようにしてください。
    • ヒント:岩と同じ感じです。※ただし、敵の爆発には【explosion_enemy】エフェクトがあるので、それを使ってください。

敵の弾を作る

  • 次は、敵が飛ばす弾を作りましょう。
  • 基本的には、プレイヤーと同じで良いので、Prefabにある【Bullet】(プレイヤーの弾)を、Hierarchyビューに持ってきて、オブジェクトにしてください。
  • オブジェクトしたら名前を変更しておきます。【EnemyBullet】にでもしておきましょう。

敵の弾を作る

  • 見た目だけ変更しておきたいので、Projectビューの【Materials】フォルダを開いてください。
  • プレイヤーの弾として使ったのが【fx_bolt_orange】というマテリアルでしたが、敵用の弾マテリアルは用意されていません。
  • そのため、Materials】フォルダを右クリックし【Create】→【Material】を選択してください。
  • 新しいマテリアルが追加されていますので、名前を【fx_bolt_cyan】にしておきましょう。※(cyanはシアン色ということ)
  • 次に、inspectorビューから以下の項目を変更します。
    • 次へ。

敵の弾を作る

  • Standard】の項目をクリックすると、メニューが開きますので、【Mobile】→【Particles】→【Additive】を選択してください。
  • 下のようになればOKです。次へ。

敵の弾を作る

  • これで後は画像を設定するだけです。次に、下の画面の【Select】をクリックしてください。
  • 画像を選択するウィンドウが開きますので【fx_lazer_cyan_diff】を選択してください。
  • これで、敵の弾のマテリアルは完成です。

敵の弾を作る

  • マテリアルの変更します。
  • マテリアルの変更は【Mesh Renderer】から行います。
  • EnemyBullet】のinspectorビューの【Mesh Renderer】の中の【Materials】を開くと以下のようになっていると思います。
  • 赤枠のところを押すと、マテリアルを選ぶウィンドウが開きますのでfx_bolt_cyan】を選択してください。
  • これで、敵の弾が完成しました。
  • 出来た敵の弾は、Prefabにして、元のオブジェクトは削除しておきましょう。
  • また、Tagが【Bullet】になっていると思いますので、EnemyBullet】などに変更しておきましょう。
    • ​後は、この敵の弾を作り出すスクリプトを作ればOKですね。次へ。

敵の弾を作る

  • 新しいスクリプトを作成し、名前を【EnemyBullet】などにしておきましょう。
  • まずは、以下のように書いてください。
  • 新しいメソッドを使っています。
  • この【InvokeRepeating】(インヴォークリピーティング:繰返しを呼び出す)メソッドは、【特定のメソッドを、特定の間隔でくり返す】ためのメソッドです。
    • 「ん?どっかで聞いたぞ?」と思った方、正解です。つまりはコルーチンをカンタンに作れるメソッドと思ってOKです。
      • 次へ。

敵の弾を作る

  • InvokeRepeating使い方はシンプルです。
    • 第1引数に、呼び出すメソッド名※string型で指定しないといけないので""で囲んで呼び出します。
    • 第2引数に、1回目を呼び出すまでの時間(秒)
    • 第3引数に、2回目以降、呼び出すまでの間隔(秒)
      • を指定するだけです。
  • ちなみにコルーチンにすると下の感じです。※書かなくてOK
  • 尚、【delay】【fire】の部分は変数でなくても良いです。
  • 今回は、inspectorビューから変更できるように、public変数にしています。

試してみよう

  • 下の動画のように敵が弾を打ってくるようにしてください。
    • ヒント:完成までのステップは以下です。
      1. EnemyBullet】スクリプトに【Fire()】メソッドを作る。Fire()】メソッドはプレイヤーが弾を撃つところを参考にしてください。合わせて、変数も追加する必要があります。
      2. EnemyBullet】スクリプトを正しくアタッチし、変数【delay】【fire】に値を設定する。
      3. 弾の進む方向も調整する。

試してみよう

  • 敵の弾にプレイヤーが当たると、爆発してゲームオーバーになるようにしてください。
  • ただし、敵の弾が【敵】【岩】【プレイヤーの弾】に当たっても何もおきない(素通りする)ようにする必要があります。
  • これには色々な方法が考えられます。単純に新しいスクリプトで判定と取ればカンタンではありますが、今回は新しいファイルは増やさずに、すでに敵や岩の辺り判定のために使っている【DestroyByContact】の【void OnTriggerEnter(Collider co)】の中身を変更して、実装してください。
  • 元々のコードを追加するだけでなく、書き換えてもOKです。※別のところに不具合が出ないように注意してください。
  • 必要に応じて、スクリプトのアタッチや値の設定、タグの設定なども行ってください。
    • 少し発想の転換が必要なので、意外と難しいのですが、時間の許す限りトライしてみましょう。
      • ※出来たら見せてください。
  • ​ヒント:メソッドは【return;】が実行された時点で、そのメソッドの処理を終わらすことが出来ます。※returnだけ(返す値がない)でも使うことができます。void型の場合は値があるとエラーです
    • 例えば【OnTriggerEnter()】メソッドには①②③という処理がまとめられているとします。
    • この場合OnTriggerEnter()】を呼び出すと、①②③が実行されるはずですが、もし①の処理の中で【return;】が実行されると、②と③の処理は行わずにメソッドの処理は終わります。
      • これを使えばシンプルに書くことが可能です。

お疲れ様です

  • 以上で、敵の動きは完成しました。
  • 次回は最終段階として、ゲームに音を足して、タイトル画面も作りましょう。
  • スペースシューター⑥】へ続く。