見下ろし視点の「おはじき」をイメージしたサンプルです。 画面上に配置された円形を始点にドラッグすることで、ドラッグ方向とは反対に円を飛ばすことができます。
ダウンロードした zip ファイルを解凍して以下のコマンドを実行してください。
npm install
akashic-sandbox .
"use strict";
var box2d = require("@akashic-extension/akashic-box2d");
/** 2次元ベクトル */
var b2Vec2 = box2d.Box2DWeb.Common.Math.b2Vec2;
/** 2×2 の行列 */
var b2Mat22 = box2d.Box2DWeb.Common.Math.b2Mat22;
/** 物理世界のプロパティ */
var worldProperty = {
gravity: [0.0, 0.0], // 重力の方向(m/s^2)
scale: 50, // スケール(pixel/m)
sleep: true // 停止した物体を演算対象としないかどうか
};
/** 物理エンジンの世界 */
var physics = new box2d.Box2D(worldProperty);
/** おはじきを弾く力 */
var power = 10;
/** おはじきのパラメータ */
var hajikiParameter = {
appear: {
width: 1.0 * worldProperty.scale,
height: 1.0 * worldProperty.scale
},
/** 物理定義 */
physics: {
/** 物理挙動 */
body: physics.createBodyDef({
type: box2d.BodyType.Dynamic // 自由に動ける物体
}),
/** 物理性質 */
fixture: physics.createFixtureDef({
density: 5.0, // 密度
friction: 1.0, // 摩擦係数
restitution: 0.999, // 反発係数
shape: physics.createCircleShape(1.0 * worldProperty.scale) // 衝突判定の形(直径 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,
assetIds: ["circle", "circle_touch", "arrow"]
});
scene.loaded.add(function() {
// 壁を生成
createWall(scene);
// おはじきを7個ランダムな位置に生成する
for (var i = 0; i < 7; ++i) {
createHajiki(scene, randomPosition());
}
scene.update.add(function() {
// 物理エンジンの世界をすすめる
// ※ step関数の引数は秒数なので、1フレーム分の時間(1.0 / g.game.fps)を指定する
physics.step(1.0 / g.game.fps);
});
var begin;
scene.pointDownCapture.add(function(event) {
begin = new b2Vec2(event.point.x, event.point.y);
});
scene.pointUpCapture.add(function(event) {
var end;
end = new b2Vec2(event.startDelta.x, event.startDelta.y);
end.Add(begin);
});
});
g.game.pushScene(scene);
}
/**
* オブジェクトの中心座標を計算する
* @param {Object} obj 中心座標を計算するオブジェクト
*/
function calcCenter(obj) {
return physics.vec2(obj.width / 2, obj.height / 2);
}
/**
* ランダムに生成された座標を返す
* ※ 生成される座標は画面の外枠から 1.0m 内側に限定される
*/
function randomPosition() {
var scale = worldProperty.scale;
var x = g.game.random.generate() * (g.game.width - scale) + scale;
var y = g.game.random.generate() * (g.game.height - scale) + scale;
return physics.vec2(x, y);
}
/**
* 衝突判定を持つ矩形を生成する
* @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 {g.Scene} scene 描画を行うシーン
* @param {Object} parameter 円の生成パラメータ
*/
function createCircle(scene, parameter) {
// 画像をまとめる空のエンティティを生成
var entity = new g.E({
scene: scene,
width: parameter.appear.width,
height: parameter.appear.height
});
scene.append(entity);
// 表示用の円形を生成
// ※ AkashicEngineでは円を描画することができないので、画像で描画する
var circle = new g.Sprite({
scene: scene,
src: scene.assets["circle"],
srcWidth: 100,
srcHeight: 100,
width: parameter.appear.width,
height: parameter.appear.height
});
entity.append(circle);
// タッチされたとき用の円形スプライトを生成
var circle_touch = new g.Sprite({
scene: scene,
src: scene.assets["circle_touch"],
srcWidth: 100,
srcHeight: 100,
width: parameter.appear.width,
height: parameter.appear.height,
hidden: true
});
entity.append(circle_touch);
// タッチの有無で画像を切り替える
// タッチされたときにタッチ用の画像を表示
entity.pointDown.add(function() {
circle.hide();
circle_touch.show();
});
// タッチが解除されたときに通常時の画像を表示
entity.pointUp.add(function() {
circle.show();
circle_touch.hide();
});
// 表示用の円形と衝突判定を結び付けて返す
return physics.createBody(entity, parameter.physics.body, parameter.physics.fixture);
}
function createWall(scene) {
// 左の壁を生成する
var leftWall = createRect(scene, wallParameter);
var leftWallPos = physics.vec2(0, 0);
leftWallPos.Add(calcCenter(leftWall.entity));
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);
}
/**
* おはじきを生成する
* @param {g.Scene} scene 描画を行うシーン
* @param {b2Vec2} position おはじきを設置する座標
*/
function createHajiki(scene, position) {
var hajiki = createCircle(scene, hajikiParameter);
hajiki.b2body.SetPosition(position);
hajiki.entity.touchable = true;
// 足りない物理情報を付与する
// ※ 今回は見下ろし視点なので、独自に抵抗を持たせる
// 速度の減衰率を設定
hajiki.b2body.SetLinearDamping(0.5);
// 角速度の減衰率を設定
hajiki.b2body.SetAngularDamping(0.5);
var arrow = null; // 矢印画像
var anchor; // タッチ開始座標
// おはじきを引っ張ったときの処理を追加
hajiki.entity.pointDown.add(function(event) {
// 既に矢印画像が生成されている場合は削除しておく
if (arrow !== null) {
arrow.destroy();
}
// おはじきの移動をストップ
hajiki.b2body.SetLinearVelocity(physics.vec2(0, 0));
hajiki.b2body.SetAngularVelocity(0);
// タッチした位置の絶対座標を計算する
// ※ Box2Dが必要とするタッチ座標は、画面左上を原点とした絶対座標。
// ※ g.PointDownEventから取得できる座標は、ポインティング対象からの
// 相対座標なので、タッチ座標に対象となるb2bodyの回転を与えた後、
// b2bodyの座標を足すことで絶対座標へと変換する。
// ※ 回転の軸をg.Entityの中心にするため、回転の前にタッチ座標から
// 中心座標を引いておく。
anchor = physics.vec2(event.point.x, event.point.y);
anchor.Subtract(calcCenter(hajiki.entity));
anchor.MulM(b2Mat22.FromAngle(hajiki.b2body.GetAngle()));
anchor.Add(hajiki.b2body.GetPosition());
// タッチされた座標に矢印画像を生成
arrow = new g.Sprite({
scene: scene,
src: scene.assets["arrow"],
srcWidth: 100,
srcHeight: 100,
x: anchor.x * worldProperty.scale,
y: anchor.y * worldProperty.scale,
/* アンカーの位置を矢印の先端に設定 */
anchorX: 0,
anchorY: 0.5,
/* 矢印の大きさはドラッグ距離で決定 */
scaleX: 0,
scaleY: 0
});
scene.append(arrow);
});
hajiki.entity.pointMove.add(function(event) {
// pointDownEventからの移動量
var delta = physics.vec2(event.startDelta.x, event.startDelta.y);
// 矢印の長さを変更
var mouseMovement = delta.Length() * worldProperty.scale;
arrow.scaleX = mouseMovement / arrow.width;
arrow.scaleY = mouseMovement / arrow.height;
arrow.angle = Math.atan2(delta.y, delta.x) * (180 / Math.PI);
arrow.modified();
});
hajiki.entity.pointUp.add(function(event) {
// pointDownEventからの移動量
var delta = physics.vec2(event.startDelta.x, event.startDelta.y);
delta.NegativeSelf();
delta.Multiply(power);
hajiki.b2body.ApplyImpulse(delta, anchor);
arrow.destroy();
arrow = null;
});
}
module.exports = main;
© DWANGO Co., Ltd.