Unity公式チュートリアルSurvival Shooter WITH PK Chapter.17.5「リファクタリング」

17.5 リファクタリング

幻の第17回でコードを引っ掻き回した結果、PlayerShootingクラスが鬼のように見づらくなりました。今回は、リロードと武器チェンジの処理を別クラスに引っぺがし、射撃処理自体も関数化することで可読性を高めるリファクタリングを行います。どちらかというと、「こうなりました」という報告。

PlayerReloadクラス

今回Interfaceを練習がてら使ってみました。ただ、武器種ごとにリロードを実装するつもりだったのが結果的にReload関係のクラスは1つで済んだので、Interfaceの用途としては正しくありません。

Interfaceの説明については、こちらの説明が簡潔で非常に理解しやすかったです。
https://qiita.com/Akira_Kido_N/items/7cd18944173cd4cd7229

イメージをつかんだらこちらを読んでより本質的な理解に進むとよいかと。
https://niconare.nicovideo.jp/watch/kn3404

PlayerChangeWeaponクラス

switchの中をもっと共通化できないか30秒くらい悩んでできなさそうだったのであきらめました。

PlayerShootingクラス

全部張ると長くなるので、UpdateとShootメソッドだけ。特にShootメソッドの可読性が上がってます。

リファクタリングはうまくはまるとパズルを解いたような快感がありますね。漫画やゲームの整理が昔から好きだったのでその楽しさにも似てるかも。

次回はコチラ

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.18「カメラシェイク」

前回はコチラ

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.16「調整:攻撃中の移動速度を遅くする」

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.17「調整:ノックバックを実装する」

17.調整:ノックバックを実装する

アクションゲームには欠かせないといってもいいノックバック演出。攻撃を当てた瞬間に、敵やプレイヤーの動きが止まってからリアクションが始まるというもの。

実装に当たって改めてノックバックについて調べてみると、様々なタイプがあるようです。ゲーム内の時間を丸ごと止めるもの、敵だけ、プレイヤーだけ止めるものなどなど。どのようなノックバックがあるかについては、下記の動画が大変参考になりました。

ゲーム開発でのヒットストップ調査

なんでもチャージショットで決めてしまうサムスが強すぎる【スマブラWiiU】

格闘でなく射撃であることを考慮すると「敵だけを止める」タイプのヒットストップが適しているかと思いました。以下、ヒットストップについての考え余談。

ヒットストップとはなんぞ

せっかくのアクションゲームだしヒットストップとか入れてみるか(開発延長)
剣とか格闘のゲームでは欠かせない演出だけど、銃とかシューティングではあんまり見ない気がする?サムスのチャージショットくらいしか例が浮かばない

射撃攻撃とヒットストップについて
仮説1:ヒットストップとは「吹っ飛ばし」の再現である
なので、ライフルとかサブマシンガン、レーザーみたいな貫通の攻撃とはあまり相性が良くない
逆に射撃であっても、ミサイルとか波動拳とかかめはめ波みたいな、衝撃の攻撃とは相性が良い

仮説2:ヒットストップとは「抵抗」の再現である
サンドバックを蹴れば、かならずサンドバックからの跳ね返りが存在し、無抵抗で振り抜くことはできない
その抵抗の感覚を再現するのがヒットストップであるため、自分の体から離れてしまった攻撃からは抵抗を受けないため、射撃とは相性が良くない

ここまで書いてて思ったけど、要するに現実世界に存在する「作用反作用」のフィードバックをいかにしてプレイヤーに与えるか、という問いに対する1つの答えってことかな
逆に言えば、ヒットストップを設けることで、射撃攻撃であっても「吹っ飛ばし」や「抵抗」のような作用反作用のフィードバックをプレイヤーに与えられる、ということだろうか

あとカメラシェイクか
こっちは実装したことあるからすぐいけるはず
ひとくちにヒットストップといってもさまざまなパターンがある模様
それらの挙動を比較してくれているこの動画がかなり参考になる。まずはこれを見て方向性を決めよう

やっぱり格闘と射撃とでは、適切なヒットストップの種類異なってくる気がするなぁ
射撃は自分に反作用のフィードバックがないわけで、自分が止まるタイプのヒットストップはおかしいよなあ
撃つ瞬間に自分だけ硬直⇒当たった時に相手だけ硬直して吹っ飛ぶ なら自然かな?

サムス:撃った瞬間反動でちょっと後ろに下がってる。ヒットストップというよりその反動で重量感を出しているもよう
ベヨネッタ:当たった瞬間その場で硬直してから吹っ飛んでる。硬直中は完全に動きが止まるわけじゃなくて、やられモーションのままぶるぶるしてる?それともエフェクトでそうみえるだけ?

こっちで見ると、食らったほうはその場で上下に細かく揺れたり震えたりしてるのがわかる
撃ったほうはストップしてるというか反動で後ろに下がるor硬直してるだけだな

ていうか格闘でも、攻撃したほうにはヒットストップ入れてないな
不利になっちゃうから当たり前か

ヌルっと止まると気持ちよくない、パキッとその場で止まって欲しいのでWaitForSecondsをつかうか。

WaitForSecondsの間に、別の変数やオブジェクトを格納してしまいNullReferenceエラー
LineRendererの終点
 WaitForSeconds後にLineの終点を更新していたため、更新される前にLineが描画されていた

ノックバックの時に操作するNavmeshAgent
 WaitForSeconds中に壁など敵以外のオブジェクトを打つと、shootHitの対象がエネミーからそのオブジェクトになってしまい、NavMeshAgentを見つけられるNullエラー

実装したけど削除しました!!!

一度実装してみたけど、思ったよりも爽快感に寄与せずゲームプレイがダラけただけだったので削除しました!
攻撃が当たった瞬間=ノックバック処理の実行直前に、NavMeshのVelocityを0にするStopMotionメソッドを事項してます。

がんばって作ったけど削除しました!!!
あと、これを実装するときにPlayerShootingクラスが超でぶになって見づらかったので、リファクタリングを一緒にしてます。これについては次回まとめます。

次回はコチラ

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.17.5「リファクタリング」

前回はコチラ

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.16「調整:攻撃中の移動速度を遅くする」

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.16「調整:攻撃中の移動速度を遅くする」

16.調整:攻撃中の移動速度を遅くする

前回までで、主だった昨日の実装は一段落つきました。ここからは実際に遊んでみてのフィードバックをもとに、細かい部分の実装をしていきます。
気になったのは、逃げながら攻撃していればほぼ追いつめられることが無い点でした。敵の移動速度>プレイヤーの速度となるよう調整するのが一案ですが、例えばシールド象の移動速度を速くすると少々オーバーパワーのようです。そこで、「攻撃中だけプレイヤーの移動速度が遅くなる」ようにします。

PlayerMovementクラスにメソッドを追加

移動速度を遅くするメソッドと、遅くなった移動速度を回復させるメソッドを追記します。

このメソッドの実行は、PlayerShootingクラスから行います。

PlayerShootingクラスから実行

まずは遅くする処理。Shootingメソッドの中で実行します。
また、PlayerMovementの取得は、PublicでInspectorより行います。

次に移動速度を回復させる処理。こちらはUpdateメソッドから。

実装確認

撃ったら移動速度が遅くなる⇒チョットしたら戻る の挙動ができていればOK!

第17回がお蔵入りしたので次回はコチラ

※第17回ではノックバックの実装を行いましたが、イマイチだったので削除してます。それでも読みたい物好きはコチラ

前回はコチラ

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.15「特殊な敵を実装する」

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.15「特殊な敵を実装する」

15.特殊な敵を実装する

前回の記事で書いたように、今回のゲームでは敵と武器とは対になります。

(断続的に登場するオーソドックスな敵)

これはすでに実装しているZonbunnyちゃんで良いでしょう。

(一斉に大量登場するが、HPが低い敵)

Zonbearちゃんのパラメータを調整すれば実現できそうです。

(盾orバリアを持っておりかつ、ダメージを食らわないでいると盾の耐久が回復する敵)

実装が必要ですね。今回はHellephantちゃんをこのシールド持ちにしましょう。

そうしてできた完成図がコチラ。

実装の段取りはこのようになります。

  • Unity上でShieldとなるObjectを用意する
  • Shieldに射撃RayがぶつかったらShieldのHPを減らす
  • ShieldのHPがゼロになったらShieldを破壊する
  • Shieldがダメージを受けずに一定時間経過したら、ShieldのHPを回復させる
  • スナイパーライフルで射撃した時はShieldと本体の両方にダメージを与える
  • Shieldにダメージが入った時、シールドの色を変えたり、大きさを小さくしたり、ヒットのエフェクトを表示する

Unity上でShieldとなるObjectを用意する

Hellephantの子Objectとして、3D Object -> cubeを作成し、ShpereColliderをアタッチします。シールドに衝突判定は設けないため、isTriggerにチェックを入れておきます。また、LayerをShootableにしておくことをお忘れなく。後の工程でこれに気づかず、2時間くらいハマりました。

Shieldの色を半透明にしたいため、専用のMaterialを用意します。Projectビューで右クリック -> Materialを作成し、Albedoから半透明の赤に調整し、ShieldObjectのMeshRendererにあるMaterialに設定します。

設定はこんな感じ。

ShieldHealthManagerの実装

Shieldの管理を行うクラスとしてShieldHealthManagerを用意します。もちろんShieldObjectにアタッチ。
ついでに、ヒット時に大きさを変えたり、エフェクトを表示する処理も一緒に書いてしまいます。色を変える処理はUpdateメソッドもいじる必要があるので事項で説明します。

Shieldにダメージを与える & その際に大きさを変えたりエフェクトを表示する

まずは基本の部分として、TakeShieldDamage()メソッドを実装します。

EnemyHealthクラスのTakeDamage()メソッドをひな型に、変数の名前を書いたりShieldだけの処理を入れたりしてます。
次に、このメソッドの実行元として、PlayerShootingクラスのShoot()メソッドを修正します。

EnemyHealthの判定を行う前にEnemyShieldの判定を差し込んでいます。
isTriggerにチェックを入れたColliderでもRayが衝突判定を返してくれるようにUnity側の設定を変更します。
Edit>ProjectSetting>PhysicsよりQuerriesHitTriggersにチェックを入れればOKです。

Shieldを回復させる & ヒット時にシールドの色を変える

一定時間Shieldへのダメージがなかったら、ShieldのHPを回復させます。これにより、スナイパーライフル以外の武器で倒すには集中砲火が必要となり、ほかの敵と合わせて出現した時のいやらしさが倍増します。

回復の処理は非常にシンプルですね。HPの値と一緒にShieldの大きさも戻してあげます。
シールドの大きさの変化率は数値を直で入れてしまってますが、本当はShieldのHPと、Colliderの大きさとの関係から計算式にすべきです。が、ややこしくなりそうなので今の段階では先送ってます。

また、ヒット時、Shieldの色にアルファ値を足してあげることで透明度を変更しています。
その直後からだんだんと色味を戻すため、Lerp関数を使っています。これはぜひ引き出しとして持っておきたい手法。

これにてShieldの実装は完了~~!!動かしてみて、攻撃を当てるたびに色が変わって小さくなること、HPがゼロになったら消滅すること、スナイパーライフルでは中身ごと攻撃できることを確認したらおしまいです。

次回はコチラ

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.15「特殊な敵を実装する」

前回はコチラ

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.14「武器変更を実装する」

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.14「武器変更を実装する」

14.武器変更を実装する

武器の変更システムに思ったよりも時間を食いましたですよ。強化のシステムはいったん延期して、最後に優先度を再検討しませふ。

クラスの構成について

武器変更はPlayerShootingクラスに実装していきます。本当は別クラスに武器の特性等を実装していくべきと思うのですが、やってみたらどの変数をどこに置いてどこから呼び出して……の切り分けに混乱しまったく実装が進まなかったので諦めました。問題意識としては持っているが能力が追い付かない、ってかんじでふ。かなC。

どんな武器を実装するか

今回は「どんな敵を実装するか」から考え、その敵への対抗手段として武器種を用意する、というアプローチをとります。例えば、「バリアを張っており一撃では倒せない敵」に対して、「バリアを貫通し一撃で倒すことのできる武器」を用意するといった具合に。
また、各武器にはデメリットを必ず用意します。ほかの武器の長所を裏返したデメリットを付与することで使えない武器(死に武器)が存在しにくくなります。
先の貫通武器であれば「ただし連射が効かない」といったように。

今回は以下の三つの武器(敵)を用意することにしました。
* サブマシンガン:連射が効き、攻撃力も並み(断続的に登場するオーソドックスな敵)
* ショットガン:広範囲の敵を一度に攻撃できるが、弾一発の攻撃力が低い(一斉に大量登場するが、HPが低い敵)
* スナイパーライフル:貫通し、攻撃力も高いが連射が効かない(盾orバリアを持っておりかつ、ダメージを食らわないでいると盾の耐久が回復する敵)

概要

武器切り替え機能の追加
武器ごとのステータスを設定:ShootメソッドとReloadメソッド

武器切り替え機能

三種類の武器を、キー1,2,3を押すことで切り替えられるようにします。また、切り替えには既定の時間がかかり即時の切り替えはできなくします。

  • enumで宣言した値を、playerWeapon変数に現在の装備として代入
  • Shootメソッド、Reloadメソッドを調整して、ダメージ値やリロード弾数の部分は引数を受けるように修正:引数の詳細は後ほど解説
  • playerWeapon変数を見て、Shootメソッドに渡す変数を切り替える

ChangeWeaponメソッドの追加

Invokeでは引数を渡すことができないのでcoroutineを使うことにしました。
HUDのアイコンを変えたり、残弾数表示の区切りを変えたり、発射レートの値を変えたりしています。

Shootメソッドの改修

めっちゃ長いですが、ショットガンの場合(isDiffuse == true)とスナイパーライフルの場合(isPierce == true)の場合分けを行い、それぞれ処理を追加してます。

拡散能力を付与する

Rayを配列化して、ランダムな前方に複数射出するようにしています。この時、射線の数だけLineRendererを付与したオブジェクトが必要となるため、gunLine変数も同様に配列として宣言し、GunBarrelEndオブジェクトにぶら下げた射線用のオブジェクトからLineRendererを取得しています。

貫通能力を付与する

Physics.Raycastはぶつかった最初のオブジェクトの情報しか取得しないため、スナイパーライフル装備時はPhysics.RaycastAllを使って、Rayがぶつかったすべてのオブジェクトの情報を取得しています。

この時、Enemy以外にもぶつかっているので、EnemyHealthを取得できずTakeDamageを実行しようとしてもNullエラーが出ることがありました。めんどくさがらずEnemyHealthがNullだった場合の分岐を設けなければいけませんでした。

Reloadメソッドの改修

Changeメソッドと同様の理由でcoroutineを使うことにしました。

クラス設計について

冒頭にも書きましたが勉強不足を実感してます。あまりにもメンテ性が悪すぎる……。この記事を書くためにコードを読み返すのが苦痛すぎました。雰囲気でここまでやってきましたが、体系的な勉強を始める必要がありそうです。

次回はコチラ

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.15「特殊な敵を実装する」

前回はコチラ

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.13「敵に射撃がヒットした時、ノックバックする」

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.13「敵に射撃がヒットした時、ノックバックする」

13.敵に射撃がヒットした時、ノックバックする

今回の実装は大きく2つ。本当は1つでもよいのだが、よりコンセプトをとがらせるため追加の実装を行う。
* 銃から発射しているRayが敵にヒットした時、Rayの方向に敵を移動させる
* ノックバックの距離と角度にすこしランダム性を持たせる
* 予定調和にせず緊張感を保たせたい

Rayが敵にヒットした時、Rayの方向に敵を移動させる

ヒットした時の処理に、Rigidbody.AddForce(shootRay.direction)を追加すれば実装できると思っていたが、試してみたところAddForceが効いている感じがない。

公式リファレンスを確認してみると、NavMeshAgentを付与したキャラクターを動かしたいときには、NavMeshAgent.velocityを使えとのこと。

ではそれを踏まえてもう一度。PlayerShootingクラスをいじっているが、EnemyMovementクラスに新たなメソッドを用意してもいいかもしれない。
武器種によってノックバック距離を変えるならPlayerShootingくらす、エネミーによるならEnemyMovementクラスに書くのがよいのだろうが、両方でやりたい場合はどうしたらいいだろうか。今は分からないので棚上げにして、実装を進めよう(EnemyMovementクラスに記述して、PlayerShootingクラスから調整用の引数を渡して実行、が正解な気がする)。

挙動はこんな感じ。

ノックバックの距離と角度にランダム性を持たせる

上まで出来ていればそう難しくない。
距離はnbPowerの取る値をランダム化させれば良いし、角度もshootRay.directionのxとzの値に少しランダム値を±させてやればよい。

これで実装できたのがコチラ。値の幅は調整の余地があるけども、ランダムでノックバックするようになりました。ただ、触ってみた感触もしかしたらランダムいらないかも。思ったより違和感がある。値の調整でも違和感が拭い去れなければ削除しよう。

次回はコチラ

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.14「武器変更を実装する」

前回はコチラ

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.12「射撃にリロードを設ける」

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.12「射撃にリロードを設ける」

12.射撃にリロードを設ける

必要なことは3つ
* 射撃可能回数に制限を設ける(1マガジンで撃てる数)
* ボタンを押すと射撃可能回数が最大まで回復する
 * マガジンの数は無限。資源の管理はこのゲームの面白さに据えないので。
 * オートリロードは設けない。俺のリロードは革命(レボリューション)なので。
 * リロード中射撃できない時間を設ける。マグチェンジではとうてい味わえない緊張感と高揚を戦闘にもたらすので。

完成形はこんな感じ

弾数制限&リロードの基本を実装

PlayerShooting.csをいじるよ。具体的にはこんな感じ
* 残弾数としてint変数currentAmmo、装填数としてint変数maxAmmoを用意
* Shoot()を実行するたびにcurrentAmmoを減らす
* Shoot()の実行条件にcurrentAmmoが0でないことを追加
* Rを押したらcurrentAmmoにmaxAmmoを代入するReload()メソッドを実行

ミスった点として、Shoot()実行前の残弾数をチェックするタイミングがあります。
最初、

という風に、最初に判定してreturnで抜けてしまったところ、残弾が0になった瞬間Update内のすべてのメソッドが効かなくなりました。
ifの一塊だけ抜けて、Update内の次の関数に行ってほしかったんですが、残弾数の判定⇒0なのでUpdateメソッド自体を抜ける⇒次フレームで再度Updateメソッド実行⇒…の無限ループしちゃいました。幸いすぐ気づいて直せました。

リロードタイムを設ける

リロードボタン(Rキー)を押してn秒後にReload()メソッドを実行するようにします。

これでリロード中は射撃ができないようになりました。リロードタイムがこんなにも息吹を……!!

[追記]嘘です。この実装だとリロード中も射撃できます。あとで直す。

残弾数のUI表示

3枚のスプライトを重ね、FillAmountを使って表現します。

実際に用意した画像ファイルは4枚、一番上の外枠と、内枠の区切りを示す一本の黒い線とで2枚に分けました。バランス調整や、別の武器の実装で装填数の異なるUIを作るとき、応用が利くようにしたかったためです。等間隔になるようなPosition設定がめんどいので、1枚で書いてもそんなに手間が変わらないかも。

HUDCanvas下にImageオブジェクトを4つ追加してそれぞれ画像ファイルを設置。
Projectビュー内の画像ファイルTextureTypeをSprite(2D and UI)にするのを忘れずに。

PlayerShootingスクリプトで制御する必要があるのは真ん中の黄色いスプライトのみです。

ついでに、武器変更機能を見越して、装備中の武器がわかるようなHUDも追加しました。

リロード中の表現

グラフィックでやろうと思ったんですが、AssetStoreに無料でいい感じのオーディオ素材があったのでそちらで実装してみます。

MentalDreams, Weapon Soldier Sounds Pack

リロードタイム3秒で丁度SEの長さとあってますが、今後のバランス調整を経てズレてきた場合は、SEの長さを調整する必要が出てきそうです。

これでリロード周りの実装は終わりです。次回はノックバックを実装しますよ~
それとタイトル名決めました。
SurvivalShooter Who In The Hell Punches or Kicks? -やさしい地獄へようこそ-
略してSurvivalShooter WITH PKです。どうぞよろしく。

次回はコチラ

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.13「敵に射撃がヒットした時、ノックバックする」

前回はコチラ

Unity公式チュートリアルSurvival Shooter応用Chapter.11.5「コンセプトを考える」

Unity公式チュートリアルSurvival Shooter応用Chapter.11.5「コンセプトを考える」

11.5.コンセプトを考える

前回の記事を書いた後、大事な手順を忘れていたことに気づいた。
「このゲームをどんなゲームにしたいか」というコンセプトの確認だ。危うく開発が迷走するところであった。
次の実装に入る前に、コンセプトの整理と先に挙げた5つの要素がそれに沿っているか、過不足はないか確認しよう。

このゲームの醍醐味をどこに置くか

このゲームは、何を面白いと感じてもらうものなのか
何をどうしてどうするためのゲームなのか
なぜこのゲームを作りたいと思ったのか、このゲームで何を伝えたいのか

コンセプトはワンピースにおけるログポースだ。桃鉄における目的地だ。母を訪ねて三千里における母だ。
このコンセプトがなければゲーム開発は迷走する。チームでの開発ならいわずもがな、たぶん個人でも規模が大きくなれば何が正解かわからなくなってくるはずだ。その時に立ち返るためのものである。

今回のSurvivalShooterでは、コンセプトを下記の通り定義する。

襲ってくるたくさんのゾンビを、命からがら全滅させるゲーム

なんてことないコンセプトだと思うかもしれない。だが、下記のようなコンセプトを立てた場合と比べてみてほしい。イメージできるゲームの完成図はだいぶ違うのではないだろうか。

  • 武器を持たない一般人がゾンビに見つからないように、朝が来るまで生き残るゲーム
  • 圧倒的な数のゾンビを、圧倒的な火力で蹂躙するゲーム
  • ゾンビがうろつくマップを、限られた資源だけで何日生き残れるか競うゲーム

コンセプトから実装項目を考える

コンセプトによって実装すべき機能、優先度が変わる。
今回のコンセプトでは、どんなゲームデザインをすべきだろうか。

  • 襲ってくるたくさんのゾンビ
    ゾンビは自分に向かって進んでこなければならない。
    ゾンビの同時出現数も多くなるだろう。

  • 命からがら全滅させるゲーム
    ライフは貴重なものになる。
    また、そもそもダメージを食らわないようなプレイングに対してメリットを設けるべきだろう。
    全滅させるための手段を用意する必要がある。
    ゲームクリアの条件になるのはゾンビの全滅だ。

今回順序が逆になってしまったが、前回の記事で挙げた項目とコンセプトとが一致しているかを確認しよう。

  • 11.敵の出現をWaveで管理する
    ゾンビの出現数を管理することは必須である。プレイ開始直後から100体のゾンビに襲われてはかなわない。
    難易度のデザイン、ゲームクリアに欠かせない機能であるため、(Waveという形にするかはさておき)これは最優先で実装すべきだ。

  • 12.射撃にリロードを設ける
    無制限に攻撃できる場合、(敵が一方的に強くならない限り)プレイヤーは命の危機を感じないし、ゲームプレイにも工夫を凝らさない。
    攻撃のあとに必ず「逃げなければいけない」フェーズが訪れることで、プレイヤーに「(生き残るために)今攻撃すべきかどうか」の駆け引きを強いることができる。これも欠かせない機能といえるだろう。

  • 13.敵に射撃がヒットした時、ノックバックする
    敵に攻撃を当てたことへの何よりの報酬は、敵が死ぬことだ。だが、一発で死んでしまっては面白くない。とはいえ、死ぬまで何の報酬がもたらされないのもストレスだ。中間の報酬として「逃げるのが有利になる」ノックバックを与えるのは悪くない。

  • 15.敵の個性を強くする
    たくさんのゾンビを撃ち殺しているうちに、プレイヤーは飽きてくるだろう。彼らに刺激を与えるために「5発撃っても死なないゾンビ」や「遠いところから攻撃してくるゾンビ」を追加しよう。油断していたプレイヤーたちは目を醒まし、再び対策に頭をひねるだろう。

  • 14.武器変更と武器強化アイテムを出現させる
    夢中になって意地悪なゾンビを追加しているうちに、プレイヤーの持つ武器では対処しきれないモンスターを作ってしまうかもしれない。これではプレイヤーはストレスや理不尽を感じてしまうだろう。フェアなゲームとするために、彼らにも新たな武器を与えよう。嬉々としてゾンビ狩りに精を出してくれることだろう。

さあ実装しよう

前回挙げた実装項目の中には、幸いアテを外しているものはなかった。自分の中でコンセプトは割としっかり固まっていたようだ。が、実装を進める中できっと「これも入れたら面白いだろうな」と思う機能が出てくるだろう。すでにいくつか新しいアイデアは生まれている。だがすべての機能を実装することは時間の面でも根気の面でも不可能だ、取捨選択をしなければならない。その時、ここでコンセプトを明文化していたことが役に立つであろう。

次回はコチラ

Unity公式チュートリアルSurvival Shooter WITH PK Chapter.12「射撃にリロードを設ける」

前回はコチラ

Unity公式チュートリアルSurvival Shooter応用Chapter.11「敵の出現をWaveで管理する」

Unity公式チュートリアルSurvival Shooter応用Chapter.11「敵の出現をWaveで管理する」

今回からは、「こうやったらより面白くなるんじゃないかな」というポイントをいくつか挙げて、自分流にアレンジしていくよ。具体的には以下の5つを実装していこうと思う。

11.敵の出現をWaveで管理する
12.射撃にリロードを設ける
13.敵に射撃がヒットした時、ノックバックする
14.武器変更と武器強化アイテムを出現させる
15.敵の個性を強くする

それぞれを実装する理由は各記事で解説していくよ。さっそく始めよう!
[追記]コンセプトの整理を忘れていたので次回の記事で、各項目の実装理由を整理しまする。

11.敵の出現をWaveで管理する

面白いゲームプレイには、だんだん上昇していく難易度と目標が不可欠だ。現状では敵の出現は散漫かつやむことがなく、自身が力尽きるまで悪夢が終わることはない。
出現する敵の種類、数を一つのWaveとしてパッケージングし、この問題に対処しよう。

実装概要

EnemyManagerスクリプトの書き換え
* SpawnメソッドにエネミーのPrefabを引数として渡すことで、アタッチするスクリプト1つで完結させる

* 実際にはWaveのさらに細かい概念を用意(便宜上Levelと呼ぶ)、各レベルごとにメソッドSpawnWaveW_L()で、スポーンするエネミーの数、種類を指定する

* 各Waveの開始時には、Wave開始を告げるUIを表示する
* 最終Waveの最終レベルで出現するエネミーを全部倒したらクリア画面に移行する

Spawnメソッドを書き換え、出現する敵をスクリプトで指定できるようにする

ずばりこうじゃ!

これで、各GameObjectに設定したPrefabのエネミーをスポーンさせることができる。

Wave毎にメソッドを用意し出現する敵を決める

例えばWave1のLevel1で出現する敵を呼び出すメソッドはこうなる。

超シンプルですね。
では、このメソッドの実行はどこから呼び出そうか?
そのアプローチが次。

Waveクリアを判定し次のWaveへ進む

これには、EnemyManagerクラスのみならずEnemyHealthクラスにも手を加える必要がある。
ざっくり言うと「Levelの初めにスポーンした敵を倒して残りが0体になったら、次のレベルのSpawnWaveメソッドを実行する」という実装です。

ちなみに、Wave1-1はStartメソッドからInvoke関数で実行してます。

まずはEnemyManagerクラスがコチラ。
※Levelと命名すべきところもWaveと命名して実装してしまってます。
 実装時は”Wave1-1″でいいや~と思ってましたが、ブログにまとめる段になって”Wave1-1“を説明する言葉がないと不便なことに気づき急遽Levelと呼ぶことにしたためです。

そしてEnemyHealthクラスの調整がコチラ。

ここまでできれば機能の実装はほぼ終わったようなもの。
あとはWave開始を知らせるUIとクリア画面を作って表示するだけです。

Wave開始を知らせるUIのアニメ作成とその表示

チュートリアル内でゲームオーバー画面を作った時と同じ要領で、AnimationのClipを追加する。

完成形はこんな感じ

HUDCanvas下にWaveTextを追加し、見た目を整える。設定はこんな感じ。

HUDCanvasのアニメータにWaveClipを追加して、WaveTextが左から登場して右に消えていくようアニメを作る。設定はこんな感じ。

ここで一瞬詰まったのが、テキストの位置を動かすときにPosition(Z)でなくAnchoredPositionをプロパティにしなければならないということ。(下記Reddit参考)

Animation on Rect Transform I can’t see position X & Y. What is position(Z)? Can I get the X & Y position on position(Z)? from Unity3D


理由を考えるに、画面サイズが変わった時にPositionで指定していると意図しない挙動になりそうだから、とかかしら。

アニメータのNewStateからWaveClipへのTransitionを引き、Waveトリガーを作成しくっつける。

WaveClipからNewStateへ戻るTransitionも忘れずに。こちらのTransitionはHasExitTimeにチェックをつけておこう。

各Wave最初のLevelのエネミースポーンメソッドに、SetTriggerを追記したらOK!

クリア画面のアニメ作成とその表示

こちらもWave表示とやることは同じ。

クリア画面の完成形

HUDCanvas下にClearTextを追加。

設定はこう

ClearClipを作成。

Animatorを設定。

Transitionは一方通行でOK。

Levelクリア処理に、「最終Levelをクリアした時」の条件分岐を追記。ついでにスペースキーでリトライもできるようにしておく。

これで実装は終わり!次は武器の残弾とリロードをやっていくぞ!

[追記]コンセプトの整理を忘れていたので、次回はそれを挟む。実装はそのあとから。

次回はコチラ

Unity公式チュートリアルSurvival Shooter応用Chapter.11.5「コンセプトを考える」

前回はコチラ

Unity公式チュートリアルSurvival Shooter日本語実践Chapter.10

Unity公式チュートリアルSurvival Shooter日本語実践Chapter.10

10.GameOver

とうとう最終章。今回はプレイヤーの体力が0になった時のゲームオーバー処理を実装してする。
画面UIをアニメーションさせる方法が今回のキモ。

ゲームオーバー画面の下準備

ゲームオーバー画面背景

HUDCanvas下にImageオブジェクトを作成、名前をScreenFaderとする。
Anchor:Altをクリックしながら一番右下を選択する。これによってCanvas領域全体に画像を引き延ばす。
Color;いい感じの水色に設定する。これがゲームオーバー画面の背景となる。

ゲームオーバー画面テキスト

HUDCanvas下にTextオブジェクトを作成、名前をGameOverTextとする。設定は下記の通り。
Anchor:中央
Width,Height:300,50
Text:Game Over!
Font:LuckiestGuy
FontSize:50
Allignment:中央真ん中
Color:白
Shadowコンポーネントを追加し、EffectDistance(2,-2)

HUDCanvas内の配置順整理

Scene内のオブジェクトは、Hierarchy画面で上にあるものから順に重ねて描画される。一番上のものが一番奥に、一番下のものが一番手前に描画されることになる。オブジェクト内の子オブジェクトも同様だ。HUDCanvas内には、ゲームオーバー画面に表示したいものとそうでないものがあるため、配置順を下記の通りに整理する。 
(上から)
HealthUI
DamageImange
-ここから下がゲームオーバー画面に表示したいもの-
ScreenFader
GameOverText
ScoreText

ゲームオーバー画面のアルファ値をゼロに

ゲームオーバー画面にかかわるパーツ(ScreenFaderとGameOverText)のColorからアルファ値をゼロにして透明にしておく。

ゲームオーバー画面のアニメーション作成

Animation作成

なんと!UnityのAnimationはキャラクターのモーションだけでなくUIその他なんでもアニメにできるのだ!今回はUIのアニメを作成するぞ。
HUDCanvasを選択した状態でメニューバーからWindow>Animation>Animationを選択する。このAnimationはGameビューの表示位置にドッキングさせておくと便利らしい。Unity2018ではAddCurveのボタンがAddPropertyになっているので注意。RecordModeをオンにしておくとSceneビューで直接動きをチェックしながら作れて便利っぽい、が今回は特に利用しないっぽい。

AddPropertyからアニメさせる要素を追加していく。今回は下記の4つ。
GameOverText.Text.Color:30フレーム目でアルファ値を1に設定
GameOverText.RectTransform.Scale:0フレーム目で(0,0,0)、20フレーム目で(1.2,1.2,1.2)、30フレーム目で(1,1,1)に設定
ScreenFader.Image.Color:30フレーム目でアルファ値を1に設定
ScoreText.RectTransform.Scale:30フレーム目で(0.8,0.8,0.8)に設定

ゲームオーバー画面表示処理に移行してから全体で30フレームですべてのアニメが終了するのは早すぎるので、アニメの開始タイミングを1秒と30フレーム後に変更。

Animationウィンドウの要素の意味は大体こんな感じ

なんてこった……!たったこれだけで、「90フレームかけて透明から水色の背景に遷移しつつ、白のゲームオーバーテキストを色・大きさ共にフェードインさせ、ついでにScoreTextの大きさもちょっぴり小さくして動きのメリハリをつける」アニメーションが完成しちまったぜ!あとはこのアニメを再生するタイミングを制御するだけだな!
オッといけない、このAnimationはLoop設定がOnになっているから、GameOverClipからLoopTimeのチェックを外すのを忘れないようにしないといけない。

AnimatorControlleのHUDCanvasをダブルクリックで開き、空のStateを作成したらそれがデフォルトStateとなるよう設定。そこからGameOverClipへのTransitionをつなぎ、遷移条件のParameterとしてTriggerのGameOverを作成、設定しておく。

GameOverManagerスクリプト

HUDCanvasオブジェクトに、GameOverManagerスクリプトをアタッチしたら実装はほぼおしまいだ!GameOverスクリプトの中身を確認するぜ!あ、あとBGMのPlayOnAwakeをオンに戻しておくのを忘れずにな!

実装確認

無事に動けばおめでとう!これでこのチュートリアルはおしまいだ!
ここでやめてしまってもいいんだが、少しいじってこのゲームをもっと面白くしたいと思う!のでもうちょっとだけ続くんじゃ!

第11回はコチラ

Unity公式チュートリアルSurvival Shooter応用Chapter.11「敵の出現をWaveで管理する」

第9回はコチラ

Unity公式チュートリアルSurvival Shooter日本語実践Chapter.9