ボールの自由落下(衝突判定無し)
まずは簡単なところから
床とボールを表示させ、ボールを落下させてみましょう
目次
[FreeFall.py]
# -*- coding:utf-8 -*- import visual as vs import ode vs.scene.center = (0, 2, 0) """ ODE環境の設定 """ # ODEワールドの作成 + ワールドの重力設定 world = ode.World() world.setGravity((0, -9.81, 0)) # 剛体の作成 + 質量特性の設定 Ball_Body = ode.Body(world) M = ode.Mass() M.setSphere(2500.0, 0.5) Ball_Body.setMass(M) Ball_Body.setPosition((0, 4, 0)) """ VPython環境の設定 """ Floor = vs.box(length=4, height=0.2, width=4) Floor.pos = vs.vector(0, 0, 0) Ball_VS = vs.sphere(pos=Ball_Body.getPosition(), radius=0.5, color=vs.color.red) if __name__=="__main__": dt = 0.05 total_time = 0.0 while total_time<2.0: """ ODEによってVPythonのボールの位置を更新 """ Ball_VS.pos = Ball_Body.getPosition() vs.rate(1/dt) # VPythonのアニメーションを進める world.step(dt) # ODEの計算を進める total_time += dt vs.exit()
床をすり抜けてしまいました。
これは床とボールの衝突判定をしていないためすり抜けてしまいます。
コードを解説していきますが、その前にまずODEの概略について説明しましょう。
ODEの基本の2つ: 剛体と形状
ODEはまず、剛体(Body)と形状(Geom)の2つの要素から成ります。
剛体は質量特性を管理するオブジェクト
形状は衝突判定を管理するオブジェクトです
剛体も形状も、それぞれが一つのモノを表しますのでこれらを複数管理するオブジェクトが必要になります。
それがWorld(剛体の世界)とSpace(形状の世界)です。
以下に対応表を書いておきます。
種類 | 空間 | 含まれるオブジェクト |
---|---|---|
剛体・質量特性 | World | Body |
形状・衝突判定 | Space | Geom |
コードの解説~ODEの設定~
今回はODEの設定で
""" ODE環境の設定 """ # ODEワールドの作成 + ワールドの重力設定 world = ode.World() world.setGravity((0, -9.81, 0)) # 剛体の作成 + 質量特性の設定 Ball_Body = ode.Body(world) M = ode.Mass() M.setSphere(2500.0, 0.5) Ball_Body.setMass(M) Ball_Body.setPosition((0, 4, 0))
としてWorld(剛体の世界)しか作成していないので衝突判定ができなかったため、床をすり抜ける事態が起きました。
衝突判定についてはまた別の機会で上げます。
Worldの作成
剛体を計算させるためにはまず剛体が含まれる世界、Worldを作る必要があります。
# ODEワールドの作成 + ワールドの重力設定 world = ode.World() world.setGravity((0, -9.81, 0))
ode.WorldでWorldのインスタンスworldを作成し、world.setGravityで重力を設定します。
この時、world.setGravityの引数は(x, y, z)という3次元のデータにしましょう。
タプル、リスト、VPythonのvector、numpyのndarrayのどれかにするといいでしょう。
VPythonではvector, ODEではタプルなどとするのは面倒なので、VPythonとODEの連携をするなら色々な高速な関数が入ってるnumpyを使うのがいいかもしれません。
今回は多くの例に習ってタプルにしました。
剛体の作成・配置
worldを作成したのでこの中に剛体を作成・配置していきましょう。
# 剛体の作成 + 質量特性の設定 Ball_Body = ode.Body(world) M = ode.Mass() M.setSphere(2500.0, 0.5) Ball_Body.setMass(M) Ball_Body.setPosition((0, 4, 0))
ode.Body(world)で、作成したworldにBody(剛体)を作成します。
剛体は質量特性を設定するオブジェクトなのでこのBodyにode.Massで質量特性を設定します。
M.setSphere(密度[kg/m3], 半径[m])としてMに球状の質量特性=密度+半径を設定します。
この場合、半径0.5[m]の質量分布は2500.0[kg/m3]で一定です。
次に作成した質量特性Mを剛体Ball_BodyにBall_Body.setMass(M)で設定します。
最後にBall_Body.setPosition*1で剛体の初期位置を設定します。
コードの解説~VPythonの設定~
VPythonの設定は完全に見た目だけのものです。
内部的な計算は全てODEでなされているのでVPythonではODEの設定をどう反映させるかだけが問題となります。
""" VPython環境の設定 """ Floor = vs.box(length=4, height=0.2, width=4) Floor.pos = vs.vector(0, 0, 0) Ball_VS = vs.sphere(pos=Ball_Body.getPosition(), radius=0.5, color=vs.color.red)
床とボールの作成・設定
FloorとBall_VSを作成します。
vs.box(length, height, width)という形で引数を受け付けています。
(length, height, width)=(x, y, z)という形で軸が対応しています。
作成したFloorの位置をFloor.pos=vs.vector(x, y, z)という形で設定します。タプル、リスト、numpyのndarray、VPythonのvectorでも大丈夫です。今回はVPythonのvectorにしてみました。
この位置はboxの中心ではなく、6面のうちのどこかの面の中心が(x, y, z)に一致するようになっています。それだけではもちろんボックスの向きが分かりません。そこでFloor.axis=(x, y, z)という形でボックスの向きのベクトルを設定できます。
コードの解説~シミュレーションループ:VPythonとODEの連携~
実は連携というほど大げさなものじゃないです。
ODEで計算 → VPythonで見た目を描画
を繰り返すだけです。
if __name__=="__main__": dt = 0.05 total_time = 0.0 while total_time<2.0: """ ODEによってVPythonのボールの位置を更新 """ Ball_VS.pos = Ball_Body.getPosition() vs.rate(1/dt) # VPythonのアニメーションを進める world.step(dt) # ODEの計算を進める total_time += dt vs.exit()
dtはODEを計算するための時間ステップです。
dt=0.05[sec]ごとに計算が進むことになります。
Ball_VS.pos = Ball_Body.getPosition()によってODEで計算した結果をVPythonに渡します。
vs.rate(1/dt)によってVPythonの描画スピードを1/0.05=20[frames/sec]と設定します。
world.step(dt)によってODEの計算をdtだけ進めます。
whileループを外れたあと、vs.exit()によってVPythonの描画を終了しウィンドウを閉じます。
*1:0, 4, 0