JavaScript のゲームプログラミング入門
8. そこそこジャンプ:スコアと壁の変化
[ 7. そこそこジャンプ:壁と当たり判定 ] の続きです。
壁を表示して、移動させたり当たり判定を入れました。
ゲームオーバーも表示しましたね。
スコアを表示しよう
スコアの表示を入れます。
スコアが入るとゲーム画面らしさが出ますね。
表示優先を考え、処理の最後に入れます。
スコアのような情報は、一番手前に表示するのが基本です。
Crafty.init(500,350, document.getElementById('game'));
Crafty.background('#87ceeb');
// 壁
Crafty.sprite(49,147,"wall.png",{Wall:[0,0]});
Crafty.e('Wall, 2D, Canvas, Collision')
.attr({x:500, y:250})
.bind('EnterFrame', function() {
this.x -= 5;
if (this.x < -50) this.x = 500;
});
// プレイヤー
Crafty.sprite(72,100,"player.png",{Player:[0,0]});
Crafty.e('Player, 2D, Canvas, Gravity, SpriteAnimation, Jumper, Collision')
.attr({x:50, y:50})
.reel('walk',300,[[0,0],[1,0]]) // 歩きアニメ設定
.animate('walk', -1) // 歩きアニメ再生
.jumpSpeed(350) // ジャンプの強さ
.collision(15,30, 55,30, 55,85, 15,85) // 当たり判定の範囲を変更
.onHit('Wall', function () { // 壁に当たったら
Crafty.pause(); // ポーズする
Crafty.e('2D, DOM, Text').attr({x:95, y:100, w:400})
.text("GAME OVER").textColor('#dc143c')
.textFont({size:'48px', weight:'bold'});
})
.gravity('Floor');
// 地面
Crafty.e('Floor, 2D, Canvas, Color')
.attr({x:0, y:320, w:500, h:40}).color('#830');
// スコア
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");
// マウスのクリックを取得 注意:ClickではなくMouseDownを使う
Crafty.s('Mouse').bind('MouseDown', function(e) {
Crafty("Player").jump();
});
実行してみます。
左上にスコアが表示されました。
表示の仕組みは、ゲームオーバーとほぼ同じです。
ただ、Scoreという名前を付けた点が違います。これは後で説明します。
Crafty.e("Score, DOM, 2D, Text") // テキスト表示のパラメータ
.attr({ x:20, y:15, w:280 }) // 表示座標と幅
.textFont({size:'20px', weight:'bold'}) // フォントサイズとスタイル
.textColor('#111') // 色
.text("SCORE: 0"); // 表示する文字列
変数について
プログラミングでは 変数(へんすう) という入れ物を作ることができます。
文字列や数値を入れておき、計算や参照に繰り返し利用するのです。
例えば、財布をイメージしてください。
初めに 100円を入れたとします。
次に 1000円を入れます。このとき、財布の中を見ると 1100円あることが分かります。
そこで 500円取り出したら残りは 600円になります。
当たり前のことを書いてますが、これは財布という入れ物を使ったからできることです。
変数のおもな特徴は次の通りです。
残念ながら変数のすべてを説明することはできません。(長くなるので)
少しずつ説明していきます。
気になることがあったら自分でも調べてみてください。
今回、スコア用に score という変数を宣言しました。
変数を使うときletで宣言する必要があります。
宣言と同時に初期化することもできます。
0 を代入しているので、この変数は数値として扱うことが分かります。
// スコア
let score = 0;
得点を入れよう
得点を入れるタイミングを考えます。
シューティングやアクションなら敵を撃破したときですよね。
このゲームでは敵を倒したりしないので、どうするのか悩みます。
考えた末、壁が画面外へ出て右へ戻すタイミングにしました。
これが一番シンプルで、難易度を上げたりしても対応できます。
// 壁
Crafty.sprite(49,147,"wall.png",{Wall:[0,0]});
Crafty.e('Wall, 2D, Canvas, Collision')
.attr({x:500, y:250})
.bind('EnterFrame', function() {
this.x -= 5;
if (this.x < -50){
this.x = 500;
score += 100;
Crafty("Score").text("SCORE: "+score);
}
});
1行で書いていた if 文を波かっこ付きに変更しました。
ここにスコアの加算と表示変更の処理を入れます。
実行してみます。スコアが増えるようになりました。
処理をざっくり説明します。
if (this.x < -50){ // x座標が -50より小さいとき
this.x = 500; // x座標を 500にする
score += 100; // score に 100 加算する
Crafty("Score").text("SCORE: "+score); // スコア表示の書き換え
}
変数 score への加算は次のようにも書けます。
これは、score に入っている値に 100 を足して、その計算結果を score に代入しているのです。
score = score +100; // score に 100 加算する
スコア表示の書き換えについてです。
スコア表示のとき Score という名前で作成したことを覚えてますか?
その名前を使って文字列表示の内容を変更します。
そして文字列 “SCORE: " と score の内容を連結して text( ) で表示しています。
文字列の連結には + 記号を使います。
算術と同じ + の記号を使いますが、文字列を含んでいると文字列として連結されます。
下の例では 3+4 のみ結果が 7 になり、他は 34 と表示されます。
console.log(3+4); // 7
console.log("3"+4); // 34
console.log(3+"4"); // 34
console.log("3"+"4"); // 34
ちなみに、console.log( ) はコンソール画面に出力する命令です。
かっこの中に数式や文字列など入れて、実行中の状況を知るために使います。
壁の速度を変えよう
ゲームとしての見た目は完成しました。
ただ、遊んでみるとゲームらしさが感じられません。
それはジャンプしているだけだからかな、と思うこともあります。
でもこれは本当の理由ではありません。
一番の原因は変化がないことです。
試しに壁の速度をランダムにしてみましょう。
// 壁
Crafty.sprite(49,147,"wall.png",{Wall:[0,0]});
Crafty.e('Wall, 2D, Canvas, Collision')
.attr({x:500, y:250, speed:5})
.bind('EnterFrame', function() {
this.x -= this.speed;
if (this.x < -50){
this.x = 500;
this.speed = Crafty.math.randomNumber(3,8);
console.log(this.speed);
score += 100;
Crafty("Score").text("SCORE: "+score);
}
});
実行して、壁の速度が変わることを確認しましょう。
console.log(this.speed) を入れました。
コンソールを開けば乱数の値も確認できます。
プログラムの説明です。
まず、速度のプロパティ speed を用意します。
初期値は 5 にしました。
.attr({x:500, y:250, speed:5})
this.speed で移動計算をします。
this.x -= this.speed;
Crafty.math.randomNumber(3,8) は 3 から 8 までの乱数(小数点あり)を作ります。
this.speed = Crafty.math.randomNumber(3,8);
どうですか、ちょっと気を抜くとゲームオーバーになります。
これだとジャンプのタイミングを見極める必要が出てきて、ゲームらしさが増したと思います。
壁の高さを変えよう
もう一つ、変化を付けてみましょう。
壁の高さをランダムに変えてみます。
// 壁
Crafty.sprite(49,147,"wall.png",{Wall:[0,0]});
Crafty.e('Wall, 2D, Canvas, Collision')
.attr({x:500, y:250, speed:5, y0:250})
.bind('EnterFrame', function() {
this.x -= this.speed;
if (this.x < -50){
this.x = 500;
this.speed = Crafty.math.randomNumber(3,8);
this.y = this.y0 - Crafty.math.randomInt(0,8)*5;
console.log(this.y);
score += 100;
Crafty("Score").text("SCORE: "+score);
}
});
この変更で壁の高さは次のように変わります。
プログラムの説明です。
基本の高さのプロパティ y0 を用意します。
初期値は 250 にしました。
.attr({x:500, y:250, speed:5, y0:250})
Crafty.math.randomInt(0,8) は 0 から 8 までの乱数(整数)を作ります。
それに 5 を掛けているので 0,5,10,15…35,40 と変化します。
基本の高さから乱数を引いて、y座標に入れます。
this.y = this.y0 - Crafty.math.randomInt(0,8)*5;
遊んでみてどうでしょうか?
私の感想は、思ったほど変化を感じなかったです。
複数の壁が出現するパターンだと有効かもしれませんね。
完成です
お疲れさまでした。
index.html の全プログラムを載せておきます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>そこそこジャンプ</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(49,147,"wall.png",{Wall:[0,0]});
Crafty.e('Wall, 2D, Canvas, Collision')
.attr({x:500, y:250, speed:5, y0:250})
.bind('EnterFrame', function() {
this.x -= this.speed;
if (this.x < -50){
this.x = 500;
this.speed = Crafty.math.randomNumber(3,8);
this.y = this.y0 - Crafty.math.randomInt(0,8)*5;
console.log(this.y);
score += 100;
Crafty("Score").text("SCORE: "+score);
}
});
// プレイヤー
Crafty.sprite(72,100,"player.png",{Player:[0,0]});
Crafty.e('Player, 2D, Canvas, Gravity, SpriteAnimation, Jumper, Collision')
.attr({x:50, y:50})
.reel('walk',300,[[0,0],[1,0]]) // 歩きアニメ設定
.animate('walk', -1) // 歩きアニメ再生
.jumpSpeed(350) // ジャンプの強さ
.collision(15,30, 55,30, 55,85, 15,85) // 当たり判定の範囲を変更
.onHit('Wall', function () { // 壁に当たったら
Crafty.pause(); // ポーズする
Crafty.e('2D, DOM, Text').attr({x:95, y:100, w:400})
.text("GAME OVER").textColor('#dc143c')
.textFont({size:'48px', weight:'bold'});
})
.gravity('Floor');
// 地面
Crafty.e('Floor, 2D, Canvas, Color')
.attr({x:0, y:320, w:500, h:40}).color('#830');
// スコア
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");
// マウスのクリックを取得 注意:ClickではなくMouseDownを使う
Crafty.s('Mouse').bind('MouseDown', function(e) {
Crafty("Player").jump();
});
</script>
</body>
</html>
シンプルでしたがゲームを完成させました。
何も知らなかったときより「へーこうやって動かすんだ」的な感想はあったと思います。
まだまだ教えたいことが沢山あります。
タイトル画面やリトライさせる方法、敵を複数出現させるやり方などなど。
これからも記事を書いていきます。
いっしょにゲームプログラミングを続けていきましょう。