Unityでインディゲーム道!

プログラム、Unity初心者がインディゲーム制作を目指して日々思うことなどを書き綴ります。

カメラを揺らして、画面上の敵を振動で倒すブロックを作る!| Unity 2D ポリモーフィズム 継承

下から叩くと画面を揺らし、画面上にいる敵キャラを全部倒す、というブロックをUnity2Dで再現してみました!一気に敵を一掃できる爽快感溢れるアクションになりますが、これを上手くやるためにはカメラを揺らすだけでなく、ポリモーフィズム継承を上手く活用する必要があります。 

f:id:miur-us:20180601012939g:plain

 

敵キャラをまとめて処理

 敵キャラといっても、様々なキャラがいます。マリオで言えばクリボー、ノコノコをはじめとした多くのキャラクタがおり、多様な動きをしています。

 クラスを横断する一括処理を実現させるにはポリモーフィズムを使います。いろいろな敵キャラを"ひとまとめ"にし、一括して処理する必要があるからです。

 

 つまり、"衝撃波で攻撃する"という処理を各キャラに行うわけですが、、ひとつのアクション対してのそれぞれ違ったリアクションを取らせる、ということになります。それをシンプルにコーディングするには、継承ポリモーフィズムによる抽象化が必要です。

 

継承とポリモーフィズム

 継承とはコードの量を減らすためのアイディアで、ポリモーフィズム複雑な処理を人間的な感覚でシンプルに書き表すためのテクニックです。

f:id:miur-us:20180531230304p:plain

 敵キャラクターという概念があり、そこから様々な具体的なキャラクターに派生します。各々のキャラクタは違いがあるといえど、基本的なパラメータ、動作を共通しています。体力があるかもしれませんし、移動するでしょうし、プレイヤーへの攻撃動作もあるかもしれません。そうした共通部分をスーパークラス、基底クラスで定義します。

 それを継承した各々の具体的なキャラの独自性の定義は、そのキャラのクラスですれば良いわけです。そうすれば、各敵キャラに共通する部分は省略することが出来ます。

 

 ポリモーフィズムとは、要は共有されたメソッドの中身をそのクラスにあった内容にする、実装を変えるということです。つまり、移動する、Move()というメソッドがあったとして、あるキャラは歩いて移動するかもしれませんし、他のキャラは飛び跳ねる、または飛ぶ。魚系のキャラはそれが泳ぐ、ということになるかもしれません。今回の例だと、ボール型と花型の敵は動かないですが。

 

 また、"プレイヤーに踏まれる"という動作などについては、クリボーはペッタンコになってしまいますし、ノコノコは殻にひっこみます。トゲゾーは何も起きず逆にマリオがやられてしまいます。つまり、同じ動作に対して、違った反応、結果が定義されていることになります。

 ポリモーフィズム多態性)とは、違った形態を持たせるということです。

 

Unity上での継承

 Unityで継承する際には、UpdateなどのUnity固有のメソッドも各クラスに継承させる必要があります。この時、protectedを使います。親子関係にあるクラス間のみアクセス可能、となります。そして、継承クラスのUpdate内で、base.Update()とすることで、基底クラスのUpdateを実行します。

 

↓このクラスで定義した、プレイヤーがブロックを叩いた時の処理は継承されたクラス内のUpdate内にbase.Update()と記述することで引き継がれます。

f:id:miur-us:20180601003520p:plain

 衝撃波の出るブロックは原型となるBoxControllerクラスから派生しています。アイテムが出るボックスもありますし、いろいろあるボックスのうちのひとつです。

 どんなボックスでもプレイヤーがしたからジャンプで叩くと飛び跳ねるという動作を共有しています。継承してしまえば、各クラスごとにこのコードを書く必要がなくなります。繰り返しますが、継承とは余分な、重複するコードを無くし、読まなければいけないコードの量を減らすためのテクニックです。

f:id:miur-us:20180601004036p:plain

↓はShockBoxControllerスクリプトですが、base.Update()があります。

 f:id:miur-us:20180601004415p:plain

BoxController内のvirtual Updateでの処理が実行されることになります。

 

Unity上でのポリモーフィズム

  基底クラスでvirtualとして定義したメソッドを継承クラスでoverrideすることで、そのクラス独自の動作を出来るようにします。今回で言えば、ブロックを叩いた後に、敵がやられるわけですが、"どうやられるか"という反応をそれぞれ違ったものにするということです。

f:id:miur-us:20180601004846p:plain

 あるキャラは吹っ飛ぶかもしれませんし、あるキャラはバラバラになるかもしれません。まったく効かず、微動だにしないキャラもいるかもしれません。今回、衝撃波によるダメージを"DamagedDeath"というメソッドとして定義していますが、させたい動作をそれぞれコーディングすればいいわけです。 

f:id:miur-us:20180601004921p:plain

 Enemyクラスでは中身がありませんが、地を這う敵キャラは"飛び跳ねて下に落ちる"という処理をしています。花の敵キャラは"逆さまになって、まっすぐ下に落ちる"ようにしています。

f:id:miur-us:20180601005716p:plain

f:id:miur-us:20180601005722p:plain

死亡判定されると、まず回転させて、下に移動させるという処理で、動きを作っています。

 

まとめて処理する!(アップキャスト)

  さて、では各クラスでoverrideとして定義した"DamagedDeath"メソッドをどう活用すれば良いか!それには各キャラをおおざっぱに"敵キャラ"として認知する必要があります。

 UnityにはPhysics2Dに、ある地点の周辺のコライダーを集めるOverlapBoxAllメソッドがあります。つまり、これを使って画面上の敵キャラのコライダーを集めます。

f:id:miur-us:20180601004723p:plain

 このコライダーを使って、ボックスのクラス内にEnemyクラスのインスタンスを作ります。つまり、いろんな敵キャラを具体的な各々のクラスのインスタンスとして、生成するのではなく、漠然とした"敵キャラ"インスタンスとして生成するということで、柔軟性を持たせます。これこそ"抽象化"になります。詳細を無視して、概念的に物事を捉えるということです。

 

 つまり、様々なキャラをひとまとめに出来、大らかな同タイプのオブジェクト群として扱えるようになります。

enemy.DamagedDeath();

 なので、そうして集めた"敵キャラ"の集合に対して、ボックスは「衝撃波出すので、各々リアクション取ってね!」とお願いします。つまり、"DamagedDeath"が実行されます。つまり、各クラスで実装された異なる処理がなされるということになります。

 

カメラを揺らす!

 今回の件で更に重要なのは、視覚効果であるカメラの揺れです。これは何にでも使えるアイディアですよね。爆弾が爆発した時にも使えそうですし、いろいろ使い道があります。注意としては、やりすぎるとプレイヤーが酔ってしまうということですね。特に縦揺れはキツイ。

f:id:miur-us:20180601011601p:plain

変数など。動く幅、スピードなどの変数を宣言。

f:id:miur-us:20180601010845p:plain

 やることは多いので、揺れを起動するメソッドをShockActivationとして定義。叩いた時点でのカメラの位置の検出、ブロックを光らせるなど、様々なヘルパーメソッドを実行します。

 

 これには動く足場の件で学んだことを生かしています。まぁ、サイン波に沿ってカメラを一定時間揺らす、というだけのことなんですけどね。

 ブロックを叩いた瞬間のカメラの位置を中央"Center"として、そこから左右に揺れ動く、という処理にしています。叩いた瞬間を最大の揺れにして、時間経過ごとに揺れとスピードが弱まっていく、という処理にしています。それぞれ、speed, widthにしていますが、毎フレームごとにdeltaTime分を引いていく、という処理です。

f:id:miur-us:20180601010621p:plain

cam.transform.position = center +

           new Vector3("横揺れ", "縦揺れ", 0);

 縦揺れも加えていますが、揺れの周期も幅も横揺れよりも弱くしています。

 

より、リアルに。

 揺れている!という感じを更に感じさせるにはどうすればいいのか。プレイヤーが一定時間動けなくなるのがいいなと思いました。実際、そういうゲームは多いですよね、地面が揺れてるとプレイヤーは動けなくなる、みたいな。ロックマンのガッツマンとかもそうでしたね。

f:id:miur-us:20180601011008p:plain

 これは単純にブロック側のスクリプトがプレイヤーキャラ操作のスクリプトを一定時間、非アクティブにすることで実現しています。コレをやると、画面が揺れている間、プレイヤは動けなくなるので、あぁスゴイ揺れなんだなーという演出になります。FindGameObjectWithTagでプレイヤーとカメラのオブジェクトを探して、GetComponentから直接、オンオフにするのが楽ですが、少し長いのでStopPlayer()というヘルパーメソッドを作りました。

 

より、白熱した体験を!

 今回の動作を再現して思ったのは、やっぱゲームに爽快感を与えるには、こういう派手な仕掛けが必要なんだな!ということです。このアイディアの初出って、初代マリオブラザーズなんでしょうか?POWブロックを再現しました。

 こういう見た目が派手なものが大事ですよね、加えて敵キャラを一掃出来るのが楽しい。やっぱ、ゲームはやってて楽しくないと意味ないですよ!!