このゲームは、matter.jsでつくりました。
ゲーム説明
スターデストロイヤー級宇宙船で惑星系を破壊するゲームです。昔懐かしい小惑星撃退ゲームのリメイク版です。
スマホ:画面右側タップで右、左側タップで左に回転。左右タップで前進です。
PC:カーソル「→」「←」で左右回転「↑」で前進、スペースで魚雷発射です。
スマホとPCでプレイできます。
HTML5で使えるjavascriptの2D物理エンジンは何種類かありますが、使い方についての情報はあまりありません。そこで2D物理エンジンの使い方として、このゲームを例に記述しようと思います。
まずゲームで使いやすい2D物理エンジンを探したところ、planck.jsとmatter.jsが候補に挙がりました。取りあえずGitHubからplanck.js-masterとmatter-js-masterを取って来ました。
まずplanck.jsからです。planck.js-masterにはexampleというフォルダがあります。そこに、サンプルのソースがあるのでそれをローカルで動かしてみます。無料で使えるMicrosoft Expression Web 4(日本語版)などで一度ソースを開いて、すべてをコピーしてメモ帳にペーストして、文字コードUTF-8で同名で保存します。すると読みやすくフォーマットしたものになります。
適当な名前でフォルダを作り、そこにplanck.js-masterからplanck.jsとplanck-with-testbed.jsそして保存したソースをおきます。もう一つ、メモ帳で下記の内容のindex.htmlを文字コードUTF-8でつくり保存します。
index.html
<!doctype html>
<html>
<body>
<script src="planck-with-testbed.js"></script>
<script src="8-Ball.js"></script>
</body>
</html>
これはソースの名前が”8-Ball.js”の例です。
index.htmlを実行すればサンプルがローカルで実行できます。ソースをメモ帳で開き少し変更すれば、サンプルの動きが変わるのが確認できます。
planckはマウスだけでなくキーボード入力にも対応しています。座標系は特殊で扱いにくい印象です。画像を使うのは少し面倒です。GitHubにはデフォルトでレンダラーを使用しないのでwindow.requestAnimationFrame(function())を使えと書いてあります。
次にmatter.jsを調べます。上記と同じに適当なフォルダを作りmatter-js-masterからmatter.jsとexamplesフォルダにあるサンプルのソースを見やすく整形して保存します。そして下記index.htmlを文字コードUTF-8でつくり保存します。
index.html
<!doctype html>
<html>
<body>
<script src="matter.js"></script>
<script src="car.js"></script>
</body>
</html>
これはソースの名前がcar.jsの例です。
しかしindex.htmlを実行しても動きません。ネットで調べるとサンプルcar.jsの最初と最後の所を書き換える必要があるようです。
元のcar.js
var Example = Example || {};
Example.car = function() {
var Engine = Matter.Engine,
Render = Matter.Render,
Runner = Matter.Runner,
~
stop: function() {
Matter.Render.stop(render);
Matter.Runner.stop(runner);
}
};
};
修正したcar.js
/*var Example = Example || {};
Example.car = function() {*/
(function(){//上のコードをコメントアウトしてこの行を追加
var Engine = Matter.Engine,
Render = Matter.Render,
Runner = Matter.Runner,
~
stop: function() {
Matter.Render.stop(render);
Matter.Runner.stop(runner);
}
};
//};この行をコメントアウトして下の行を追加
})();
これでmatter-js-masterのサンプルが動くようになりました。
matterはキーボード入力に対応してないのでjavascriptで処理する事にします。座標系はjavascriptのままです。レンダラーに対応してます。いくつかのイベント処理もできます。
フレーム毎実行はMatter.Events.on(engine, ‘beforeUpdate’, function())でできます。ゲームを作れることが分かりました。
まず、簡単なソースでレンダラーを確かめます。同じフォルダに何か画像を用意してください。
index.html
<!doctype html>
<html>
<head>
<script src="matter.js"></script>
</head>
<body>
<div id="canvas"></div>
<script src="boxball.js"></script>
</body>
</html>
boxball.js
(function() {
var Engine = Matter.Engine,
Render = Matter.Render,
World = Matter.World,
Bodies = Matter.Bodies;
// create engine
var container = document.getElementById('canvas');
var engine = Engine.create(container, {
render: { //レンダリングの設定
options: {
wireframes: false,
//ワイヤーフレームをoffで画像表示
width: 800,
height: 600,
//background: 'background.png' //画像があればコメントアウト解除
}
}
});
// create box and ball and ground
var boxA = Bodies.rectangle(400, 70, 60, 60, {
render: {
sprite: {
//texture: 'box.png' //画像があればコメントアウト解除
}
}
});
var ballA = Bodies.circle(400, 300, 50, {
render: {
sprite: {
//texture: 'ball.png' //画像があればコメントアウト解除
}
}
});
var ground = Bodies.rectangle(400, 500, 810, 30, {
isStatic: true
});
// add all of the bodies to the world
World.add(engine.world, [boxA, ballA, ground]);
// run the engine
Engine.run(engine);
})();
問題なく動きます。
次に入力についてテストします。
boxball.js
(function() {
var Engine = Matter.Engine,
Render = Matter.Render,
World = Matter.World,
Bodies = Matter.Bodies,
MouseConstraint = Matter.MouseConstraint,//マウス操作
Mouse = Matter.Mouse;
// create engine
var container = document.getElementById('canvas');
var engine = Engine.create(container, {
render: { //レンダリングの設定
options: {
wireframes: false,
//ワイヤーフレームをoffで画像表示
width: 800,
height: 600,
//background: 'background.png' //画像があればコメントアウト解除
}
}
});
// create box and ball and ground
var boxA = Bodies.rectangle(400, 70, 60, 60, {
render: {
sprite: {
//texture: 'box.png' //画像があればコメントアウト解除
}
}
});
var ballA = Bodies.circle(400, 300, 50, {
render: {
sprite: {
//texture: 'ball.png' //画像があればコメントアウト解除
}
}
});
var ground = Bodies.rectangle(400, 500, 810, 30, {
isStatic: true
});
// add all of the bodies to the world
World.add(engine.world, [boxA, ballA, ground]);
// マウスドラッグ追加
var mousedrag = MouseConstraint.create(engine);
World.add(engine.world, mousedrag);
var RIGHT = 0;
var LEFT = 0;
//キー入力
document.onkeydown = function(event){
if (event.keyCode == 39) {RIGHT = 1};
if (event.keyCode == 37) {LEFT = 1};
};
document.onkeyup = function(event){
if (event.keyCode == 39) {RIGHT = 0};
if (event.keyCode == 37) {LEFT = 0};
};
//フレーム毎実行
Matter.Events.on(engine, 'beforeUpdate', function() {
if (RIGHT == 1) {Matter.Body.setAngularVelocity(boxA, Math.PI/6)};
if (LEFT == 1) {Matter.Body.setAngularVelocity(boxA, -Math.PI/6)};
});
// run the engine
Engine.run(engine);
})();
問題なく動きます。
次は衝突判定です。
(function() {
var Engine = Matter.Engine,
Render = Matter.Render,
World = Matter.World,
Bodies = Matter.Bodies,
MouseConstraint = Matter.MouseConstraint,//マウス操作
Mouse = Matter.Mouse;
// create engine
var container = document.getElementById('canvas');
var engine = Engine.create(container, {
render: { //レンダリングの設定
options: {
wireframes: false,
//ワイヤーフレームをoffで画像表示
width: 800,
height: 600,
//background: 'background.png' //画像があればコメントアウト解除
}
}
});
// create box and ball and ground
var boxA = Bodies.rectangle(400, 70, 60, 60, {
label: 'box', //本体の名前
render: {
sprite: {
//texture: 'box.png' //画像があればコメントアウト解除
}
}
});
var ballA = Bodies.circle(400, 300, 50, {
label: 'ball', //本体の名前
render: {
sprite: {
//texture: 'ball.png' //画像があればコメントアウト解除
}
}
});
var ground = Bodies.rectangle(400, 500, 810, 30, {
isStatic: true
});
// add all of the bodies to the world
World.add(engine.world, [boxA, ballA, ground]);
//衝突判定
Matter.Events.on(engine, 'collisionEnd', function(event) {
pairs = event.pairs;
for (i = 0;i < pairs.length;i++) {
var pair = pairs[i];
if (pair.bodyA.label !== 'ball' && pair.bodyB.label == 'box' || pair.bodyA.label == 'box' && pair.bodyB.label !== 'ball') {
Matter.Body.translate( ballA, {x: Math.floor( Math.random() * 101) - 50, y: -200});
}
};
});
// マウスドラッグ追加
var mousedrag = MouseConstraint.create(engine);
World.add(engine.world, mousedrag);
var RIGHT = 0;
var LEFT = 0;
//キー入力
document.onkeydown = function(event){
if (event.keyCode == 39) {RIGHT = 1};
if (event.keyCode == 37) {LEFT = 1};
};
document.onkeyup = function(event){
if (event.keyCode == 39) {RIGHT = 0};
if (event.keyCode == 37) {LEFT = 0};
};
//フレーム毎実行
Matter.Events.on(engine, 'beforeUpdate', function() {
if (RIGHT == 1) {Matter.Body.setAngularVelocity(boxA, Math.PI/6)};
if (LEFT == 1) {Matter.Body.setAngularVelocity(boxA, -Math.PI/6)};
});
// run the engine
Engine.run(engine);
})();
問題なく動きます。楽しいゲームを作る手助けになれば幸いです。
コメント