カッパでも分かるiOSアプリゲーム開発

カッパがひたすらゲーム制作に関することを書くブログです。Railsに関するTipsもたまにまとめてます。

クリティカルヒットの計算式について考えてみたカッパ

スポンサードリンク

K2の居ない会社、その時点でこの会社の株価はゼロに等しい。
なのでカッパはゲームにおけるクリティカルヒットの計算式について考察してみる事にした。


ゲームでは確率は必要不可欠な要素だ。
多少のランダム性がある事によりプレイヤーは一喜一憂したりと心を揺さぶられる。
ドラクエの会心の一撃やダメージのブレ幅など非常に大事な要素だと思う。

余談「ドラクエの会心の一撃の確率」

  • シリーズによってまちまち
  • だいたい 1/64 や 1/32 を想定しているらしい
  • しかしアリーナは開発連発娘として例外のようだ
  • PS版のアリーナの会心の一撃が「レベル/256」だったらしい。LV99で約 2/5 か。恐ろしい娘だ

シリーズによっては「運の良さ」は会心の一撃に基本的に無影響らしい。マジか。

かっぱゲームにおける確率処理

  • まず rnd(n) というメソッドを用意する(n は Int型)
  • これは 0 から n-1 の整数をランダムで返す関数だ
  • rnd(3) ならば 0,1,2 のいずれかを返す
    // 0 から max までの乱数
    func rnd(_ max : Int) -> Int {
        if(max <= 0){
            return 0
        }
        let rand = Int(arc4random_uniform(UInt32(max)))
        return rand
    }
  • このメソッドに関しては大医学の頃から使ってる
  • SpriteKitに専用の乱数ジェネレーターが追加されたりしたけど、あくまで rnd(n) を使いたい。慣れ&老害というやつだ
if rnd(100) == 0 {
  // 処理
}
  • ↑が 1/100 の確率で起こる処理になる。1%の確率
if rnd(100) < 30 {
  // 処理
}
  • ↑が 30%の確率で起きる処理

100 通りの乱数のうち、0~29 が該当するから 30/100 で 30%。合ってる気がする。

自分は処理が適当なので実は分母が 1 ずれてたとかありそうで怖い。 < を <= に変えただけで微妙な差異が出ちゃうし

クリティカルヒットの出る確率=「運の良さ」にした場合

  • 「運の良さ」をクリティカルヒットの出る確率に影響させる場合
  • 基本的にカッパのゲームはパラメーターが無限だが、「運の良さ」だけは有限にするべきか迷う
if rnd(100) < luck {
 // クリティカルヒット
}
  • これだと運の良さが3だと、3%の確率でクリティカルヒットが出る
  • しかしこれだと運の良さが100以上になるとクリティカルヒットしか出なくなるという悲しい問題が発生する
  • また、100以上に運を上げる意味がなくなる悲しみ

運の良さを乱数に直結させたい

if rnd(luck) >= 100 {
  // クリティカルヒット
}
  • これならば luck が高くなるほどクリティカルヒットが出やすくなる
  • クリティカルを極限まで上げても一定確率でクリティカルが出ない事はあり得る
  • しかし問題点として、luck を 100 以上にしなければいつまでもクリティカルが出ないという問題が挙げられる
  • クリティカルの初期値を100 にしても良いのだが、うーん
  • この場合、luck を 1 あげるとクリティカル率はどう上がっていくのか
luck 101  :  1/101 でクリティカルヒット
luck 102  :   2/102 でクリティカルヒット
luck 103 :  3/103 でクリティカルヒット

つまり (luck-100)/luck の確率でクリティカルヒットが起きる。
(だんだん計算怪しくなってきた)

仮に luck を xから1上げると、クリティカル発生率が (x+1-100)/(x+1) - (x-100)/x だけ上がる事になる。

(x+1-100)/(x+1) - (x-100)/x = { (x-99)x - (x-100)(x+1) }/x(x+1)
                                          = { x^2-99x-(x^2-99x-100) }/x(x+1)
                                          =  100/x(x+1)

つまり 100/x(x+1) だけクリティカル率が上がる。x の値が大きくなるほど、上昇率は微小なものとなる。少し良さげな設計だ

まとめると下記のような感じ

luck 100 まではクリティカルは出ない
luck 101 で約 1%の確率でクリティカルが出る
luck 150 で約 33%の確率でクリティカルが出る
luck 200 で約 50%の確率でクリティカルが出る
luck 300 で約 66%の確率でクリティカルが出る
luck 999 で約 90%の確率でクリティカルが出る

定数100の値を変える事で良いバランスにはなるかもしれない。
イメージ的には対数的な曲線を描く事になる。

定数を変えてみた

まず要件として

  • 運の良さが1でも極小ながらクリティカルヒットが出るようにしたい
  • いい感じのバランスにしたい(なんだこの要件は)

とりあえず luck が 1 でもクリティカルが出るようにし、かと言って全体的なクリティカルヒットの発生率を抑えたパターン

if rnd(luck + 1000) >= 1000 {
  // クリティカルヒット
}

これならば確率は下記の通り

luck=1   1/1001 の確率でクリティカル(約0.1%)
luck=2    2/1002 の確率でクリティカル(約0.2%)
luck=50 50/1050 の確率でクリティカル(約4.7%)
luck=100  100/1100の確率でクリティカル(約9.1%)
luck=150  150/1150の確率でクリティカル(約13%)
luck= 999 999/1999 の確率でクリティカル(約49.9%)

クリティカル率を表示する計算式は

luck/(1000+luck)

これはとてもシンプルだ。
これならばスキルや装備によるクリティカル率の補正が簡単に付けられる

例えばクリティカル5%上がるスキルをセットしたならば

let crt = luck/(1000+luck) + 0.05

if rnd(100) <= crt*100 {
  // クリティカルヒット
}

問題はクリティカルの効果だ

  • クリティカルヒット=相手の防御無視 だと魔法を使う意味がなくなってしまう(脳筋の救済措置とも取れるが)
  • そもそも相手の防御無視というロマンが強すぎてクリティカル連発してれば勝てるゲームになってしまいそうだ
  • 「運の良さ」パラメーターを上げるのは勇気がいるので報われるのは良い事なのだが  強すぎたらクソゲーになるし弱すぎてもクソゲーになってしまう
  • バランス難しいねぇ
  • 「通常の3倍ダメージ」だと防御が高い相手にダメージが 1 -> 3 に変わっただけで何のロマンもないという問題
  • いっそクリティカルヒット時は rnd(lv) の値だけダメージ増加とかしても良いが、しかし物理ダメージに筋力が関与しないのはどうなのか
  • とりあえずクリティカルヒット時は専用エフェクトを用意して格好良く見せたい。火花の色を変えるだけだけど

総括

  • 確率は難しいンゴねぇ