言語:C全般
環境:VC++ 2008 EE
3DCG:Softimage Mod Tool
ゲームプログラマー目指して勉強している者です。
現在 C++ 修得にむけて頑張っています。
Began study since 2009/8/21
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
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 との相違点などは各自ご自分で調べてください。
ではまた今度。
ふぅ~む。
だめだなこりゃ。
そもそも無理があったんだ。
浮動小数点数の精度で表すことのできる桁を超えちゃってるから・・・・。
どうもです。
どうやらこのシリーズも Part.4 に行く前に終わっちゃいそうです。
当然のことながら時間というものは小さければ小さいほど動きが激しいですよね。
つまりは、小数点以下が低ければ低いほど数値の動きが激しいということです。
しかも、何度か計測時間を比較したところどれもナノ秒以上の桁はちゃんとあります。
しかし最後の桁がほとんど 8, 6, 4, 0 のどれかで値をとっているのです。
そこで思いました(遅すぎですが)。
こりゃ~浮動小数点数の精度を超えちゃってるなと。
double型 で保障されている精度は 6桁 までです。
ですので余裕で超えちゃっているわけです。
だから少数点以下が低ければ低いほど精度が低いのか・・・。
ちなみに long double の型の変数に格納してもさして変化はありません。
(精度は処理系依存だと思うので、あなたのパソコンでは違うかもしれません。)
だから、たぶん小数点以下中ごろから数値が狂ってきていると思われます。
たぶん数値が丸められているのかな?
ゆえに、各桁の数値がかぶってきてしまうのです。
でも、一応これに対処するためプログラム中ではどの桁から数値をとってくるか変えています。
それでもあまり変化はありません。
これが第一の問題です。
第二の問題には、いくら処理時間が違うとはいえ小数点以下上位の数値は同じようなものになってしまいます。
だからこそ、小数点以下下位の方から数値を取ってきたいのに浮動小数点数の精度を超えてしまっているせいで値が丸められます。
ゆえに、同じような数値ばかりが返ってきてしまうのです。
よって生成される乱数も同じような数値ばかりになってしまいます。
例えば、乱数を生成する範囲を 0 ~ 100 までとした場合、1桁の乱数は 1 ばかりが頻出します。
2桁の乱数も同じような数値ばかりが頻出します。
よって、ものすごく精度の悪いものになってしますのです。
これらの問題から無理があるなと感じました。
少なくとも今の僕の実力では解決することは難しいです。
しかし!
今回のプログラミングは大変勉強になりました。
いろいろな知識が得られたと思います。
これからはたぶんゲームを作っていきますのでどうぞお見届けください。
(てぇ~ことは結局のところ Part.4 はお目にかかんないの?・・・・・・・・・。)
test(.exe)
どうもです。
今回はコンピュータの処理時間を取得してそれを乱数にしてしまおうという目論見のもとプログラミングしていきました。
全く同じ処理でも絶対に同じ処理時間ではないだろうと思っていたのでやってみようということになったのですが、この場合取得できる時間が秒単位では精度が悪すぎます。
ですので、まず高い精度で時間を取得できる関数を探しました。
検索したところいろいろあったのですが、少なくともマイクロ秒(100万分の1秒だったと思う)ぐらいはほしいので、 gettimeofday などの関数を使おうかと思ったのですが、
'sys/time.h':No such file or directory
つまり、インクルードしなければいけない sys/time.h が存在しないのです。
一応自分でも sys フォルダ内を見てみたが、似たようなのはあるのだが確かに time.h はなかった。
別の方法を探すことにしましょう。
そこで見つけたのがパフォーマンスカウンタの現在値を調べるものでした。
「パフォーマンスカウンタとはなんだ!?」
CPUの内部に搭載されている、CPUクロックを元にカウントしているカウンタのことである。
こいつはパソコンに電源が入ると同時にカウントを始める。
よって、「計測したい処理を開始する前のカウンタの現在値」※1と、「処理が終わった後のカウンタの現在値」※2を取得することで処理中にどれだけカウントしたかが分かる。
求め方は ※2 から ※1 を引くだけでよいのです。
あとはその差分をカウンタの1秒あたりの周波数(クロック周波数)で割ることで秒単位の時間が得られるわけです。
では、それぞれを値を得る関数を紹介します。
QueryPerformanceCounter()
QueryPerformanceFrequency()
それぞれカウンタの現在値と周波数を得る関数です。
それぞれの関数には数値を格納するための変数のアドレスを渡します。
その変数の型は LARGE_INTEGER 型となります。
ちなみに LARGE_INTEGER は大きな整数を扱う型(ていうか共用体です)なのですが、大きすぎて LowPart , QuadPart などという風に分けられていていろいろ面倒なので、今回は __int64型 を使います。
(この型についての詳細は自分で調べてみてください。)
よって、
__int64 start, end, freq;
QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
QueryPerformanceCounter((LARGE_INTEGER *)&start);
//計測したい処理
QueryPerformanceCounter((LARGE_INTEGER *)&end);
という風な感じになります。
あとは、カウンタ値の差分をクロック周波数で割ります。
その値を格納する変数を pastmsec とします。
さらにそれに 1000 をかけたものを rand_val としたのが、上記の test.exe です。
(試験ファイルですので 30 回出力させています。)
しかし、このままでは乱数としては精度が悪すぎるのです(同じ数や似たような数値ばかりがでてしまう)。
そこでとりあえず、出力する数値が直前に出力した数値と同じ場合は出力しないようにしています。
ですので1桁の数字がないところがところどころあるかもしれませんが、こういうことなので気にしないでください。
ちなみに計測している処理は、 1千万回の空文 です。
2つの独立した動作を実現するためにどうやって入力待ちの状態を改善するかいろいろ考えたが、入力待ちの状態にする前に入力があるかどうか調べることができれば大変便利だということで、いろいろと探してみたらぴったりの関数がありました(ほかにもいろいろとあると思います)。
その名も kbhit です。
一応 ケービーヒット なんて発音してるんですがどうなんでしょうか?
こいつは標準入力ストリームにデータがあるかどうかを調べ、データがあれば 1 を、なければ 0 を返します。
こいつを使うには stdio.h か stdlib.h をインクルードしなければいけません。
使い方としては、
if(kbhit())
ch = getch();
などのように使います。
ではこの関数を使って改良したボールよけゲームをすぐにでも作ります。
プログラミング言語ではじめて作るプログラムとして定番な Hello World です。
とりあえず、C で当たり前のように使っていた stdio.h のような位置にいる(?)のが iostream です。
ちなみに iostream ではなく iostream.h(.cpp) のこともあるようです。
そして #include のあとには using namespace std がいるようです。
画面に出力する関数みたいなのが cout で、これ自体は標準出力ストリームをさすものです。
こいつに対象を流すためのものが挿入子(出力ストリーム演算子)である << です。
全体としてはこうなります。
cout << "Hello World\n"
逆に cin は標準入力ストリームをさしており、抽出子 >> というものを使い
cin >> x
というような感じになります。
疑問なのが char型 の変数に格納されたものを表示するときなのだが。
例えば、
char ch = 'C';
cout << ch;
とすれば C が表示されるが、もしこの文字の数値(アスキーコードによる)を表示させたい場合どうすればいいのか。
一応予想としては int にキャストすればいいのかなーとは思っているのですが・・・。
ちょっと試してみよう・・・・・・・・おぉ~予想通りの結果になった。
だがこれだといちいちキャストしないといけないのだろうか?
別に苦にはならないが、もしかしたら何か他の方法があるかもしれないのでとりあえずは様子見で。
ちなみに .cpp ファイルをコマンドプロンプトでコンパイルする場合は単純に cl ○○.cpp としても変な警告がでて気味が悪くなる。
実際には EHsc というオプションが必要だそうだ。
cl /EHsc ○○.cpp
気をつけないとコンパイルしようとしたときにもたつくことになる。
まだまだC++のさわりのさわりのさわりなので今後からはC++がメインになると思います。
ただ作品なんかや覚書ではまだ C のほうでやってないことなどが多すぎるので、そちらのカテゴリーは C がまだ占拠しそう。
ではまた今度。