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変数にしています。
試してみよう
下の動画のように敵が弾を打ってくるようにしてください。
ヒント:完成までのステップは以下です。
【
EnemyBullet
】スクリプトに【
Fire()
】メソッドを作る。
※
【
Fire()
】メソッドはプレイヤーが弾を撃つところを参考にしてください。合わせて、変数も追加する必要があります。
【
EnemyBullet
】スクリプトを正しくアタッチし、変数【
delay
】【
fire
】に値を設定する。
弾の進む方向も調整する。
試してみよう
敵の弾にプレイヤーが当たると、爆発してゲームオーバーになるようにしてください。
ただし、敵の弾が【敵】【岩】【プレイヤーの弾】に当たっても何もおきない(素通りする)ようにする必要があります。
これには色々な方法が考えられます。
単純に新しいスクリプトで判定と取ればカンタンではありますが、
今回は
新しいファイルは増やさず
に、すでに敵や岩の辺り判定のために使っている【
DestroyByContact
】の【
void OnTriggerEnter(Collider co)
】の中身を変更して、実装してください。
元々のコードを追加するだけでなく、書き換えてもOKです。※別のところに不具合が出ないように注意してください。
必要に応じて、スクリプトのアタッチや値の設定、タグの設定なども行ってください。
少し発想の転換が必要なので、意外と難しいのですが、時間の許す限りトライしてみましょう。
※出来たら見せてください。
ヒント:メソッドは【
return;
】が実行された時点で、そのメソッドの処理を終わらすことが出来ます。※
(
return
だけ(返す値がない)でも使うことができます。
void
型の場合は値があるとエラーです
)
例えば【
OnTriggerEnter()
】メソッドには①②③という処理がまとめられているとします。
この場合
【
OnTriggerEnter()
】を呼び出すと、①②③が実行されるはずですが、もし①の処理の中で【
return;
】が実行されると、②と③の処理は行わずにメソッドの処理は終わります。
これを使えばシンプルに書くことが可能です。
お疲れ様です
以上で、敵の動きは完成しました。
次回は最終段階として、ゲームに音を足して、タイトル画面も作りましょう。
【
スペースシューター⑥
】へ続く。