忍者ブログ
なんとなくだらだらと。方向性はまだ決まってない。 当方のプログラムでは、山田巧さん作成のDXライブラリを利用させていただいてます。 本サイト http://homepage2.nifty.com/natupaji/DxLib/index.html DX Library Copyright (C) 2001-2008 Takumi Yamada.
[16] [15] [14] [13] [12] [11] [10] [9] [8] [7] [6]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

厨二病の本領発揮。
知ったかぶりでゲームバランスがどうのこうの、AI がヘボいだの言いたい放題だけど
実際に関わってみたらどんなもんかねえ。


CRPG011.png





とりあえず、今の計算でどうなのか集計してみる。
テストプログラムなので細かいことは気にしない。

ヒストグラムそのいち
main.cpp

void Hist_Atk(Unit &ut)
{
    int h[100]={0};
    for(int i=0; i<100000; i++)h[ut.Attack()]++;

    // 集計結果
    cout << ut.getName() << " Attack Histgram\n";
    for(int i=0; i<100; i++)
        if(h[i]) cout << "Damage(" << i << ") : " << h[i] << "\n";

    cout << endl;
}


int main()
{
    srand(static_cast<unsigned int>(time(NULL)));

    Unit player,enemy;
    player.Init("ゆうしゃ", 30, 8, 5);
    enemy.Init("ゴブリン", 12, 6, 3);

    Hist_Atk(player);
    Hist_Atk(enemy);

/* ここからコメント
    int cnt = 1;
    while(1){
        cout << "\ncnt:" << cnt << "\n";
        player.ShowStatus();
        enemy.ShowStatus();

        if(NormalAttack(player,enemy))break;
        if(NormalAttack(enemy,player))break;

        if((++cnt)>100)break;
        Sleep(600);
    }
*/



Hist_Atk()関数でやってることは
1.配列h[]を用意。要素数は想定してるダメージ数の最大値+α
今のところは、4~11の範囲で済むはずだけど、「かいしんのいちげき」とか組み込むかも
しれないし、後々考えれば100超えもあるかもしれない。
2.試行回数10万回、Unit.Attack()関数を呼ぶ。返ってきた値を集計する。
3.集計結果の表示。集計結果ゼロの値は無視。

CRPG006.png



まあだいたい予想どおり。
攻撃力の値の半分から1.5倍に設定したから、攻撃力8なら4~11までがほとんど同じ値。
これでもよさそうなもんだけど、攻撃力は期待値「8」ぐらいは持たせたい。

ようやく、Attack() 関数(メソッド)を触るときが来た。

サイコロひとつ振ったときの値は1~6まで。出る数字は1~6まで、すべて同じ値が期待できる。
ふたつ振ったときの合計は2~12。だけど2から12まで、同じ確率では出ない。
2(12)の出る確率は1通りで1/36だけど、ちょうど真ん中の7が出る確率は6通りで6/36。
同じ乱数を重ねていくと、真ん中あたりの数字の期待が高まる。

計算式を、攻撃力/2 + (0~攻撃力までの乱数)/2 + (0~攻撃力までの乱数)/2
で作ってみる。

Unit.h


// 攻撃・アタックポイント
int Unit::Attack()
{
    return((rand()%Atp + rand()%Atp + Atp)/2);
}


実行。
CRPG007.png

うむ。期待どおり。
Atp8のときは7~8、Atp6のときは5~6の数値が出やすくなった。

ちょっと気になる点があるので、Atpにゲタを履かせてみる

Unit.h


// 攻撃・アタックポイント
int Unit::Attack()
{
    int ap = Atp+1;
    return((rand()%ap + rand()%ap + ap)/2);

}


実行。

CRPG008.png

どうだろうか。前のと比べてダメージが1ポイント増えたけど、山の形が奇麗になったような
気がする。
このへんはセンスの問題。いいと思ったらこれでいいし、納得いくまで調整するのも人生。


防御のほうは、期待値を気にせず、長方形なダイアグラムでいいと思ってるんだ俺。
防御側も、いちどヒストグラム出しておくか?

main.cpp


void Hist_Def(Unit &ut)
{
    int h[100]={0};
    for(int i=0; i<100000; i++)h[ut.Defence()]++;

    // 集計結果
    cout << ut.getName() << " Defence Histgram\n";
    for(int i=0; i<100; i++)
        if(h[i]) cout << "Damage(" << i << ") : " << h[i] << "\n";

    cout << endl;
}




メインから呼び出し

    Hist_Atk(player);
    Hist_Atk(enemy);
    Hist_Def(player);
    Hist_Def(enemy);

/* ここからコメント
    int cnt = 1;


実行。
CRPG009.png

おやびっくり。
というほどでもないけど。
コンピューターの都合上(というか、計算さぼってるせいなんだけど)、乱数はひとつ少ない値を
出してくるんだよね。
MAX値に限りなく近い数字を出すから、切り捨てたら当然MAX値が出るのは期待できない。
というわけで、防御値にも+1のゲタを履かせる。

Unit.h


// 防御・ディフェンスポイント
int Unit::Defence()
{
    int dp = Dfp + 1;
    return((rand()%dp + dp)/2);
}



実行結果。

CRPG010.png

きれいな長方形に満足。
割り算をなるべく遅らせるのは意味あるのかな?
使ってる変数がすべてint型だから、あまり関係ないような気もしないでもない。



こうやって軽くテストプログラム書いて10万回でもぶん回せば、ちょっとおかしなところがあれば
出てくるもんだけど。
なかなか普通のテストプレイだけでは乱数の偏りとか穴は見つけにくいよね → 某社製ゲーム



ちょっと残念な、あと一歩ゲームへの愚痴は置いといて。

この手の「戦闘だけゲー」のゲームバランスとか考察してみる。

1.ほぼ一撃で撃破できる強さなら「楽勝」
2.撃破に2~3ターンかかって、被ダメの合計が3割程度なら「稼ぎどころ」
3.撃破に3~5ターンかかって、被ダメが一撃3割越えたら「強敵」
4.撃破に3ターン以上かかって、被ダメが一撃5~6割越えたら「やばい」
5.一撃で8割以上ダメージ喰らって、一撃死の可能性があったら「無理」

だいたい2で稼いで余力あれば3に挑戦。リターンが大きければ4にも挑戦するってところ。
段階的に3の敵が2になる頃、4の敵がランクダウンして挑戦できるぐらいに調整できれば勝ち。
そのペースが最後まで続けば、神ゲーの神様になれる。

この基準はあくまでも俺センス。今作ってる「タイマンRPG」を想定しての、厳しめの設定。
しかも、一撃で終わらない「ドラクエ」系。叩き叩かれで消耗して、MPはほとんど回復で使うために
温存するタイプ。爽快感はあまり無いかも。

これが3対3とかだと変わってくるし、SRPGとか戦闘ルールが変わるとまた違ってくる。



適当に「こんなもん?」で作ってみたけど、「ゆうしゃ」と「ゴブリン」のバランスなかなか良いと
自画自賛。
レベル1パラメーターの計算には定評のある俺だ。
なんせ、何度も挫折してるからな。数えきれないぐらい。

ちょっと気になるので、通常攻撃そのもののヒストグラムも作ってみる。

main.cpp


void Hist_NA(Unit &lst, Unit &rst)
{
    int dmg;
    int h[100]={0};

    for(int i=0; i<100000; i++){
        dmg = (lst.Attack() - rst.Defence());
        if(dmg <= 0) dmg = 0;
        h[dmg]++;
    }
    // 集計結果
    cout << "NormalAttack Histgram  " << lst.getName() << " が、" << rst.getName() << " に攻撃

\n";
    lst.ShowStatus();
    rst.ShowStatus();
    for(int i=0; i<100; i++)
        if(h[i]) cout << "Damage(" << i << ") : " << h[i] << "\n";

    cout << endl;
}




メインから呼び出す
main.cpp

    Hist_Def(player);
    Hist_Def(enemy);

    Hist_NA(player, enemy);
    Hist_NA(enemy,player);

/* ここからコメント
    int cnt = 1;



実行結果がこれだ。

CRPG011.png

攻撃8 → 防御3 だと、期待値4~7あたり?
攻撃6 → 防御5 だと、期待値2~3あたり。
単純に「攻撃 - 防御」の図式で済むわけでもないか。

ヒストグラム表示のためにゼロ以下の補正を省いてみたんだけど、ダメージゼロ以下なら
「攻撃を受け止めた!」にしてもよさそうかな。


PR


忍者ブログ [PR]
カレンダー
12 2025/01 02
S M T W T F S
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
フリーエリア
バーコード
ブログ内検索
P R
アクセス解析
カウンター