こまごまとした処理の解説編
ご訪問いただき誠にありがとうございます。
『避けゲー』プログラミングの解説シリーズは今回で最終回となります。今回はステージの共通処理となっている、主人公が倒されたときの消えていく演出と復路のときに障害物がゆらゆらと揺れている処理、描画の基本について解説いたします。
実際の「避けゲー」で遊びたい方は以下よりアクセスしてください。
ソースコードはGitHub上にアップしていますので適宜参考にしてください。
主人公が倒されたときの演出
主人公である自キャラが障害物に触れるとゲームオーバーになりますが、そのときに徐々に消滅していく演出にしています。その制御はdraw()関数内の以下の部分で行っています。
draw()関数からの抜粋(avoider.jsから抜粋)
function draw() {
if (!gameover){
~中略~
}else if(deatheffect<=20){
//ゲームオーバー時の自キャラの消滅の描画
ctx.beginPath();
ctx.ellipse(x, 160, 20, 20-deatheffect, Math.PI, 0, Math.PI*2);
ctx.fillStyle = "#DDDDDD";
ctx.fill();
ctx.closePath();
deatheffect=deatheffect+0.2;
}
~中略~
}
楕円の弧を描くellipse()メソッド
void ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle [, counterclockwise]);
Canvas 2Dで楕円を描くときはellipse()メソッドを使用します。パラメータ(引数)の定義は以下の通りです。
x | 楕円の中心のX軸(水平)座標 |
y | 楕円の中心のY軸(垂直)座標 |
radiusX | 楕円の長辺の半径(負の値は設定できない) |
radiusY | 楕円の短辺の半径(負の値は設定できない) |
rotation | 楕円の傾き(ラジアンで表現) |
startAngle | 楕円が始まる角度で、正の X 軸から時計回りの角度をラジアンで表現したも |
endAngle | 楕円が終わる角度で、正の X 軸から時計回りの角度をラジアンで表現したもの |
counterclockwise(省略可能) | trueの場合、円弧を反時計回りに始まりから終わりの角度に向けて描く 既定値は false(時計回り) |
ラジアン角では円の1周(360度)は2π(2パイ)です。startAngleを「0」、endAngleを「Math.PI*2」とすることで360度の円を描くことができます。counterclockwiseは省略可能で省略すると「false」で設定されるため、もし楕円を反時計回りに描きたい場合はcounterclockwiseに「true」を設定してください。今回は楕円を描く途中は描画にはしていませんので、counterclockwiseはfalseでもtrueでも見た目は変わりません。
障害物がゆらゆら揺れる演出
最終ステージまで行って宝箱を開けると、その後、復路のステージでは障害物がゆらゆら揺れて、往路のときよりも緊張感を高める演出にしています。障害物がゆらゆら揺れることで障害物にも当たりやすくなり難易度を少し上げています。しかも復路では一定時間が過ぎると後ろから大きな障害物が追いかけてくるため、そこからも逃げないといけません。ユーザにゲームを飽きさせない工夫としています。
game()関数からの抜粋(avoider.jsから抜粋)
function game() {
~中略~
if (Re) {
//復路側の処理
Rex=Rex+Rem;
~中略~
//障害物をゆらゆら揺らして少し難易度を上げる
if (Rex>=10) {
Rem=-1;
}else if (Rex<=-10) {
Rem=1;
}
~中略~
}else{
~中略~
}
}
主人公が最終ステージで宝箱に近づくとReフラグに「true」がセットされます。このReフラグによって往路と復路の判定を行っています。変数Rexが各ステージの障害物で共通で使用しているx座標のオフセット値です。往路時はRexは常に「0」としていますので、障害物は揺れていませんが、復路時にはこのRexが上記の処理によって「-10」から「10」までの増減を繰り返すことで障害物が揺れる動きになっています。このようにベースとなる値にオフセットを付けることで、同じような動きをするが、他のキャラクタとは少し違った動きをする、みたいな制御が出来るようになります。是非、ゲーム制作の参考にしてみてください。
描画はパラパラ漫画
ゲームなどコンピュータで何かを描画するという仕組みはパラパラ漫画と同じです。少しずつキャラクタをずらしながら描画することでキャラクタが動いて見えています。描画の更新速度が遅いとキャラクタがカクついて動いているように見えますし、描画速度が速いと滑らかな動きをします。ただし、描画速度が速くても、キャラクタの移動量が大きすぎるとやはりカクついて不自然な動きに見えます。描画速度とキャラクタの移動量を上手く調整することがキャラクタを自然な動きに見せるコツになります。
もう1つ重要なことがあります。それは描画する度に画面をクリアしておくことです。ゲームエンジンやフレームワークによっては自動で画面をクリアしているものもあるかもしれませんが、基本的には毎回、自分で画面をクリアする必要があります。
今回はメイン関数であるgame()関数内の先頭行でキャンバスのクリアを行っています。
game()関数からの抜粋(avoider.jsから抜粋)
function game() {
ctx.clearRect(0, 0, canvas.width, canvas.height); //キャンバスのクリア
~中略~
}
キャンバスをクリアするclearRect()メソッド
void ctx.clearRect(x, y, width, height);
clearRect() メソッドは、矩形領域のピクセルを透明な黒 (rgb(0 0 0 / 0%)) に設定します。矩形の左上の角は (x, y) にあり、大きさは width と height で指定されます。
x | 矩形領域の始点の X 座標 |
y | 矩形領域の始点の Y 座標 |
width | 矩形領域の幅を指定 |
height | 矩形領域の高さを指定 |
キャンバスをクリアするのはclearRect() メソッドを使わなくても可能です。例えば画面の背景が黒であれば、fillStyleで黒に設定したあと、rect()メソッドを使って矩形を描画すれば画面をクリアしていることになります。
ではもし画面をクリアしておかないとどう描画されるでしょうか?画面をクリアしておかないと前の描画が残ったままになります。試しにclearRect() メソッドの呼び出しをコメントアウトしてコードを無効にして動かしたときの画面を掲載しておきます。
clearRect() メソッドを呼ばなかったときの映像
前回の描画が残ったままになりますのでこのような描画になってしまいます。お絵かきソフトのように前の描画も残しておく必要がある場合は逆に画面を毎回クリアしてはいけませんね。
コンピュータでの描画はパラパラ漫画ですので、背景も毎回クリアしないといけないということを覚えておきましょう。
まとめ
今回も最後までご覧いただきありがとうございます。
今回は細々とした内容をお伝えしました。どれもゲーム制作に応用できる内容だと思います。今回の解説までで『避けゲー』のほとんどの処理を見てきたことになります。『避けゲー』はコメント行を含めてもわずか600行強の短いプログラムです。今回のような単純なゲームでもこれだけたくさんの要素や工夫が盛り込まれています。簡単なゲームでも1つ作れば、ものすごくプログラミング力が上がります。ぜひ皆さんもゲームプログラミングにチャレンジしてみてください。その際に今回のシリーズが参考になれば執筆者冥利に尽きます。
次回からは『弾幕インベーダー』の解説シリーズを始める予定です。またお付き合いいただけるとうれしいです。
【PR】FXを始めるなら【DMM FX】!
主人公が消滅するシーンは円が垂直方向に潰されるような演出にしています。それを行っているのが上記のコードです。主人公が障害物に触れるとgameoverフラグに「true」がセットされるため、else if側の処理を通るようになり、そこから主人公が消滅していく描画が始まります。
主人公の消滅の演出には楕円を描くellipse()メソッドを使っています。y軸(垂直方向)の値(y軸方向の半径)を20から0になるまで徐々に減らしていっています。x軸の値(半径)は20としているため、y軸の値が20のときは真円、y軸の値が19~1のときは楕円で、y軸の値が0になると表示が消えます。これを10msecに1ずつy軸の値を減らすことで円が徐々に潰れていくような描画になります。