目指せUnityマスター[3週目]
Q.何この記事?
A.新卒Webエンジニアが、Unityマスターになるまでの過程を描いていく記事。
三週間続いた( ・´ー・`)
今回の目玉は、コルーチン(IEnumerator)を使った最適化かな。
今週やったこと
Create with Code
先週と同じ、今回はUnit4だけ。
Unit4 - Gameplay Mechanics
だんだんゲームっぽくなってきた。
ステージにいる敵を全部倒すと、次のウェーブになるとかそういう感じ。
ゴール
Gameplay Mechanicsって言ってるように、”〇〇を動かす~”とかじゃなくて、ウェーブシステムを実現したり、アイテムを拾って自分を強化したり、だんだん敵が強くなっていったりとかでゲーム性を生み出せるようになること。
成果物は...なんていうんだろ。おはじき的な?
Lesson4.1 Watch Where You're Going
ここでは、カメラの動かし方について学んだ。
今回はカメラがプレイヤーに追従したり、自動で横スクロールしたりしないで、ステージの中心を焦点にして左右キーで回転させることになるから、それを実現した。
具体的には、
1.焦点用のEmpty(FocalPoint)を生成する。
2.子オブジェクトとしてMainCameraを入れる。
3.FocalPointをスクリプトでRotateする。
こうすればいい。汎用性は高そうなので覚えておきましょう。
あと、
gameObject.transform.forward
で、gameObjectが向いている向きを”絶対座標”で取得することができる。これは便利。
Lesson4.2 Follow the Player
ここでは、敵が自分に寄ってくるようにする方法を学んだ。
といってもベクトル関係の計算をするだけなので、
//標的オブジェクト - 標的を向くオブジェクト
playerObject.transform.position - enemyObject.transform.position
これだけでいい。あと、
(適当なVector3).nomalized
これで、"適当なVector3"を大きさが1の方向ベクトルとして扱えるようにしてくれる。これをしないと、今の場合は敵が遠いほど自分のとこに向かってくるのが速くなってしまう。
他はメソッドを分けたりだとか小技程度だったので割愛。
Lesson4.3 PowerUp and CountDown
ここでは、強化アイテムをとると自分が強くなって、効果時間が過ぎると元に戻るようにする。
ここで新しく出てきた「コルーチン」を使うことになる。
「コルーチン」は非同期関数の一種で、IEnumeratorインタフェースでメソッドを定義して使えるようになる。今は下のコードみたいに、
private IEnumerator PowerUpCountdownRoutine() { yield return new WaitForSeconds(7); this.hasPowerUp = false; this.powerUpIndicator.SetActive(false); Debug.Log("PowerUpFalsed"); }
て感じで定義してあげるといい。この時の戻り値は、それ以降に記述した処理を何秒後に行うか決めることができて、
yield return new WaitForSeconds(7); //以降の処理を7秒後に行う。
とコメントに書いてあるような感じになるので、時間で効果が消えるとかそういう系に使うと便利。
あとUnityマニュアル(https://docs.unity3d.com/ja/2018.4/Manual/Coroutines.html)で調べてみると、最適化にも使えるらしい。
どういうことかというと、例えばシーン中に何体の敵がいるかどうかを一定時間ごとに調べたい時とか、
void Update(){ int enemyCount = GameObject.FindGameObjectsWithTag("Enemy"); }
とかにすると、この"FindGameObjectsWithTag()"は公式リファレンスでも速度の遅い関数として説明されており、毎フレーム実行するのは好ましくない。そのためこれを最適化して0.1秒ごとに呼び出すために、
private static int enemyCount; private int GetEnemyCount(){ int enemyCount = GameObject.FindGameObjectsWithTag("Enemy"); return enemyCount; } private IEnumerator OptimiseUpdate(){ while(true){ this.enemyCount = GetEnemyCount(); yield return new WaitForSecont(0.1f); } }
とかって書ける。これ記事書いてる時点で気づいたから、このLessonで使えなかったな。覚えとこ。
Lesson4.4 For-Loops For Waves
ここでは、for文とInstantiate()つかって複数の敵を生成させたり、ステージに敵がいなくなったら新しい敵をスポーンさせたりした。
気になったのが、このチュートリアルでは毎フレーム"FindGameObjectsWithTag()"してるんだよな。
当時の自分はコルーチンで最適化できることに気づかなかったから、Enemyスクリプトに
private SpawnManager spawnManager; private static int enemyStock=0; void Start(){ //EnemySpawnerオブジェクトにあるC#ScriptコンポネントのSpawnManagerを取得 this.spawnManager = GameObject.Find("EnemySpawner").GetComponent<SpawnManager>(); //敵(Enemyオブジェクト)が生成されるたびに、enemyStockをインクリメントする。 Enemy.enemyStock++; } //Unity標準メソッドで、オブジェクトがDestroyされたときに呼び出される。 private void OnDestroy(){ Enemy.enemyStock--; if(Enemy.enemyStock == 0) { //シーンに存在する敵がいなくなった時に、SpawnManagerクラスのOnDestroyLastEnemyメソッドを呼ぶ this.spawnManager.OnDestroyLastEnemy(); } }
こんな感じにして、あたかもSpawnManagerクラスにOnDestroyLastEnemyっていうイベントがあるかのようにしてみた。使う時は、
public class SpawnManager : MonoBehavior { public void OnDestroyLastEnemy() { //ここにステージの敵が全ていなくなった時に実行したい処理を書く。 } }
という感じに使える。ただこれはどうなんだろう...、オブジェクト指向的に「一つのクラスに一つの役割」とかでなんかこう、もやもやする感じがある。まぁ”〇〇Controller”とか”〇〇Manager”をコンポジション(new Controller(部品1,部品2, ....)みたいなの)を使わずに作ってる時点で原則から外れてるんだけどさ。
結構コードとしては気に入ってるから、EnemyクラスじゃなくてEnemyEventクラスとかをEnemyオブジェクトにくっつけて、イベントを受け取るだけのクラスとして使えばよさげかな。
【追記】
コルーチンの呼び出し方を書き忘れてた。
StartCoroutine("コルーチン(IEnumerater型メソッド)名");
Challenge4 Soccer Scripting
ここでは今までに学んだことを使ってプロジェクトを修正するのと、スペース押したら向いてる方向に加速したり、敵がだんだん強くなって行ったりさせるだけ。特に頭を悩ませるようなことはなかったし、新しい気付きみたいなのもなかったので割愛。
振り返り
今回は始めて効いた「コルーチン」と、それを使った最適化の方法とか勉強できてよかった。
あと自分なりにスクリプトを改善したりして、今までただ作るだけだったらそこで終わりだけど、記事にして振り返ってみると「次はこう使うとなお良さそう」っていうのが見えてくるから、割と充実したレッスンだった。
ちなみにCreate with Codeは全部で5レッスンだから、あと1レッスン(UI周りのレッスン)でひと段落つく。だから次の連休でミニゲーム企画してみよっかな。
ここで気を付けないといけないのが、”思いついてすぐに作ろうとしないこと”。
そろそろ基本・詳細設計から進めて、きちんとしたコードを書けるようになっておきたい。てか設計がうまくできてれば変に躓くこともないからね!設計は「すっきりわかるJava入門」とか、適当なWebの記事とかを参考にしようと思ってる。
何はともあれ、来週は企画からだな。
今週もお疲れ様です( ´_ゝ`)