【JavaScript】落ち物パズル「泡」【さじさんver】
どうも!LSSです!!
落ち物パズルシリーズ、今回は番外編!
大幅に雰囲気の変わったゲームをお楽しみください^^
落ち物パズル「泡」
ルール
↑こちらに、とても分かりやすく画像で説明されています。
簡単に書くと「色のついた泡の周囲(斜め含む8マス)を透明の泡で囲めばOK!」です^^
Designed by id:conasaji
このゲームは、LSSが当ブログにて制作していた落ち物パズルの「消し実装」前に、さじさん(id:conasaji)が改変に挑戦してくださって生まれたものです^^
その、作っていただいたものを今度は逆にLSSがパク消し機能を付与したものが、今回の記事となります!
コード
<style><!--
#gamen{
position:relative;
background-color: gray;
}
#gamen a{
color:black;
text-decoration:underline;
}
#gamen a:hover{
color:khaki;
}
.km{
position:absolute;
border-radius:30px;
text-align:center;
border-width:3px;
border-style:outset;
background-image:radial-gradient(circle at 70% 30%,#ffffffc0,transparent);
transition:0.2s;
}
.c1{background-color:pink;border-color: white;}
.c2{background-color:aqua;border-color: white;}
.btnlr{
display:inline-block;
position:relative;
width:110px;
background-color: lightcyan;
text-align:center;
border-radius:5px;
border:5px outset lightcyan;
background-image:radial-gradient(circle at 70% 30%,#ffffffc0,transparent);
}
.btndp{
display:inline-block;
position:relative;
background-color: orangered;
text-align:center;
border-radius:5px;
border:5px outset orangered;
background-image:radial-gradient(circle at 70% 30%,#ffffffc0,transparent);
}
--></style>
<div id="gamen"> </div>
<p> </p>
<div id="g2"> </div>
<p>
<script>// <![CDATA[
wMax=4;//横マス数
hMax=5;//縦マス数
kms=9;//コマ種類数
sz=60;//1マスあたりのサイズ
sy=9;//落下速度調節用
psf=false;//一時停止管理フラグ
scr=0;//スコア
fws=0;//花の数
masu=[ ];
for(i=0;i<wMax;i++){
masu[i]=[ ];
for(j=0;j<hMax;j++){
masu[i][j]=0;
}
}
w=wMax*sz;//画面幅
h=hMax*sz;//画面高さ
document.getElementById("gamen").style.width=w+'px';
document.getElementById("gamen").style.height=h+'px';
mx=0;//操作中コマ横位置
my=0;//操作中コマ縦位置
mz=0;//操作中コマ種類
mz_n=0;//NEXTコマ種類
gamen_i();
function tmc(){
if((my<h-sz) && (masu[mx][Math.floor(my/sz)+1]==0)){
my+=sz/sy;
kmv();
}else{
masu[mx][Math.floor(my/sz)]=mz;
clearInterval(tm);
kensa();
tms();
if(my<sz){
clearInterval(tm);
document.getElementById("gamen").style.backgroundColor='gold';
txt='ゲームオーバー<br/>';
txt+='スコア:'+scr+' 泡:'+fws;
g2.innerHTML=txt;
}else{
kminit();
gamen_w();
kmv();
}
}
}
function tms(){
tm=setInterval("tmc()",200);
}
function rakka(){
while(masu[mx][Math.floor(my/sz)+1]==0){
scr++;
my=Math.floor(my/sz)*sz+sz+sz/3;
}
}
function gamen_i(){
txt='<a href="#!" onclick="tms();kminit();gamen_w();return false;">ここをクリックしてスタート!</a>';
txt+='<br/><br/><br/><a href="#!" onclick="kms=4;tms();kminit();gamen_w();return false;">ハードモード</a>';
gamen.innerHTML=txt;
}
function gamen_w(){
txt='';
txt='<div style="position:absolute;height:'+sz*0.6+'px;font-size:'+sz/4+'px;top:0px;left:0px;padding:3px 3px;">Score:'+scr+' 泡:'+fws+'</div>';
txt+='<div class="km c'+mz_n+'" style="height:'+sz*0.6+'px;font-size:'+sz/4+'px;top:0px;right:0px;padding:0px 10px;">NEXT '+mz_n+'</div>';
for(i=0;i<wMax;i++){
for(j=0;j<hMax;j++){
if(masu[i][j]>0){
txt+='<div class="km c'+masu[i][j]+'" style="top:'+j*sz+'px;left:'+i*sz+'px;width:'+sz+'px;height:'+sz+'px;">'+masu[i][j]+'</div>';
}
}
}
txt+='<div id="koma" class="km c'+mz+'" style="top:'+my+'px;left:'+mx*sz+'px;width:'+sz+'px;height:'+sz+'px;">'+mz+'</div>';
gamen.innerHTML=txt;
txt2='';
if(psf){
txt2+='<a href="#!" onmousedown="tms();psf=false;gamen_w();">再開</a> ';
}else{
txt2+='<a href="#!" class="btnlr" onmousedown="mx-=(mx>0 && masu[mx-1][Math.ceil(my/sz)]==0?1:0);kmv();" ondoubleclick="return false;">←に移動</a>';//何故かonclickだと反応が鈍い
txt2+='<a href="#!" class="btnlr" onmousedown="mx+=(mx<wMax-1 && masu[mx+1][Math.ceil(my/sz)]==0?1:0);kmv();" ondoubleclick="return false;">→に移動</a>';
txt2+='<a href="#!" class="btndp" onmousedown="rakka();kmv();" ondoubleclick="return false;">落とす</a> ';
txt2+='<br/>';
txt2+='<br/>';
txt2+='<a href="#!" onmousedown="clearInterval(tm);psf=true;gamen_w();">一時停止</a>';
}
g2.innerHTML=txt2;
}
function kmv(){
document.getElementById('koma').style.top=my+'px';
document.getElementById('koma').style.left=mx*sz+'px';
}
function kminit(){
mx=3;
my=0;
do{
mz=mz_n;
mz_n=Math.floor(1+Math.random()*kms);
}while(mz==0)
}
function kensa(){
kscnt=0;//消しカウント
//全マス検査開始
for(i=1;i<wMax-1;i++){
for(j=1;j<hMax-1;j++){
if(masu[i][j]>0 && masu[i][j]<3 && masu[i-1][j-1]>2 && masu[i][j-1]>2 && masu[i+1][j-1]>2 && masu[i-1][j]>2 && masu[i+1][j]>2 && masu[i-1][j+1]>2 && masu[i][j+1]>2 && masu[i+1][j+1]>2){
kscnt++;
fws++;
}
}
}
gamen_w();
if(kscnt>0){
scr+=100;
for(i=0;i<wMax;i++){
for(j=0;j<hMax;j++){
masu[i][j]=0;
}
}
}
}
// ]]></script>
</p>
※赤文字部分が、さじさんとLSSが書き換えた部分になります。(他にカットした部分もあります)
消し判定について解説します
if(
masu[i][j]>0 && masu[i][j]<3
&& masu[i-1][j-1]>2 && masu[i][j-1]>2 && masu[i+1][j-1]>2
&& masu[i-1][j]>2 && masu[i+1][j]>2
&& masu[i-1][j+1]>2 && masu[i][j+1]>2 && masu[i+1][j+1]>2
){
処理部分
}
↑function kensa()の中のif文をこのように書き換えました。
変数 masu[横座標][縦座標]は、4×5の盤面に「どの泡が置かれているか」を記録している変数です。
例えばmasu[0][0]なら盤面の一番左上のマスを、masu[3][4]なら一番右下のマスの状態を表す数値が記録されています。
その値が0の時は空きマス、1~9の時は「その数字の泡が置かれている」ものとしています。
今回のゲームでは、色付き泡は1と2で、3以上は透明の泡です。
変数iとjでループを回しており、
(for(i=1;i<wMax-1;i++){ for(j=1;j<hMax-1;j++){の部分)
「masu[i][j]を中心とした場合に、条件が成立しているか?」をこのif文で判定しているんですね。
masu[i][j]>0 && masu[i][j]<3
は、「中央が1か2である事」(&&はand条件、日本語でいう「なおかつ」に相当)
&& masu[i-1][j-1]>2 && masu[i][j-1]>2 && masu[i+1][j-1]>2
j-1は中心より1マス分上の行で、中心となるiの左隣の座標であるi-1、およびi、およびi+1の全てが「2より大きい(3以上)=透明の泡である事」
&& masu[i-1][j]>2 && masu[i+1][j]>2
同様に、「中心の左隣と右隣が透明の泡である事」
&& masu[i-1][j+1]>2 && masu[i][j+1]>2 && masu[i+1][j+1]>2
同様に、中心より1マス分下の行の3つ全てが「透明である事」
…の全ての条件が成立した時、
「色付きの泡を透明の泡で包んだ」
という事になります^^
挙動の変更
LSSが作ったほうの落ち物パズルは、
「条件成立したコマだけを消し、その上にあったものは落下」
という仕組みにしていましたが、今回はゲームの性質上、その仕組みをばっさりカットし、「条件が成立したら全てのマスがリセット」されるようにしました。
また、「イージーモード」はこのゲームルールの場合、逆に難易度が上がるので、文言だけ「ハードモード」に変更していますwww
謝辞
もちろん、遊んで楽しんでいただいただけでも、めちゃくちゃありがたいです^^
それが更にこうして「自分なりの改変」 に挑戦していただけると、コード載せたり解説書いたりした甲斐があります^^
さじさん、ありがとうございました!
余談
さじさんが改変に挑戦された折、「泡が自然落下で着地した時にぽよんってなる」演出が生まれました。
偶然生まれたようですが、「泡」っぽさがより出る素敵な演出でしたので、
「落とす」ボタンを押した時にも、よりオーバーにポヨらせてみましたw
この細かい動きで質感を演出するのもいいですね^^
勉強になります!
7/14追記
CSS部分にtransition:0.2sを追加しました。(コード表記にも反映)
これにより、泡の動きが滑らかになりました^^
https://little-strange.hatenablog.com/entry/2020/07/13/070000
と同じ変更です。
ってなとこで、今回はこのへんで!
次回もまた、よろしくお願いします^^