忍者ブログ
Profile
HN:gp-hss

職業:高校生

趣味:3DCG

言語:C全般

環境:VC++ 2008 EE

3DCG:Softimage Mod Tool

自己紹介:
ゲームプログラマー目指して勉強している者です。
現在 C++ 修得にむけて頑張っています。

Began study since 2009/8/21

Latest CM
[06/28 soulmorning]
Latest TB
(02/25)
1  2  3  4  5  6  7  8  9  10 
×

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

author : gp-hss ×

RPG(.h)


とりあえずいろんなものをまとめてやりました。
いろいろと修正したり追加したりしています。
まだまだやることが残っています。
今回は途中経過の報告ということで。

では。

拍手[0回]

PR
author : gp-hss ×

RPGalg(.h)


どうもです。
今回は経験値やステータスの上昇率などゲーム内で使うアルゴリズムをヘッダーにまとめてやりました。
しかし、まだ途中のものです。
とりあえず解説していきましょう。


unsigned short GetDamage()
何かものすごく仮引数が多いですが我慢してください。
この関数は敵もしくは自分に与えられるダメージを計算する関数です。
計算式は、

攻撃力×技の威力)÷敵の防御力経験値の総和÷(敵の防御力敵の魔法防御力)+0 ~ level までのランダム値

まず絶対自分の攻撃力とその技の威力はダメージに関係しますよね。
関係性を × にするか+にするか迷いましたが × にしました。
ということで、敵の防御力との関係性ももちろん-ではなく ÷ になりますね。
あとは個人的に経験値もダメージに影響するかなということで経験値に関することも加えています。
だって経験のある人と素人じゃぜんぜん違いますよね。
そしてその今までの経験値を敵の総合的な防御力で割っています。
このままだとダメージがほぼ一定になってしまいますので最後にランダム値を加えています。


unsigned short GetExp()
この関数では敵を倒すことによって得られる経験値を取得できます。
計算式は、

(敵の全てのステータス情報の) (総和相加平均0 ~ level までのランダム値)×2
なんとこのゲームでは敵のステータスをそのまんま自分の経験値にすることができるのです。
しかもそれをさらに倍にします。
ていうかこのぐらいがちょうどよくなる。と思う。


unsigned short GetLvUpExp()
この関数では1つレベルアップするのに必要な経験値を取得できます。
計算式は、

(自分の全てのステータス情報の) 総和 × level

あとはこの計算によって得られた数値を十の位または百の位まで四捨五入しています。
このアルゴリズムでは決まって一定の間隔の値が返されます。


unsigned short GetHpStatusUpValue()
この関数ではレベルアップすることによってHPステータスの上昇値が取得できます。
計算式は、

((1つレベルアップするのに必要な経験値用の経験値 ÷ level) × 5 ) ÷ 100 + -2  ~ +2

1つレベルアップするのに必要な経験値用の経験値とは、レベルアップするごとに 0 になる経験値を指します。
今までの全ての経験値ではありません。
たいていの場合は 3 ~ 7 におさまります。
努力値なんてもんはなく運で大小がきまります。
非常に理不尽なアルゴリズムです。サボってすいません。


unsigned short GetBasicStatusUpValue()
この関数ではHP以外のステータスの上昇値を取得できます。
上の関数との違いは × 5 の部分が × 2 なだけです。
たいていの場合は 0 ~ 4 におさまります。



今回紹介したのはあくまで作りかけの途中の部分だけです。
こういったアルゴリズムってどうやってつくるのかわからないから勘だけでつくったのですがどうでしょうか。
まだまだ作っていきますのでお楽しみに。

ではまた今度。

拍手[0回]

author : gp-hss ×

characters(.h)
ability(.h)
Hero_Status_Info(.txt)
Enemy_Status_Info(.txt)
Ability_Status_Info(.txt)


とりあえず、新年明けましておめでとう。
今年のガキ使はまぁまぁおもしろかったな。
でも新たな仕掛けが出てきて良かったと思う。
拳銃なんかはそうなるんだろうなとわかっていたけど笑ってしまった。
僕はけっこうメンバー同士で笑いを仕掛けているところが好きなんだ。
あとは笑いを我慢しまいと硬直している姿なんかが。
それにしても何なんだろうあの田中の引き出しを開けるときの顔は。
おかしくてたまらない。
ダービーのときの浜田を選んだときの理由が「気持ち悪いから」には思わず吹いたなぁ。
今年も笑いをくれてありがとう。


そんなわけで本題ですが、前回の記事にもこんなものを作ろうとしていると書いたのですが、ちょっと本格的に始動しました。
まずは、主人公や敵などのキャラクターの情報をどのようにまとめて、どのように取得しようかと考え、結局構造体を使うことにしました。
構造体はヘッダーに書いて、ソースファイルからインクルードすることで引き出せるようにしています。
そしてまた別のファイルにメンバの値などの情報を書いて、 fscanf などを利用しそれを取得しようと考えております。
値などは簡単に読み取れますが、名前は他にも文字があるので読み取りづらいです。
ですのでランダムアクセスを可能にする fseek などを利用して名前などの文字を取得します。
そしてこのように別のファイルに値などの情報を記憶させておくことでデータのセーブ・ロードを可能にしたいなと考えています。
ちなみに今回は記憶ファイルにテキストファイルを利用しているのだが普通市販されているようなゲームではどんな風にしているのでしょうね。

結構本格的ですが、所詮コマンドプロンプト上で実行していくのでグラフィックなどには期待しないでください。
あくまでもプログラムの質が重要だと思っているので。
ちなみに今一番どうしようかなぁと考えているのはマップやダンジョンの壁の表示をどのようにしようかなということ。
描画できる範囲は決まっているし、その範囲内の小さいダンジョンマップなんて作る気はないのでけっこう悩んでいるのだ。
イメージ的にはマップの全体像なんかがあって、範囲内に入っている部分のみ描画させるという風にしたいなと考えているのだが、大丈夫だろうか。
まぁやってみるより他ないですよね(笑)

ちなみに戦闘へは敵と接触することで移行するようにしたいと思っています。
接触したら例の継ぎ目アニメーションを流してやりたいな。
あと、主人公が敵の目視できるようなエリア(半径 3 ぐらい?)に入ったら、敵は主人公を追いかけるようにします。
エリアを抜ければ追いかけてきません。
エリアは形てきにはがくがくの円になるか、もしめんどくさいようならば正方形にでもしようと考えています。
初めての本格的ダンジョンゲームですので十分です。


自分の記事を自分で読んで思うのだが本当に僕って文章作成能力が欠けているよなぁ・・・。

拍手[0回]

author : gp-hss ×

battle_seam_test(.cpp)
battle_seam_test(.exe)


ポケモンとかでもありますが、戦闘直前の継ぎ目の部分をプログラミングして再現してみました。
現在、RPG(ダンジョンゲームみたいな)のようなゲームを作ろうかなぁ~と考えており、そこでテストプログラムとして戦闘に行く前の継ぎ目の部分を作ってみました。

最初のアニメーション部分の描画方法は、まず真ん中に ■ を出力したあと、 ↓ ← ↑ → の順に ■ を出力させています。
どの方向に ■ を出力させるかの判定方法として switch 文を利用しています。
ちなみに、整数を 4 で割った余りは 0 , 1 , 2 , 3 のうちのどれかですよね。
ちょうど方向の数(下、左、上、右、計 4 つ)と同じ数の値が生成できます。
ここでは順番に 0 = ↓ , 1 = ← , 2 = ↑ , 3 = → というふうにしています。
列挙体を使って数値に名前をつければよりわかりやすくなります。
次に考えなければいけないのが、その方向にいくつの ■ を出力させるかということです。
この問題は頭だけで考えると解決するのに時間がかかってしまいそうです。
ですので、いらない紙と何か書くものを用意して情報を書き留めることで問題の解決を迅速にします。

□ □ □ □ □
□ □ □ □ □
□ □ ■ □ □
□ □ □ □ □
□ □ □ □ □

まず上記のような図を描きます。
では情報を書き留めていきましょう。
 
1 回目は下に ■ を 1 つ出力させます。
 2 回目は左に 1 つ
 3 回目は上に 2 つ
 4 回目は・・・・・・・・・

といった情報を簡単に下記のように書きましょう。

 1 : ↓ = 1
 2 : ← = 1
 3 : ↑ = 2
 4 : → = 2
 5 : ↓ = 3
 6 : ← = 3
 
何か気づきませんか?
というよりもはや自明ですよね。
そうです、 n 回目にある方向に出力した後、 n が偶数なら次から出力する ■ の個数が一つ増えるのです。
もしくは、 n 回目にある方向に出力する前、 n が奇数なら前回出力した ■ の個数 +1 の数の ■ を今回から出力する。
ということになります。
この法則を利用してある方向に出力する ■ の数を求めています。
今回のプログラムでは前者の性質を利用していますので、 偶数 回目に ■ を出力したあと次回から出力する ■ の数を 1 つ増やしています。
ちなみに、 偶数 回目のときは ← もしくは → です。
これもプログラムをつくる上では無駄な処理をさせないためにも重要なことです。

アニメーション描画を終えたらまず画面のすべての ■ を消します。
そしてまた描画します。そしてまた消します。
これを繰り返すことで点滅させています。
ここで重要なのが、点滅描画に移ると ■ の描画方法が変わっているということです。
アニメーション描画の方法だと無駄に処理が多く、点滅描画がうまくいきません(変な点滅になる)。
そこでシンプルな描画方法に変えて全く同じように ■ を描画しています。
 ((SIZE_VAL - 1) / 2) + 1 の意味は自分で考えてみてください。

描画プログラム中、よく Sleep() という関数がでてきますが、これは第一引数にミリ秒単位の整数値を与えることでその時間文処理をストップさせることができます。
描画処理をこのようにちまちまストップさせないと、あまりに描画が早すぎてアニメーションにはとても見えないのです。
点滅描画においても同様です。


そういえば今回は C++ を使っているので多少 C 言語とは違いますが、ほとんど同じですね。
 C++ では、 for 文などの条件部ところで変数を宣言することができます。
今回はそれだけしか C との違いはありません。
ちなみに条件部で宣言された変数はその条件部を持った文の処理が終わったときに一緒に削除されます。
これから勉強のためちまちま C++ も入れていきますので、 C との相違点などは各自ご自分で調べてください。

ではまた今度。

拍手[0回]

author : gp-hss ×

とうとうこいつを解説するときが来ました。
この下長くなりますのでご注意を。

    1.グローバル変数の説明
int x
 ○ または × を置く座標の x 座標値。
 5 , 10 , 15 の x 座標に置くようにする。
それぞれの座標値は画面に出力される表の横に並んだ 1 , 2 , 3 と同じ座標値。

int y
 ○ または × を置く座標の y 座標値。
 1 , 3 , 5 の y 座標に置くようにする。
それぞれの座標値は画面に出力される表の縦に並んだ 1 , 2 , 3 と同じ座標値。

int y2
縦横の数値を入力したあと、「その場所には置けません。(改行)もう一度入力してください。」という表示がされる y 座標値。
正確には「その場所には置けません。」という出力があった座標の y 座標値。

int p_y
第1回目の入力をさせる y 座標値。
または、第1回目に ○ :  と出力される座標の y 座標値。
その y 座標値は 7 。

int x_y[][]
 ○ または × が置かれた座標にどちらが置かれたか記憶するオブジェクト。
 [ 0 ][ 0 ] ~ [ 2 ][ 2 ] までの範囲で数値を格納する。
例えば [ 0 ][ 2 ] に数値が格納された場合、出力された表の横 1 , 縦 3 の位置に ○ か × のどちらかが置かれたことを意味する。

int ans
出力された表からの視点で、縦、横、斜めに値を足したときに生成される値を格納するオブジェクト。
例えば横 1 で縦の場合、 x_y[ 0 ][ 0 ] ~ x_y[ 0 ][ 2 ] の値を足した値が格納される。 
右上がりの斜めの場合は、 x_y[ 0 ][ 2 ] , x_y[ 1 ][ 1 ] , x_y[ 2 ][ 0 ] を足した値が格納される。

int ans_val[]
各ゲーム(試合)での勝敗を表す数値を格納するオブジェクト。

int ans_i
オブジェクト ans_val[] を操作するためのオブジェクト。

int ans_i_j
ゲームが何試合行われたかを表す数値を格納するオブジェクト。


    2.指定した座標へカーソルを移動させる方法の説明
まずは、標準出力のハンドルを得るために HANDLE 型の変数( hOut とします)を宣言します。
次に、カーソルの座標値を扱っている構造体の型をもつ変数( coset とします)を宣言します。
そして、先ほど宣言した hOut に標準出力のハンドルを取得させます。

    hOut = GetStdHandle(STD_OUTPUT_HANDLE);

そしてカーソルの座標値を指定します。

    coset.X = 2;
    coset.Y = 2;

最後に指定した座標値にカーソルを移動させます。

    SetConsoleCursorPosition(hOut, coset);

これで完了です。
型については、こんな型があるんだな程度に思ってください。
ちなみに x 座標において 1 とは 1 バイト分を指しますが、 y 座標においては 2 バイト分の大きさを指します。
つまりカーソルを x 座標において 1 右に移動させた場合のカーソル移動距離を 1 とすると、カーソルを y 座標において 1 下に移動させた場合のカーソル移動距離は 2 なのです。
よって、 y 座標で移動させる場合は x 座標で移動させる場合の 2 倍の距離を移動します。
説明はわかりにくいと思いますが、やってみるとわかります。
何かを動かすプログラミングをするときはこういったことを考慮しなければいけません。
少なくともそのプログラムがコマンドプロンプト上で実行される場合ではのことですが。


   3.main()以外の関数の説明
void output_hyou()
まずは横の表を作っていきます。
 i を 3 で初期化し、 14 未満までの範囲で実行するようにし、1回処理が終わるごとに i に +5 したものを i に格納しています。
よって、それぞれの処理で i は以下のように値を変化させます。
1回目:i = 3
2回目:i = 8
3回目:i = 13
処理後:i = 18
 co.Y は 0 のまま一定ですが、 co.X は 3 , 8 , 13 と値を格納していきます。
それぞれの x 座標で [ kの値 ] を出力させています。
 k は 0 で初期化されていますが、 ++k なので k をインクリメントした値が出力されます。
 k++ だと k の値が出力され、その後 k がインクリメントされます。
 [ は1バイト文字なので数値自体は 4 , 9 , 14 の x 座標値に出力されます。

次に縦の表を作っていきます。
基本的には横の表を作るのと同じです。
 i の変化は、
1回目:i = 1
2回目:i = 3
3回目:i = 5
処理後:i = 7

最後に "横   縦" という文字列を出力します。
 ( x , y ) = ( 7 , 7 ) の位置から出力させています。


void output_setumei()
まずは i の変化について見ていきましょう。
1回目 ~ 5回目:i = 1 ~ 5
処理後:i = 6
カーソルの x 座標値は一定です。
文字列を出力するまえにカーソル位置を移動させています。
ここでどの文字列を出力させるかの選定で switch 分を利用しています。
 i = 1 のとき、 ( x , y ) = ( 55 , 1) の位置から文字列が表示されます。
つまり、行ごとにどの文字列を表示させるか選定しているわけです。


int maru_batu(int count)
この関数は勝敗判定のための関数です。
勝敗が定まったときには指定の場所に勝敗結果通知を出力します。
では上から順に for 文を見ていきましょう。

1.
 i と j はそれぞれ 0 ~ 2 までの値をとります。
i = 0 : j = 0 ~ 2
i = 1 : j = 0 ~ 2
i = 2 : j = 0 ~ 2
という具合で値をとっていきます。
そして、 x_y[ j ][ i ] というふうになっています。
よって、 x_y[ 0 ~ 2 ][ i ] の値を足した値を ans に格納しています。
つまりこれは、 「横」 の勝敗判定だということがわかります。
ans が 3 なら ○ が勝ち 、 -3 なら × が勝ちというふうになっています。
なぜかというと、○ が入力された場合はその座標を表す x_y[][] に 1 を、× の場合は -1 を格納しているからです。
勝敗がついたならば return 1 、つまり 1 を返します。
勝敗がついていない場合は、最後に ans を 0 で初期化しています。
ここで初期化しないと、複合代入演算子による演算でどんどん ans の値が増えていってしまうだけですね。

2.
これは前回の for 文の i と j が逆になっているだけですね。
つまり、 「縦」 の勝敗判定だということがわかります。
それ以外は前回の for 文と全く同じです。

3.
この for 文とセットなのは2つの if 文と1つの式文です。
これは、右下がりの 「斜め」 の勝敗判定です。
右下がりの場合、 x_y[ 0 ][ 0 ] , x_y[ 1 ][ 1 ] , x_y[ 2 ][ 2 ] ですね。
だからまず for 文でそれを足した値を ans に格納しているのです。
あとは前の for 文と全く同じです。

4.
この for 文では最後の右上がりの 「斜め」 の勝敗判定です。
この場合、x_y[ 0 ][ 2 ] , x_y[ 1 ][ 1 ] , x_y[ 2 ][ 3 ] が該当しますね。
ということで、 for 文に行く前に j を 2 で初期化し、for 文の中でデクリメントしています。
そしてすべての値を足した値を ans に格納しています。
あとは前の for 文と全く同じです。


最後の if 文では引き分け判定をしています。
 count が 10 のとき ○ と × で埋め尽くされているということになりますので、引き分けとなり 1 を返します。
なぜ 10 なのかは後の関数の説明で明らかになります。


最後に勝敗がつかなかった場合には 0 を返しています。 


void input_marubatu()
この関数は入力に関する制御から勝敗判定まで一挙に取り扱っています。
まず 入力をさせる回数は 9 回だけでいいというのは分かりますね。
なぜなら ○ と × を置けるところは 9 個しかないのですから。
ということで、この for 文は 9 回繰り返されるようにしています。
そして i が偶数か奇数かで ○ を入力させるターンか × を入力させるターンかを決定しています。
今回は偶数なら ○ のターンとなります。
ここでは分かりやすく i を 2 で初期化しています(本当は 0 です)。
○ のターンであれば ○ を出力させる関数をもってきています。
そして勝敗判定をする関数を呼び出し、勝敗がついたら for 文を抜けて関数を終了させるようにしています。
× のターンも同じことをさせています。


void output_maru()
まずは入力項目を出力させています。
 p_y ははじめ "横   縦" が出力された y 座標と同じ値をもっています。
つまり、まず "横   縦" が出力された行の先頭にカーソルを置き、文字列を出力させる前に改行文字を使って改行したところに項目を出力させています。
改行することで、次の行の先頭にカーソルが移動されます。
改行したことで1つ下の行にカーソルが移ったので p_y + 1 としています。
そして、なぜカーソルを x 座標 8 と 13 にしているのかというと、これはただ単に"横"という文字と"縦"という文字の位置に合わせるためです。
こうすることによって、プレイヤーは何の値を入力しているのか明示的になります。
最後に p_y++ することで次からは1つ下の行で今やったことをさせています。

次の if 文では横1 ~ 3 、縦1 ~ 3 までの範囲外の値が入力されたとき、指定の場所にその場所には置けないのでもう一度入力するように促す文を出力させます。
最初の if (p_y == (y2 + 1)) では今から出力する促し文が前回出力した促し文と重複していないか判断して、重複している場合は、前回の促し文を消す関数を呼び出しています。
促し文は入力項目と同じ y 座標値に出力しますので重複している場合は p_y == (y2 + 1) となるのです。
促し文を出力した後は goto 文で先ほどの入力のところまでプログラムの処理を戻しています。

次の if 文ではすでに何かが置かれていないかどうかを判定して、おかれている場合は重複の確認と促し文の出力をしたあと goto 文で戻しています。
 ○ か × かがおかれている場合、必ずその位置を表す x_y[][] に 1 か -1 が格納されています。

入力が正常に終わったならば、その座標を表す x_y[][] に ○ 表す 1 を格納しています。
例えば、横[1] , 縦[2] に ○ がおかれた場合、 x_y[ 0 ][ 1 ] に 1 が格納されます。

次に x と y に格納された値を専用の座標値に変換する関数を呼んで、そのあとにその位置にカーソルを移動して ○ を出力しています。


void output_batu()
ほとんど先の関数と同じで、相違点は入力が正常に完了したあとにその位置を表す x_y[][] に × を表す -1 が格納されるということだけです。


void cls_new()
この関数では空白で埋め尽くすことによって文字などを消しています。
行ごとに文字列を消すのに十分な量の空白を上書きしていきます。
ちなみにこの関数は、前の試合の勝敗と説明のところ以外を消すのに使います。
たとえば、もう一度ゲームをするときなどに使います。


void cls_string()
重複した促し文を消すのにつかいます。
促し文が表示されている座標を指定して、そこに空白を上書きしています。


void output_before()
前回の試合の結果を指定の場所に出力します。
前の試合の勝敗は別のオブジェクトに記憶させているので、条件分岐でどれを出力させるか指定しています。


void cls_new_be()
前の試合の結果の表示を消すために使います。
勝敗を記憶するオブジェクトは過去5回までしか記憶できないので、6回目からはまた新たに記憶するオブジェクトの先頭から順に勝敗を記憶させていきます。
よって、表示もまた新たに1から作っていかなければならないのです。


void x_y_val()
x , y の値を専用の座標値に変換します。
x の場合は座標値と横表の値との関係性を利用して変換していますが、 y のように switch をつかって変換させるのが簡単です。
ただ取りうる値の範囲が広いときは switch だと面倒なので、前者のような関係性や法則性を利用するわけです。


    4.main関数の説明
とりあえず WORD 型の変数の宣言は無視してください。
不必要です。
do によってもう一度試合をするときはもう一度この処理をさせるようにさせています。
最初の if はもう一度処理をさせる場合のオブジェクトの初期化です。

次に順次関数を呼び出して処理を進めさせます。
そして勝敗がついて main() に帰ってきたらその勝敗の結果を専用のオブジェクトに格納します。
最後に終了する場合の説明ともう一度する場合の説明を出力して getchar() で入力された情報を取得しています。




あぁ~疲れた。
もう無理・・・・・。
ていうか後半ものすごく雑だなぁ。
説明も下手だし。
ということでわからないところがあったらコメントください。
個別に返答していきます。

ではまた今度。

拍手[1回]

author : gp-hss ×
忍者ブログ | [PR]
 | PAGE TOP
write | reply | admin