Edited at

Javascript で動く軽量物理エンジン OimoPhysics と3Dレンダラ Three.js で書いた短いサンプルコード

以前から「OimoPhysics」を使ってゲームを作ってみたいと思っていた。Three.js は仕事でも使ってるくらいなのだが、物理エンジンに関しては、まったくのど素人。

OimoPhysics は、Javascriptで動く軽量な物理エンジン。

Three.jsは、みなさんご存知の3D描画ライブラリ。

で、ググるものの、サンプルコードが見つからない。最大あって、ここ👇くらい。

http://el-ement.com/blog/2018/01/13/oimophysics-released/

Oimo.js のコードはいくらか見つかるのだが、Oimo.js は、OimoPhysics の分家らしいので、ここは本家 OimoPhysics にこだわってみたい。

OimoPhysics

https://github.com/saharan/OimoPhysics

Oimo.js

https://lo-th.github.io/Oimo.js/#kinematic3

Three.js

https://threejs.org/

で、見つけたのがこのサンプル👇。いやぁ、すごい力作です。いろいろな物理エンジンとレンダラの組み合わせがすごいです。

https://qiita.com/cx20/items/3ebed669fb9c9e797935

http://jsdo.it/cx20/Goua

これ以上ググっても、堂々巡りな感じがしてきたので、こちらのコードを足がかりに、自分が理解できるレベルまで写経してみようと思った。(先人のコードに感謝)

その結果がこちら👇。やく166行のhtml。


See the Pen
OimoPhysics.js + Three.js
by yamazaki.3104 (@yamazaki3104)
on CodePen.

<html style='margin: 0;height: 100%;'>

<!-- Three.js r106 --><script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/106/three.js"></script>
<!-- TrackballControls.js --><script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js@r106/examples/js/controls/TrackballControls.js"></script>
<!-- OimoPhysics.js v1.2.0 --><script src="https://cdn.jsdelivr.net/gh/saharan/OimoPhysics@1.2.0/bin/js/OimoPhysics.js"></script>
<body style='margin: 0;height: 100%;'>
<script>

class THREEJS
{
constructor()
{
const w = document.body.clientWidth
const h = document.body.clientHeight

this.renderer = new THREE.WebGLRenderer()
this.renderer.setClearColor( 0x8888dd )
this.renderer.setSize( w, h )

this.camera = new THREE.PerspectiveCamera( 40, w / h, 0.1, 1000 )
this.camera.position.x = 0
this.camera.position.y = 20
this.camera.position.z = 30
this.trackball = new THREE.TrackballControls( this.camera )

this.scene = new THREE.Scene()

let directionalLight = new THREE.DirectionalLight( 0xffffff, 1 )
directionalLight.position.set( 0.2, 0.5, 0.3 )
this.scene.add( directionalLight )

// this.scene.add( new THREE.AmbientLight( 0x101020 ) )

document.body.appendChild( this.renderer.domElement )
}

add_mesh( _arg )
{
let mesh = new THREE.Mesh(
new THREE.BoxGeometry( _arg.w, _arg.h, _arg.d ),
new THREE.MeshLambertMaterial( { color: _arg.color } )
)
mesh.oimo_rigid_body = _arg.body
this.scene.add( mesh )
}

render()
{
for ( let mesh of this.scene.children )
{
if ( ! mesh.oimo_rigid_body ) continue

const pos = mesh.oimo_rigid_body.getPosition()
mesh.position.x = pos.x
mesh.position.y = pos.y
mesh.position.z = pos.z

const ori = mesh.oimo_rigid_body.getOrientation()
mesh.quaternion.x = ori.x
mesh.quaternion.y = ori.y
mesh.quaternion.z = ori.z
mesh.quaternion.w = ori.w
}

this.trackball.update()
this.renderer.render( this.scene, this.camera )
}
}

class OIMO_PHYSICS
{
constructor( _threejs )
{
this.oimo_world = new OIMO.World()
this.oimo_world.gravity = new OIMO.Vec3( 0, -9.80665, 0 )

let ground_cnf = new OIMO.RigidBodyConfig()
ground_cnf.type = OIMO.RigidBodyType.STATIC // DYNAMIC, KINEMATIC, STATIC
ground_cnf.position = new OIMO.Vec3( 0, -0.2, 0 )
let rigid_body = new OIMO.RigidBody( ground_cnf )

let ground_shape_cnf = new OIMO.ShapeConfig()
ground_shape_cnf.geometry = new OIMO.BoxGeometry( new OIMO.Vec3( 25, 0.2, 25 ) )
rigid_body.addShape( new OIMO.Shape( ground_shape_cnf ) )

this.oimo_world.addRigidBody( rigid_body )

this.threejs = _threejs
this.threejs.add_mesh( { body: rigid_body, w: 25*2, h: 0.2*2, d: 25*2, color: 0x333333 } )
}

create_rigid_body( _arg )
{
let body_cnf = new OIMO.RigidBodyConfig()
body_cnf.type= OIMO.RigidBodyType.DYNAMIC // DYNAMIC, KINEMATIC, STATIC
body_cnf.position = new OIMO.Vec3( _arg.x, _arg.y, _arg.z )
let rigid_body = new OIMO.RigidBody( body_cnf )

let shape_cnf = new OIMO.ShapeConfig()
shape_cnf.geometry = new OIMO.BoxGeometry( new OIMO.Vec3( _arg.w / 2, _arg.h / 2, _arg.d / 2 ) )
rigid_body.addShape( new OIMO.Shape( shape_cnf ) )

this.oimo_world.addRigidBody( rigid_body )

this.threejs.add_mesh( { body: rigid_body, w: _arg.w, h: _arg.h, d: _arg.d, color: _arg.color } )

return rigid_body
}

render( _sec )
{
this.oimo_world.step( _sec )
this.threejs.render()
}
}

// MAIN
let oimo_p = new OIMO_PHYSICS( new THREEJS() )

const dataSet = [
0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,
0,0,0,0,0,0,3,3,3,3,3,0,0,1,1,1,
0,0,0,0,0,3,3,3,3,3,3,3,3,3,1,1,
0,0,0,0,0,2,2,2,1,1,2,1,0,3,3,3,
0,0,0,0,2,1,2,1,1,1,2,1,1,3,3,3,
0,0,0,0,2,1,2,2,1,1,1,2,1,1,1,3,
0,0,0,0,2,2,1,1,1,1,2,2,2,2,3,0,
0,0,0,0,0,0,1,1,1,1,1,1,1,3,0,0,
0,0,3,3,3,3,3,5,3,3,3,5,3,0,0,0,
0,3,3,3,3,3,3,3,5,3,3,3,5,0,0,2,
1,1,3,3,3,3,3,3,5,5,5,5,5,0,0,2,
1,1,1,0,5,5,3,5,5,4,5,5,4,5,2,2,
0,1,0,2,5,5,5,5,5,5,5,5,5,5,2,2,
0,0,2,2,2,5,5,5,5,5,5,5,5,5,2,2,
0,2,2,2,5,5,5,5,5,5,5,0,0,0,0,0,
0,2,0,0,5,5,5,5,0,0,0,0,0,0,0,0
]

const box_size = 1.5
for ( let y=0 ; y<16; y++ ) {
for ( let x=0 ; x<16; x++ ) {
oimo_p.create_rigid_body( {
x: (x-7) * box_size * 0.95, y: box_size * 0.5, z: (y-7) * box_size * 1.2,
w: box_size*0.2, h: box_size*1, d: box_size*1,
color: [ 0xDCAA6B, 0xffcccc, 0x800000, 0xff0000, 0xffff00, 0x0000ff ][ dataSet[ y * 16 + x ] ],
})
}
}

for ( let i=0 ; i<16; i++ ) {
oimo_p.create_rigid_body({
x: -7 * box_size, y: box_size * 3, z: (i-7) * box_size * 1.2,
w: box_size, h: box_size, d: box_size,
color: 0xeeeeee
})
}

function animate()
{
oimo_p.render( 1 / 30 )
window.requestAnimationFrame( animate )
}
window.requestAnimationFrame( animate )

</script>
</body>
</html>

おもに、Three.js と OimoPhysics の役割がわかりやすくなるように class を分けた。

「Javascript で class なんて使わないよねー」って言っていたのに、今回は使ってみちゃいました。

この使い方であっているかは疑問。

さて、これを足がかりにして、次はジョイントを理解して、最終的には「じゃがいもゲーム」を作りたい!!

ってか、Joint のコードがまったく見当たらないのですが。どの辺りから攻めたらよいのでしょう(呆然)

このあたりかなぁ

https://github.com/saharan/OimoPhysics/blob/master/demos/src/demo/core/JointsDemo.hx