2013年06月26日

りりこポッキー

blogがプログラムの話題で埋め尽くされているので絵描けよと圧力が掛かる。
130626.jpg
仕方なく30分ぐらいでタブレット一発描き。ドット絵のほうが良かったかも?
posted by JUNOSOFT at 23:41| Comment(0) | 雑談

2013年06月25日

luaのバイナリ形式

こんにちは。コーヒーを飲みすぎて気分が悪くなったプログラム担当です。

Luaの癖の強い作法が最初はいやだったのですが(配列が1起算とか、否定等号が != でも <> でもなく ~= だとか)だいぶ見慣れてきてストレス無く扱えるようになってきました。

ところで Lua スクリプトをロードするとき luaL_loadstring なんかを使っていると、テキストを解析してコンパイルする時間がかかるため、スクリプトが多くなってくるとロードだけで時間がかかってしまいます。
幸いにも Lua にはコンパイル済みバイナリを書き出す機能があるので、それを使ってキャッシュファイルを作り、二回目以降の起動ではコンパイル済みのスクリプトファイルから直接ロードすることができます。

やり方は簡単で、まずテキスト形式の Lua スクリプトを普通にロードし、それを lua_dump で書き出します。
読み込むときには luaL_loadstring ではなく lua_load を使うと、テキスト形式でもバイナリ形式でもどちらでも読み込んでくれます。

テキストluaからバイナリluaを作るコードはこんな感じです:

static char g_buf[1024];
const char * reader(lua_State *L, FILE *fp, size_t *size) {
 if (feof(fp)) {
  *size = 0;
  return NULL;
 } else {
  *size = fread(g_buf, 1, sizeof(g_buf), fp);
  return g_buf;
 }
}

static int writer(lua_State *ls, const void *data, size_t size, FILE *fp) {
 fwrite(data, size, 1, fp);
 return 0; // no error
}

void make_bin_lua(const char *bin_filename, const char *text_filename) {
 FILE *fin = fopen(lua_filename, "r");
 FILE *fout = fopen(bin_filename, "wb");
 lua_State *ls = luaL_newstate();
 lua_load(ls, (lua_Reader)reader, fin, "", NULL);
 lua_dump(ls, (lua_Writer)writer, fout);
 lua_close(ls);
 fclose(fout);
 fclose(fin);
}

いやー、 Lua っていいですね!

posted by JUNOSOFT at 04:54| Comment(0) | プログラミング

2013年06月19日

Androidで3D物理エンジン

こんにちは。先日、某ログハウスの展示場に行ってきたのですが、思ったほど高くなくて驚いたプログラム担当です。金は独身のうちに使っておけ、という家訓が自分の背中を押しているのを感じます。

ところで今度作ろうとしているアプリ、ガチの3D物理演算ライブラリを必要としています。ところが以前使った Box2D は当然2D専用なわけで、今回のプログラムには役に立ちません。良いものがないかと調べてみたところ、Bullet というものを見つけました。ためしにダウンロードしてみたんですが、初めからVC2010向けのプロジェクトファイルが入ってる、ってのがとても良いですね!本体ライブラリのほかに大量のサンプルプロジェクトも入っていて、非常に簡単に試すことができました。

http://bulletphysics.org
http://code.google.com/p/bullet/downloads

ただこれ、結構ファイルが多くて Makefile 書くのが大変そう…。とりあえず Android-NDK で HelloWorld のコンパイルが通るように頑張ってみたいと思います。

うまくいったら、また報告しますね。
posted by JUNOSOFT at 23:25| Comment(0) | プログラミング

2013年06月18日

データのロード

こんばんは。なぜか修羅場が終わりません (涙


今日は、ゲームに使うデータファイルをロードするときの話です。

画像ファイルやアニメファイル、サウンドファイルなどのロードをそのまま素直にやっていると、ファイル数が多くなってきたときに意外とロード時間がかかるようになってきます。
そこであらかじめ、自分のプログラムにとって扱いやすいフォーマットに変換して保存しておき、ゲーム中ではそのファイルを読み取るようにすればロード時間が早くなります。
例えば、画像ファイルはは PNG で管理し、ゲームでは DDS 形式に変換してから使うとします。
(あくまでも例えで、自分は DDS 形式を使ったことはありませんけど)


普段グラフィック担当とやり取りするときはPNGのまま扱い、ゲームに組み込むときにDDSに変換すれば、効率よくロードできます。
ところがこのやり方、ロード時間が早くなるのは良いのですが、DDSへの変換という手間が増えてしまいます。
画像をほんの少し変更したときでも、わざわざDDSに変換してからでないとゲーム中で確認できません。


ならばハイブリッド形式で、PNGとDDSの両方に対応し、DDSがあればそれを使い、なければPNGを使うようにすればよさそうです。


実際、しばらくそのようにしていたのですが、そのうち変換作業自体が面倒になってきました。
PNGのままでも使えるとはいえ、結局どこかのタイミングでDDSに変換する必要があるわけです。
ツールで変換すること自体が面倒です。
そこで、ゲーム自体に変換機能を持たせることにしました。PNGをロードした場合は、自動的にDDSに変換して保存しておく…と。
当然この変換機能が付いているのはデバッグ版だけで、公開版にはありません。(#ifdef _DEBUG で切り分ける)


で、しばらくそのようにやっていたのですが、そのうちさらに面倒になってきました。
PNGとDDSがある場合はDDSを優先してロードするのですが、PNGを更新した場合はDDSも自動更新してほしいなーと。
というわけで、DDSを作るときにPNGのタイムスタンプも同時に記録しておき、以後DDSを使うときには変換元となったPNGのタイムスタンプと、実際に存在するPNGのタイムスタンプを比較し、1秒でも異なっていたらPNGが変更されているとみなしてDDSを再生成します。
そんな面倒な事をしなくても、DDSよりPNGの日付が新しかったらDDSを更新すればよさそうな気もしますが、バックアップからPNGを復元した場合など、必ずしもDDSより新しい日付になるとは限りません)


これは思った以上に快適です。面倒なことは機械に任せて、人間様はどんどんサボりましょう。

posted by JUNOSOFT at 20:57| Comment(0) | プログラミング

2013年06月15日

解像度とアスペクト比

こんばんは。疲れ果てたので、小ネタです。

画面のアスペクト比。
フルスクリーンモードの時、画面の縦横比に関係なく、ゲーム本来の縦横比で画面を表示するようにしたかったのですが、簡単かと思ったらそうでもありませんでした。

そんなの簡単、現在のディスプレイ解像度を取得できればあとはやり放題、のはずなのですが、そうではありません。
なぜなら、切り替え可能な解像度は必ずしもディスプレイ本来の解横比とは一致しないからです。
もともと比の異なる解像度にされていた場合、ゲーム側ではそれが本来の解像度であると騙されてしまいます。
自前で正しい縦横比のディスプレイモードに切り替えればよいのですが、そもそも使用中のディスプレイの物理解像度はどうやって取得するのでしょう…?

使用可能な解像度を列挙して、そのなかで一番サイズの大きいもの=そのディスプレイの物理解像度とすればよさそうですが、ところがどっこいノートPCなどでは物理解像度よりも大きな値に設定できたりします。

まあ結局そんなところまで考えてもしょうがない、ということで、ゲーム起動時に設定されていた解像度=そのディスプレイの物理解像度であるという前提で処理するようにしました…。

人間、あきらめが肝心です。

posted by JUNOSOFT at 04:35| Comment(0) | プログラミング

2013年06月07日

アクションとか

こんにちは。まだまだ仕事で修羅場が続いております。
例によってこの文章をいつ書いているんだというツッコミは無しの方向で…

今日はプレイヤーのアクション管理とかについてです。

エースオブワンドではさまざまなアクションを取り入れたのですが、基本動作「移動」について少しだけお話します。


・移動属性
プレイヤーは移動に関して、2種類の状態を持っています。
地上状態と、空中状態です。
キャラクターのアニメに使用する絵には、1枚ごとに「地上」か「空中」どちらかの属性を設定してあります。
ポイントは、アニメ単位で地上・空中を設定するのではなく、絵(フレーム)単位で設定することです。
例えばジャンプアニメは、予備動作(ふんばり)→上昇→下降→着地の流れになっています。
ここで、予備動作の絵は「地上」属性にしておきます。
上昇・下降中の絵には「空中」属性に、着地してからの絵は「地上」属性にします。
このように全ての絵を地上または空中に分類し、再生中のプレイヤーの絵がどちらの属性なのかによって移動処理を変えます。


・地上状態
地上状態ではどんなアクションをしても必ず地面にスナップ(吸いつき)し、地面から離れることはできません。
どんな凹凸や坂でも必ず地面に沿って移動します。
地上状態であるにもかかわらず地面が存在しない場合は、自動的に空中状態に移行します。
つまり、キャラクターのアニメを落下状態にするということです。

さらに、どんな地上状態から落下したかによって2つの落下パターンがあります。
走っている状態から落下したら「通常落下」です。
それ以外の状態から落下した場合は「予期しない落下」です。
スライディング中に段差から落ちたり、前進を伴う攻撃中に段差から落ちたりした場合などはすべて「予期しない落下」です。
予期しない落下中は操作不能で左右の速度調整などができず、着地したとたんにバタンと倒れこんでしまいます。


・空中状態
空中状態ではどんな方向にも移動できますが、地面に接触した瞬間に地上状態に戻ります。
地上状態に戻るとは、着地アニメに移行するということです。
また、ある程度の上昇速度で天井に衝突した場合は、頭ぶつけ状態に移行します。
頭ぶつけ状態はダメージ扱い(アニメがダメージ絵になるだけで、シールドは減らない)で落下します。


・動く床
ここから先はエースオブワンドの話ではないのですが、ちょっと付け加えておきます。
ゲームによっては上記に加えて「別のキャラクターの上に乗っている」という状態があります。
上下左右に移動する床とか、押したり壊したり上に乗ったりできる箱とかです。
この場合、プレイヤーは「自分の足元に居るキャラクター(地面キャラ)」と「そのキャラクターからの相対座標」を保持します。
これがNULLなら誰の上にも乗っていないし、非NULLならそのキャラクターの上に乗っている状態です。
誰か(地面キャラ)の上に着地した場合には、そのキャラクターと相対座標をプレイヤーに登録し、リンクさせます。
プレイヤーのアニメが「地上状態」になっている限り、プレイヤーの座標は地面キャラからの相対座標で計算するようにします。
プレイヤーが歩いた場合などは、移動速度を元にして相対座標を変化させます。
「空中状態」になった場合は地面キャラとのリンクを切り、地面キャラとは無関係に移動できるようにします。

こうすることで、地面キャラが移動すると、それに対応してプレイヤーも勝手に移動してくれるようになります。
また、この仕組みをプレイヤーだけでなく箱にも適用させることで、複数の箱が積み重なっている状態とか、
その状態で一番下の箱を動かすとそれに乗っている全ての箱が動くとか、そういうことができるようになります。


・というわけで
もっといろいろ書こうと思っていたのですが、だんだんややこしくなってきました。
アクションゲームはこういうこと考えるだけでも面倒ですね。
もっと単純でうまい方法があるのかもしれませんが、とりあえず自分はこういうふうにやっています、というお話でした。
さて、仕事に戻るとしましょう…。
posted by JUNOSOFT at 13:22| Comment(0) | プログラミング