【Unity】RPGを作るチュートリアルその20 ダメージ計算式の方針決めと実装

【Unity】RPGを作るチュートリアルその20 ダメージ計算式の方針決めと実装

シンプルなRPGをUnityで作るチュートリアルシリーズの20回目です。あっという間に20回に到達したような気持ちもありつつ、まだ終わりが見えないので序盤のようなまっさらな気持ちでいます(遠い目)

第19回では戦闘画面のUIのうち、メッセージウィンドウを実装しました。この実装が終わったところで戦闘の画面で最低限必要なUIは作成できました。

今回は戦闘の機能や流れを作っていくにあたってとても大切なダメージ計算式の方針決めと実装を行います。

 

 

制作環境

MacBook Pro 2023 Apple M2 Max

Unity6 (6000.0.30f1) Silicon

 

作業内容と順序

シンプルなRPGを作る上でどんな作業が必要か、どんな順番で作っていくと良さそうか、別ページで検討しました。基本的にこの流れに沿って進めていきます。

 

チュートリアルの一覧

このシリーズ全体の一覧は以下のページにまとめています。

 

前回の内容

前回は戦闘画面で表示するUIのうち、メッセージウィンドウを実装しました。

 

ダメージ計算式

ダメージ計算式! それはRPGの戦闘において根幹をなす要素!

とテンション高めで入りましたが、実際ダメージ計算式によって、計算結果のダメージは大きく変わっていきます。また、これを踏まえて味方や敵のパラメータを調整していくため、他の部分に与える影響も大きくなっています。

与えるダメージの大きさは、序盤は小さく、後半になるにつれて大きくなっていくのが一般的です。表示されるダメージが増えていくことで成長している感覚が得られるので、後半はダメージは増えるけれど倒せる回数は変わらないかちょっと多いくらい、なんて感じでHPの量で調整することも多いかと思います。

この回では「ダメージ計算式」と一括りにしていますが、魔法やアイテムの回復量計算、逃走率の決定や行動順の決定なども含めて実装を行います。なので「戦闘に関連する数値の計算」を実装していきましょう。

最低限必要な計算式は以下の4つで、

  • 攻撃時のダメージ計算
  • 魔法やアイテムの回復量の計算
  • 逃走成功率の計算
  • 行動順の計算

を実装しましょう。作っている中で必要そうな式が出てきたら、その都度追加していきたいと思います。

 

ダメージ計算式の種類

ダメージ計算式を考えるのは多くの開発者の頭を悩ませる行為で、先人の試行錯誤を踏まえつつオリジナリティを出すことに挑戦する開発者たちの苦悩は計り知れません。ダメージの計算においては、加減のみか、乗除も加えるか、累乗も入れるか、と数学的な演算の範囲をどこまで広げるかによって結果が大きく変わることもあり、ゲーム内で使用したいパラメータがどこまで寄与するかの塩梅を調整しながら組み立てていくという、ある種賽の河原で石を積み立てるかのような行為でもあります。

数学的な演算の範囲によって種類を分けてみると、

  • 加減による計算
  • 乗除による計算
  • 累乗による計算

と3つに分けて考えるのが良いかと思います。

 

加減による計算

パラメータ同士で計算する、あるいはパラメータに係数をかけた後に計算する方式です。近年一番有名な呼び方だと「アルテリオス計算式」があります。このアルテリオス計算式は、「攻撃力 – 防御力 = ダメージ」というとても単純な引き算方式で、パラメータが分かっているとダメージの予測が立てやすい反面、バランス調整が難しいというデメリットもあります。

「アルテリオス計算式」の字面だけ見ると古代ギリシャで作られた計算式のような印象を受けますが、ファミコンのゲームである「アルテリオス」の名前が使われている式です。上述の通り、「攻撃力 – 防御力 = ダメージ」という計算式がこのゲームで使われており、RTA界隈で色んな意味で有名なbiim兄貴がこのゲームをプレイした後、また別のゲームで同じ計算式が出てきた時に命名されました。このダメージ計算式自体はそれより以前にもあったものですが、象徴的な名前がついてからは「アルテリオス計算式」が使われている印象です。

ドラクエも「攻撃力 * 2 – 防御力 * 4 = ダメージ」のようにパラメータに係数がかかっているものの、パラメータ同士では引き算となるのでこの括りに入ります。他にもマリオストーリーやファイアーエムブレムなどでもこの形式のダメージ計算が使われています。

 

乗除による計算

乗除が入る計算としては、パラメータ同士の掛け算があったり、割り算があったりするものです。掛け算があるのは命中回数の概念があるFFシリーズ、割り算があるのはポケモンシリーズが有名です。

足し算や引き算に比べて値の変動率が大きくなるので、掛け算の場合はダメージ量としては大きなものになるケースが多くなっています。FFだと命中回数が伸びれば伸びるほどダメージが大きくなります。ゲームの後半になると4桁のダメージがぽんぽん出てくるのもスケールが上がった感じがして楽しくなります。

割り算が入る場合には、パラメータが大きくなってもある程度安定したダメージ量になるので、パラメータの範囲内で調整がしやすくなるかもしれません。ポケモンだとHPは数百の範囲、各パラメータも数百の範囲に収まっています。レベルの影響もあるので、そこで強さの表現がされている感じですね。

 

累乗による計算

累乗が入るとダメージの伸びがかなり大きくなります。FF10では計算式の中で力の3乗なんてのが出てくるので、たとえば力が1増えるだけでも大きくダメージが伸びていきます。スフィア版による成長でも、1回につき1から4までの増加だったので、序盤で急激に上がりすぎないようにはなっています。ダメージの範囲も、9999ではなく99999までと一桁多いので、それを考慮した計算になっているようです。

ダメージ幅が広くなると調整もその分大変になるので、RPG作成にある程度慣れるまでは、上の乗除あたりまでにしておいた方が扱いやすくなるかと思います。

 

上記を踏まえて、このチュートリアル内でどのような計算式を採用するか考えていきたいと思います。

 

計算式の方針

今回作りたい以下の計算に関して、方針を決めていきたいと思います。といっても「【Unity】簡単なRPGを作る場合に必要な作業を考えてみよう」のページで大体決めてあったので、それを踏襲したいと思います。

  • 攻撃時のダメージ計算
  • 魔法やアイテムの回復量の計算
  • 逃走成功率の計算
  • 行動順の計算

 

攻撃時のダメージ計算

ダメージ計算については初期のドラクエに倣って、

(攻撃する側の攻撃力) / 2 – (攻撃を受ける側の防御力) / 4 = ダメージ

にしましょう。実際には左辺の計算時に乱数も含めて計算するため、

((攻撃する側の攻撃力) / 2 – (攻撃を受ける側の防御力) / 4) * (乱数) = ダメージ

になる想定です。乱数幅は±20%程度の想定で、Randomクラスを使って0.8から1.2の間の乱数を取得します。攻撃力の割り算、防御力の割り算についてはfloatで計算し、乱数と掛け算した後の最終結果でintに切り上げます。攻撃を受ける側の防御力が高いと計算結果がマイナスになることから、最低ダメージ保証として1を設定します。

最低ダメージ保証を入れるかどうかは好みにもよるところで、ストーリー的に考えた時に、格上を殴ってダメージが入るかどうかを検討してみたり、ゲーム的に考えて少しでもユーザの行動を反映させたいと検討してみたりと、多角的に考えてみると良いかと思います。格上を殴るなら、そもそも命中するかどうかのところで判定させる方が良いかもしれません。強い人なら相手の攻撃を受けないように立ち回るでしょうし、強くてもガッチガチに防具を固めた人なら動きにくくて命中はするけど、ほとんどダメージが入らないから最低保証のダメージだけ入る、みたいな感じです。お互いに命のやり取りをしている状態での攻撃と考えれば、それはもう死に物狂いで全力で行くでしょうから、当たりさえすればダメージが入る、と考えるのも良いかと思います。

クリティカルについては今回は導入しないこととします。発生率を固定にしたり、特定のパラメータ(運など)に依存させたりなど、導入するパラメータに応じて決定するとグッド。クリティカルの発生率については、期待値を計算して設定できるとより調整しやすくなります。クリティカル発生時のダメージ倍率も影響してくるので、ExcelやNumbers、Googleスプレッドシートなどを使ってそれぞれのパラメータを変えながら計算してみて、ちょうど良い値に調整していくと良いかと思います。

 

魔法やアイテムの回復量の計算

また、今回は回復魔法、回復アイテムも導入します。そのため、回復用の計算式も想定しておきましょう。

(基礎回復量) * (乱数) = 回復量

とシンプルにしておくのが良さそうです。乱数幅はダメージ計算と同様に±20%程度にしたいと思います。パラメータの種類がたくさんある場合は、魔法は魔力などを加味してより回復量が大きくなるようにするのも良いかもしれません。今回は魔力に相当するパラメータは入れないので、上記の式で進めます。アイテムの場合は誰が使っても似たような量で回復させるため、魔力などは計算に含めない形にすると良いかと思います。

魔法でもクリティカルが発生するゲームもあるので、その場合は上述のダメージ計算にあるように、期待値を計算して調整するとグッド。

 

逃走成功率の計算

逃げる時の成功率計算の式も用意します。

(基礎成功率) + ((逃げる側の素早さ) – (逃げられる側の素早さ)) = 成功確率

※(逃げる側の素早さ) – (逃げられる側の素早さ)が0未満の場合は0に補正

としておきましょう。ここで、基礎成功率は割合ではなく%表示の値を使います。成功率80%なら80を入れます。ここでも逃走率の最低保証として50を設定しましょう。格上から逃げる場合、簡単には逃げられないと考えるのが現実的ですが、ゲーム的には格上と戦うと高確率でゲームオーバーになるので、全く逃げられないとプレイ時のストレスがマッハとなることから運次第で逃げられるようにした方が良さそうです。

今回は運のパラメータはありませんが、逃げる際に運のパラメータを使うのもユーザ側からイメージしやすくなるかと思います。

 

行動順の計算

素早さを使う計算として、戦闘中の行動順決定に関しても式を用意します。

(素早さ) * (乱数) = 行動優先度

として、行動優先度の大きいキャラクターから行動させます。同値の場合は差がつくまで乱数で判定します。今回は導入しませんが、防御を選択した場合は、行動開始前に防御状態にするなどの処理を入れます。自分の番が回ってきてから防御、だと素早い敵と戦う際には防御前に攻撃を受けたりしますからね。

また、行動優先度を無視するターン内最速行動、ターン内最遅行動の魔法やスキルなどを導入する場合は、行動速度の計算によらず処理させるとグッド。味方と敵で同時にターン内最速行動をする場合は、魔法側の優先度や素早さの計算によって順番を決めることになるかと思います。

 

実装の方針

それぞれの方針が決まったら、コードにしていきます。今回は複数クラスから参照できるように、staticなクラスを作って、その中で計算を行うようにします。

戦闘関連のスクリプトファイルは多くなりそうなので、フォルダを分けて整理しておきます。Projectウィンドウから「Assets/Scripts」のフォルダにて、新規フォルダを作成します。名前は [Battle] にしました。

フォルダの作成
フォルダの作成

 

作成した「Battle」のフォルダに移動し、空のスクリプトを作成します。名前は [BattleCalculator] にしました。

スクリプトの作成
スクリプトの作成

 

このクラスでは、上で方針を決めた4つの計算式をpublicなメソッドとして実装していきます。

戦闘中に必要なメソッドを呼び出して値を取得していきます。攻撃の判定では割り算をfloatで行なっています。最終的なダメージ算出まではfloatで計算することで、端数の切り捨てをなるべく避けるようにしています。

逃走の判定については、確率の計算だけではなく逃走できたかどうかの判定まで含めています。行動順決定に関しては、乱数による補正を返して、呼び出し元で優先度の大きい順から行動させます。

このクラスの動作確認については、後続のチュートリアルの中で行なっていきます。今回は方針を決めて、計算用のクラスを作成したところで作業完了にしましょう。

 

今回のブランチ

 

まとめ

今回は戦闘時のダメージ計算や逃走の判定など、戦闘に関連する計算の方針を決め、staticなクラスとして実装を行いました。戦闘の動作を作っていく際にこのクラスを使って各種の値を取得していきます。

次回は戦闘の管理クラスを作成し、戦闘のフェーズなどを決めていきたいと思います。フェーズに関してはenumで定義して、「今このフェーズだったら」のように分岐させて処理させたいと思います。

 

     

ゲーム開発の攻略チャートを作りました!

CTA-IMAGE

「ゲームを作ってみたいけど、何から手を付けていいか分からない!」


そんなお悩みをお持ちの方向けに、todoがアプリをリリースした経験を中心に、ゲーム作りの手順や考慮すべき点をまとめたe-bookを作成しました。ゲーム作りはそれ自体がゲームのように楽しいプロセスなので、「攻略チャート」と名付けています。


ゲームを作り始めた時にぶつかる壁である「何をしたら良いのか分からない」という悩みを吹き飛ばしましょう!