忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。


XNA本

注文していたXNAの本が届きました

Racing GameのBenjamin Nitschkeさんの書いた
"XNA Game Programming"
と、別の人の"XNA GAME STUDIO Creator's Guide"です。

まだパラパラめくっただけですが、"XNA Game Programming"のほうは
HLSLをバリバリ使っているようです。
著者のベンジャミンさんは「SimpleEffectは柔軟性がないから使わない」
みたいなことを言っています。
XNAではじめてゲームプログラミングを始める人にとっては拷問ですが、
「かんたんXNA」もそろそろHLSLに入ろうかと思っていたところなの
でかえって都合がよかったかもしれません。

(なんだかXNAでは、デザインパターンをふんだんに使ったHelloWorld
みたいなことが頻発しているような気がしなくもありません。)

これから読むつもりですが、まぁ、ともかく楽しみです。

拍手[0回]

PR

バネ付きカメラ その3 (補足:条件)

バネ付きのカメラは、空気抵抗が小さいと振動してしまいます。
それでは困ります。
カメラが振動しないくらい、空気抵抗は大きくなければなりません。

その条件はこうです:

damping ≧ 2 (stiffness × mass) ^ (1 / 2)

意味は:

空気の、カメラに対する粘っこさ ≧ 2 (バネの強さ × カメラの質量) ^ (1 / 2)

この式を満たすときは、カメラは振動しません。
ですから、この式を満たすようにしなければなりません

完全に余談になりますが、この式の=が成り立つときのことをクリティカル・ダンピングといい、=が成り立たないときをオーバー・ダンピングといいます。
そしてこの式そのものを満たさないときのことをアンダー・ダンピングといい、バネは振動してしまいます。

拍手[1回]


バネ付きカメラ その2(ソースコード)

このChase Camera Sampleはこの、乗り物が動くと徐々に動き出してついていくカメラのサンプルなのです。
これを実現するのに、このサンプルではカメラと宇宙船の間にバネをつけています

つまり宇宙船が動き出せば、バネでつながったカメラがそれに引っ張られて少し動きはじめ、その後バネの収縮によってより速いスピードで動くようになります。
止まるときもすぐに止まるのではなく、ばねの伸びる力によって徐々に止まっていきます。宇宙船の向きが変わったときも同じです。
きちんと、画面が徐々に動き出すような仕組みになっています。

ただしこれだけではいけません!
というのも、エネルギー保存の法則により、一度動き出したバネはいつまでも永久に伸び縮みを繰り返すからです。
このままでは、宇宙船が動くと、カメラは宇宙船に近づき、遠ざかり、近づき、遠ざかり、近づき・・・・・・を永遠に繰り返すことになります。
・・・これでは逆に3D酔いになりやすくなりそうです。

この問題を解決するには、現実世界と同じように、空気抵抗などを考えてやればOKです。
※このバネの動きを抑える力のことを、制動(ダンピング)といいます。

空気抵抗がある程度大きければ、バネはもう振動することはありません。
宇宙船に近づいたあとは、もう離れることはないのです。(ただし、空気抵抗が小さすぎると少しだけ振動してしまうので注意です)

空気抵抗は、普通の速さでは、速度のに比例します。(ものすごく速い物体だと速度の2乗に比例するようになりますが、ここでは無視しましょう)
イメージとしては、速度が2倍になれば抵抗も2倍で、速度が3倍になれば抵抗も3倍です。
つまりある程度速度が速くなると、空気抵抗と力がつりあって、動かす力がなくなります。
これが、アリが地球上の空気のあるところならどんな高さから落ちても死なない理由であり、人が雨に当たって死ぬことのない理由であり、カメラのついたバネが振動せずに止まる理由なのです。

以上のことを表したコードは以下のようになっていました:

Vector3 stretch = position - desiredPosition;
Vector3 force = -stiffness * stretch - damping * velocity;


意味としてはこんな感じです:

力 = -バネの強さ × バネの伸び - カメラに対する、空気の粘っこさ × カメラの速度;

ここで作ったforceから加速度を求め、速度に追加して、それを位置に追加すればバネのシミュレートは完成です。

//加速度分を追加
Vector3 acceleration = force / this.mass;
this.velocity += acceleration * elapsed;

//速度分を追加
this.position += velocity * elapsed;


メソッド全体ではこのような感じでした(ChaseCamera.csより。コメントは日本語訳):

        public void Update(GameTime gameTime)
        {
            if (gameTime == null)
                throw new ArgumentNullException("gameTime");

            UpdateWorldPositions();

            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            // バネの力を計算
            Vector3 stretch = position - desiredPosition;
            Vector3 force = -stiffness * stretch - damping * velocity;

            // 加速度の分を追加
            Vector3 acceleration = force / mass;
            velocity += acceleration * elapsed;

            // 速度の分を追加
            position += velocity * elapsed;

            UpdateMatrices();
        }




拍手[2回]


バネ付きカメラ その1(前置き)

XNAのサンプルの1つChase Camera Sample(XNA Creators Club Online)についてわかったことをいくつかメモしておきます。
コードについてはその2
バネが満たさなければならない条件はその3です。

このサンプルは何なのかというと、動いた宇宙船を追跡するカメラです。ただしバネ付きの。

3Dシューティングゲームやレースゲームなどは、プレイヤーが動かす乗り物にカメラがついていきます。
プレイヤーの動かす宇宙船が前に進めばカメラも前に動き、宇宙船が左を向けばカメラも左を向きます。

※これは当たり前のことで、宇宙船を動かしたのに画面がついていかなければ、自分の宇宙船が画面に写らなくなってしまいます。これではゲームになりません。(もっとも、「画面に映っていない自分の宇宙船を上手く操作する」という別のゲームとして遊ぶことは出来るかもしれませんが。)

これ(「これ」というのはもちろん「画面に映っていない自分の宇宙船を上手く操作する」というゲームのことではなくて、乗り物が動けば画面も動くということを指しています)を実現する方法として最も簡単なのは、乗り物が動いたときに、それと同じだけカメラも動かすことです。

camera.jpg
ただこれはあまりよくないかもしれません。
というのも、安っぽい感じがするからです。前進するボタンを押すとすぐに同じだけ動いて、離すとすぐに止まるのです。それよりは変化が徐々に現れた方がリッチな感じがしないでもありません。
スイッチを入れればすぐに光る豆電球よりもちょっと時間のかかる蛍光灯の方がリッチな感じがしなくはないかもしれません。

その他の理由として、この方法だと3D酔いしやすくなるという話もあります。
実はこの話の真偽のほどはわからないのですが、全くありえないという話でもないかもしれません。
乗り物酔いというのは、人間が進化した環境(数千年前くらいまで)でありえなかった動きによってもたらされるものであり、このカメラの動かし方はまさにそれであるとも考えられるからです。

人間は数千年前まで自動車や電車からは無縁で、そのため自動車や電車の中で酔わない淘汰圧が無かったのです。
ならば自動車や電車の中で酔うようになってもふしぎではありません。
これはちょうどテスト駆動開発と同じ話で、つまり人間には「乗り物の中で酔わない」というテストが存在しなかったので「乗り物の中で酔わない」という機能も実装されなかったのです。

同じ話がこのカメラの動かし方にも言えるかもしれません。
つまり、人間の進化した環境では、自分の足を動かすのでない限り、自分のした行動はある程度のタイムラグがあって反映されるはずで、ボタンを押す指の動きが直ちに画面全体の動きに反映されてしまうこのカメラの動かし方は、酔いを引き起こしやすくてもふしぎはないかもしれません。


脱線しましたが、つまりここで重要なのは別のカメラの動かし方が必要だということです。

とくに重要なのは、画面が徐々に動き出すということです。




拍手[2回]


XNAで透過

Microsoftのゲーム開発ツールであるXNAで透過を行いたいときの注意についてメモしておきます。

XNAでは、(というか、これはXNAに限ったことではないのですが、)表示するものを透明にすることが出来ます。
これは、ゲームを作るときに、例えばビームや爆発の炎などの、奥が透けて見えるものを表示するのに役に立つでしょう。

ちょうどこんな感じです:
adb7b883.JPG
(この炎のイメージはXNA Creators Club OnlineのサンプルParticle 3D Sampleのexplosion.pngを利用しています。)






3つの炎のイメージが重なって表示されています。奥のほうの炎が透けて見えます。

xnaAlphaBlending2.JPG










ちなみに、透過を行わなかったら奥の炎は透けて見えません。
noAlphaBlending.JPG









透過を有効にするには描画直前に次のようにします。

GraphicsDevice.RenderState.AlphaBlendEnable = true;
GraphicsDevice.RenderState.AlphaBlendOperation = BlendFunction.Add;
GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
GraphicsDevice.RenderState.DestinationBlend = Blend.One;

しかしこのままでは問題があって、それは何かというと、炎の描画の順番によって炎が透けないことがあるということです。(つまり回転させると、透けたり透けなかったりします。)
xnaAlphaBlending3.JPG
(上の4行以外に何もしないと、中途半端に透明になります。ここでは、一番右の炎が手前の炎のイメージに隠れてしまっています。一方、左奥の炎は透けて見えます。これは、透けるか透けないかに3つの炎を描画する順番が関係しているためです。)






次の行を追加すると、問題が解決されるようです。(一番初めの画像のようになります)

GraphicsDevice.RenderState.DepthBufferWriteEnable = false;

拍手[0回]