1
1

More than 3 years have passed since last update.

JavaScriptでaddForceみたいな物理演算を実装した形跡があって実際に動いているけどどういうコードなのか分からないという話

Posted at

pixi.jsで物理演算を実装したかったという気持ちは覚えている。
1年くらい前に必死こいてそれっぽいものを作ったのも覚えている。
しかし、今見直してみると何が書いてあるのかイマイチ読み解けない。
なので戒めとしてここに晒すことにした。

script.js
//Aliases
let Application = PIXI.Application,
    Sprite = PIXI.Sprite,
    Rectangle = PIXI.Rectangle,
    Graphics = PIXI.Graphics;

const width = 800;
const height = width*0.7;

let app= new Application({
    width:width, 
    height:height,
    antialias: false,
    transparent: false,
    resolution: 1,
    backgroundColor: 0xf0ffff,
    autoResize: true,
});

document.getElementById("game").appendChild(app.renderer.view);

let rectangle;
const recwid = 10;
const rechei = 100;
let stage;
let hand;

setup();

function setup(){
    //クリック感知用の透明なコンテナ
    stage = new Sprite();
    stage.width = width;
    stage.height = height;
    stage.x = stage.y = 0;
    stage.interactive = true;
    stage
        .on("pointerdown",onClick)
        .on("pointermove",mouseMove)
        .on("pointerup",outClick)
        .on("pointerout",outClick);
    app.stage.addChild(stage);

    //動く長方形
    rectangle = new Graphics();
    rectangle.beginFill(0x66CCFF);
    rectangle.drawRect(0,0,recwid,rechei);
    rectangle.endFill();
    rectangle.x = 400;
    rectangle.y = 100;
    rectangle.vx = rectangle.vy = rectangle.ax = rectangle.ay = rectangle.rv = 0;
    rectangle.m = 1000;
    rectangle.mm = 10000;
    rectangle.rotation = 40;
    rectangle.pivot.set(recwid/2,rechei/2);
    app.stage.addChild(rectangle);

    //長方形を引き寄せる手
    hand = new Graphics();
    hand.beginFill(0x9966FF);
    hand.drawCircle(0,0,10);
    hand.endFill();
    app.stage.addChild(hand);
    hand.x = 400;
    hand.y = 100;

    app.ticker.add(delta => play(delta));
}



const grav = 100;//重力
const maxVel = 30;//速さの最大
const maxrv = 10;//改定速度の最大
const handForce = 200;//手が引っ張る力
const mouseForce = 10;//マウスが引っ張る力
function play(delta) {

    //マウスで引っ張る部分の座標計算
    rectangle.xx = rectangle.x+Math.sin(-rectangle.rotation)*rechei/2;
    rectangle.yy = rectangle.y+Math.cos(-rectangle.rotation)*rechei/2;
    //手に引っ張られる部分の座標計算
    rectangle.xxx = rectangle.x-Math.sin(-rectangle.rotation)*rechei/2;
    rectangle.yyy = rectangle.y-Math.cos(-rectangle.rotation)*rechei/2;

    rectangle.ax = 0;
    rectangle.ay = 0;
    if(clicking) addForce(rectangle,0,1,mouseForce*(mouse.x-rectangle.xx),mouseForce*(mouse.y-rectangle.yy));//マウス方向に力が働く
    addForce(rectangle,0,0,0,grav);//重力は常に働く
    if(clicking) addForce(rectangle,0,-1,handForce*(hand.x-(rectangle.xxx+rectangle.vx)),handForce*(hand.y-(rectangle.yyy+rectangle.vy)));//手が棒を持つ

    rectangle.vx += rectangle.ax/rectangle.m;
    rectangle.vy += rectangle.ay/rectangle.m;

    rectangle.vx = Math.min(Math.max(rectangle.vx,-maxVel),maxVel);
    rectangle.vy = Math.min(Math.max(rectangle.vy,-maxVel),maxVel);
    rectangle.av = Math.min(Math.max(rectangle.av,-maxrv),maxrv);

    rectangle.rotation += rectangle.rv*2*Math.PI/60;
    rectangle.x += rectangle.vx;
    rectangle.y += rectangle.vy;
}


function hypo(a,b,c,d){//二点間の距離 0の時は0.0001を返す(0除算回避)
    let f = Math.pow(Math.pow(a-c,2)+Math.pow(b-d,2),1/2);
    if (f!=0) return Math.pow(Math.pow(a-c,2)+Math.pow(b-d,2),1/2);
    else return (f+0.0001);
}

function addForce(obj,px,py,x,y){//pは-1~1
    let angle = Math.PI/2 -obj.rotation;

    let d1 = hypo(0,0,x,y);
    let d2 = hypo(0,0,px*obj.width/2,py*obj.height/2);
    let d3 = hypo(0,0,px,py);

    let cos1 = Math.cos(angle);
    let sin1 = Math.sin(angle);
    let cos2 = x/d1;
    let sin2 = y/d1;
    let cos3 = (px*obj.width/2)/d2;
    let sin3 = (py*obj.height/2)/d2;

    obj.rv += d3*d1*((cos1*cos3-sin1*sin3)*cos2 +(sin1*cos3-cos1*sin3)*sin2)/obj.mm;

    obj.ax += x;
    obj.ay += y;
}



//クリックイベント
let mouse = {};
mouse.x = 0;
mouse.y = 0;
let clicking = false;
function onClick(e){
    this.data = e.data;
    mouse.x = this.data.getLocalPosition(this.parent).x![Animation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482250/29dae441-65ee-17b8-f77d-bafdf5470aae.gif)

    mouse.y = this.data.getLocalPosition(this.parent).y
    clicking= true;
}
function outClick(e){
    clicking = false;
    this.data = null;
}
function mouseMove(e){
    this.data = e.data;
    mouse.x = this.data.getLocalPosition(this.parent).x
    mouse.y = this.data.getLocalPosition(this.parent).y
}

Animation.gif
長方形に物理演算が適用されており、クリックしている間は手(紫の円)に強く引っ張られ、同時にマウスポインタに弱く引っ張られるというものだ。

元のコードにはコメントがほとんど書かれていなかったので、今分かる部分には最低限のマナーとしてコメントで補完をした。
特にaddForce関数の中身が分からない。三角関数の加法定理と睨み合いながら無理やり動くようにしたんだろうなということは分かる。助けて~~~~~

1
1
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
1
1