2009年2月19日木曜日

気持ちにゆとりを

C言語で、
printf("%02X¥n", 0x80);

とやると、普通に
80

と表示されます。

ところが、
char ch = 0x80;
printf("%02X¥n", ch);

とやると、アレレ
FFFFFF80

と表示されてしまいます。

実はこれ、chが signed charで宣言されているのが原因ですね。
unsigned char 0x80;
printf("%02X¥n", ch);

とやると、ちゃんと
80

と表示されます。

今日ちょっと仕事が溜まって焦ってた時にこれでハマってしまいました。いや、冷静になって考えればすぐにわかるようなことなんですけどね...仕事は気持ちに余裕をもつことが大事ですね...と。

2009年2月16日月曜日

ステートパターン


ここのテトリスでは、メニュー画面、プレイ画面、ハイスコア画面の管理にデザインパターンの一つであるステートパターンが使われています。ステートパターンはゲームの設計手法としてはよく使われているようですね。

ステートパターンの特徴としては、単一のクラスで状態の変化に応じてif文やswitch文を使ったコーディングをするとコードが徒に長く読みにくくなるのを、窓口役と各状態に応じたクラスに分割し、ポリモーフィズムをうまく使って、コードをすっきりと保守しやすいものにできるということでしょうか。
ここでは、以下のような構成になっています。

CStateManager


現在のステートを保持し、Drawメソッド等、外部(ウィンドウクラス)とのインタフェースとなるメソッドや、ステートを変更するChangeState()が定義されています。

CGameState


ステートのインターフェースを定義するクラスです。コンストラクタで渡されるCStateManagerのインスタンスを保持します。

CMenuState


メニュー画面を管理するクラスです。CGameStateを継承します。

CPlayState


テトリスプレイ用のゲーム画面を管理するクラスです。CGameStateを継承します。

CHighScoreState


ハイスコア画面を管理するクラスです。CGameStateを継承します。

CMenuState、CPlayState、CPlayStateの各クラスは、CGameStateから継承したDrawメソッドを実装しており、CStateManagerのDrawメソッドから委譲される形で呼ばれます。
各ステートへの変更は、メニュー画面でのメニュー選択やゲーム画面でのESCキーの入力に応じて、現在のステートから、繊維先のステートへCStateManagerへ依頼する形で実現されます。このChangeStateメソッドは次のようになっています。

void ChangeState(CGameState* pNewState)
{
if (m_pActiveState)
m_pActiveState->LeaveState();
m_pActiveState = pNewState;
m_pActiveState->EnterState();
}

ここで呼ばれている、LeaveState()とEnterState()もCGameStateで定義されていますが、これもセオリー(よくある)パターンですね。
CGameStateからは次のように呼ばれます。

m_pStateManager->ChangeState(pNewState);

例えばCMenuStateからは次のように呼ばれます。

switch (m_iCurrentSelection)
{
case 0: // 新規ゲーム
if (!m_pCurrentGame)
m_pCurrentGame = CPlayState::GetInstance(m_pStateManager);
m_pCurrentGame->Reset();   // 前回内容をクリアして
ChangeState(m_pCurrentGame); // ゲーム画面へ
break;

case 1: // ゲーム再開
if (m_pCurrentGame && !m_pCurrentGame->IsGameOver())
ChangeState(m_pCurrentGame); // 途中のゲームがあれば再開
break;

case 2: // ハイスコアの表示
ChangeState(CHighScoreState::GetInstance(m_pStateManager));
break;
:

ってぐだぐだ書くより、図で説明した方がわかり易いんですけどね。(^_^;

2009年2月11日水曜日

ソース解析

今日は、参考のためにここのソースを読んだり、クラス構成を調べたりしました。

で、その時にソースの解析に、Doxygenを使ってみました。

Doxygenはご存知の方はご存知と思いますが、ソースを解析してhtmlやPDFのドキュメントを生成してくれるツールです。クラス図やコラボレーション図なんかも生成してくれます。C/C++だけでなく、JavaやPHP、C#等にも対応しています。

というわけでその時のメモをば、

以下、作業はWindowsでやっています。

インストール



DoxygenからWindows用のインストーラをダウンロードしてきて、インストールします。

また、図を生成するためには、Graphvizも必要なので、これもダウンロードしてインストールします。

設定



インストールが終わったら、Doxygenと、GraphvizのbinディレクトリにPATHを通しておきます。

使い方



以下、Dos窓で作業します。

適当な作業ディレクトリで
doxygen -g

とすると、Doxyfileという設定ファイルの雛形が作成されます。設定ファイル名は指定も可能です。

生成された設定ファイルを適当に編集します。
PROJECT_NAME = プロジェクト名
INPUT = ソースパス # 解析するソースの場所

OUTPUT_DIRECTORY = 出力先 # ブランクの場合はカレント

OUTPUT_LANGUAGE = Japanese # 出力言語

EXTRACT_ALL = YES # doxygenの書式で書かれていないソースを適当に処理させる

EXTRACT_PRIVATE = NO # プライベートメンバも出力する場合はYES

EXTRACT_STATIC = NO # スタティックメンバも出力する場合はYES

EXTRACT_〜 # 他いろいろ



GENERATE_HTML = YES # HTMLの出力

GENERATE_LATEX = NO # Latex用の出力をする場合はYES



HAVE_DOT = YES # graphvixを使って図を描かせる場合はYES


※これ以外にDoxywizardというGUIなツール(一緒にインストールされます)を使って設定することも可能です。

実行
doxygen

作業状況がコンソールに表示されます。特にエラーが表示されなければ成功です。

生成されたindex.htmlを見てみましょう。

クラス階層や関数の呼び出し順序など見やすく整形してくれてます。いやー、これ便利ですね。

もっと便利に使うためには、やはりDoxygenの記法に沿ったコメントの記述を心がけねばですね。

2009年2月8日日曜日

テクスチャ

今日は四角形にテクスチャを貼り付けてみました。貼り付けたのはGIMPについてた素材をpng形式で保存した画像です。



実はOpenGL自体には、画像の読み込み機能はありません。なので画像ファイルの読み込みは、自分で作成するか、どっかのライブラリの機能を使って実装することになります。ここでは、DevILを使わせて頂きました。

で、ダウンロードしたファイルに含まれていたマニュアルに従ってやってみますがうまくいきません。実行時に、拡張子が正しくないとか言われてしまいます。pngは標準(Windows版)でサポートされているはずなのでそんなはずはないのですが、で、よくよく見てみるとどうやらファイル名がユニコードで渡されているために、ファイル名が正しく認識されていないようなんです。そういえばユニコード文字を使う設定にしておりました。(日本語を使う関係上仕方がないのですが...)さらによく見ると、DevILのダウンロードしたファイルの中にちゃんとユニコード版も含まれておりました。こちらをリンクしてやったらうまくいきました。めでたしめでたし。

ところで、DevILの使い方をいろいろ調べてる最中に、こんなOpenGLのチュートリアルを見つけました。ここ、(・∀・)いい!です。英語ですが、クラスベースのウィンドウや、ウェイトをいれたゲームループ(ウェイトがないとCPUを余計に使っちゃう)の作成方法や、簡単なゲームエンジン(2Dですが)とそれを使ったサンプルを丁寧に説明してくれてます。実は当初の予定を中止して、今日はほとんどここを読んでいました。個人開発ではよくあることです。(^_^;

暫くは、ここのサンプルに合わせていろいろ基本クラスを整備してこうかなと思ってます。

このサイト、探してみると他にもいろいろ有意義な記事がありそうです。The Code Project Open License (CPOL)って恥ずかしながら今まで知りませんでした。ありがとー。ここに書いてもしょうがないけど。

2009年2月5日木曜日

ウィンドウ


今日は特にこれといった進展はありません。というか、やること多すぎて、何から手をつけていくべきか思案中なのです。

とりあえず、気晴らしに四角形を表示させてみました。なんでかというと、ゲームを作るうえでは、色々なパラメータを設定したりする画面が必要になると思うんですが、当然ながらこれも手作りになります。つまり、UI(ユーザインターフェース)のデザインも機能も自分で用意しなきゃならんのです。めんどくせー。でもま、OSが提供する素の部品が使えたとしてもゲーム用としてはなんだか素っ気無いし、どっちみち作らないといけないのです。

で、その時に気をつけないといけないのが、特に3Dのゲームの場合は、ゲームのキャラクターや彼らが行動するゲームシーンの描画とUIのウィンドウの描画を分けて管理しないといけないということです。3Dのゲーム世界には上下左右の他に奥行きがあります。遠くのものは小さく、近くのものは大きく見えるという遠近感があります。だけどウィンドウには奥行きは必要ありません。画面の上下、左右にだけ動かせれは通常は問題ありません。(最近は3Dデスクトップなどというものがあったりしますが、ここでは無視ということで...)

で、この遠近感のある描画(透視投影)とそうでない描画(正投影)を切り替えるのが以下の処理になります。

透視投影

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, width/height, 0.1f, 100.0f);


正投影

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, width, 0.0, height);

それぞれ投影用の行列を初期化した後、変換の設定をしています。

gluPerspective(...)の方は、視野角と投影面の縦横比(画面サイズに合わせています)と奥行きの範囲を指定しています。

gluOrtho2D(...)の方は単純に、画面の横と縦のサイズを指定しています。

実はこれらはgluというプレフィクスで始まっていることからわかるように、GLU(OpenGL Utility LIbrary)の関数なんですね。GLUはOpenGLの低レベルなコマンドを使って便利な関数を提供してくれています。OpenGLを使った開発を行う場合には必須のライブラリといってもいいと思います。OpenGLの投影用のコマンドとしては、glFrustum(...)やglOrtho(...)という関数があるんですが、ここは素直にglu〜の方を使っときましょ。

上の画像の立方体が透視投影で、薄緑の矩形(仮のWindowということで)が正投影で描画したものですが、ちょっとわかり難いですね。あと、少し透かしています。

つか、仕事が忙しくてあまり時間がとれない。土日にがんばるかな。はぁ...

2009年2月4日水曜日

atofの罠

今日もOpenGLとは関係ない、仕事中に遭遇したことです。

数字の文字列を実数に変換するatof()という標準関数があります。で、この関数のmanを見ると、stdlib.hをインクルードすることになっているんですが、何故かstdlib.hをインクルードしなくても、コンパイルできちゃいます。これでハマりました。

次のようなソースを

#include <stdio.h>

int main() {
double d = atof("1.2345") * 100000;
printf("%f¥n", d);

return 0;
}


Linuxのgccでコンパイルして実行すると、
hoge@xxxx:~/dev/c$ ./test
-31200.000000

などという変な値を返してきます。

これにstdlib.hをインクルードしてコンパイルすると
hoge@xxxx:~/dev/c$ ./test
123450.000000

と、ちゃんとした値を返してくれます。

最初、これに気づかず小一時間も悩んでしまいました。なんなんだろ、これ、stdlib.hをインクルードしない時って、atofの実体としていったい何がリンクされているんだろ???

これって既知の問題なんでしょうか? かれこれC言語を十年以上も使っていますが、初めて遭遇しました。

ちなみに、VisualC++でコンパイルしてみたら、stdlib.hをインクルードしない場合はエラーを返してくれました。

2009年2月2日月曜日

Skypeの罠

今日はOpenGLの話じゃありません。

仕事でPHPを使った開発をやったりするんですが、ソースを書いてそれをいちいちテスト用のサーバにアップロードして検証するのも面倒なので、自分のPCでApacheを動かして検証してみるといったことをよくやります。で、今日も久しぶりにApacheを自分のPCで起動して、とりあえずhttp://localhost/にアクセスしてみますが、あれれ、いつも出てくるApacheのWelcomeページがでてこない。かといってNot Found(404)になるでもなく...いろいろ思考錯誤してみるも、うまくいったと思ったら、また見られなくなったり。試しにポート番号を変えて起動してみたら、問題なく繋がります。そこで、Apacheを停止して、

netstat -ano
して使用されているポートを調べてみると、案の定80番ポートが使用されていました。

プロセスIDから突き止めると、どうやら犯人はSkypeのようです。

どうも設定でSkypeのポートを変えられるみたいです。

Skypeの「ツール」メニュー→「設定...」→「詳細」→「接続」で、
「上記のポートに代わり、ポート80を使用」のチェック外す。
この後、Skypeを再起動して、やっとApacheが使えるようになりました。めでたしめでたし。

しかし、Skypeで80番ポートを使ってるとは盲点でした。

2009年2月1日日曜日

日本語入力



クロスプラットフォームで作りたいと言った端からいきなり機種依存なところです。(^_^;

普通にWindowsアプリとか作る場合は、OSやコントロールが標準で提供してくれる機能を使って普通に日本語入力できるんですが、OpenGLみたいなグラフィクスライブラリを使って一からアプリを作る場合(特にフルスクリーン対応の場合)は、文字の入力機能も自前で作らなければなりません。それ以前にまず、描画するためのウィンドウを用意しないといけません。OpenGLを使う場合の定番といえば、glutというクロスプラットフォームなウィンドウシステム用のライブラリがあるんですが、本格的に使うには少し機能が貧弱なのと、日本語入力に対応していないという問題があります。他にもSDLというこれまた有名なライブラリもありますが、こちらもどうやら日本語入力に対応していないようです。

ということなので、ここは涙を飲んで、おもいっきし機種依存のコードを書くことにします。WindowsだとWindowsAPI、Macだと、CocoaとかCarbon、Linuxだと、GTK+とかQT使ったり直にXlib使ったりでしょうか。ひとまずここは馴染みのあるWindowsでやってみることにします。

というこで、久しぶりにWindowsAPIと格闘です。といっても一から書くのは面倒くさいので、これまた有名なNeHeから適当にテンプレートを拝借してきました。ついでにフォント表示についてもここのチュートリアルを参考にしました。

日本語入力に対応するためには、WM_IME_NOTIFYとか、WM_IME_COMPOSITIONなどのIME用のメッセージに応答しなければなりません。http://www.kumei.ne.jp/c_lang/sdk3/sdk_278.htm をとっても参考にさせて頂きました。m__m

...んで、悪戦苦闘の末、なんとか入力できるようになったんですが、OSが自動的に出す変換候補のリストを出さないようにすることができません。これを出さないようにしないと特にフルスクリーンの場合画面がちらついてしまいます。ちゃんと、メッセージ補足してデフォルトの処理を呼ばないようにしてるはずなのに、うまくいかない...と諦めかけていたところ、この記事を見つけました。そもそもやり方が間違ってたんですね。

デフォルトの処理を呼ばないようにするんじゃなくて、フラグを落としてデフォルトの処理を呼ぶのが正解と...


case WM_IME_SETCONTEXT:
{
// return 0; <== これじゃダメ
lParam &= ~ISC_SHOWUIALL;
break;
}
:
return DefWindowProc(hWnd, uMsg, wParam, lParam);


ったく紛らわしい。

とりあえず、リスト表示はされなくなりました。まだリスト表示っぽいタイミングで画面のちらつき(フルスクリーンの場合)があったりとか、フォントをビットマップ描画しているので粗いとか、入力の都度ビットマップ生成しているのでキャッシュとか使うようにしないと効率悪いとか、いろいろ問題があるんですが、まぁ今日の所はひとまずよしとしときます。

はぁ、ちかれた。