fc2ブログ

Javaゲーム制作記

いろいろ作ってます  

ミリ秒のsleepで正確にFPSを固定する方法

いろいろな方法があるようですがどうも問題があったりしたので 自分なりのやり方を書いておきます。


いろいろ省略してありますがこんな感じのメインループを作ったとします。



休止の部分には、60FPSになるように 1000÷60=16.66666...≠16 ミリ秒休止をしています。

しかしこれではメイン処理に時間がかかったとき遅れてしまいます。
実行・休止全部含めて16ミリ秒でないといけないのです。

そこで、以下のように修正します。



これで大体の問題が解決しました。
しかし一つだけ問題が残っています。
動かしてみると分かると思いますが、実はこのコードは60FPSでは動きません
62、63FPSあたりをウロウロしているはずです。


この原因は、最初に 1000÷60=16.66666...≠16 としてしまった所にあります。
小数点以下を切り捨ててしまっているので、
図のように1ループにつき0.66666...ミリ秒の誤差が生じてしまっています。


(緑のブロックが1ループです)

これを無くす方法として、「誤差を次ループへ持ち越す」というものがあります。
休止すべき理想の時間(この場合16.66666...)と実際に休止した時間との差を、
次ループの休止時間から引いてやるだけです。
そうすることで、誤差を常に1ミリ秒以下の状態に抑えることができます。


(黄色の部分が持ち越された誤差です)

この方法では、小数点以下を保持しておく必要が出てくるため、整数型では足りなくなってしまいますが、
固定小数点数を使うことでこの問題を回避できます。
整数型に1000倍した数を格納しておいて、必要なときに1000で割って戻すというような方法です。

また、2の累乗の乗除算はビット演算化できるので、高速に処理することができます。


これらを元に、メインループを書き直すと以下のようになります。



これでようやく正確なFPSが出るようになりました。
fps = 60 の部分をいじれば好きなFPSに調節できます。
コードはご自由にお使いください。

※JavaにはSystem.nanoTime()というメソッドもありますが外部から操作されやすいため余りお勧めできません。
スポンサーサイト



[ 2011/10/28 16:47 ] Java | Comment(3)

任意多角形の三角形分割

今日はゲームの更新ではありませんが、
物理ゲームを作る過程で生まれた問題の解決方法です。
「任意多角形の三角形分割」
です。

今使っている物理エンジンは、凸包(凹んでない多角形)しか扱えません。
とすると、多角形の作成に制限が出てしまいます。
前に、
ほんとは用意しないつもりだったけど、手が滑って(?)
「ぐりぐりポリゴンが書けるツールを用意する」
と言ってしまったので、せっかくなので作ります。

"任意多角形の三角形分割"などと書きましたが、
「適当な図形を全部三角形で作ってみよう」という事です。
すごく参考になったページがあります。
[お勉強] 非凸多角形の三角形分割
※勝手にリンク貼りました。すみません

手法とかはこのページを見れば大体分かるのだけど、
ちょっと難しくてわからないと言う人向けに詳しい解説を用意しました。
長くなるので続きからどーぞ
[ 2010/07/19 11:31 ] Java | Comment(2)

3Dモデルをディスプレイに表示するまでの手順(レンダリング)の説明

前回はメモ書きでしたが今回はちゃんと解説ということで。。。

とりあえずタイトル通り3Dモデルをディスプレイに表示するまでの手順
をなるべく簡潔に解説します。

3D表示をするためにはまず3Dのモデルが必要になります。
3Dのモデルは頂点と面で構成されています。
今回はこんな三角形にしました。
3D

Z方向は全て0です。

座標系は図のようにYが上方向、Xが横方向、Zが奥行き方向です。
3D


とりあえずモデルは準備できました。
次に、モデルを写すカメラ(視点)を用意しなくてはなりません
そしてモデルの座標をカメラから見た座標に変換します。
3D

そして最後にこのモデルを・・・
Z軸方向でつぶします!
画面は2Dなので奥行き(Z軸でしたよね)は関係ありません。
するとZ方向がなくなって事実上の2Dになりました
後はこれをそのままディスプレイに表示するだけです。


なんとなくどうやっているかわかりましたでしょうか?
今までのことをまとめると、
3D座標上にカメラを置いて写真を撮り、それを表示する
ということです。

しかしこれには膨大な量の計算が必要になります。
それを高速で行うのがGPU(グラフィックスボード)です。
そしてそのGPUに命令を送るのがDirectXやOPENGLといったライブラリなわけです。
GPUにレンダリングをしてもらうことをハードウェアレンダリングといいます。

しかしJavaからはGPUを操作できないので、この処理をCPUにやってもらうわけです。
これはソフトウェアレンダリングといいます。
GPUは描画処理専用に作られていますが、CPUは描画専用ではありません。
なので、かなり重くなるわけです。
最近ではCPUもそれなりの性能になってきたので、大量のポリゴンを表示したりしない限りは特に問題はないでしょう。

________________________________________

現在この3D表示をするプログラムを製作中です。
開発はオープンソースにしたいと思っています。
[ 2010/02/12 11:05 ] Java | Comment(2)

3Dに必要な知識メモ書き

3Dを自分で表示するに当たっての必要な知識を忘れないようにメモ書きしておきます。
読みにくいですがすいません

・ベクトル

x,y,zのパラメーターを持つ。

2ベクトルの内積:x * x2 + y * y2 + z * z2

2ベクトルの外積:
x' = y * z2 - z * y2
y' = -x * z2 + z * x2
z' = x * y2 - y * x2

・行列

3D表示では4*4の要素が必要になる。
ここから引用

スケーリング
[ Sx 0 0 0 ]
[ 0 Sy 0 0 ]
[ 0 0 Sz 0 ]
[ 0 0 0 1 ]

X軸回転
[ 1 0 0 0 ]
[ 0 cosθx sinθx 0 ]
[ 0 -sinθx cosθx 0 ]
[ 0 0 0 1 ]

Y軸回転
[ cosθy 0 -sinθy 0 ]
[ 0 1 0 0 ]
[ sinθy 0 cosθy 0 ]
[ 0 0 0 1 ]

Z軸回転
[ cosθz sinθz 0 0 ]
[ -sinθz cosθz 0 0 ]
[ 0 0 1 0 ]
[ 0 0 0 1 ]

移動
[ 1 0 0 0]
[ 0 1 0 0]
[ 0 0 1 0]
[Px Py Pz 1]

掛け合わせることで一つの行列として扱うことができる

これらを全部クラス化すればできる・・・はず。
[ 2010/02/11 11:06 ] Java | Comment(0)

Graphics2Dクラスの便利な使い方

Graphics2Dクラスの使い方を最近いろいろと知ったので書くことにしました。
まず


・Graphics2Dクラスを取得しよう

使い方を知っていても、Graphics2Dの変数がなければ意味がありませんw
取得方法は、

Graphics2D g2 = (Graphics2D) g;

簡単です。キャストするだけです。
ちなみにgはGraphicsクラスです。


・アンチエイリアス

アンチエイリアスとは図形を描いたときに見えるぎざぎざをなくすものです。
二枚の画像を用意しました。
アンチエイリアス無し

アンチエイリアス有り
下の方がきれいに見えるのがわかると思います。
下がアンチエイリアスをかけた状態です。

g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
    RenderingHints.VALUE_ANTIALIAS_ON);

これで図形がきれいに描画されます。
以下の方法でテキストにもアンチエイリアスをかけられます。

g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
    RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

・画像描画時に透明度を設定する

普通、図形を半透明描画するときにはColorの四番目の引数に透明度を設定します。
しかしこの方法では画像は半透明にできません。
そこで以下の方法を使います。

g2.setComposite(AlphaComposite.getInstance(
    AlphaComposite.SRC_OVER, ここに透明度を0~1で指定));

0だと完全に透明(つまり意味無し)、
1だと完全に不透明になります。
0.5あたりにするといいと思います。
ただし、これ以降の描画は全て半透明になってしまいますので、ご注意を。
元に戻す際には、

g2.setComposite(AlphaComposite.getInstance(
    AlphaComposite.SRC_OVER, 1));

としてやればOKです。

・画像の回転
Graphics2Dでは画像を回転できます。

g2.rotate(回転角度ラジアン, 回転の中心座標X, 回転の中心座標Y);

これで以降の描画は全て回転が適用されます。
ただし、これ以降全ての描画に回転が適用されてしまうので、ご注意を。
直すには、

g2.rotate(-回転角度ラジアン, 回転の中心座標X, 回転の中心座標Y);

とします。
よーするに回した分反対に回してやるということです。

____________________________________________________________

他にもtranslateやscaleなど便利なメソッドが沢山ありますが、
使い方は似ているので、調べてみると使えるかもしれません。

では今回はこの辺で。。。
[ 2010/02/06 14:21 ] Java | Comment(4)
プロフィール

saharan

Author:saharan

Twitter
検索フォーム