スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

DirectX9.0備忘録(日本語描画編)

DirectX9.0環境で日本語の描画の実装について。

その67 改・高速フォント文字
http://marupeke296.com/DXG_No67_NewFont.html


その67 改・高速フォント描画:サンプルプログラム
http://marupeke296.com/DXGSmp_No67_NewFont.html


フォントを用いた描画に関しては、既にこちらのサイトにしっかり書かれているので、
ここでは「このサイトを使って、自分のゲーム用のライブラリにどう起こしたか」を中心にメモしておきます。

作業の予定

いきなりクラス化するのは考える事が多すぎるので、とりあえずサンプルをコピーし、フォント描画の部分だけ取り出す。

フォント描画の部分を切ったり貼ったりして、自由な文字列を描画できる関数を作ってみる。

描画関数の引数などから、クラス設計を考える。

実際のゲームプロジェクトで、設計を元にクラス化する。


実作業ログ

サンプル用の新規プロジェクト作成。DirectX用のディレクトリ設定を毎回やるのは面倒なので「DXSandBox」と命名し、今後はこのプロジェクトでDirectX関係のサンプルを動かしたいと思う。

サンプルをコピペして、動作確認。特に問題はなかった。

サンプルは1文字の描画なので、どうすれば2文字以上描画出来るか調べる。

とりあえず描画に必要な情報を構造体にまとめてみる。

文字コードから、その構造体に描画情報をセットする関数を作る。

構造体で複数文字対応

DX文字列1

生成した文字画像とその設定を保持するFontCharacterクラス、
フォントの設定と、そのフォントで生成したFontCharacterを保持するFontクラスを作り、
FontクラスのDraw関数を呼ぶと文字列が描画されるようにした。

Font MSGothic;
MSGothic.Init( "MS ゴシック", 26, FW_BOLD );

MSGothic.Draw( X, Y, "テキスト" );


FontCharacterは、文字コードをキーにして、std::mapで管理。添字アクセス出来ないけど、検索速いからヘーキヘーキ!

DX文字列2


とりあえず文字列は描画できました。

あとは、色を変えたり、枠線つけたり、文字送りとか実装できたら完璧。


・悩んだ所

!どう関数を分けるか

各種処理の繋がりが分からないので、関数の分け方が悩ましい。

この場合は処理の分け方を考えるより、作りたい関数を決めて、その関数に合わせて処理を分けることで作業が、スムーズになった。

ただ正しい処理の順番にならないことがあるかもしれないので、その時はリファクタリングが必要になるかも。

!フォントテクスチャの管理方法

とりあえず、同じ文字の生成はしたくないので、std::mapを使って、文字コードをキーにして、テクスチャを持っているFontCharacterオブジェクトを保持しました。

std::map< UINT, FontCharacter * > m_FontCharacters;

void Font::CreateCharacter( HDC hDc, UINT nChara ) //文字作成
{
// ----- 文字追加 -----
std::map< UINT, FontCharacter * >::_Pairib p = m_FontCharacters.insert( std::map< UINT, FontCharacter * >::value_type( nChara, (FontCharacter*)NULL ) );
if( p.second )
{ //新しい文字を作る
p.first->second = new FontCharacter( hDc, nChara ); //生成
}
}


FontCharacter *Font::GetFontCharacter( UINT nChara ) //文字取得
{
std::map< UINT, FontCharacter * >::iterator it;
it = m_FontCharacters.find( nChara ); //検索
if( it != m_FontCharacters.end() )
{ //名前があった
return it->second;
}
else
{ //なかった
return 0;
}
}


ただ、一文字一文字で管理してるので、かなり効率が悪そうです。
その辺の管理のうまい方法を模索するか、ID3DXFont使うか…。

!Font.hでFontCharacter.hをインクルードしているのに、「FontCharacterがありません」というビルドエラーが出る。

FontCharacte.hのインクルード文と、Font.hで、両方のヘッダを読み込んでいるコモン的なヘッダをインクルードしていたので、FontCharacter.hのインクルード文を必要なものだけにしたら直った。

!FontCharacterのinline指定した関数が呼び出せない

inlineを消せば直った。

inlineにするとリンクできなくなる
http://social.msdn.microsoft.com/Forums/vstudio/ja-JP/6c754499-f4c2-4fbd-baba-f6c063be229b/inline?forum=vcgeneralja


どうやら、inline指定した関数は、その.cppでしか呼び出せないらしい。

ヘッダにいろいろ書くのはイヤだけど、

class FontCharacter
{
public:

// ----- コンストラクタ・デストラクタ -----
FontCharacter( HDC Hdc, UINT nChara );
~FontCharacter();

int GetNextOffset( void ){ return m_Glyphmetrics.gmCellIncX; } //次の文字をどこから始めるか

IDirect3DTexture9 *GetTexture( void ){ return m_pTex; } //テクスチャ
IDirect3DVertexBuffer9 *GetVertexBuffer( void ){ return m_pVertexBuffer; } //頂点バッファ
D3DXMATRIX *GetLocalMat( void ){ return &m_LocalMat; } //行列

private:

UINT m_nChara; //文字コード

TEXTMETRIC m_TextMetric; //文字情報
GLYPHMETRICS m_Glyphmetrics; //文字配置情報

IDirect3DTexture9 *m_pTex; //テクスチャ
IDirect3DVertexBuffer9 *m_pVertexBuffer; //頂点バッファ
D3DXMATRIX m_LocalMat; //行列

};


こんな感じにして、解決。
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

ゲーム制作チェックリスト

最近は、ゲーム制作をして、そのフィードバックをいただける環境に身を置いています。
その中で、よく指摘される点をリスト化してみました。


□画面内で動きが無い場面がないか

ポーズ画面や、ゲームオーバー画面などで多いです。

動いてる画面から、止まった画面になると、プレイヤーの思考が止まります。「あれ?」ってなります。
テストプレイしている自分では気づきにくいですが、他人にやってもらうとすぐ気付かれます。

カーソルを少し動かすだけでもいいので、常に画面に動きを作る必要があります。


□操作に対して、反応が無い時がないか

出来ない操作をした時に多いです。

おいらの作ったゲームでは、「必殺技を使おうとしたら、発動しなくて、戸惑ってしまった」という意見が散見しました。
ボタンを押して何も反応がないと、これもプレイヤーが「フリーズした?」など違和感を感じさせてしまいます。

ビープ音を鳴らすなどして、プレイヤーに反応しましょう。


□画面遷移に演出をつけたか

自分一人で制作していると気づきにくいです。

場面が変わったことをプレイヤーに強く示すためにも、一フレームで切り替わるようなことはないようにします。


□ゲームの状態遷移に演出をつけたか

「画面遷移に演出をつけたか」と同じく、プレイヤーに難易度が変わったことや、ピンチになったことなど、ゲームの状態もプレイヤーに分かるようにします。

プレイヤーに気付いてもらうのではなく、気付かせるようにしたい。


□やってて楽しいことを、したくなる演出があるか

これも、自分一人で制作していると気づきにくいです。
更に、他の人にやってもらっても指摘されずらいです。

演出を付ける「自分のゲームの楽しいところ」を探す必要もあります。ゲームの楽しいところの勘違いが起こらないように、いろんな人にやってもらって楽しいところを間違えないようにしたいところ。


□タイトル画面で「決定」ボタンが分かるか

キーボード操作のPCゲームは特に気をつけます。

おいらは、「Zキーが決定」が当たり前と思っていますが、「スペースキーが決定」「エンターキーが決定」「Zキーはジャンプボタン」などプレイヤーの「決定ボタン」は多種多様です。

一度分かれば後は楽ですが、それが分かるまでプレイヤーに決定キーを探す総当たり入力を強いることになってしまいます。その間にゲーム終了ボタンなど押されて、ゲームが閉じたりしたら最悪です。


□ゲームのプレイを記録しているか

こちらは製作者側のためのものです。

プレイヤーが自分のゲームをどう遊んでくれたかを記録しておくと、次の製作に向けて良いデータが取れます。

何よりも「自分のゲームが遊ばれた」という、自分の作ったものに自信が持てることが大きいです。

ただ、ダウンロードするゲームだと、プレイデータを送信してもらわないと行けないので、データが得られないこともあるかも…。





もっとあるかもしれませんが、とりあえずこれだけ。

こうしてまとめて見ると、プレイヤーに対してのことばかりですね。

[C#][Lua]LuaInterfaceでコルーチン

昨日に引き続きLua関連。

LuaInterfaceにはサイトでよく紹介されてた、lua_newthreadやらlua_resumeがないので、かなり焦った。
けど、なんとか動くものが出来たのでメモメモ。

おいらにはLuaInterfaceでcoroutineの関数を呼び出すやり方が分からなかったので、
Lua側でそれらの制御をしてもらうことにした。

で、最初はこんな感じのコードになった。

lua
function Update() --~別のソースファイルで内容を書く
end

function StartUpdate()
LuaUpThread = coroutine.create( Update );

com, states = coroutine.resume( LuaUpThread );
return states
end

C#
public void LuaUpdate() //こいつを毎フレーム呼ぶ
{
Console.WriteLine( (string)lua.GetFunction("StartUpdate").Call()[0] );
}


わかる人にはわかるだろうけど、これがとんでもないコードで、なけなしの睡眠時間がこのせいで吹っ飛んだ。
これを実行するとどうなるかと言うと、ずっとUpdateの最初から最初のyieldまでずっとループする。

おかげでキャラクターが同じところを右往左往する事態になり、
最初はキャラクター制御しているコードばかりいじってこれに気づくのに大変時間がかかった。

毎回create呼んで、スレッドを新しく作り直してるのである。あほですね。

lua
function Update()
end

LuaUpThread = coroutine.create( Update );

function StartUpdate()
ThSt = coroutine.status(LuaUpThread);
if ThSt == "dead" then
LuaUpThread = coroutine.create( Update );
end
com, states = coroutine.resume( LuaUpThread );
return states
end

ということで、コルーチンを一つだけ登録するようにした。完璧。動いた。超うれしい。
でも深夜なのであんまり気分が上がらない。

書いてみると、呆気無いなぁ。とても5時間頭を抱える問題とは思えない^q^

参考にしたサイト
コルーチンで状態遷移をLuaで制御
Lua 5.1 リファレンスマニュアル
コルーチン初歩の初歩
Lua/AI/Error
まさかROのWikiを見るとは思わなかった。

[C#][Lua]Luaのあれこれおぼえがき

ゲーム制作の助けになると思い、C#でツール作る練習をしとります。
そして前々から気になっていたスクリプト言語の組み込みとやらをやってました。

組み込むにあたっての手順の覚書となんかハマったところを。

準備

VisualC#2010Expressを使用してます。
1.
LuaInterfaceを落とす。
.NET Framework 4.0に対応したのを、親切な方が作ってくださってたのでそれを使う。
LuaInterface for 4.0

CustomBuiltの方をダウンロードして解凍しとく。lua51,lua511,LuaInterfaceの3つのdllが入ってる。

2.
組み込むプログラムのexeファイルと同じフォルダに、さっきの3つのdllをコピーする。
たぶんbin/Debugかbin/Release

3.
組み込むプロジェクトの参照設定を左クリック→「参照の追加」→「参照」タブ
で、さっきコピーした3つのdllのうち"LuaInterface.dll"だけ追加する。
コピーするのめんどくさがって、3つ一気に追加したら、エラー出まくって数時間頭抱えたアホはおいら一人でいいんや

4.
適当なところで、
Lua lua = new Lua();
と書いて、エラーが出なかったら成功。出たら/(^o^)\

これで準備完了。

luaファイルを読み込む
Lua lua = new Lua();
lua.DoFile( luafile );

でオッケー

DoFileの後にLua側の変数を見たり、関数を使う。
DoFileの前ではLua側にC#側の変数を登録することができる。

用済みになったら、lua.Close();でバイバイ。

Lua側のグローバル変数を見る
LuaInterfaceの偉大な力によって、超簡単に見れる。スタックwwww

var x = lua["x"];

これだけ。

※ただしDoubleに限る。intはキャストできねえぞオラってエラー出るから、Convert.ToIntつかってね☆ミ
ゲームの設定とかはもうこれでバッチリですね。


Lua側の関数を使う
lua.GetFunction("fuction name").Call(par);

引数(par)にはクラスも登録できる。すごいなーあこがれちゃうなー。

Luaでメソッドを使う時はClass:Method()みたいに「:」を使う。

ただ、privateなメンバは勿論だけど、操作できないっぽい。
既存のPointクラスとかを指定するのはNGかな。

戻り値どうすんだおい
でもーウチーPointとかーRectangle使いたいんでー戻り値だけでもーマジ必要なんすけどー
なあなたにもちゃんと対応してるLuaInterfaceさんマジリスペクトッスよ。

object[] returns = lua.GetFunction("func").Call();
var return1 = returns[0];

で問題解決。
戻り値2つ目以降は添字を変えればおk。

引数が1つなら、
var x = lua.GetFunction("getSize").Call()[0];
でもいい。

C#側の変数をLuaで使う
DoFileやDoStringを呼ぶ前に、

lua["a"] = a;

と書いとく。

勿論、引数と同じくクラスも渡せるみたい。
「いろいろ処理するメソッドを書いたクラスを渡して、あとはLuaで制御」なんていいですね。

4/22追記
Luaにプレイヤークラスを毎フレーム渡すとして、

lua["Player"] = p;

と書いて、毎フレーム代入するとメモリリークする(どうやら同名のグローバル変数が作られるみたい)

サンプル
Lua
function Console()
c.str = "(´・_・`)Luaからだよー";
c:WriteLine();
end

C#
public class myConsole
{
public string str;
public void WriteLine()
{
Console.WriteLine(str);
}
}

myConsole c = new myConsole();
lua["c"] = c;

lua.DoFile("Scripts/test1.lua");
lua.GetFunction("Console").Call();

出力
(´・_・`)Luaからだよー


ひとまずこんなもんかな。この記事書くにあたってちょこちょこ実験したりして、
いろいろ覚えれたので、新しいことする時はこうやって記事にしよう。

参考サイト・記事
YouTube/C# Scripting - Lua
結果だけでなく過程も見てください/C#でゲームのマップエディタを作る (1)
C#でゲームつくるです/Lua使ってみる
C#でゲームつくるです/Luaでオブジェクト指向っぽい何か


2012/04/12追記
プロパティgetのは出来るみたい?
最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QR
プロフィール

ぴお

Author:ぴお
ようこそ

自分のゲーム制作での備忘録や製作記録を書き留めておくブログです。

■やってるサービス
Twitter:piorimumu

Pixiv
http://www.pixiv.net/member.php?id=1126385

Twitter
このページのトップへ
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。