JavaScriptでゲーム作ろう

enchant.jsはもうかなり古いゲームエンジンなんですが今でもちゃんとゲームを作ることができます。

enchant.jsの良い所は特に開発環境を用意する必要がなくテキストエディタとブラウザさえあればすぐに開発でることです。まったくの初心者でもすぐにゲーム開発できます。初心者にとってこの手軽さはかなり大きなポイントだと思います。

 

テキストエディタとはプログラム書いたりするソフトでwindowsのメモ帳もそうなんですけど、メモ帳で書くのはちょっと厳しいのでこれだけは用意してください。ちなみに私はBracketsというテキストエディタを使っています。もしテキストエディタを持ってない方はこちらからダウンロードできます→Brackets

 

これからランゲームの作り方を書いていくんですが、私も素人開発者なので私のやり方が絶対に正しいなんてことはありません。「私はこういう風に作りました」ということを書いていくだけなのでプログラムとして正しくなかったり、無駄なことしてるところもあったりすると思います。なのであくまで「ゲームの作り方の一つの例」として見てください(‘ω’)ノ

 

それからenchant.jsでゲームを作るにはJavaScriptというプログラミング言語を使って行うんですがJavaScriptがそもそもわからないという方は先に少し基本文法を勉強してください。(※書きました→ブラウザゲーム開発で使うJavaScriptの超基本文法【初心者向け】)

 

また、enchant.jsの基本的な部分に関してもここではあまり説明しません。基本的な部分(画像の表示方法とかね)の説明はもうすでに多くのサイトでされているのでわからないところは検索してください。

 

では、さっそくゲームの開発に入るわけなんですが、いくらenchant.jsを使って作っていくとは言っても一から作るのはとても大変なので私の作成したテンプレートをベースに開発していきます。必要最低限のプログラムが既に作ってあるのでゲーム制作がスムーズに進みます。

 

ダウンロードはこちら→ファイルのダウンロード

 

私のテンプレートには一緒にenchant.jsも入っていますが他のプラグインは入っていないので欲しい方はenchant.jsのページから全部ダウンロードしてください→enchant.jsのダウンロード(ダウンロードできなくなりました)

 

私のテンプレートプログラムはこんな感じになってます(クリックすると動きます)。

 

テンプレートプログラムを実行してみる

templateファイルをダウンロード&解凍してindex.htmlファイルをダブルクリックしてみてください。ブラウザが開いてゲームが始まります。

 

タイトル画面が表示されて画面タッチをするとゲーム画面に切り替わります。ゲーム画面にはへのへのもへじのキャラクターが表示されていて画面をタッチしながら動かすとキャラを動かすことができます。

 

左上にはポーズボタンがあり、ボタンを押すとポーズ画面になります。RESUMEをタッチするとゲーム再開、TITLEをタッチするとタイトル画面に戻ります。

 

右上には時間のカウントダウンが表示されていて0になるとゲームオーバー画面になります。ゲームオーバー画面ではツイートボタンが表示され、押すとツイート画面が表示されツイッターに投稿できるようになります。

 

この時点では音は再生されませんが音の再生機能もついています。音楽ファイルを用意すればゲーム内で使うことができます。無料で使える音楽素材サイトをまとめているのでよかったらチェックしてみてください。

無料で利用できる音楽・効果音素材サイト集

 

テンプレートの説明

まずテンプレートの説明をしたいと思います。

var FPS = 30;
//画面サイズ関係
var SCREEN_WIDTH = 640; //画面幅
var SCREEN_HEIGHT = 900;//画面高さ
var GAME_SCREEN_HEIGHT = 900;//ゲーム画面
var GAME_SCREEN_WIDTH = 640;//ゲーム画面

FPSは1秒間に画面を更新する回数です。一般的なテレビゲームは60FPSなんですがenchant.jsはブラウザ上で動くため処理が遅いので半分の30FPSにしておきます(調べてみてね)。今のスマホなら30FPSでもだいたい問題なく動くと思います(ちょっと前までは30FPSでは処理落ちしていた)。

 

画面サイズは解像度とアスペクト比です。数字が大きいほど画面の表示がきれいになりますが大きな画像が必要になりデータが重くなるのでこのくらいが良いかと思います。アスペクト比はスマホは3:2くらいなんですがブラウザのアドレスバーがどうしても表示されるため縦を少し短くしています。

 

SCREEN_WIDTHとGAME_SCREEN_WIDTHの2つあるのはSCREEN_WIDTHは画面のサイズ、GAME_SCREEN_WIDTHはゲーム画面のサイズです。例えば画面下にコントローラー部分を作ったりする際にGAME_SCREEN_HEIGHTをコントローラー分小さく設定して使ったりするためです。

//ゲーム場面用定数
var SCENE_TITLE = 1;
var SCENE_MAINGAME = 2;

これはゲーム画面を切り替えるときに使います。数字で管理するよりわかりやすくするために変数を利用ます(上の画面サイズも同様ですね)。全部大文字で書いてある変数はプログラム内で変更しない変数と決めておきます。そしてこの変数を数字の代わりに使います。こうしておけばもし後で設定を変更したいときがあった場合(画面サイズを変えたいとか)、ここだけを修正すれば済みます。

//データはここへ
var ASSETS = {
    img_title:'img/title.png',
    img_player:'img/player.png',
    img_pause:'img/pausebutton.png',
    img_buttons:'img/buttons.png',
    img_gameover:'img/gameover.png',
    /*
    se_start:'sound/aaa.mp3',
    se_ok:'sound/bbb.mp3',
    se_cancel:'sound/ccc.mp3',
    se_select:'sound/ddd.mp3',
    bgm_main:'sound/abc.mp3',
    bgm_gameover:'sound/def.mp3',
    */
};

ASSETSはゲームで使う画像などのファイル情報を管理するオブジェクトです。必要なファイルはここに書いておいて後で一気に取り込みます。音のファイルの部分はコメントアウトしてます。音楽ファイルがあればコメントを解除してファイルを指定してください。

 

次はキャラクターと背景の画像を表示させます。サンプルとしてはこんな感じになります。

 

キャラクターの画像を用意する

まずはプレイヤーキャラクターを表示させてみましょう。私はこの画像を使います。

プレイヤーの画像

ご自分で用意したい方は128×128の大きさでキャラクターを描くかプログラム内の大きさを用意した画像のキャラクターの大きさに変更してください。

画像はplayer.pngで保存してimgフォルダに入れるか、別名の場合はASSETSオブジェクトのファイル名を変更してください。

それから毎回タイトル画面から始まると面倒臭いので、window.onload内にあるsystem.changeSceneの引数を以下のように変更します。

system.changeScene(SCENE_MAINGAME);

こうすると常にゲーム画面から始まってくれます。

プレイヤークラスを作る

プレイヤークラスはSpriteクラスを継承して使います。継承というのは元となるクラスの機能を引き継いで新しいクラスを生成することを言います。初心者の方はちょっとわからないかもしれませんが、とりあえず継承という便利なものがある、とだけ覚えておいてください。

var Player = enchant.Class.create(enchant.Sprite, {
    initialize: function(w, h){
        enchant.Sprite.call(this, w, h);
        //ここに必要な処理を描く
    }
});

enchant.jsのクラスの継承はこのような感じに書きます。今回はSpriteを継承していますが他のクラスを継承する場合はSpriteのところを変更すればOKです。

そして、MainGameScene内の

var player = new Sprite(64, 64);

var player = new Player(128, 128);

に変更します。とりあえずこれでプレイヤークラスが出来ました。

プレイヤーをアニメーションさせる

次はプレイヤーをアニメーションさせます。

キャラクターをアニメーションさせるにはSpriteクラスにあるframeプロパティを変更することでアニメーションしているように見せることができます。frameについてはググってもらうとすぐ出てくると思うので割愛します。

Playerクラスに下のようにonenterframeというメソッドを付け加えます。

var Player = enchant.Class.create(enchant.Sprite, {
    initialize: function(w, h){
        enchant.Sprite.call(this, w, h);
    },
    onenterframe: function(){
        this.frame++;
    }
});

そうして実行するとパラパラ漫画のようにプレイヤーの画像が切り替わって見えると思います。ただこれだと速すぎるのと画像が全部繰り返されてしまうのでよくありません。なので以下のように変更します。

var Player = enchant.Class.create(enchant.Sprite, {
    initialize: function(w, h){
        enchant.Sprite.call(this, w, h);
        this.walkPattern = [0, 1, 2, 1];
        this.walkIndex = 0;
    },
    onenterframe: function(){
        if(this.age % 5 == 0){
            if(++this.walkIndex >= this.walkPattern.length){
                this.walkIndex = 0;
            }
            this.frame = this.walkPattern[this.walkIndex];
        }
    }
});

this.ageはSpriteにあるクラスが生成させた時からの経過フレーム数が入っているプロパティです。%は除算の余りを出す演算子です。つまり経過フレーム数を5で割った余りが0の時、要するに5フレームに1回だけアニメーションさせるようにします。

あと歩いている画像が3枚しかないため0→1→2→0ではアニメーションがおかしくなるため今回は配列でアニメーションのフレームを管理するようにしました。これで自然に歩いているように見えると思います。

背景を表示する

次は背景を表示させてみましょう。こんな感じの画像を640×900のサイズで用意してください。これをbg.pngの名前で保存してimgフォルダに入れてください。

背景画像

そしてASSETSオブジェクトにこのように追加してください。

img_bg:'img/bg.png',

MainGameScene内の自機クラス生成の前に以下を記述してください。

//背景を表示
var bg = new Sprite(GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT);
bg.image = core.assets['img_bg'];
screen.addChild(bg);

これで背景が表示されます。背景が表示されるとなんとなく雰囲気が出てゲームらしくなってきた感じがあって嬉しいですね( *´艸`)

背景を動かしてみる

今回背景の移動はMainGameSceneのループ処理内に書いてみます。

//メインゲームシーンのループ処理------------------------------------
this.addEventListener('enterframe', function(){
    //bgm.loop();//BGMループ再生
    if(timer.isTimeUp){//タイムアップ
        //bgm.stop();
        var gameover = new GameoverScene(this);//ゲームオーバーシーンへ
        removeChildren(this);//シーンの子要素を削除
        return;
    }
    bg.x -= 2;//背景の座標を動かす
    label_score.text = ('00000000' + system.score).slice(-8);//得点表示
});

bg.xが背景のx座標で、それをループ処理内で毎回2ずつ引いています。こうすることで画像が左にずれていきます。しかしそうすると画面の右側がどんどん空いてきてしまいます。

なのでもう一つ背景画像を用意します。

var bg2 = new Sprite(GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT);
bg2.image = core.assets['img_bg'];
bg2.moveTo(GAME_SCREEN_WIDTH, 0);//画面右側に配置
screen.addChild(bg2);

bg2という名前でbgと同様に作成しますが、配置をbgの右側にします。

そしてメインループ内に同様に座標移動処理を書きます。

bg.x -= 2;
bg2.x -= 2;

これでさっきよりはマシになりましたがしばらくするとやっぱり右側が空いてきてしまいます。なので二つ目の画像の左端が画面の左端まで来たときに画像を最初の位置に戻してやります。

bg.x -= 2;
bg2.x -= 2;
if(bg2.x < 0){
    bg.x = 0;
    bg2.x = GAME_SCREEN_WIDTH;
}

これで背景がずっと動き続けているように見せることができます(・∀・)

とりあえずこれでキャラクターが歩いているような雰囲気が出てきましたね!今回までのプログラムはこちらで確認できます→チェックする

キャラクターをジャンプさせるぞ!

ついにゲームらしい部分に入ってきましたね!!(・∀・)

とりあえず画面をタッチしたらキャラクターがジャンプするようにしてみます。

今回の最終的な目標がこちらです(画面をタッチしてみてね)。

で、その前に、まずキャラクターの位置をちゃんと決めたいと思います。Playerクラスを作るときのplayer.moveToメソッドの引数をキャラクターを配置したい場所に変更します。

var player = new Player(128, 128);
player.image = core.assets['img_player'];
player.moveTo(100, 365);//ここの数値を変更する
screen.addChild(player);

画面をタッチしたらジャンプ!

画面をタッチしたらジャンプするということなので、画面をタッチしたかどうかの判定が必要になります。なのでPlayerクラスに判定用のフラグを作ります。

var Player = enchant.Class.create(enchant.Sprite, {
    initialize: function(w, h){
        enchant.Sprite.call(this, w, h);
        this.walkPattern = [0, 1, 2, 1];
        this.walkIndex = 0;
        this.isJump = false;//ジャンプ判定用フラグ
    },

そしてMainGameScene内のタッチイベントの部分を以下のように修正します。

//自機の操作(このシーンへのタッチで操作)
this.addEventListener('touchstart', function(e){
    player.isJump = true;
});
this.addEventListener('touchend', function(e){//touchendに変わってるので注意
    player.isJump = false;
});
さらにループ処理内の背景の移動処理のあとに以下を追加します。

player.jump();
そしてPlayerクラス内に以下のメソッドを追加します。

jump: function(){
    if(this.isJump){
        this.y -= 10;
    }  
},

これで画面をタッチしているあいだキャラクターが上昇していきます。

だけどこれだとジャンプじゃないですよね。上昇が終わったら落下してもらわないと意味がありません。

キャラクターを落下させる

キャラクターが落下するということはジャンプ中でない時は常に落下中ということになります。なのでまずisJumpがfalseの間は落下するようにします。

if(this.isJump){
    this.y -= 10;
}else{
    this.y += 10;
}

しかしこれだと地面の下まで行ってしまうので地面で止まるようにします。

そこでまず、地面の位置の定数を作ります。先ほどのプレイヤーの位置をここに入れます。

var GROUND_Y = 365;

そして最初にキャラクターの位置を決めたmoveToメソッドの引数もこの定数に変えておきます。そしてPlayerクラスのjumpメソッド内を以下のように変更します。

if(this.isJump){
    this.y -= 10;
}else{
    this.y += 10;
    if(this.y > GROUND_Y){
        this.y = GROUND_Y;
    }
}

これで地面にめり込むことはなくなりました。だけどまだジャンプというには無理がありますよね(´Д`)

さて、どうしましょ?

ジャンプの移動値を作る

画面をタッチしている間ずっと上昇してしまうのでジャンプ用の変数を使って移動値を管理するようにします。Playerクラスに以下のプロパティを追加します。

this.vy;

そしてタッチイベントを以下のように変更します。

//自機の操作(このシーンへのタッチで操作)
this.addEventListener('touchstart', function(e){
    if(!player.isJump){
        player.isJump = true;
        player.vy = -25;
    }
});
this.addEventListener('touchend', function(e){
    //処理なし
});

さらにPlayerクラスのjumpメソッドを以下のように変更します。

jump: function(){
    if(this.isJump){
        this.y += this.vy++;
        if(this.y > GROUND_Y){
            this.y = GROUND_Y;
            this.isJump = false;
        }
    }
},

これで実行してみると滑らかにジャンプしているように見えます。

簡単に説明すると、vyというプロパティーでキャラクターのy座標の移動幅を管理するわけですが、まず画面をタッチした際に、すでにジャンプ中ではないか判定します。すでにジャンプ中なのにジャンプるするとおかしなことになってしまいますからね(^^;)

そしてジャンプ中でなければisJumpをtrueにしてy座標の移動値であるvyに値を入れます。ここでvyの値がマイナスになっているということがポイントです。

jumpメソッド内ではまずジャンプ中かどうかを判定します。ジャンプ中であればキャラクターの座標にvyを足します。そして足し終わったらvyに1を足します。ここ重要です(・∀・)

こうやってマイナスの値が入っていたvyに1を毎回足していくと少しずつvyの値が増えていきそのうちプラスに変わります。マイナスの間はキャラクターは上昇していきますが、プラスに変われば下降します。このvyの変化がキャラクターがジャンプしているかのような動きを作ってくれます。どう?シンプルだけどすごいでしょ!(・∀・)

さてさて、これでジャンプは完成!!と言いたいところですが、やるならもっとハイレベルなものを目指したい!ってことでジャンプの高さをコントロールできるようにしてみます。

ジャンプの高さをタッチの長さで調整する

タッチしている時間が長いほど高くジャンプ、短いとちょこっとジャンプできればジャンプの動きとしてはもはや完璧と言っても過言ではないかと思います。そしてそれを実現する方法が意外とシンプルなんです( *´艸`)

this.addEventListener('touchend', function(e){
    if(player.isJump && player.vy < 0){
        player.vy /= 2;
    }
});

touchendイベントはタッチが終了したとき発火するイベントなのでタッチが終了したときに、ジャンプが上昇中(vyがマイナス)であればvyの値を半分にします。たったそれだけ( *´艸`)

ジャンプの画像を変えよう

せっかくなのでジャンプ中の画像を変更しましょう。

Playerクラスのonenterframeメソッド内を以下のように変更します。

onenterframe: function(){
    if(this.isJump){//ジャンプ中なら
        if(this.vy < 0){//上昇中
            this.frame = 4;
        }else{//下降中
            this.frame = 5;
        }
    }else{
        if(this.age % 5 == 0){
            if(++this.walkIndex >= this.walkPattern.length){
                this.walkIndex = 0;
            }
            this.frame = this.walkPattern[this.walkIndex];
        }
    }
}

ジャンプ中かどうか判定して画像を変更させます。そして上昇中か下降中かでも画像を変えるようにしました。これで画像も切り替わってよりジャンプらしくなりましたね(*´ω`)

最後に一か所、着地したときにすぐにジャンプの画像から歩いている画像に変わるようにjumpメソッド内の着地に判定のところに画像を変える処理を加えます。

if(this.y > GROUND_Y){
    this.y = GROUND_Y;
    this.isJump = false;
    this.frame = 0;//この一行を追加
}

これでキャラクターのジャンプ操作はばっちりですね(*´ω`)今回のプログラムの内容はこちらから見ることができます→プログラムをチェック

 

次の完成目標はこちらです

障害物の画像を用意する

では今回はこんな感じで64×64サイズで画像を用意します。

障害物

あ、背景が白くて見えないですが白いトゲがあります(^^;)当たったら死ぬ感じの奴です。

で、画像を読み込むようにASSETSに書き加えます

img_gameover:'img/gameover.png',
img_bg:'img/bg.png',
img_obstacle: 'img/obstacle.png',//これ

障害物は英語でobstacleらしいのでこの名前にしました

障害物クラスを用意する

とりあえず障害物のクラスを用意します。プレイヤークラスを作った時と同様にSpriteクラスを継承して作ります。

var Obstacle = enchant.Class.create(enchant.Sprite, {
    initialize: function(w, h){
        enchant.Sprite.call(this, w, h);
    },
});

で、MainGameScene内のプレイヤークラス生成の前にクラスの生成を行います。

//敵クラス
var obstacle = new Obstacle(64, 64);
obstacle.image = core.assets['img_obstacle'];
obstacle.moveTo(600, GROUND_Y);
screen.addChild(obstacle);

表示位置なんですが、プレイヤーの邪魔にならなければ意味がないのでプレイヤーキャラクターの前に来るように

obstacle.moveTo(600, GROUND_Y);

という感じで配置してみます。

するとですね、

こんな感じに宙に浮いて表示されてしまいます。なんででしょうか?

これはGROUND_Yの指定が間違っているんですね。前回GROUND_Yの値を指定したとき自機が地面の上に乗る丁度いい位置を指定しましたが、これは自機の画像の左上の位置を指しているんですね。

player.yは画像の左上の位置を指すので自機が地面に当たっているかどうかはplayer.yにplayer.height(高さ)を足して判断する必要があります。ってことで修正をします。


var GROUND_Y = 493;//365+128
jump: function(){
    if(this.isJump){//ジャンプ中なら
        this.y += this.vy++;
        if(this.y > GROUND_Y - this.height){
            this.y = GROUND_Y - this.height;
            this.isJump = false;
            this.frame = 0;
        }
    }
},
obstacle.moveTo(600, GROUND_Y - obstacle.height);
player.moveTo(100, GROUND_Y - player.height);

GROUND_Yの位置を実際に画像が地面になっている位置にします(前回365だったのでそこに自機の高さ128を足した値。私が適当に書いた絵なので変な位置になっている(^^;))。

で、ジャンプの判定位置は初期位置はそれぞれのキャラクターの高さ分引いて合わせています。これで大きさの違うキャラクターを出しても正しく地面の上に乗るように表示させることができます。

障害物を画面スクロールに合わせて移動させる

障害物を表示させても止まったままじゃゲームにならないので移動させましょう。で、移動させるなら背景のスクロールに合わせて動かしてみましょう。

確か背景の移動スピードが2だったと思うので障害物も同様に2だけ動かします。なのでとりあえず障害物クラスにmoveメソッドを作ります。

move: function(){
    this.x -= 2;
}

でこれをMainGameSceneの背景の移動処理の下に書きます。

bg.x -= 2;
bg2.x -= 2;
if(bg2.x < 0){
    bg.x = 0;
    bg2.x = GAME_SCREEN_WIDTH;
}
obstacle.move();//ここ
player.jump();

これで実行すると障害物が右から左に移動してきます。

移動スピードを修正したい

さてさて、移動できるようになったのは良いんだけど、どうやら移動量が2だとかなり遅いようですね(^^;)なのでスピードアップをさせましょう。

しかし、そうすると修正箇所が複数出てきますね。こういうところはどうしましたっけ?

そう!変数で管理するんですよね!(・∀・)そうするとその変数だけ修正すれば済みます。というわけで変数に変えて変更しましょう。

var SCROLL_SPEED = 8;

こんな感じで変数を用意してあとは必要な個所を修正してください(背景の移動と障害物の移動のところね)。

さて、ここで細かい話なんですが移動スピードが速くなると自機のアニメーションがなんだか遅い気がしませんか?せっかくなのでここも直しておきましょう。Playerクラスのonenterframeメソッド内の条件式を

if(this.age % 3 == 0){

と変えてやればアニメーションのスピードが速くなります。

これで移動が速くなったのはいいけれど、障害物が一回画面を横切ったらもう何もないですね(´Д`)これじゃゲームにならんですね

障害物の出現管理

というわけで次は障害物が画面の左端で消えてしまったらもう一回右から出してやりましょう。

move: function(){
    this.x -= SCROLL_SPEED;
    if(this.x + this.width < 0){
        this.x = GAME_SCREEN_WIDTH;
    }
}

こんな感じにしてやると左端で消えたらまた右から出てきます。

ただ、これだとずっと同じことを繰り返してるだけでつまらないので出てくる高さを変えてみましょう。

move: function(){
    this.x -= SCROLL_SPEED;
    if(this.x + this.width < 0){
        this.x = GAME_SCREEN_WIDTH;
        this.y = GROUND_Y - random(1, 3) * this.height ;//ここ追加
    }
}

追加した式のrandomは私が作った関数で引数に指定した(最低の数、最高の数)の範囲内でランダムに数字を出してくれます(気になる人はinwan.jsを見てみてね)。

で、この処理では1、2、3の3段階の高さをランダムで出すようにしました。ランダムで出た数にキャラの高さをかけ、それをGROUND_Yから引いた位置に表示させています。

0からでないのは0だと障害物が地面に埋まる位置になってしまうからです。少なくとも障害物の高さ分地面より上にしなければいけないので最低の数が1になっています。

 

次はは障害物と自機との当たり判定と当たった時の処理を追加していこうと思います。出来上がりはこんな感じ(‘ω’)ノ

当たり判定について

まず最初に当たり判定について簡単に説明したいと思います(`・ω・´)

私たちが遊ぶテレビゲームというのは画面に画像が表示されて、その画像を操作したりして遊ぶわけなんですけど、これはただ画面に画像が表示されているだけで実際に当たるなんてことは当然ないわけです。なのでプログラムで当たったかどうか判別する処理を書いて、そしてゲームが実行されている間ずっと当たったか当たってないかという判定(当たり判定)をコンピューターがしています。

で、その当たり判定の基本的な判定方法として矩形(四角)の当たり判定と円の当たり判定というのがあります。

どちらを使うかはゲームによるんですが、マリオのようなアクションゲームでは矩形を使います。なぜなら、マリオを想像してもらえば分かりやすいと思うんですがブロックなど四角のものがステージを構成しています。なので矩形の当たり判定を用いた方が正しく判定されやすいわけです。

円の当たり判定はシューティングゲームで使われたりします。敵の弾など丸いものが多いため円の当たり判定の方が向いているわけです。

もちろん他にも当たり判定の方法はありますが最初に覚えるべき当たり判定はこの2つです(※書きました→ゲームで使われる基本的な当たり判定をいくつか紹介【初心者向け】)。

で、今回は一応アクションゲームなので矩形の当たり判定をとりあえず使ってやってみましょう(・∀・)

で、enchant.jsには当たり判定処理が最初から入っていて使うことができます。が、これが画像の大きさ(自機だと128×128)で判定してしまうので余白の部分でも当たってしまい実用的ではありません(;´Д`)

というわけで私が作った当たり判定を使いましょう。

function rectCollision(L1, R1, T1, B1, L2, R2, T2, B2){
    if(L2 <= R1 && L1 <= R2){
        if(T2 <= B1 && T1 <= B2){
            return true;
        }
    }
    return false;
}

これをプログラムのどこかに追加してください。あ、解説はググってください(^^;)(解説が大変なので。。)

当たり判定の処理を追加

当たり判定の関数をメインループから直接呼んでもいいんですが当たった場合の処理を自機や障害物に書くことになるのでちょっと違うやり方にしてみます。

まず障害物クラスに

collision: function(chara){
    if(chara.collision(this.x, this.x + this.width, this.y, this.y + this.height)){
        //当たった時の処理(今回はないよ)
    }
}

このようにメソッドを追加します。引数はplayerです。そして障害物クラスからPlayrerクラスの当たり判定メソッドそ呼びます。

collision(L, R, T, B){
    if(rectCollision(L, R, T, B, this.x, this.x + this.width, this.y, this.y + this.height)){
        console.log("hit!");
        return true;
    }
    return false;
}

プレイヤークラスにcollisionメソッドを追加します。このなかで当たり判定関数を呼び出して判定します。

そして最初の当たり判定の呼び出しをMainGameScene内のループ処理に書きます。

//背景の移動処理
bg.x -= SCROLL_SPEED;
bg2.x -= SCROLL_SPEED;
if(bg2.x <= 0){
    bg.x = 0;
    bg2.x = GAME_SCREEN_WIDTH;
}
obstacle.move();
player.jump();
obstacle.collision(player);//これ

これで当たった場合にコンソールに「hit!」と表示されます。(F12を押してコンソールってタグのところね)

ここで一つ別件ですが説明しておきたいことがあります。MainGameSceneループ内にいくつか処理を書きましたが実はこれらは各クラスのonenterframe内に書くことができます。じゃあ、なんでわざわざメインループ内に書いたのか?
実はonenterframe内に書くと処理の順番がクラスを作った順番になると思います(確認していないけど)。だけど、処理によっては「これの次にこれをする」みたいなこともあるので私は基本的にほとんどの処理はメインループ内から呼ぶようにしています(アニメーションとかはあんまり関係ないからonenterframeでもいいとおもう)。

で、話を当たり判定に戻して、障害物・プレイヤークラスの各collisionメソッド内のif文はtrueが帰ってきたら当たっているので当たった処理を書きます。この方がメインループ内で当たり判定をしてから各クラスの処理用メソッドを呼ぶより書きやすいんじゃないかな、と私は思ってます(^^;)

当たる範囲を変更する

enchant.jsの当たり判定は画像の大きさのまま判定してしまうので自作関数で当たり判定をすることにしたのに今の段階では画像の幅でやってて自作関数でやってる意味がないです。なので当たる範囲を変更します。(当たり判定の状況が分かり辛いのでスクロールスピードを落として確認してね)

当たり判定の画像

今現在はこの画像の黒枠の部分が当たり判定の位置になっているんですがこれを黄色の枠まで狭めます。これで多少は当たり判定がマシになると思います。

というわけで、当たる範囲を保存する変数を作ります。

Playerクラスのinitializeメソッド内に以下のような変数を作成します。

this.hitArea = [40, 90, 20, 128];

この配列は左端・右端・上端・下端の位置を順に入れてます。それぞれの位置は自機のx座標y座標からの位置にしています。数値はだいたいできめましたけど(^^;)

で、当たり判定のところを

if(rectCollision(L, R, T, B, this.x + this.hitArea[0], this.x + this.hitArea[1], this.y + this.hitArea[2], this.y + this.hitArea[3])){

のように修正します。

障害物も同様に修正します。

this.hitArea = [10, 54, 10, 54];
if(chara.collision(this.x + this.hitArea[0], this.x + this.hitArea[1], this.y + this.hitArea[2], this.y + this.hitArea[3])){

これで当たり判定の範囲が良くなったと思います。ただ、当たり判定はゲームにとってはとても大事な部分なのでゲームができてきたら実際にプレーしながら修正していく必要があると思います。

また、今回は当たり判定の範囲が一つしかないですが、キャラの状態によっては(ジャンプ中、しゃがむなど)で切り替える必要も出てきます。本格的にゲームを作ろうとなってくるとかなり大変な作業になってくる部分だと思います(;´Д`)

当たった後の処理

当たり判定はつけたものの当たっても自機は平然と走り続けてます。当たったらどうにかなってもらわないとゲームにならないので当たったときの処理を作っていきましょう。

ではプログラムを追加していくわけですが、今回はいっぱいあります。

まずは生きてるか死んでるかのフラグをPlayerクラスに作ります。

this.isDead = false;

でPlayerクラスの当たり判定部分を変更します。

if(this.isDead){//すでに当てってたら抜ける
    return;
}
if(rectCollision(L, R, T, B, this.x + this.hitArea[0], this.x + this.hitArea[1], this.y + this.hitArea[2], this.y + this.hitArea[3])){
    this.isDead = true;//やられた!
    this.vy = -10;//ちょっと跳ねるような演出を入れたいので
    this.frame = 24;//やられた用の画像に変更
    return true;
}

もうすでに当たっていた場合は当たり判定の必要がないのでreturnで抜けます。まだ当たっていない状態で当たったらフラグをtrueにして画像の切り替えなどをします。

そしてonenterframeにやられたときの処理を追加します。

onenterframe: function(){
    if(this.isDead){
        this.y += this.vy++;
        if(this.y > GAME_SCREEN_HEIGHT){
            var gameover = new GameoverScene(this);//ゲームオーバーシーンへ
        }
        return;
    }
    if(this.isJump){//ジャンプ中なら
    //・・・・

ジャンプと同じような処理で画面下に落下させます。画面外に出たらゲームオーバーシーンに切り替えます。それ以降の処理は必要ないのでreturnで抜けます。

jumpメソッド内もすでに当たっていた場合は抜けるようにしておきます(操作させない)

jump: function(){
    if(this.isDead){
        return;
    }

これで自機が障害物にあたったら泣きながらぴょんと飛び上がって落ちていくようになります。

以上でございまする!

当たり判定の処理もついて一応ゲームという感じに出来上がりました。ここまでのプログラムはこちらで確認できます→プログラムを見る

本当はもうちょっと色々つけたりしたかったんですけど、そうなると色々変更箇所が出てきて大変になってくるのでここで切り上げることにしました(;´・ω・)

なのでここから先はご自分で考えて修正したり付け足したりしてみてください(‘ω’)ノ