LoginSignup
7
6

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-06-30

以前から「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

jsdo.itは 2019/10/31 を持ちまして終了いたしました。

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

その結果がこちら👇。やく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

7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6