ドラッグして飛ばす

赤い箱をドラッグすると、ドラッグした座標に追従して赤い箱が動きます。 ドラッグが解除されたとき、事前のドラッグ操作に合わせて箱を投げることができます。

動かし方

ダウンロードした zip ファイルを解凍して以下のコマンドを実行してください。

npm install
akashic-sandbox .

ソースコード

"use strict";

var box2d = require("@akashic-extension/akashic-box2d");

// 2×2 の行列
var b2Mat22 = box2d.Box2DWeb.Common.Math.b2Mat22;

/** 物理世界のプロパティ */
var worldProperty = {
  gravity: [0.0, 9.8], // 重力の方向(m/s^2)
  scale: 50, // スケール(pixel/m)
  sleep: true // 停止した物体を演算対象としないかどうか
};
/** 物理エンジンの世界 */
var physics = new box2d.Box2D(worldProperty);

/** 箱の生成パラメータ */
var boxParameter = {
  /** 表示情報のパラメータ */
  appear: {
    width: 1.0 * worldProperty.scale,
    height: 1.0 * worldProperty.scale,
    cssColor: "crimson"
  },
  /** 物理定義 */
  physics: {
    /** 物理挙動 */
    body: physics.createBodyDef({
      type: box2d.BodyType.Dynamic // 自由に動ける物体
    }),
    /** 物理性質 */
    fixture: physics.createFixtureDef({
      density: 1.0, // 密度
      friction: 0.5, // 摩擦係数
      restitution: 0.3, // 反発係数
      shape: physics.createRectShape(1.0 * worldProperty.scale, 1.0 * worldProperty.scale) // 衝突判定の形(1m × 1m の矩形)
    })
  }
};
/** 壁の生成パラメータ */
var wallParameter = {
  appear: {
    width: 0.3 * worldProperty.scale,
    height: g.game.height,
    cssColor: "royalblue"
  },
  physics: {
    body: physics.createBodyDef({
      type: box2d.BodyType.Static // 固定されて動かない物体
    }),
    fixture: physics.createFixtureDef({
      density: 1.0,
      friction: 0.3,
      restitution: 0.7,
      shape: physics.createRectShape(0.3 * worldProperty.scale, g.game.height)
    })
  }
};
/** 床・天井の生成パラメータ */
var floorParameter = {
  appear: {
    width: g.game.width,
    height: 0.3 * worldProperty.scale,
    cssColor: "royalblue"
  },
  physics: {
    body: physics.createBodyDef({
      type: box2d.BodyType.Static
    }),
    fixture: physics.createFixtureDef({
      density: 1.0,
      friction: 0.3,
      restitution: 0.7,
      shape: physics.createRectShape(g.game.width, 0.3 * worldProperty.scale)
    })
  }
};

function main() {
  var scene = new g.Scene({ game: g.game });

  scene.loaded.add(function() {
    // 左の壁を生成する
    var leftWall = createRect(scene, wallParameter);
    // ※ box2dの座標は実距離(単位はメートル)で指定する
    var leftWallPos = physics.vec2(0, 0);
    // ※ 指定された座標はオブジェクトの中心座標に設定される
    leftWallPos.Add(calcCenter(leftWall.entity));
    // ※ 表示はbox2dのbodyの座標に同期するので、box2dの座標だけ書き換える
    leftWall.b2body.SetPosition(leftWallPos);

    // 右の壁を生成する
    var rightWall = createRect(scene, wallParameter);
    var rightWallPos = physics.vec2(g.game.width - rightWall.entity.width, 0);
    rightWallPos.Add(calcCenter(rightWall.entity));
    rightWall.b2body.SetPosition(rightWallPos);

    // 床を生成する
    var floor = createRect(scene, floorParameter);
    var floorPos = physics.vec2(0, g.game.height - floor.entity.height);
    floorPos.Add(calcCenter(floor.entity));
    floor.b2body.SetPosition(floorPos);

    // 天井を生成する
    var ceil = createRect(scene, floorParameter);
    var ceilPos = physics.vec2(0, 0);
    ceilPos.Add(calcCenter(ceil.entity));
    ceil.b2body.SetPosition(ceilPos);

    // 箱を生成する
    var box = createRect(scene, boxParameter);
    box.b2body.SetPosition(calcCenter(g.game));

    // タッチされている座標(Box2D上の絶対座標)
    var anchor;

    // 箱を触れるようにする
    box.entity.touchable = true;

    /** マウスと箱の紐づけ */
    var mouseJoint = null;

    // 箱がタッチされたときの処理
    box.entity.pointDown.add(function(event) {
      // 既にマウスジョイントが生成されていた場合は消しておく
      if (mouseJoint !== null) {
        physics.world.DestroyJoint(mouseJoint);
        mouseJoint = null;
      }

      // タッチした位置の絶対座標を計算する
      // ※ Box2Dが必要とするタッチ座標は、画面左上を原点とした絶対座標。
      // ※ g.PointDownEventから取得できる座標は、ポインティング対象からの
      //    相対座標なので、タッチ座標に対象となるb2bodyの回転を与えた後、
      //    b2bodyの座標を足すことで絶対座標へと変換する。
      // ※ 回転の軸をg.Entityの中心にするため、回転の前にタッチ座標から
      //    中心座標を引いておく。
      anchor = physics.vec2(event.point.x, event.point.y);
      anchor.Subtract(calcCenter(box.entity));
      anchor.MulM(b2Mat22.FromAngle(box.b2body.GetAngle()));
      anchor.Add(box.b2body.GetPosition());

      // マウスと箱の紐づけを作成
      var mouseJointDef = new box2d.Box2DWeb.Dynamics.Joints.b2MouseJointDef();
      // 紐づける2つの物体
      // ※ 今回はマウスと結び付けるので、bodyAにはworld.GetGroundBodyを指定する。
      mouseJointDef.bodyA = physics.world.GetGroundBody();
      mouseJointDef.bodyB = box.b2body;
      // bodyAとbodyBの衝突判定を行うかどうか
      mouseJointDef.collideConnected = true;
      // マウスの引き寄せる力
      // ※ この値が大きいと、マウスにぴったりと追従するように動き、
      //    小さいと、ヨーヨーのように少し遅れてマウスに追従する。
      mouseJointDef.maxForce = 1000.0 * box.b2body.GetMass();
      // ジョイントの初期位置
      // ※ この座標を基準にジョイントが生成されるので、以降マウスの位置を変更しても
      //    この座標と現在のb2bodyの座標との距離が保たれるような挙動を行う。
      mouseJointDef.target = anchor;

      // マウスジョイントを生成
      mouseJoint = physics.world.CreateJoint(mouseJointDef);

      // 色を変更する
      box.entity.cssColor = "teal";
      box.entity.modified();
    });
    // タッチ中の座標が移動したときの処理
    box.entity.pointMove.add(function(event) {
      // タッチ座標を更新する
      anchor.Add(physics.vec2(event.prevDelta.x, event.prevDelta.y));
      mouseJoint.SetTarget(anchor);
    });
    // 箱が離されたときの処理
    box.entity.pointUp.add(function() {
      // 箱とマウスの紐づけを解除
      physics.world.DestroyJoint(mouseJoint);
      mouseJoint = null;

      // 色を元に戻す
      box.entity.cssColor = "crimson";
      box.entity.modified();
    });

    scene.update.add(function() {
      // 物理エンジンの世界をすすめる
      // ※ step関数の引数は秒数なので、1フレーム分の時間(1.0 / g.game.fps)を指定する
      physics.step(1.0 / g.game.fps);
    });
  });

  g.game.pushScene(scene);
}

/**
 * 衝突判定を持つ矩形を生成する
 * @param {g.Scene} scene 描画を行うシーン
 * @param {Object} parameter 矩形の生成パラメータ
 */
function createRect(scene, parameter) {
  // 表示用の矩形(1m × 1m)を生成
  var rect = new g.FilledRect({
    scene: scene,
    width: parameter.appear.width,
    height: parameter.appear.height,
    cssColor: parameter.appear.cssColor
  });
  scene.append(rect);

  // 表示用の矩形と衝突判定を結び付けて返す
  return physics.createBody(rect, parameter.physics.body, parameter.physics.fixture);
}

/**
 * オブジェクトの中心座標を計算する
 * @param {Object} obj 中心座標を計算するオブジェクト
 */
function calcCenter(obj) {
  return physics.vec2(obj.width / 2, obj.height / 2);
}

module.exports = main;

© DWANGO Co., Ltd.