乗車日記

自転車ときのこ

自転車シミュレーター計画再開

Enchant.jsでリアルタイム表示が可能になったので、自転車シミュレータ計画再開。
今度はフォークだけでなく、フレームも人間もシミュレートしてみたい。
剛体の計算はいろいろと矛盾が出てきそうなので、質点間を張力で結ぶモデルにしたい。剛体的なものもトラス構造で再現できるはず。各点をオブジェクトで表すと、各点は周りのつながっている点の情報だけを持っていれば良いので、行列を使うより効率が良い。各点が自律的に動くようなモデルは普通に角と面倒くさいのでこれまでやったことはなかったが、イベント駆動型のプログラムだと書きやすいことを知った。なかなか面白い。

今日のところは自転車フレームの形を作って動かしてみた。物理パラメータやスケールは適当。リアエンドとヘッドチューブに水平の力を加えている。今のところダンピングが入っていないのでフレームが振動する。
http://jsdo.it/tasanokona/s7Q2/fullscreen

enchant();
var NODE = enchant.Class.create(Sprite,{
        initialize: function(x_,y_,mass_,num_connect_){
            enchant.Sprite.call(this, 1, 1);
            this.image = game.assets[files.icon0];
            this.mass=mass_;
            this.num_connect=num_connect_;
            this.x=x_;
            this.y=y_;
            this.scale(10);
            this.vx=0.0;
            this.vy=0.0;
            this.ax=0.0;
            this.ay=0.0;
            this.connect=Array(num_connect_);    
            this.r0=Array(num_connect_);
            this.k=Array(num_connect_);
            this.addEventListener("enterframe", function(){
                this.ax=0;
                this.ay=0;
                for(var i=0;i<this.num_connect;i++){
                    var dx=node[this.connect[i]].x-this.x;
                    var dy=node[this.connect[i]].y-this.y;
                    var r=Math.sqrt(dx*dx+dy*dy)
                    var dr=r/this.r0[i]-1.0;
                    var f_=this.k[i]*dr/r
                    this.ax+=f_*dx;
                    this.ay+=f_*dy;
                }
            });
            this.addEventListener("init", function(){
                for(var i=0;i<this.num_connect;i++){
                    var dx=node[this.connect[i]].x-this.x;
                    var dy=node[this.connect[i]].y-this.y;
                    this.r0[i]=Math.sqrt(dx*dx+dy*dy);
                }
            });
            this.addEventListener("move", function(){
                this.x+=(this.vx+this.ax*hdt)*dt;
                this.y+=(this.vy+this.ay*hdt)*dt;
                this.vx+=this.ax*dt;
                this.vy+=this.ay*dt;
            });
            game.rootScene.addChild(this);

        }

    });


window.onload = function(){
    GameSize=320;
    game = new Core(GameSize*4, GameSize);
    game.fps = 30;
    files = { 
        icon0: "http://jsrun.it/assets/u/R/9/j/uR9j0.png"
    }; 
    game.preload(files.icon0);

    game.onload = function(){
        game.rootScene.backgroundColor = "black";
        dt=1/30.0;
        hdt=dt/2.0;
        NumNODE=4;
        var k0=1000; //N/100%
        node=new Array(NumNODE);
        node[0]=new NODE(0,180,1,2);
        node[1]=new NODE(100,180,1,3);
        node[2]=new NODE(80,110,1,3);
        node[3]=new NODE(180,110,1,2);

        node[0].connect[0]=1;
        node[0].connect[1]=2;
        node[0].k[0]=k0;
        node[0].k[1]=k0;
        
        node[1].connect[0]=0;
        node[1].connect[1]=2;
        node[1].connect[2]=3;
        node[1].k[0]=k0;
        node[1].k[1]=k0;
        node[1].k[2]=k0;

        node[2].connect[0]=0;
        node[2].connect[1]=1;
        node[2].connect[2]=3;
        node[2].k[0]=k0;
        node[2].k[1]=k0;
        node[2].k[2]=k0;

        node[3].connect[0]=1;
        node[3].connect[1]=2;
        node[3].k[0]=k0;
        node[3].k[1]=k0;
        
	    for(var i=0;i<NumNODE;i++) node[i].dispatchEvent(new enchant.Event("init"));
        game.addEventListener("exitframe", function(){
            node[0].ax+=20;
            node[3].ax+=20;
    	    for(var i=0;i<NumNODE;i++) node[i].dispatchEvent(new enchant.Event("move"));      
        });
        
    };
    game.start();
};