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

[物理エンジン]を使って、ゲームを作ってみたくなった。

[物理エンジン]:https://ja.wikipedia.org/wiki/%E7%89%A9%E7%90%86%E6%BC%94%E7%AE%97%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%B3

前回、書かせていただいた記事がこちら。
「Javascript で動く軽量[物理エンジン] OimoPhysics と3Dレンダラ Three.js で書いた短いサンプルコード」

https://qiita.com/yamazaki3104/items/797b28bba833c06f5f25

その後、OimoPhysicsの続きというか、ジョイントのコードをひたすら書いていたのだが、、、どうも、うまく書けない、、、というかコードと実際の動きが付合しない。
ので、遠回りかもしれないが、他の物理エンジン cannon.js でも書いてみようと思った。

cannon.js

http://schteppe.github.io/cannon.js/

今回、写経させていただいたコードがこちら。前回といい、今回といい、お世話になりっぱなしです。ありがとうございます！！
~~http://jsdo.it/cx20/qM1k~~
~~（なんか、最近よく止まっている気がする＞jsdo.it）~~
> jsdo.itは 2019/10/31 を持ちまして終了いたしました。


写経した結果がこちら。

なんか、摩擦係数がちがうみたいで、、、非常にもどかしい。（その後、摩擦係数を指定してなめらかに動くようになりました。おさわがせしました。）
<p class="codepen" data-height="400" data-theme-id="0" data-default-tab="result" data-user="yamazaki3104" data-slug-hash="YmXzjY" style="height: 400px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="cannon.js + three.js example code">
  <span>See the Pen <a href="https://codepen.io/yamazaki3104/pen/YmXzjY/">
  cannon.js + three.js example code</a> by yamazaki.3104 (<a href="https://codepen.io/yamazaki3104">@yamazaki3104</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

前回より、若干短くなって、149行。

```html
<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>
<!-- cannon.js v0.6.2     --><script src="https://cdnjs.cloudflare.com/ajax/libs/cannon.js/0.6.2/cannon.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.cannon_rigid_body = _arg.body
        this.scene.add( mesh )
    }

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

            mesh.position.copy(   mesh.cannon_rigid_body.position   )
            mesh.quaternion.copy( mesh.cannon_rigid_body.quaternion )
        }

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

class CANNON_PHYSICS
{
    constructor( _threejs )
    {
        this.cannon_world = new CANNON.World()
        this.cannon_world.gravity.set( 0, -9.80665, 0 )
        this.cannon_world.broadphase = new CANNON.NaiveBroadphase()
        this.cannon_world.solver.iterations = 10

        this.threejs = _threejs
    }

    create_rigid_body( _arg )
    {
        const body  = new CANNON.Body( {
            mass:       _arg.mass,
            shape:      new CANNON.Box( new CANNON.Vec3( _arg.w/2, _arg.h/2, _arg.d/2 ) ),
            position:   new CANNON.Vec3( _arg.x, _arg.y, _arg.z ),
            material:   new CANNON.Material( { friction: 0.1, } ), // 摩擦係数 0.1 マテリアルを作成
        } )
        this.cannon_world.add( body )
        this.threejs.add_mesh( { body: body, w: _arg.w, h: _arg.h, d: _arg.d, color: _arg.color } )
    }

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

// MAIN
let cannon_phy = new CANNON_PHYSICS( new THREEJS() )

cannon_phy.create_rigid_body( {
    mass: 0, x: 0, y: -0.2, z: 0, w: 50, h: 0.4, d: 50, color: 0x333333,
} )


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++ ) {
        cannon_phy.create_rigid_body( {
            mass: 1,
            x: (x-7) * box_size * 0.95, y: box_size * 0.5, z: (y-7) * box_size * 1.2,
            w: box_size*0.1, 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++ ) {
    cannon_phy.create_rigid_body({
        mass: 3,
        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()
{
    cannon_phy.render( 1 / 60 )
    window.requestAnimationFrame( animate )
}
window.requestAnimationFrame( animate )

</script>
</body>
</html>
```
Three.js のコードは、ほぼほぼ前回といっしょ。

OimoPhysics の部分を cannon.js に変えただけ。割と容易にできました。

摩擦の設定に関しては以下のコードを参考にさせていただきました！！ありがとうございました。

「Three.jsとCANNON.jsで作る「VR3D脱出迷路アプリ」作成（その1）」

https://qiita.com/DAI788/items/4d7397e7918eb9be6d81

```JS
var groundMat = new CANNON.Material('groundMat');    //マテリアルを作成
groundMat.friction = 0.3;       //摩擦係数
groundMat.restitution = 0.5;    //反発係数

phyPlane = new CANNON.Body({mass: 0});      //ボディを作成
phyPlane.material = groundMat;              //ボディにマテリアルを設定
```

次こそ、ジョイントのコードを書く！！

追記です。
ジョイント（ヒンジだけど）のコード⬇️を参考にして、くるまらしきモノを乱入させてみた。

https://github.com/schteppe/cannon.js/blob/master/demos/hinge.html

コードはちょっと長くなってしまって、短くはないかな。コードが見たいひとは、CodePenからどうぞ。

<p class="codepen" data-height="400" data-theme-id="0" data-default-tab="result" data-user="yamazaki3104" data-slug-hash="xvwJwo" style="height: 400px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="cannon.js + three.js example code 2">
  <span>See the Pen <a href="https://codepen.io/yamazaki3104/pen/xvwJwo/">
  cannon.js + three.js example code 2</a> by yamazaki.3104 (<a href="https://codepen.io/yamazaki3104">@yamazaki3104</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>

みなさまのおかげで、だいぶ理解できてきた気がする。

この調子で、ゲームにしていこう！！