[PR]
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
プログラミング、3DCGとその他いろいろについて
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
この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();
}
Microsoftのゲーム開発ツールであるXNAで透過を行いたいときの注意についてメモしておきます。
XNAでは、(というか、これはXNAに限ったことではないのですが、)表示するものを透明にすることが出来ます。
これは、ゲームを作るときに、例えばビームや爆発の炎などの、奥が透けて見えるものを表示するのに役に立つでしょう。
ちょうどこんな感じです:
(この炎のイメージはXNA Creators Club OnlineのサンプルParticle 3D Sampleのexplosion.pngを利用しています。)
3つの炎のイメージが重なって表示されています。奥のほうの炎が透けて見えます。
ちなみに、透過を行わなかったら奥の炎は透けて見えません。
透過を有効にするには描画直前に次のようにします。
GraphicsDevice.RenderState.AlphaBlendEnable = true;
GraphicsDevice.RenderState.AlphaBlendOperation = BlendFunction.Add;
GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
GraphicsDevice.RenderState.DestinationBlend = Blend.One;
しかしこのままでは問題があって、それは何かというと、炎の描画の順番によって炎が透けないことがあるということです。(つまり回転させると、透けたり透けなかったりします。)
(上の4行以外に何もしないと、中途半端に透明になります。ここでは、一番右の炎が手前の炎のイメージに隠れてしまっています。一方、左奥の炎は透けて見えます。これは、透けるか透けないかに3つの炎を描画する順番が関係しているためです。)
次の行を追加すると、問題が解決されるようです。(一番初めの画像のようになります)
GraphicsDevice.RenderState.DepthBufferWriteEnable = false;