今回は、JavaScriptでフェードイン及びフェードアウト処理を実装したいと思います。
フェード処理と言うと、透明度を変更して再現することが多いですが、ポケモンでモンスターに遭遇する時や、ボス戦を迎えた時のようなエンカウント風フェード処理を作りたいと思います。
エンカウント時に使えるフェードイン・アウト処理【JavaScript】
前回まで作成していたソースコードにフェードに関する処理を追記します。
<!DOCTYPE html> | |
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<meta charset="utf-8" /> | |
<script type="text/javascript" src="https://code.jquery.com/jquery-3.4.1.js"></script> | |
<title></title> | |
</head> | |
<body onload="initialize()"> | |
<canvas id="test" width="520" height="320"></canvas> | |
<img id="map001" src="map001.png" style="display:none" /> | |
<img id="map002" src="map002.png" style="display:none" /> | |
<img id="character1" src="character1.png" style="display:none" /> | |
<img id="keyimg" src="keyimg.png" style="display:none" /> | |
<script> | |
"use strict"; | |
var map = [//マップデータ | |
[0,0,0,0,0,0,0,0,0,1], | |
[0,0,0,0,0,0,0,0,0,0], | |
[0,0,0,0,0,0,0,0,0,1], | |
[0,0,0,0,0,1,0,0,0,0], | |
[0,0,0,0,0,1,0,0,0,0], | |
[0,0,0,0,0,0,0,0,0,0], | |
[0,0,0,0,0,0,0,0,0,1], | |
[0,0,0,0,0,0,0,0,0,0], | |
[0,0,0,0,0,0,0,0,0,0], | |
[0,0,0,0,0,0,0,0,0,0], | |
]; | |
var gc,px=0,py=0; | |
var count = 0; | |
function initialize(){ | |
gc = document.getElementById("test").getContext("2d"); | |
document.onkeydown = keydown; | |
document.onmousedown = mousedown; | |
paint(); | |
} | |
function keydown(e){ | |
charactermove(e.keyCode); | |
} | |
function charactermove(keyCode){ | |
var hitx = px,hity = py;//当たり判定用の変数を作成 | |
switch(keyCode){ | |
case 39: | |
if((map[hity][hitx+1]) == 0){ | |
px++;//右移動 | |
} | |
break; | |
case 37: | |
if((map[hity][hitx-1]) == 0){ | |
px--;//左移動 | |
} | |
break; | |
case 38: | |
if(py > 0){ | |
if((map[hity-1][hitx]) == 0){ | |
py--;//上移動 | |
} | |
} | |
break; | |
case 40: | |
if(py < 9){ | |
if((map[hity+1][hitx]) == 0){ | |
py++;//下移動 | |
} | |
} | |
break; | |
} | |
paint(); | |
} | |
function mousedown(e){ | |
var keyCode=0; | |
var mouseX = e.offsetX; | |
var mouseY = e.offsetY; | |
if(320 < mouseX && mouseX < 520 && 0 < mouseY && mouseY < 200){ | |
mouseX -=420; | |
mouseY -=100; | |
if(Math.abs(mouseX) > Math.abs(mouseY)){ | |
keyCode = mouseX < 0 ? 37 : 39; | |
}else{ | |
keyCode = mouseY < 0 ? 38 : 40; | |
} | |
} | |
charactermove(keyCode); | |
} | |
function paint(){ | |
for(var y = 0; y<map.length;y++){ | |
for(var x = 0;x<map[y].length;x++){ | |
if(map[y][x] == 0){ | |
gc.drawImage(map001,x*32,y*32,32,32); | |
} | |
if(map[y][x] == 1){ | |
gc.drawImage(map001,x*32,y*32,32,32); | |
gc.drawImage(map002,x*32,y*32,32,32); | |
} | |
} | |
} | |
gc.drawImage(character1,px*32,py*32,32,32); | |
gc.drawImage(keyimg,320,0); | |
} | |
$(function(){//フェードイン及びフェードアウト | |
setInterval(function(){ | |
count++; | |
gc.fillStyle = "black" | |
for(var y = 0; y<map.length;y++){ | |
for(var x = 0;x<map[y].length;x++){ | |
switch(count){ | |
case 1: | |
gc.fillRect(y*32,x*32,16,16); | |
break; | |
case 2: | |
gc.fillRect(y*32+16,x*32,16,16); | |
break; | |
case 3: | |
gc.fillRect(y*32,x*32+16,16,16); | |
break; | |
case 4: | |
gc.fillRect(y*32+16,x*32+16,16,16); | |
break; | |
} | |
} | |
} | |
for(var y = 0; y<map.length;y++){ | |
for(var x = 0;x<map[y].length;x++){ | |
switch(count){ | |
case 5: | |
if(map[y][x] == 0){ | |
gc.drawImage(map001,0,0,16,16,x*32,y*32,16,16); | |
} | |
if(map[y][x] == 1){ | |
gc.drawImage(map001,0,0,16,16,x*32,y*32,16,16); | |
gc.drawImage(map002,0,0,16,16,x*32,y*32,16,16); | |
} | |
break; | |
case 6: | |
if(map[y][x] == 0){ | |
gc.drawImage(map001,16,0,16,16,x*32+16,y*32,16,16); | |
} | |
if(map[y][x] == 1){ | |
gc.drawImage(map001,16,0,16,16,x*32+16,y*32,16,16); | |
gc.drawImage(map002,16,0,16,16,x*32+16,y*32,16,16); | |
} | |
break; | |
case 7: | |
if(map[y][x] == 0){ | |
gc.drawImage(map001,0,16,16,16,x*32,y*32+16,16,16); | |
} | |
if(map[y][x] == 1){ | |
gc.drawImage(map001,0,16,16,16,x*32,y*32+16,16,16); | |
gc.drawImage(map002,0,16,16,16,x*32,y*32+16,16,16); | |
} | |
break; | |
case 8: | |
if(map[y][x] == 0){ | |
gc.drawImage(map001,16,16,16,16,x*32+16,y*32+16,16,16); | |
} | |
if(map[y][x] == 1){ | |
gc.drawImage(map001,16,16,16,16,x*32+16,y*32+16,16,16); | |
gc.drawImage(map002,16,16,16,16,x*32+16,y*32+16,16,16); | |
} | |
break; | |
} | |
} | |
} | |
if(count > 8){ | |
gc.drawImage(character1,px*32,py*32,32,32); | |
clearInterval(); | |
} | |
},300); | |
}); | |
</script> | |
</body> | |
</html> |
結構ソースコードが長くなってきましたね。数字が多くてちょっとややこしいかもしれません。
jQueryを使う
今回の処理は、JavaScriptのライブラリでお馴染みのjQueryを使うので、jQueryの宣言をしておきます。
公式サイトのライブラリからダウンロードする方法もありますが、CDN(コンテンツデリバリーネットワーク)を使う事でその必要もありませんので、こっちのほうが便利です。
HTMLは、上から読み込みますのでjQueryの宣言は頭のほうで行いましょう。
フェードイン・フェードアウト(112行目~189行目)
実際にフェードイン及びフェードアウトを行っている処理は、112行目から189行になります。
ページの読み込みが完了したら、フェード処理を実行したいので 「$(function()」を使います。
私の場合は、マップチップ1枚のサイズが32px×32pxとなっています。
フェードインもアウトも同じですが、画像を4分割して上の画像にある番号順に各マップの描画を行います。
この処理は、setIntervalを使って300ミリ秒毎に繰り返すようにしています。この間隔が長いとフェード感が無くなってしまうので、300ミリ秒ぐらいが丁度いいかもしれません。
フェード処理は、全部で8工程となるので、ケース毎の処理を変えるために、Switch文を使用します。count関数を使って、1~4がフェードアウト、5~8がフェードインに対応します。
まず、フェードアウトでは上にある画像のように、1枚の画像を4分割した左上を黒い四角形で塗りつぶすようにします。塗りつぶしはfillStyle、四角形の描画はfillRectで行います。
描画を行う際の座標に関しては、使用している画像のサイズによって適宜変更する必要がありますので、32px×32pxの画像を使っていない場合は値の調整を行ってください。
私の場合は、32を半分にした16をx及びyに足す事で、描画する位置を変更出来るといった感じです。そんなに難しい計算でもないので、初心者向けかなと思います。
画面全体を黒で塗りつぶしたら、今度はフェードイン処理に移行します。
フェードインでは、登録しておいたマップデータを利用する必要があるので、キャラクター描画時と同様にdrawImageを使います。
gc.drawImage(map001,0,0,16,16,x*32,y*32,16,16);
ちょっと引数が多くてややこしいですが・・・。
各引数をイメージにするとこんな感じです。最初の①②でトリミングを行う左上の座標を指定します。最初はそれぞれ0でOKですね。
③④でトリミングする幅と高さを指定します。この値に関しては常に同じになりますね。
後は、工程に合わせて描画する座標を変更すれば良いですね。
最終的にフェード処理が終わったら、clearIntervalを使ってsetIntervalの処理を止めます。
ゲームを実行してみると、昔ながらのポケモン風エンカウントフェードの完成です。
まとめ
エンカウント後は、違うマップデータを読み込むことで、シーンチェンジも出来ますね。
レトロなゲームには雰囲気バッチリなので、使いこなしてみてください。