読者です 読者をやめる 読者になる 読者になる

エディトリアルデザイナーのアプリ制作日記

Unityで遊ぶ個人開発者のメモ書き

Unityアクションゲーム制作記 その22 スケールを操作するだけの簡単UIアニメーション

Unity アクション ゲーム デザイン

youtu.be

 たまには少しくらい役立つ情報をということで、基本の話ですがメニュー周りでめっちゃ使っている、お手軽アニメーションの話をば。映像は、良いか悪いかと言われれば……良くはないよなぁと自覚している趣味200%のポーズメニューです^^; これに限らず、今回のゲームではメニューなどのUI周りにおいて、出現・消滅させる時になるべく動きをつけるような形にしています。

 メニューやUIで使っているのは、uGUI+DOTweenというUnityではおなじみの黄金コンビ(敵のHPパラメータバー・3Dモデル以外はすべてuGUI)。そのUIを動かす方法としてものすごく多用しているのが、右や左からフレームインさせるアニメーションです。これは、ScaleやRotationの基準ポイントとなるpivotの設定をいろいろ変え、Scaleの値を動かすことで実現しています。たとえば、ゲーム中のヒットカウンターはこんな感じになっています↓

f:id:hamazakifactory:20170323035406g:plain

 で、多用するには理由があって、使ってみたら思いの外、お手軽超簡単に実装できたからです。 手順としては、

  1. Scale1の状態でUIの位置・Pivotの設定をしておく
  2. 初期状態をScale 0としておく
  3. スクリプトで「HitBoard(UIのオブジェクト).GetComponent<RectTransform>().DOScale(1f,0.5f);」を実行する

これだけでOK。Scaleを0から1へ動かせばフレームイン、1から0へ動かせばフレームアウトになるって寸法です。これくらいの簡単な動きならAnimatorを使ってもいいのですが、なんかいまだに使い方が良く分からないというか、思い通りの挙動にならないことが多いので……ちょっとしたものならDOTweenに頼りっきりになっています。

 また、座標を移動させても同じ動きができますが、始点・終点の指定が必要ですし、位置の修正があった時は直す手間が余分にかかります。スケールだけをいじるだけにすれば良いようにしておけば、修正も管理も楽ってもんです。

 ただし、スケールをいじっているということは、アニメーション中、文字や画像がひしゃげてしまうという問題もあったりします。キャラクターイラストなどしっかり見せたい画像の場合は、見栄え的にあまりよろしくないので、そんなときにはちゃんと座標を移動させるようなアニメーションを使いましょう。今回画像的なものはあまり使っていない&アニメーション時間が短いということで、アラが目立たないようになっています。

 なお、冒頭のポーズメニューはこんな感じ↓ 配置やタイミング次第でさらに動きのバリエーションが広がるということですね。今回はScaleだけですが、Rotationの動きも加えることで、さらに動きのバリエーションが広がるでしょう、きっと。ちなみにこのGIFを作っている最中に、一箇所Pivotの設定をミスってることに気づいた……あとで直そう。

f:id:hamazakifactory:20170323043101g:plain

  DOTweenに関しては、以下のリンクをいつも参考にさせてもらっています(もちろん公式のリファレンスもね)。使うために必要な情報もたくさんあるので、わからなかったすぐにググって参考にさせてもらっています。先人たちに感謝感謝です。うんうん。

www.shibuya24.info

https://anz-note.tumblr.com/post/145405933481/unitydotweenめーも

anz-note.tumblr.com

 

Unityアクションゲーム制作記 その21 縦持ち用のオープニングカメラを追加

Unity アクション ゲーム

youtu.be

 なんとなーく手間がかかりそうで後回しにしていた、オープニングの縦持ち用のカメラを追加しました。縦持ち・横持ちの2つのカメラを同時に動かして、縦・横の持ち方が変わった時にカメラを切り替えるだけという、ちょーお手軽実装。作業的には最初に作った横持ちカメラを複製して、画角や位置を微調整するだけでなんとかなりました。映像では並べて置いてみたので、比べてみるとよくわかるかと思います。本当は、縦・横それぞれ印象が変わるようにアングルとか変えたかったのですが……ゲーム本体を置いてそこまで手間をかけべきかどうか躊躇してしまったので断念しました。

 簡単とはいえ、それでも1日はかかってしまった作業。その中で、その中で意外にめんどくさかったのが、テロップ(?)処理です。縦・横どちらでもCanvaのテキストやらイメージの大きさを変わりなく見せるためには、CanvasScalerコンポーネントのReferenceResolutionとMachを縦・横切り替わった時に設定を変更すればいいだけ。とはいえ、縦・横どちらでもはみ出さない大きさと位置にしておかないと、画面からはみ出した分が見えなくなったりするのでレイアウトは少しだけ配慮が必要だったりしますが…。

f:id:hamazakifactory:20170316121450j:plain

↑横持ちの画面。 ↓縦持ちの画面。

f:id:hamazakifactory:20170316121500j:plain

 ちなみに縦・横の切り替えは、Canvasオブジェクトに画面の縦・横位置をチェックするスクリプトをつけて、リアルタイムに変更するようにしてあります。これで、縦・横で、位置や大きさそのままですませられます。しかし、文字の表示位置と回転方向を調整しないといけない場面もありました↓

f:id:hamazakifactory:20170316123323j:plain

 ちなみに横位置のテキストをそのまま縦にするとこんな感じ↓

f:id:hamazakifactory:20170316124233j:plain

 はみ出すし、見栄えも良くありません。これもCanvasScalerの設定と同じく、縦・横で個別に位置・回転をスクリプトで変えればいいだけの話……なのですが、アンカーもちゃんと設定しないといけない都合上、スクリプトで全部やるのは、座標やらなんやら全部手入力になるのは目に見えていることもありちょっとだけ面倒だなーと。

 そこで、面倒なら事前に用意してしまおうということで、縦・横それぞれのテキストオブジェクトを用意して、適宜オブジェクトを切り替える方式にしました↓

f:id:hamazakifactory:20170316130939j:plain

 こいつはアニメーションしていたオブジェクトだったので、表示の切り替えにSetActive( )でON/OFFしてしまうとアニメーションがストップしてしまいます。なので、オブジェクトのスケール値を0/1にして動かしたままにしておき、見た目を消えたようにしてあります。

 結果として、余計に手間がかかったような気もしますが、まぁ無駄はいつものことなのであまり気にしないことにします><。次の時に反省を活かせばいいってことで、うん。

Unityアクションゲーム制作記 その20 アイテムを放出する宝箱を実装

youtu.be

 ダンジョンといえば宝箱! ということで、ここのところはずっとアイテム関連の実装を粛々と進めていました。かねてより「入れなきゃ」と思い続けていたものにようやく着手できました。

 まず、アイテムを管理するためには、どんなデータが必要かなぁとスプレッドシートとにらめっこしながら考えてみたのがコレ↓

f:id:hamazakifactory:20170307181053j:plain

・itemID:アイテム固有のID番号

・category:分類用のカテゴリID(武器強化系、プレイヤー強化系、便利系など)

・categorySub:さらに利用用途に応じて分けたサブカテゴリ

(便利系>サブ1:ゲーム開始時使用可能、サブ2:コンティニュー時使用可能とか)

・itemCount:現在所持しているアイテム個数

・Ui3DItemObject:UI表示用の3Dオブジェクトのプレファブ名。

・itemuUseMethod:IDのアイテムを使用した時に必要な処理をするメソッド名。

・itemName:表示用アイテム名、翻訳ベースの英語をここで登録。

・itemDescription:アイテム説明文。

 いろいろと不要っぽいものがあったりしますが、当座はコレで大丈夫かな。次に宝箱を開けた時に、どのアイテムが入っているかどうかのテーブルを用意しました。1つの宝箱には8個のアイテム+GOLDが入っているという仕様です。

f:id:hamazakifactory:20170307182238j:plain

 宝箱にTableIDを紐付け、アイテムを入手(宝箱から放出)する時には、紐付けられたIDを指定することで、出すべきアイテムがわかるという感じです。個数だったり、落とす確率を指定できたりと細かく設定するのも考えましたが、やったところでゲーム全体のバランスを考えた調整など、おそらく自分の手には余りそうだったため、必要最低限のデータ構造でいくことにしました。

 宝箱を開けてアイテムを入手するまでに必要な流れは、以下の通り。

  1. 宝箱の判定に触れる
  2. 宝箱をフォーカスしつつ、開けるかどうかのメニューを表示
  3. 開けるボタンが押されたら、宝箱を開けてアイテムを放出
  4. アイテムを入手 

  まあ、ごく自然な当たり前の処理です。しかし、ここで頭を悩ませたのがアイテムを入手する方法と、入手しようとしたアイテムが最大所持数を超えていた時の処理です。せっかくアイテムの3Dオブジェクトを散らすのですから、走り回って拾い集める方式が常套手段でしょう。でも、走り回るのは手間……しかも、拾ったアイテムがどんなアイテムかがわかるメッセージが必要です。残念ながらログシステムといった気の利いたものもないので、ただ拾い集めるだけでは、プレイヤーが何を拾ったのかが全くわかりません。かといって1個アイテムを拾うごとにメッセージと選択肢を出して確認したりするのは現実的ではありません。ということで、しばし悩んだ末、宝箱から出た入手できるアイテムは、開けた後にまとめて表示するようにしました。スクロールビューでくるくる動かすと、アイテムのプレビューモデルも見られるようにもしてあります。ゲームを再開するまでに1アクション必要ですが、必要な情報を確実に見られる方を選択してみました↓ 

f:id:hamazakifactory:20170307184555j:plain

  で、もう1つの問題、入手しようとしたアイテムが最大所持数を超えていた時の処理です。問答無用で廃棄してしまうのが制作側としては楽なのですが、MOTTAINAI精神が頭をもたげ……結局宝箱から放出したアイテムのオブジェクトを残しておき、後で必要になった時に拾い直せるような形にしました。とはいえ、エリア移動(シーンの再構成)すると消えますけどね。これは仕方ないってことで諦めてます。

 ということで、アイテムが使えるようになってから、ゲームオーバーやらトップメニューやらのUIを改修していたのですが、とにかくデザインがまとまらない! 時間ばっかりとられて、ちょっと疲弊気味です。なるべく手順を少なく、画面遷移もしないように簡単にしたい、と要素を詰め込みすぎている、かつ、文字情報が多すぎるのが原因なのですが……。 5.5インチのZenfone2では問題なくても、4インチのiPhone5では、両目視力1.0でもかなりきつくなっています。また、ちょっと自分流が出すぎて使いづらいものになりつつあるのかなぁと、少し反省。

 いまから根本的に叩き直すのは現実的ではないので、どこかいい落とし所を探しつつ、やりたいことを詰め込めるように頑張っていこう、うん。

Unityアクションゲーム制作記 その19 ヒットストップみたいなものを入れる

Unity アクション アプリ ゲーム

youtu.be

 先週、大阪までえっちら出かけて、某ゲーム部主催の「作ったゲームを持ち寄ってみんなで遊ぶ試遊会」なるものに参加してきました。自分を追い込むための〆切を設定するために参加を決めたのですが、学ぶところもあれば、打ちのめされるところもあったりで、大変刺激を受けました。早くゲームを仕上げたーいと焦る一方、その時に感じた(&言われた)のが、「ヒットストップがあるといいかもー」です。
 一時停止ということまるっきり考えない設計をしているので、いまさら入れるのは抵抗がありました。とはいえ、ヒットストップといっても、アクションゲームや格闘ゲームのようなちゃんとしたものではなく、あくまで攻撃がヒットした時に敵の動きがちょっとだけ「ピタッ」と止まって見える、そんな感じでならいけるかな? とお試しで入れてみました。攻撃を受けた時に以下のメソッドを呼び出すようにするという簡単仕様。本当にコルーチンで時間待ちをして、アニメーション速度を0→1にするだけの単純なものです。

	public void EnemyDamageStopNow(float stime,float stopTime)
	{
		stopStartTime = stime;
		stopTime = stopTime;
		StartCoroutine(EnemyDamageWait());
	}

	IEnumerator EnemyDamageWait()
	{
		yield return new WaitForSeconds(stopStartTime);
		animEnemy.speed = 0f;	//再生スピードを0 animEnemyにanimatorコンポーネントをキャッシュ済み

		yield return new WaitForSeconds(stopTime);

		animEnemy.speed = 1f;	//再生スピードを戻す
	}

 悩んだのが敵の動きを停止させるタイミングです。最初は、敵がプレイヤーからのダメージを受けるとダメージモーションを再生するようにしているので、そのモーション内の最ものけぞったようなタイミングで止めたほうが見栄え的にいいかなぁと、アニメーションクリップの中にイベントを仕込んで上のメソッドを呼び出すようにしました。が、これが大失敗でした。
 当たり前ですが、ダメージモーションといっても敵によって長さや止めたいタイミングが違うので、わんさか敵に囲まれるような本ゲームではばらばらに止まってしまい、非常に気持ちが悪い。攻撃を当てた時の爽快感が欲しいのに、これでは本末転倒です。
 それよりも、攻撃が当たった後、ちょっとディレイを入れてから敵のモーションを止めて、その後一定時間後に動き出すだけ、と画一的に単純にやったほうが、手間もかからず見栄え的にもOKでした。ただ、ザコ敵が群がるシーンでは、止める時間が短すぎたせいか、爽快感があるかどうかは……正直よくわからん状態です。冒頭の映像を見ても「ん〜止まってるか…?」レベルの間違い探しかもしれません。とはいえ、ボスくらいの大きな敵には、ちゃんと攻撃を受けて止まっているように見えていますので、ひとまずこれでヨシとしておきましょう。不具合っぽいのも出てなさそう&またいい手が思いついたら修正するってことで、いつもの棚上げ〜にしちゃいます。
 さて、次はアイテム関係のシステムを入れるかなぁ。やることが止まらないけど、がんばらなきゃだ。うん。

Unityアクションゲーム制作記 その18 ボスキャラのセットアップのメモ書き

youtu.be

  今回は、完全に自分のためのメモ書きです(他の人にはなーんの役にも立たない100%混じりっ気なしの駄文><)。先日、アリーナモードと称するゲームモードに新しいボスを追加する際に、どうすればいいかをすっかり忘れて、すげー苦労しました。その反省を込めてのメモ書きです。なにしろ数ヶ月前に作ったいい加減な仕様なものだからなぁ……。なお、アタッチするスクリプトなどの名称は、自分で勝手に作っているものがほとんどです。

1)オブジェクトのtagとLayerの設定

 ルートオブジェクトのtagとLayerをそれぞれ、tag>enemy、Layer>EnamyMainとする。

2)ルートオブジェクトにアタッチするコンポーネント

 おなじみのAnimator、Rigidbody、CapsuleCollider

 敵の制御ルーチンの「EnemyMainAi」スクリプト

 ボスであれば、ストレス管理用の「EnemyStressMain」スクリプト

3)ルートオブジェクトにアタッチするEnemyMainAiへ、エディタから入力しておく必要のある値(他はまだ覚えているから割愛)

●EnemyMainAi

 EnemyMainCollider:プレイヤーの武器と当たり判定を行うコライダー

>近接攻撃用

 EnemyWeaponRightObjectCnt:

 近接攻撃の右手武器で判定に必要な個数(片手なら1、両手なら2)

 EnemyWeaponRightObjectObj:

 近接攻撃の右手武器で判定されるオブジェクト(判定が必要な分だけセット)

 EnemyWeaponLeftObjectCnt:

 近接攻撃の左手武器で判定に必要な個数片手なら1、両手なら2)

 EnemyWeaponLeftObjectObj:

 近接攻撃の左手武器で判定されるオブジェクト(判定が必要な分だけセット)

>遠隔攻撃用

 それぞれ必要個数分登録しておく。

 RangeWeaponStartPosObj:遠隔武器発射場所のゲームオブジェクト

 RangeWeaponPrefab:実際に発射する遠隔武器のプレハブ

 RangeWeaponEffectPrefab:発射する時に生成するエフェクトのプレフハブ

 RangeWeaponSoundEffect:発射した時に鳴らすSEの名前

4)当たり判定用のコライダーとそこへアタッチするスクリプト

・プレイヤーの武器と敵本体の当たり判定を行うためのコライダー

Layer:EnemyCollider

場所:Bip01 Pelvis(敵本体の中央)

コライダーの種類:BoxCollider

アタッチするスクリプト:EnemyDamageCongrollAi

(プレイヤーから攻撃を受けたあとの行動を決めるメソッドを呼び出すためのもの)

・敵の武器とプレイヤーを判定するためのコライダー(右・左それぞれ設定する)

Layer:EnemyAttack

場所:敵の右・左手武器のある場所

コライダーの種類:CapsuleCollider

アタッチするスクリプト:EnemyWeaponRightAi(右)、EnemyWeaponLeftAi(左)

(敵の武器とプレイヤーの接触判定があったときに呼び出されるメソッド) 

5)攻撃アニメーションに必要なアニメーションイベント

>近接攻撃(右)

 EnemyWeaponColliderOn():武器(右)の判定をONにしたいタイミングで呼び出す

 EnemyWeaponColliderOff():武器(右)の判定をOFFにしたいタイミングで呼び出す

 EnemyAttackOff:敵の攻撃(右)動作終了フラグなどの設定

>近接攻撃(左)

 EnemyWeapon2ColliderOn():武器(左)の判定をONにしたいタイミングで呼び出す

 EnemyWeapon2ColliderOff():武器(左)の判定をOFFにしたいタイミングで呼び出す

 EnemyAttack2Off:敵の攻撃(左)動作終了フラグなどの設定

 >各種特殊メソッド

  モード変更、エフェクト実行などを行うためのメソッド群。

 EnemyReactionFalse():特殊攻撃中にBlow、KnockBackを無効とする

 EnemyReactionTrue():通常に戻す

 EnemyRigidbodyPositionFreezeAll():rigidbody全座標・回転固定

 EnemyRigidbodyPositionInit():rigidbody座標・回転固定を初期状態に戻す

 EnemySummonersCircle(召喚情報):雑魚敵の召喚用

 GroundEffectSet(エフェクト名):魔法詠唱などのエフェクトを発生させるために呼び出す

>遠隔攻撃 

 RangeAttackInstantiate():呼び出されると設定された情報をもとに、遠隔武器を実際に生成する

 うーん、、、こんなもんだっけな? まぁ、気付いたら追加しておこう。うん。

Unityアクションゲーム制作記 その17 基本操作のおさらいと調整

Unity アクション アプリ ゲーム

 めずらしく更新が早いですが、今回は復習ネタが多めでいきます。

 さて、これまでさらしている動画を見ていただければわかるのですが、今回作っているゲームの操作にはバーチャルパッドを使っています。導入する際の方針としては以下の2つがありました。

  • どこを押しても対応できるようにする
  • いわゆるボタンは使わない

 どこを押しても大丈夫というのは、ユーザーがプレイする時にベストポジションで遊べるようにしたかったということもありますが、縦・横どちらでも遊べるようにしたかったのが大きいです。特に縦持ちにした時には、横にUIを置くスペースが限られる(というか置けない)ですからね。

 ボタンを実装しない理由は簡単で「自分の手では、ちゃんと狙って押せない!」から。幾つかそういったタイプのゲームもやってみたのですが、すぐに押す場所がずれてしまいストレスの溜まること溜まること。

 どこまでできるかどうか全くわからないまま、「1タップのみの操作でどこまでできるか」この点だけは最初から決めていたのですが……結果、くそ面倒なことになりましたが……(自業自得)。

f:id:hamazakifactory:20170206144418j:plain

 まず「1タップ」でできることいえば、画面を「押す」「離す」これだけ。あとは、押している時間、離している時間、移動距離との組み合わせで、操作のバリエーションを作るしかありません。キャラクターの移動に関しては、上の写真にある通り、最初にタップした場所からスライドさせた方向へ、キャラクターを移動するようにしてあります。操作としては「押しっぱなし+移動」ですね。

 攻撃自体は、タップ=「押して離す」動作で実現しています。連打することで攻撃がつながるものと、特定タイミングでタップすることでつながる連続攻撃の2つを用意していますが、当然のことながら、1タップでは移動しながら攻撃するという操作ができません。そこで攻撃している最中に、敵を少しだけ追尾するようにしています。

f:id:hamazakifactory:20170206151137j:plain

 今のところは、連続攻撃の初弾で攻撃した敵の中からプレイヤーキャラに一番近い敵をターゲッティング。以後、連続攻撃を繰り出すたびにそのキャラをちょっとだけ追尾するようにしてあります。そして連続攻撃が途切れたら、次の攻撃時に新たなターゲットを探すという具合です(黄色の1〜3)。攻撃を繰り出すたび、「常にプレイヤーに近い敵を叩いていく=敵全体に平均的にダメージを与えていく」のもありなのですが、今回は「1匹を追尾してとにかく敵の数を潰し、プレイヤーへの脅威を減らす」ことを優先としました。

 しかし、もう一つ困ったことがあって、それはプレイヤーが倒したい敵の優先順位をどうやって知ればいいのかが、現段階では知る術がないということによるものです。特にボス戦。なによりボスが重要なターゲットであり、倒すべき敵として優先度の高いものです。しかし、時にはボスが召喚した雑魚を先に片付けたい状況というのも発生することもあるので、一概にボスまっしぐらが正しい状況とは言えないのが悩ましいところ。青の1〜2の順で攻撃するか、それとも黄色の1〜3で攻撃するか……勝手に判断してやってくれるようにするにはどうすればいいのか……。脅威判定って、自分の攻撃力と敵のHPの兼ね合いで判断できるのかなぁ……とか思いを巡らせたこともあるのですが、「いまはその時ではない=そこまでやってらんねーな」とさっくり投げ出してあります。悩みどころですが、現状で致命的な問題となっていないので、今後の課題とします(=おそらく放置したままなパターン)。

 んで、ようやく今回調整した部分。最近、操作としてフリック操作による、ダッシュ移動を実装しました。操作としては「押す(ごく短い時間)+移動(そこそこ大きく)」で、緊急回避用として用意しました。実装したのは、フリックした方向に一定距離ダッシュ移動するというごく基本的なものです。

 多数の敵に囲まれた状況から脱出するための操作としては、立派に役割を果たしました。操作を覚えることでスピーディーな戦闘が楽しめるようになった……んですが、遊んでいくうちにどうにも「なーんか物足りねぇなあ」と思うように。ダッシュ移動は気持ちいいのですが、そのあとに敵を叩くためにえっちらおっちら移動しなければならない展開がちょっとだるい。まぁ、ダッシュ移動そのものが敵から離れることを第一目的としているんだから当たり前です。でもでも、もーちょっとアクティブにダッシュ移動を使えないかなーと欲張ってみたい、要は、攻撃する時に敵を追尾したように、ダッシュ移動をしたときもいい感じで、敵を追尾できないかなーということです。

 まずやってみたのは、ダッシュ操作直後に敵を追尾し始めること。回避するという概念はどこへやら、ただ単に敵まっしぐらになってしまうのでNG。しかたなく、ダッシュ移動そのものをじっくりと見直してみることに。

 今回のダッシュ移動は、一定速度で高速に移動する期間とそこから次第に減速していくというシーケンスで実現していました。そこで、高速に移動しているときはそのままにして、減速していくときに敵を追尾させていけばいい感じにならない? とやってみたところ、そこそこ思い通りに敵の後方へ回り込むような軌道が実現できました。

f:id:hamazakifactory:20170206160347j:plain

 やったーと喜んでいたのですが、問題はすぐに発生。次なる問題は、追尾する敵をどう選べばいいか? です。脅威度判定は今後の課題ということで、追尾する敵を選ぶ基準は、いまのところ距離だけ。なので、移動しているどこのポイントから、敵を選ぶだけになります。タイミングとしては、

  1. ダッシュ開始時
  2. ダッシュ移動終了時
  3. ダッシュ+減速終了時(ダッシュ完了後)

くらい。まぁ結論から言うと、ダッシュ開始時にダッシュ完了後の最終到達地点の座標を計算(直線移動なので楽ちん)、そこから一定の範囲にいる敵を探してターゲッティングするという方法に落ち着きました。

f:id:hamazakifactory:20170206164638j:plain

 こうすることで、敵のいない方向へダッシュ移動すれば確実に敵から離れられます。ダッシュ移動の距離を把握している必要はありますが、覚えてしまえばダッシュ移動して敵との距離を素早く詰めることも可能になりました。めでたしめでたし……。

 とはいかないのが現実。先ほどからちょくちょく出てきている敵の脅威判定をちゃんとやってないので、現状では、ダッシュ移動後にとりあえず手近な敵にまっしぐらしてしまいます。つまり、プレイヤーの思惑と外れた場所へ突っ込む可能性も高いということですね。プレイヤーは、ゲームに慣れてくればくるほど、先読みというか戦闘状況の先を見て戦うようになります。つまり、こういったアシスト機能が、プレイヤーが描いている戦略と変わってくると、結構なストレスを感じてしまうんですよねぇ。わかっていても解決策は浮かばず……。

 しっかし、最近は問題を解決するどころか課題だけが積み上がってきて、ゲームが出来上がる気がしなくなってきてるけど、気のせいだろうか……。まぁ引き続きがんばっていこう、うん。

Unityアクションゲーム制作記 その16 ボスらしい動きをするロジックを組み込む

 

youtu.be

  ようやく懸案となっていたボスの挙動の目処が、ある程度立ちました。本当は昨年内に終わらすつもりだったもの……ここまで引っ張ってしまった原因として、ゲームとして盛り込んだアクションのシステムにありました(要は根幹に関わる部分)。

 まず、今回制作しているアクションゲームは、1対多の戦闘がメインになっています。敵の数が多いということは、それだけプレイヤーが囲まれることも多くなり、下手をするとすぐ多数の敵にボコボコにされてしまう危険があります。そうなるとプレイヤーは、敵からボコられないために集団から離れて攻撃をする、ヒットアウェイ戦法を選択し、チクチクと戦うことに……これではゲームとして爽快感に欠けてしまいます。

 せっかく多くの敵と戦っているのだから、バッタバッタと敵をなぎ倒すような、より気持ちいい戦闘にするにはどうしたらいいんだろう? そこでひねり出したのが、攻撃を受けた相手の動きを封じるようなリアクションをさせるというものでした。

 今回用意したのは、以下の4つ(プレイヤー・敵ともに設定可能)。

  • ノーマル:ダメージのみを与える
  • ブロウ:相手を一瞬ひるませる(相手の攻撃をキャンセルする)
  • ノックバック:相手を一定距離吹き飛ばす(相手の攻撃をキャンセルする)
  • ブラックアウト(気絶):一定時間、行動不能状態にする

 基本的にプレイヤーが連続攻撃を使った際には、初弾(ノーマル)>二段目(ブロウ)>三段目(ブロウ)>フィニッシュ技(ノックバックなど)と設定してあります。初弾をノーマルにしたのは、さすがに出合頭で敵の攻撃をつぶせてしまうと、本当に一方的な無双状態になってしまうので、ここは敵にも反撃できる余地を残してあります。結果として、プレイヤーが積極的に攻めることで敵の動きを封殺でき、いい感じの無双アクションを楽しめるようになったかな? という具合です。攻撃の間合いが違ったり、遠距離攻撃をしてくる敵などバリエーションを用意して、いっぺんに登場させれば、それなりに緊張感のある戦闘バランスになっていたりしました。

 ただし、ボス戦のときに問題が起きました。雑魚の場合は、数の暴力でプレイヤーへの反撃の余地はあったのですが、ボス単体とのサシ勝負では、ブロウによって攻撃がつぶせることが仇になってしまったのです。極端な話、あまりに簡単に敵の攻撃がつぶせてしまうので、ボスを一方的に殴るだけで戦闘が終わってしまうということも……結果、ボス戦がすげーつまらないものになってしまったのです。ボスの行動自体、雑魚と同じくプレイヤーとの状況(攻撃されたとか、見つけたとか)などに応じて、幾つかの行動を選択し、ループさせているだけなのも原因の一つだったといえます。

 じゃあ、ボスでは「ブロウ」を無効化すりゃーいいじゃん? 対プレイヤー用にロジックを組み直せばいいじゃん? と思わなくもないのですが、単に「ブロウは無効」としてしまうだけでは、あまりにゲーム的な都合が見え見えになってしまいますし、ボスごとにロジックを組むのは、ボスを量産する際にかなりきつそう……。

 そんな感じで、攻撃し続けていると、いい感じに敵が判断して回避して反撃したりできないかと、ここ数ヶ月ず〜〜〜〜っと頭を悩ませていたのです。まぁ、プレイヤーに対するリアクションを逐一判定し、対応する反撃方法を組み込んでやるのが王道なんでしょう、きっと。ただ、ロジックそのものを組み込んでしまうと、どういう反撃をしてくるのかすぐプレイヤーにバレるだろうし、後々、敵の強さを調整したいときにすげーめんどくさそうだったので、ない知恵絞ってうだうだと考え込むことに。どーしよっかなーと、どうすればいいかなーと悩み続けたのですが、結局やりたいことは、

  • 状況が悪化したら、有利な状況に変化するように自律的に行動してほしい

これに尽きます。これができれば、ボスとの戦闘も駆け引きのあるものになるはず! そして、状況が悪化するということは、敵にとって不利な状況に追い込まれることにほかなりません。今回のゲームで敵にとって不利な状況とは、

  • 壁際に追い詰められる
  • プレイヤーからダメージを受け続ける

単純化すれば、この2つに集約されるかなーと。そしてこの2つそれぞれにストレス値を用意して、敵にとって不利な状況を明確に数値化することにしました。

  • 壁際に追い詰められる = 地形ストレス上昇
  • プレイヤーからダメージを受け続ける = 攻撃ストレス上昇

 ボスが壁に近づけば近づくほど=プレイヤーに追い詰められる=地形ストレスは上がる。プレイヤーから攻撃を受ければ受けるほど攻撃ストレスは上がっていく。それぞれがある一定のしきい値を超えた時に、ボスへ回避や反撃する行動を起こさせるという具合です。

 しかし、これだけでは地形・攻撃ストレスで2パターンしかバリエーションを作れません。そこでさらにストレスレベルを用意して、そのストレスレベルに応じて回避や反撃行動を設定できることにしました。ストレスレベルはひとまず5段階。ストレスレベルが上がる条件は、ストレス値がしきい値を超えた時で、ストレスレベル下がる条件は時間経過のみとしました。かなり単純な仕組みですが、しきい値(レベルに関係なく固定)とストレスレベルを下げる時間(レベルごとに設定)をうまく調整することで、敵の攻撃をより激しくできたり、いったんレベルが上がってしまったら下がりにくくしたりできるので、これによってボスの個性も出せるようになっています。

 さらに、ボスに対して有効な攻撃をするプレイヤーに対しては、常にストレスレベルが高くキープされ、激しい反撃を多くするようになり、アクションが苦手なプレイヤーに対してはストレスレベルの上昇が抑えられるといった、難易度調整も自動でできるようになりました。

 結局のところ、ボス固有の攻撃のバリエーションを考えなきゃいけないし、各種数値の設定もちゃんとしないといけないしで、今回の方法でボスの制作が楽になったわけではありません。とはいえ、ある程度の共通な仕組みでボスの量産ができそうな感じになってきました。あとはちゃんと忘れずにドキュメントを整理するだけ……なんですけど、めんどーなことはいつも後回し。これまでも、メモとしていろいろ書き残しているのですが、たまーに間違ったコマンドとか変更前の仕様とか書いてあったりして、油断がなりません(常に過去の自分と戦っている…)。

 とはいえ、大きな問題が一つ片付いて一安心。次は、テストプレイしていう間にず〜っと気になっていたダッシュとチャージの使い勝手の悪さ(=操作性の悪さ)を、少しでも良くしなければ……そろそろちゃんと考えなきゃいけないゲームそのもののデザインとともに悩むとします。がんばろっと。