こんにちは、よみあきです。

今回は「Crafty」というJavaScriptのゲームエンジンを用いて、簡単な2Dゲーム「BUMP OF FISH ver1」を作ってみました!
赤い魚が右からやってるハリセンボンをひたすら衝突しないように避けるゲームとなっています。
Craftyで作る!
Craftyとは?
Craftyは、コアライブラリーのコードがわずか88.4KBと軽量なゲームエンジンだ。コアライブラリーには基本的な機能だけが組み込まれており、個々のゲームで必要なコンポーネントを CraftyComponentsから選んで読み込むことで全体の軽量化を図っている。
コアライブラリーでは、アセット管理、スプライト、衝突検知、柔軟なイベントシステム、オーディオ操作機能を提供。CraftyComponentsには、Box2D連携、経路探索、タイルマップ、WebGLによる3D表示などが用意されている。
無料で使えるHTML5 JavaScriptゲームエンジンまとめ
ゲームエンジンを使うと簡単な文法でゲームを構成することができます!
今回は「白くまプログラミング入門」を参考に作業を進めました。
このようなゲームを作るのははじめてでしたが、かなり丁寧な説明をされていて、それなりのものを作ることができました!
絵の素材もこのチュートリアルで紹介されていた、kenneyというサイトからFish Packをダウンロードして用いました。
作ってみよう
準備
準備としてひな形のコードを書いておきます。
名前は何でもいいですが、今回はcrafty.htmlとしました。
crafty-min.jsをcrafty公式サイトからダウンロードして、このhtmlと同じフォルダに保存しておきましょう。
player.pngとenemy.pngはそれぞれkenneyというサイトのFish PackのfishTile_079とfishTile_101を使いました。
├ crafty.html
├ crafty-min.js
├ player.png
└ enemy.png
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>BUMP OF FISH</title>
<script src="crafty-min.js"></script>
<style type="text/css">
body{
padding:0px;
margin:0px;
}
</style>
</head>
<body>
<div id="game"></div>
<script>
//ここにいろいろ記述!
</script>
</body>
このままでは、ブラウザで開いても何もない状態です。
これからは<body>内の<script>にJavascript(Crafy)を書いていきます。
さっそく以下のコードを書いてみましょう!
//表示サイズ、背景色の設定
Crafty.init(500,350, document.getElementById('game'));
Crafty.background('#87ceeb');
保存して、crafty.htmlをブラウザで開いてみましょう。
すると以下の図のように、背景ができました。

横500px、縦350pxとなっており、色は水色ですね。
この背景はHTMLの<div id=”game”></div>に出力されています。
プレイヤーの作成
続いてプレイヤーを作っていきましょう!
先ほどの背景の下に以下を入力してみましょう。
//プレイヤー
Crafty.sprite(64,64,"player.png",{player:[0,0]});
var Player=Crafty.e('2D, Canvas, player')
.attr({x:50, y:50});
保存して、ブラウザで開くと魚が現れました。

プレイヤーはplayer.pngを縦横64pxで切り出されているものとなっていてます。(Crafty.spriteで設定)
表示位置は画面左上から縦横50px、50pxの位置となっています。(.attrで設定)
プレイヤーに重力の付与
続いてプレイヤーに重力を適用してみましょう!ここからがゲーム作りっぽくなっていきます…!
以下のハイライトの部分を追加しましょう。
//プレイヤー
Crafty.sprite(64,64,"player.png",{player:[0,0]});
var Player=Crafty.e('2D, Canvas, player,Gravity')
.attr({x:50, y:50})
.gravity('Floor');
保存して、ブラウザで開くと現れた魚が落ちていきました。

おめでとうございます、プレイヤーに動きを持たせることができました!
ジャンプ
続いてプレイヤーをジャンプさせてみましょう!
以下ハイライトされたコードを追加してみましょう。
//プレイヤー
Crafty.sprite(64,64,"player.png",{player:[0,0]});
var Player=Crafty.e('2D, Canvas, player,Gravity,Jumper')
.attr({x:50, y:50})
.jumpSpeed(300)
.gravity('Floor');
//連続ジャンプの設定
Player.bind("CheckJumping", function(e) {
Player.canJump = true;
});
var Jump=Crafty.s('Mouse').bind('MouseDown', function(e) {
Player.jump();
});
保存して、ブラウザで開き、画面をクリックしてみると魚が跳ねるようになりました。

おめでとうございます、プレイヤーを操作することできるようになりました!
ここでは、Crafty.bindでCheckingJumpingを指定し、空中にいるか判定します。
そして、canJumpをtrueにすることで空中でもジャンプするのを許可しています。
マウスのクリックでジャンプさせるというイベントも追加しました。
敵の作成
続いて敵を作ってみましょう!
以下のコードを追加してみましょう。
//敵
Crafty.sprite(64,64,"enemy.png",{enemy:[0,0]});
var Enemy=Crafty.e('2D, Canvas,enemy')
.attr({x:500, y:250,speed:5})
.bind('EnterFrame', function() {
this.x -= this.speed;
//画面外に出たとき
if (this.x < -64) {
this.x = 500;
this.y=Crafty.math.randomNumber(0,300);
this.speed=Crafty.math.randomNumber(5,12);
}
})
.flip("X");
保存して、ブラウザで開くと右からハリセンボンが向かってくるようになりました!

Crafty.bindでEnterFrameを指定し、毎フレーム行う処理を書いています。
敵の位置がフレームごとにspeedの分だけ左に移動させています。
また、敵が画面外に出たときは位置を戻し、縦方向の位置とスピードをランダムに変更させています。
flip(“X”)で画像の向きを左向きに変えています。
敵の当たり判定
続いてプレイヤーと敵の当たり判定を行いましょう!
今のままではプレイヤー(魚)と敵(ハリセンボン)がぶつかっても、ただすり抜けてしまい、何のゲーム性もありません…
以下ハイライトされたコードを追加してみましょう。
//プレイヤー
Crafty.sprite(64,64,"player.png",{player:[0,0]});
var Player=Crafty.e('2D, Canvas, player,Gravity, Jumper,Collision')
.attr({x:50, y:50})
.jumpSpeed(300)
.gravity('Floor')
.collision(18,20,60,15,60,53,18,44)
//ハリセンボンとぶつかった時
.onHit("enemy",function(){
Crafty.pause(); //ぶつかったら画面をポーズ
//ゲームオーバーのテキストメッセージ
Crafty.e('2D, DOM, Text').attr({x:120, y:150, w:400})
.text("YOU DIED").textColor('#dc143c')
.textFont({size:'48px', weight:'bold'});
Crafty.e('2D, DOM, Text').attr({x:120, y:200, w:400})
.text("Cause of death: Collision with porcupinefish!").textColor('#dc143c')
.textFont({size:'32px', weight:'bold'})
});
//敵
Crafty.sprite(64,64,"enemy.png",{enemy:[0,0]});
var Enemy=Crafty.e('2D, Canvas,enemy, Collision')
.attr({x:500, y:250,speed:5})
.collision(10,10,49,10,49,54,10,54)
.bind('EnterFrame', function() {
this.x -= this.speed;
//画面外に出たとき
if (this.x < -64) {
this.x = 500;
this.y=Crafty.math.randomNumber(0,300);
this.speed=Crafty.math.randomNumber(5,12);
}
})
.flip("X");
保存して、ブラウザで開くとプレイヤーと敵がぶつかった時に画面がポーズ状態になり、ゲームオーバーになります!ゲームっぽいですね!

プレイヤーと敵それぞれにcollision(x1,y1,x2,y2,…)で当たり判定の範囲を決めています。
この場合は4つの点を指定しており、四角形の範囲となっています。
ぶつかったときに画面をポーズさせるのと、テキストを表示させる処理も書いています。
//プレイヤー
var Player=Crafty.e('2D, Canvas, player,Gravity, Jumper,Collision, WiredHitBox')
//敵
var Enemy=Crafty.e('2D, Canvas,enemy, Collision, WiredHitBox')
というようにWiredHitBoxを書き加えると、赤い枠が表示され当たり判定の範囲の確認もできます。

地面と天井の当たり判定
続いて地面と天井の当たり判定を作ってみましょう!
このままでは、落ちるときはどこまでも落ち続け、
ジャンプしたときはどこまでもジャンプできてしまいます。
以下ハイライトされたコードを追加してみましょう。
//プレイヤー
Crafty.sprite(64,64,"player.png",{player:[0,0]});
var Player=Crafty.e('2D, Canvas, player,Gravity, Jumper,Collision')
.attr({x:50, y:50})
.jumpSpeed(300)
.collision(18,20,60,15,60,53,18,44)
//ハリセンボンとぶつかった時
.onHit("enemy",function(){
Crafty.pause();
Crafty.e('2D, DOM, Text').attr({x:120, y:150, w:400})
.text("YOU DIED").textColor('#dc143c')
.textFont({size:'48px', weight:'bold'});
Crafty.e('2D, DOM, Text').attr({x:120, y:200, w:400})
.text("Cause of death: Collision with porcupinefish!").textColor('#dc143c')
.textFont({size:'32px', weight:'bold'});
})
//地面とぶつかった時
.onHit("floor",function(){
Crafty.pause();
Crafty.e('2D, DOM, Text').attr({x:120, y:150, w:400})
.text("YOU DIED").textColor('#dc143c')
.textFont({size:'48px', weight:'bold'});
Crafty.e('2D, DOM, Text').attr({x:80, y:200, w:400})
.text("Cause of death: Crash!").textColor('#dc143c')
.textFont({size:'32px', weight:'bold'});
})
//天井にぶつかった時
.onHit("ceiling",function(){
Crafty.pause();
Crafty.e('2D, DOM, Text').attr({x:130, y:150, w:400})
.text("YOU DIED").textColor('#dc143c')
.textFont({size:'48px', weight:'bold'});
Crafty.e('2D, DOM, Text').attr({x:80, y:200, w:400})
.text("Cause of death: Flying into space!").textColor('#dc143c')
.textFont({size:'32px', weight:'bold'});
})
// 地面
var Floor=Crafty.e('2D, Canvas,floor')
.attr({x:0, y:450, w:500, h:40});
// 天井
var Ceiling=Crafty.e('2D, Canvas,ceiling')
.attr({x:0, y:-140, w:500, h:40});
保存して、ブラウザで開くと、何も操作せず画面外に落ちた時に、ゲームオーバー。

また、ジャンプしすぎて画面外に行った場合もゲームオーバーになるようになりました!

そう簡単に攻略はさせません。
床(Floor)と天井(Ceiling)を定義して、プレイヤーの方にそれらにぶつかった時の処理を書いています。
スコアの表示
最後にスコアの表示を作っていきましょう!もうすぐ完成です!
以下のハイライトされたコードを追加してみましょう。
//敵
Crafty.sprite(64,64,"enemy.png",{enemy:[0,0]});
var Enemy=Crafty.e('2D, Canvas,enemy, Collision')
.attr({x:500, y:250,speed:5})
.collision(10,10,49,10,49,54,10,54)
.bind('EnterFrame', function() {
this.x -= this.speed;
//画面外に出たとき
if (this.x < -64) {
this.x = 500;
this.y=Crafty.math.randomNumber(0,300);
this.speed=Crafty.math.randomNumber(5,12);
//スコアの更新
score += 100;
var Result=Crafty("Score").text("SCORE: "+score);
}
})
.flip("X");
// スコア
let score = 0;
Crafty.e("Score, DOM, 2D, Text")
.attr({ x:20, y:15, w:280 })
.textFont({size:'20px', weight:'bold'})
.textColor('#111')
.text("SCORE: 0");
保存して、ブラウザで開くと左上に「SCORE」の表示が現れました!

そして、敵を避けるたびにスコアが100上がっていきます。
scoreという変数とテキストを定義して、敵が画面外を抜けるたびにscoreに100が足されていくという処理を行っています。
まとめ
これで一通り、Flappy Birdライクなゲームを作ることができました。
最後に全体のコードを載せておきます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>BUMP OF FISH</title>
<script src="crafty-min.js"></script>
<style type="text/css">
body{
padding:0px;
margin:0px;
}
</style>
</head>
<body>
<div id="game"></div>
<script>
//表示サイズ、背景色の設定
Crafty.init(500,350, document.getElementById('game'));
Crafty.background('#87ceeb');
//プレイヤー
Crafty.sprite(64,64,"player.png",{player:[0,0]});
var Player=Crafty.e('2D, Canvas, player,Gravity, Jumper,Collision')
.attr({x:50, y:50})
.jumpSpeed(300)
.gravity('Floor')
.collision(18,20,60,15,60,53,18,44)
//ハリセンボンとぶつかった時
.onHit("enemy",function(){
Crafty.pause(); //ぶつかったら画面をポーズ
//ゲームオーバーのテキストメッセージ
Crafty.e('2D, DOM, Text').attr({x:120, y:150, w:400})
.text("YOU DIED").textColor('#dc143c')
.textFont({size:'48px', weight:'bold'});
Crafty.e('2D, DOM, Text').attr({x:120, y:200, w:400})
.text("Cause of death: Collision with porcupinefish!").textColor('#dc143c')
.textFont({size:'32px', weight:'bold'})
})
//地面とぶつかった時
.onHit("floor",function(){
Crafty.pause();
Crafty.e('2D, DOM, Text').attr({x:120, y:150, w:400})
.text("YOU DIED").textColor('#dc143c')
.textFont({size:'48px', weight:'bold'});
Crafty.e('2D, DOM, Text').attr({x:80, y:200, w:400})
.text("Cause of death: Crash!").textColor('#dc143c')
.textFont({size:'32px', weight:'bold'});
})
//天井にぶつかった時
.onHit("ceiling",function(){
Crafty.pause();
Crafty.e('2D, DOM, Text').attr({x:130, y:150, w:400})
.text("YOU DIED").textColor('#dc143c')
.textFont({size:'48px', weight:'bold'});
Crafty.e('2D, DOM, Text').attr({x:80, y:200, w:400})
.text("Cause of death: Flying into space!").textColor('#dc143c')
.textFont({size:'32px', weight:'bold'});
})
//連続ジャンプの設定
Player.bind("CheckJumping", function(e) {
Player.canJump = true;
});
var Jump=Crafty.s('Mouse').bind('MouseDown', function(e) {
Player.jump();
});
//敵
Crafty.sprite(64,64,"enemy.png",{enemy:[0,0]});
var Enemy=Crafty.e('2D, Canvas,enemy, Collision')
.attr({x:500, y:250,speed:5})
.collision(10,10,49,10,49,54,10,54)
.bind('EnterFrame', function() {
this.x -= this.speed;
//画面外に出たとき
if (this.x < -64) {
this.x = 500;
this.y=Crafty.math.randomNumber(0,300);
this.speed=Crafty.math.randomNumber(5,12);
//スコアの更新
score += 100;
var Result=Crafty("Score").text("SCORE: "+score);
}
})
.flip("X");
// 地面
var Floor=Crafty.e('2D, Canvas,floor')
.attr({x:0, y:450, w:500, h:40});
// 天井
var Ceiling=Crafty.e('2D, Canvas,ceiling')
.attr({x:0, y:-140, w:500, h:40});
// スコア
let score = 0;
Crafty.e("Score, DOM, 2D, Text")
.attr({ x:20, y:15, w:280 })
.textFont({size:'20px', weight:'bold'})
.textColor('#111')
.text("SCORE: 0");
</script>
</body>ゲームエンジン(フレームワーク)を使うと簡単な文法で、枠にはめて動きを実装できことができますね!
Craftyはアイデア次第で大きな可能性があると感じます!
ぜひいろいろ作ってみてください〜
よみあきでした。


コメント
[…] この前、「Crafty」で作った「BUMP OF FISH ver1」に改良を加えました。 […]