FPSを作ってみる@wiki
01)
最終更新:
slice
-
view
(2011/01/30)#2
進捗
残念ながら今の自分の能力からして皆様のご期待には多分,添えない.
このwikiページの存在は一度忘れて貰って,ふと思い出した頃に覗いてくれるといいな.
このwikiページの存在は一度忘れて貰って,ふと思い出した頃に覗いてくれるといいな.
さて
luaの話を書いたら長くなったので改めて進捗状況を記す.
現在は視野外で描画の必要がないOBJのカリング処理をスキップする処理を汎用性を持たせた上で組み込もうと試行錯誤.
同様に主人公から遠く離れている,見えない物体はアニメーション等の更新処理を省く場合もあるだろうという事でそれも含めて
アップデートの機構を見直し.
今までそういうのはちぃとも考えてなかったのだ.すべて1つのリストに放り込んでいたし
描画するかしないかは各々のOBJが自分で描画リストや更新リストに自身を加えたり外したりでやっていた.
否,正確には誤魔化していたと言えよう.
それでゲームが作れなくはないと思うが・・・1つのゲーム用にカッチリ作り込むしかないだろうな.
luaの話を書いたら長くなったので改めて進捗状況を記す.
現在は視野外で描画の必要がないOBJのカリング処理をスキップする処理を汎用性を持たせた上で組み込もうと試行錯誤.
同様に主人公から遠く離れている,見えない物体はアニメーション等の更新処理を省く場合もあるだろうという事でそれも含めて
アップデートの機構を見直し.
今までそういうのはちぃとも考えてなかったのだ.すべて1つのリストに放り込んでいたし
描画するかしないかは各々のOBJが自分で描画リストや更新リストに自身を加えたり外したりでやっていた.
否,正確には誤魔化していたと言えよう.
それでゲームが作れなくはないと思うが・・・1つのゲーム用にカッチリ作り込むしかないだろうな.
話を戻して,OBJ全てをリストに放り込む方式では
多数の敵が居る広大なマップなんかで毎フレーム100や1000回も判定していたんじゃあ埒があかないので却下.
普通は部屋ごとに分けてリストを確保,カメラがある部屋とそこからみえる部屋の所だけ処理するよなあ・・
多数の敵が居る広大なマップなんかで毎フレーム100や1000回も判定していたんじゃあ埒があかないので却下.
普通は部屋ごとに分けてリストを確保,カメラがある部屋とそこからみえる部屋の所だけ処理するよなあ・・
とりあえず,表示されるオブジェクトを割り出す処理やカメラからの距離ソートはそれなりに重いからluaでやりたくない.
描画順に関しても不透明ポリゴンはZカリングを期待して手前からさせたいし半透明ポリゴンは奥からでないと不具合が出る
時にはカメラ距離を無視してユーザーの指定した優先度順が適した場面もあるだろう.
そう考えるとソートアルゴリズムは分離した方が良さそうだが・・
ぐだぐだぐだ.中々決まらない.
描画順に関しても不透明ポリゴンはZカリングを期待して手前からさせたいし半透明ポリゴンは奥からでないと不具合が出る
時にはカメラ距離を無視してユーザーの指定した優先度順が適した場面もあるだろう.
そう考えるとソートアルゴリズムは分離した方が良さそうだが・・
ぐだぐだぐだ.中々決まらない.
決まらん事にはプログラムが書けないので作業してる感も無く,辛い今日この頃
(2011/01/30)
luaとか
ちょっと油断するとすぐ間が空く.
細々としたバグ修正などはしてるがいつも通り目に見える進歩がないというか.
subversionの履歴を見るに18日から27日まではパフォーマンスアップ関連の事をしていたようだが・・
Luaのどの命令が重いのか,どうやったら効率の良いスクリプトを書けるのか調べたり時間を計ってみたりもした.
いや基本的にどうあがいてもCより7~8倍程度重いわけだが.それでもなるべく効率の良いコードを書きたいと思うのは自然だろう.
細々としたバグ修正などはしてるがいつも通り目に見える進歩がないというか.
subversionの履歴を見るに18日から27日まではパフォーマンスアップ関連の事をしていたようだが・・
Luaのどの命令が重いのか,どうやったら効率の良いスクリプトを書けるのか調べたり時間を計ってみたりもした.
いや基本的にどうあがいてもCより7~8倍程度重いわけだが.それでもなるべく効率の良いコードを書きたいと思うのは自然だろう.
以下,わかった事を適当に述べる.キャッシュの無効化とかは全くしてないからフーン程度に.
言うまでも無いがtable["item"] といったテーブル参照命令は,書けば書いた回数だけ参照が行われるので
同じ関数で2度以上使う場合はローカル変数に置く.math.floor() も例外ではない.
(間にyieldがある場合は除く)
言うまでも無いがtable["item"] といったテーブル参照命令は,書けば書いた回数だけ参照が行われるので
同じ関数で2度以上使う場合はローカル変数に置く.math.floor() も例外ではない.
(間にyieldがある場合は除く)
大ざっぱに計ったらテーブル参照一回分と関数呼び出し一回分は関数呼び出しの方が若干重いくらい?
メタテーブルで関数を指定すると当然ながら関数呼び出し一回分はかかる
凝った事をしようとして深くネストさせると悲惨な事に
凝った事をしようとして深くネストさせると悲惨な事に
一回のテーブル参照に対し重そうな浮動小数点数の割り算でさえ5~6回は出来そう
テーブルのインデックスを連番の数値にすると内部で配列表現される為メモリの効率は良くなる筈だが
(少なくとも要素数10以下では)アクセス速度はハッシュの方が微妙に速かった
(少なくとも要素数10以下では)アクセス速度はハッシュの方が微妙に速かった
プログラム実行において一般的に重いと言われがちな分岐命令のコストはテーブルアクセスに比べると
微々たる物なので過度に節約する必要はない
微々たる物なので過度に節約する必要はない
メタテーブルに影響されず生のアクセスを行うrawget/rawsetは
rawgetという関数を呼び出しているのでテーブルアクセス + 関数一回分となってしまう罠
メタテーブルに影響されないとわかってるなら普通アクセスにすべき
rawgetという関数を呼び出しているのでテーブルアクセス + 関数一回分となってしまう罠
メタテーブルに影響されないとわかってるなら普通アクセスにすべき
関数の終端呼び出しはメモリ効率は(略)だけど速度的には変わらん
luaには汎用forループと算術forループがあるが前者はループする度に関数呼んでるから(略
(ipairs()って要るんか?)
算術は算術で値の評価が最初の実行時のみ行われるのが厄介で,特に多重ループ脱出時に面倒
(ipairs()って要るんか?)
算術は算術で値の評価が最初の実行時のみ行われるのが厄介で,特に多重ループ脱出時に面倒
これらの多くはバイトコード見てればすぐわかる事だけど・・・とにかくそんな感じだ
自分としては関数の呼び出しとテーブル参照が同じ位重いっていうのが結構な落とし穴だと思った.
Cだと配列から読むのなんて訳ないもんねぇ.
関数内に定数のつもりで即席テーブル作ってそこに数値を入れて何か処理するとかもやばそう.
Cだと配列から読むのなんて訳ないもんねぇ.
関数内に定数のつもりで即席テーブル作ってそこに数値を入れて何か処理するとかもやばそう.
そう.定数といえば以前はluaに定数が無いのを不満に思ってネット上であちこち探して定数の擬似的な記述方法を
参考にしたりしていたが
(グローバルテーブルにメタテーブルを設定して色々っていう方式)
上記の事を踏まえると無駄に重いという結論に至り,辞めた.
参考にしたりしていたが
(グローバルテーブルにメタテーブルを設定して色々っていう方式)
上記の事を踏まえると無駄に重いという結論に至り,辞めた.
知れば知るほどluaという言語はシンプルなもので
変数の型を調べるtype()でさえ特別扱いせずグローバル空間から関数を引っ張ってきてるのだ.
強いて言えば整数型くらいは欲しがこのシンプルさは初めてプログラム言語を触る人でも
BASIC並にとっつきやすい言語かもしれない.
変数の型を調べるtype()でさえ特別扱いせずグローバル空間から関数を引っ張ってきてるのだ.
強いて言えば整数型くらいは欲しがこのシンプルさは初めてプログラム言語を触る人でも
BASIC並にとっつきやすい言語かもしれない.
(2011/01/18)
速度が気になる
前々から気にはしていたけれど,どうせ自分しか動かさないしとテキトーに実装していた「fpsを60に保つ部分」をちゃんと作り直した.
QueryPerformanceCounterで時間を見て処理間隔を調整するという基本は変わらないがまだ時間ありそうならSleep(0)しておき,
近くなったらループを回すだけの仕様だったので当然ながらフレーム更新の間隔が毎回微妙にずれていた.※1
しかし今回はちゃんとマルチメディアタイマで次回の更新1ms手前まで待機させ※2
そこからQuery~でループして時間を正確にあわせるようにした.60fpsだとmsで割り切れない問題にも対処.
QueryPerformanceCounterで時間を見て処理間隔を調整するという基本は変わらないがまだ時間ありそうならSleep(0)しておき,
近くなったらループを回すだけの仕様だったので当然ながらフレーム更新の間隔が毎回微妙にずれていた.※1
しかし今回はちゃんとマルチメディアタイマで次回の更新1ms手前まで待機させ※2
そこからQuery~でループして時間を正確にあわせるようにした.60fpsだとmsで割り切れない問題にも対処.
で,あとは引き続きLua関連の高速化.
いくらスクリプトとはいえ依然として動作速度が満足いかないというのがあり※3
調べてみたらCの関数を呼ぶ際の処理で殆どの時間を食っている模様.
そこでスクリプト記述とコンパイル結果を参考に,あとLuaのソースもちょびっと見つつ処理効率の改善を図る.
通常のインタプリタ方式ではなく適時リコンパイルするLua-JITという物もあるがそれは最終手段ということで.
いくらスクリプトとはいえ依然として動作速度が満足いかないというのがあり※3
調べてみたらCの関数を呼ぶ際の処理で殆どの時間を食っている模様.
そこでスクリプト記述とコンパイル結果を参考に,あとLuaのソースもちょびっと見つつ処理効率の改善を図る.
通常のインタプリタ方式ではなく適時リコンパイルするLua-JITという物もあるがそれは最終手段ということで.
それと後々裏でマップ計算などさせたい為マルチスレッドを意識したリソース管理にしているが
実際にビルドされたアセンブリの結果を見ていると同期処理に結構な命令数が割かれているのが気がかり.
要は中身にアクセスする度ロック・アンロックしているわけだから仕方ないといえばそうなのだが・・
少し記述を変えればシングルスレッド専用へ切り替えられるようにはしてあるから複数スレッドで共有するリソースと
そうでない物を分ける形になりそうだ.
実際にビルドされたアセンブリの結果を見ていると同期処理に結構な命令数が割かれているのが気がかり.
要は中身にアクセスする度ロック・アンロックしているわけだから仕方ないといえばそうなのだが・・
少し記述を変えればシングルスレッド専用へ切り替えられるようにはしてあるから複数スレッドで共有するリソースと
そうでない物を分ける形になりそうだ.
なんとなくだがLuaのライブラリを既にビルドしてあるバイナリを使うのを止めて自分でコンパイルした物を使うようにした.
先々週に整数型もどきを作った事もあり,Luaの数値型が最早doubleである必要がなくなったのでfloatにしたりだとか.
で,D3Dの初期化フラグD3DCREATE_FPU_PRESERVEをはずす.※4
25bit以上の数値が必要な整数なんてフラグぐらいしかないかなあとも思ったし.
先々週に整数型もどきを作った事もあり,Luaの数値型が最早doubleである必要がなくなったのでfloatにしたりだとか.
で,D3Dの初期化フラグD3DCREATE_FPU_PRESERVEをはずす.※4
25bit以上の数値が必要な整数なんてフラグぐらいしかないかなあとも思ったし.
※1 Sleep関数は処理が戻ってくる時間が正確でない
※2 自分の環境で計測したところマルチメディアタイマは(Sleep程ではないが)0.7ms程度の遅延があるようだ
※3 2Dマップにジャンプと当たり判定装備の主人公キャラクター1つ動かすだけでCPU占有率6%っつーのはどうかねー
正直,無いと思うんだけど.pentiumIIIくらいだったらともかく・・
ちなみに描画だけしたら2%程.これはfps調整でループ回してる為
※4 話によるとD3DはFloat精度で最適化されてるようだ
※2 自分の環境で計測したところマルチメディアタイマは(Sleep程ではないが)0.7ms程度の遅延があるようだ
※3 2Dマップにジャンプと当たり判定装備の主人公キャラクター1つ動かすだけでCPU占有率6%っつーのはどうかねー
正直,無いと思うんだけど.pentiumIIIくらいだったらともかく・・
ちなみに描画だけしたら2%程.これはfps調整でループ回してる為
※4 話によるとD3DはFloat精度で最適化されてるようだ
(2011/01/08)
公開へ向けた修正等
今までDebugビルドしかしてなかったので主にReleaseビルドに伴う不具合が無いかの確認.
例えばある時点で想定されたデータ構造になっているかをデバッグ目的でチェックするAssert構文は
Releaseビルド時にはコンパイルされないように条件判定文ごと無かった事にされる.
しかし,もしミスでAssert構文中に進行に必要な関数を呼んでその返り値をチェックしていたりすると不具合の元になる.
他にも最適化によって数値の接合性が取れなくなる場合も無きにしも非ずである.
(バグの箇所がDebugではたまたま上手く動いていたがReleaseでそれが露呈したりだとか)
例えばある時点で想定されたデータ構造になっているかをデバッグ目的でチェックするAssert構文は
Releaseビルド時にはコンパイルされないように条件判定文ごと無かった事にされる.
しかし,もしミスでAssert構文中に進行に必要な関数を呼んでその返り値をチェックしていたりすると不具合の元になる.
他にも最適化によって数値の接合性が取れなくなる場合も無きにしも非ずである.
(バグの箇所がDebugではたまたま上手く動いていたがReleaseでそれが露呈したりだとか)
Lua周りも少々.
CからLuaスタックの数値を取得しようとしてlua_tointeger()を使う際に
数値が浮動小数であった場合の挙動を「常に小数点以下は切り捨てられる物である」と勘違いしていた.
またそうなって欲しいので一度浮動小数で取得しC側で変換するように変更.
(Luaのソースを覗いたら現在のFPUの丸めモードで整数変換する実装だった.高速化の為?)
CからLuaスタックの数値を取得しようとしてlua_tointeger()を使う際に
数値が浮動小数であった場合の挙動を「常に小数点以下は切り捨てられる物である」と勘違いしていた.
またそうなって欲しいので一度浮動小数で取得しC側で変換するように変更.
(Luaのソースを覗いたら現在のFPUの丸めモードで整数変換する実装だった.高速化の為?)
もうひとつ.デフォルトの実装ではLuaの数値はすべて浮動小数であるが
2DのキャラクターやHUDを表示したい場合は座標を整数で指定しないとあらぬ色補間がかかってぼやけてしまう.
もちろんこれはその都度math.floorを呼べば回避できる.しかしだ.
必要もないのに無駄な処理を入れたくないし,常に「今変数に入っているのが整数なのかどうか」
を意識するのは面倒極まりないわけである.
結局,整数の疑似クラスを作ってしまった・・・
まあでもこれでビット演算も組み込めたし良しとしようか.
2DのキャラクターやHUDを表示したい場合は座標を整数で指定しないとあらぬ色補間がかかってぼやけてしまう.
もちろんこれはその都度math.floorを呼べば回避できる.しかしだ.
必要もないのに無駄な処理を入れたくないし,常に「今変数に入っているのが整数なのかどうか」
を意識するのは面倒極まりないわけである.
結局,整数の疑似クラスを作ってしまった・・・
まあでもこれでビット演算も組み込めたし良しとしようか.
ちなみに面倒くさくて浮動小数と書いたが正確には浮動小数点数だから注意ですな.
#ああ
,実数と呼べばいいのか!
(2011/01/01)
今年の方針
今までの進捗状況ログを見返すと作業が進まなかった時の言い訳してる風に捉えられたので
進捗の名の通り進んだ事だけを記す.
また基本的にゲーム製作以外の個人的な事情,感情等は挟まないものとする.
進捗報告は文章だけだと味気ないしなるべくわかりやすく他人に説明するとともに
自分でも何所まで出来たのか確認する意味で必要なら図やスクリーンショットを載せる.
進捗の名の通り進んだ事だけを記す.
また基本的にゲーム製作以外の個人的な事情,感情等は挟まないものとする.
進捗報告は文章だけだと味気ないしなるべくわかりやすく他人に説明するとともに
自分でも何所まで出来たのか確認する意味で必要なら図やスクリーンショットを載せる.
あとこれは前項と重なるが理解を深める目的で適時講座のようなページを作る.
キャラクターの移動アルゴリズムや,「ここの判定どうやってるの?」など
質問があれば出来るだけ答えていきたい.
キャラクターの移動アルゴリズムや,「ここの判定どうやってるの?」など
質問があれば出来るだけ答えていきたい.
以上