2014年11月1日土曜日

テトリス用のブロック(テトリミノ)を複数用意する

前回作成したOBJECT変数を一つのパターンではなく、複数のパターンを持たせる様に変更します。

var block;
var OBJECTS = [
    [
        [1, 1, 1, 1],
        [0, 0, 0, 0]
    ],
    [
        [1, 1, 1, 0],
        [0, 0, 1, 0]
    ],
    [
        [1, 1, 0, 0],
        [0, 1, 1, 0]
    ],
    [
        [0, 1, 1, 0],
        [0, 1, 1, 0]
    ],
    [
        [1, 1, 1, 0],
        [0, 1, 0, 0]
    ]
];

大きな配列を用意して、OBJECTの構成の配列を複数作成して入れておきます。(OBJECTの変数名をOBJECTSに変更しています)
テトリミノを表示するためにはOBJECTSからランダムに一つ取得する必要があり、取得したものを扱える様にblock変数を用意して、下記の関数を追加します。

function getBlock(){
    r = Math.floor(Math.random() * OBJECTS.length);
    return OBJECTS[r];
}

getBlock関数は、OBJECTSの配列に入れたテトリミノのオブジェクト群からランダムでテトリミノを一つ取得するというもので、Math.floor()は引数に入れた数字の小数点を切捨て。Math.random()は0~1までの間でランダムな値を出力する(0.34256とか)。OBJECTS.lengthはテトリミノ群のオブジェクト数を返すもので、

r = Math.floor(Math.random() * OBJECTS.length);

この様に書くと、0からテトリミノ群の数の間のどれかの値を返し、OBJECTS[r]でランダムでテトリミノを取得できるというわけです。この関数を追加したら、

//四角を描く
function draw(){
    //OBJECTにある図形の配列からランダムで取得する
    block = getBlock();
    ctx.strokeStyle = "blue";
    for ( var y = 0; y < 2; y++) {
        for ( var x = 0; x < 4; x++) {
            if ( block[y][x] ) {
                ctx.strokeRect(x * SIDE, y * SIDE, SIDE, SIDE);
            }
        }
    }
}

getBlock()でテトリミノオブジェクトを取得し、事前に用意したblock変数に入れる。前のコードで if ( OBJECT[y][x] )としていたところをif ( block[y][x] )と変更することで、


読み込む度に上のテトリミノのどれかが表示されます。(グレーのグリットは実際には表示されません)

2014年10月31日金曜日

テトリスで使用するようなブロックを描写する


このようなブロックをCanvas内で描写します。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>square</title>
<script>
var canvas;
var ctx;

//四角の一辺の長さ
var SIDE = 20;

//ブロックの描写用に配列を用意する
var OBJECT = [
        [1, 1, 0, 0],
        [0, 1, 1, 0]
    ];

window.onload = function(){
    canvas = document.querySelector("#canvas");
    ctx = canvas.getContext("2d");

    draw();
};

//四角を描く
function draw(){
    ctx.strokeStyle = "blue";
    for ( var y = 0; y < 2; y++) {
        for ( var x = 0; x < 4; x++) {
             if ( OBJECT[y][x] ) {
                ctx.strokeRect(x * SIDE, y * SIDE, SIDE, SIDE);
             }
        }
    }
}
</script>
</head>
<body>
<canvas id="canvas" width="400" height="300"></canvas>
</body>
</html>

二回のfor文で、


事前に用意した配列の画像の順にアクセスし、if ( OBJECT[y][x] )でアクセスした数字が1の場合はxとyの値を使って任意の箇所に四角を描写するという処理を行います。開始場所をx * SIDEとy * SIDEで指定することで、


配列の数字に対応して図形を描写することが出来ます。

var OBJECT = [
        [1, 1, 1, 0],
        [0, 0, 1, 0]
    ];

このような配列にすると


1の並び方に従って、上の画像のような描写になります。
(灰色のグリッドは表示されません)

2014年10月30日木曜日

同じ形を並べてたくさん描写する

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>square</title>
<script>
var canvas;
var ctx;

//四角の一辺の長さを変数に入れておく
var SIDE = 20;

window.onload = function(){
    canvas = document.querySelector("#canvas");
    ctx = canvas.getContext("2d");
  
    draw();
};

//四角を描く
function draw(){
    ctx.strokeStyle = "blue";
    ctx.strokeRect(0, 0, SIDE, SIDE);
}
</script>
</head>
<body>
<canvas id="canvas" width="400" height="300"></canvas>
</body>
</html>

strokeRectで四角を描写できます。strokeRectの引数には左から(X軸の開始点, Y軸の開始点, 幅, 高さ)を指定して四角を描写します。実行すると


この様に四角が表示されます。この四角を右にたくさん並べて表示してみます。

function draw(){
    ctx.strokeStyle = "blue";
    for ( var i = 0; i < 5; i++) {
        ctx.strokeRect(i * SIDE, 0, SIDE, SIDE);
    }
}

draw関数のstrokeRect関数の前にfor分で繰り返し実行をします。for ( var i = 0; i < 5; i++ )を書くと、0から1ずつ足して、内部のコードをiが5未満の数字になるまで実行するという意味になり、iの値は0, 1, 2, 3, 4でstrokeRectを行う。第一引数の開始点を20ずつずらすということで、i * SIDEで四角を並べるように描写することができます。このコードを実行すると


このようにforで指定した分だけ四角が表示されました。for ( var i = 0; i < 10; i++ )にすると、


四角が10個表示されました。

イベントリスナでタグにイベントハンドラを登録する

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>event handler</title>
<script>
function exe(){
    window.alert("ボタンを押しました!");
}
</script>
</head>
<body>
    <input type="button" value="押す" onclick="exe();">
</body>
</html>

上記のコードのようなボタンのタグを追加して、タグ内にonclick="exe();"といったJavaScriptの関数とつなぎこむonclickのようなものをイベントハンドラと呼びます。
(他にはonkeyupやonkeydownとか)

今回のサンプルコードでは、ボタンのタグに直接記述していますが、JavaScriptのコードでボタンタグにイベントハンドラを追加することが出来ます。

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>event handler</title>
<script>
window.onload = function(){
    var button = document.querySelector("#button");
    button.addEventListener("click", exe);
};

function exe(){
    window.alert("ボタンを押しました!");
}
</script>
</head>
<body>
    <input type="button" value="押す" id="button">
</body>
</html>

ボタンのタグをdocument.querySelectorで取得できるようにidを付与して、window.onload内でボタンのオブジェクトを取得して、イベントリスナ(addEventListener)でイベントハンドラを登録します。addEventListenerの第一引数には、登録したいイベントハンドラ(今回はclick)を入れ、第二引数には登録したイベントハンドラを実行した時に実行したい関数を指定します。これで、<input type="button" onclick="exe();">と同等の実装になりました。

特定のタグではなく、表示しているブラウザ全体にイベントハンドラを追加したい時、

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>sample</title>
<script>
window.onload = function(){
    document.addEventListener("click", exe);
};

function exe(){
    alert("クリックしました!");
}
</script>
</head>
<body>
</body>
</html>

documentオブジェクトでaddEventListenerを実行して、イベントハンドラを登録すれば実行できるようになります。

2014年10月28日火曜日

キーを押して、箱を任意の方向に動かす

今まで作成してきた動く箱を、好きな方向に動かせる様にキーボードの矢印キーに対応して、矢印キーを押した方向に箱が動くように修正します。箱の動作をキーに連動させるためには、documentオブジェクトにあるaddEventListenerでキーを押した時と、キーを離した時の処理を追加します。

<script>
var canvas;
var ctx;

var mx = 20;
var my = 20;

//どのキーを押しているかを記録しておくための変数
var keyCode;

var dx;
var dy;

window.onload = function(){
    canvas = document.querySelector("#canvas");
    ctx = canvas.getContext("2d");
  
    //キーを離した(keyup)時の処理。キーの記録の変数の値を消す(nullにする)
    document.addEventListener("keyup", function(event){
        keyCode = null;
    });
  
    //キーを押した(keydown)時の処理。キーの番号を記録しておく
    document.addEventListener("keydown", function(event){
        keyCode = event.keyCode;
    });
  
    setInterval(draw, 40);
};

function draw(){
 //処理は省略
}
</script>

document.addEventListenerはイベントというユーザがブラウザ上で何か操作した時に処理を開始する(ハンドラ-)ために用意しておくもので、addEventListenerの第一引数にkeyup、keydownやclick等の操作を入れて、第二引数には実行したい処理を関数の形式で用意しておきます。document.addEventListener(“keydown”, 実行したい関数名);で何のキーを押した時に実行される処理を追加します。今回はキーを押すか、離すかで用意したkeyCode変数に現在の状況を入れてます。キーボードにある各キーには番号が振り分けられており、下矢印のキーには38が当てられています。event.keyCodeで各キーの番号を取得できます。次にsetIntervalでセットされているdraw関数を見ていきます。

function draw(){

    //今押しているキーから、どの向きに箱を移動するかを設定する関数を実行する
    changeDirection();

    //処理の順番を方向を決めた直後に変更する
    mx = mx + dx;
    my = my + dy;
  
    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, 400, 300);
  
    ctx.beginPath();
    ctx.moveTo(mx, my);
    ctx.lineTo(mx, my + 20);
    ctx.lineTo(mx + 20, my + 20);
    ctx.lineTo(mx + 20, my);
    ctx.lineTo(mx, my);
    ctx.strokeStyle = "blue";
    ctx.stroke();
}

function changeDirection(){
  
    //ボタンを押してない時の挙動、keyCodeにはundefinedかnullが入っている
    if(keyCode == undefined || keyCode == null){
        dx = 0;
        dy = 0;
    }
  
    //左のカーソル(37のキー)を押したとき
    if(keyCode == 37){
        dx = -1;
    }
  
    //上のカーソル(38のキー)を押したとき
    if(keyCode == 38){
        dy = -1;
    }
  
    //右のカーソル(39のキー)を押したとき
    if(keyCode == 39){
        dx = 1;
    }
  
    //下のカーソルを押したとき
    if(keyCode == 40){
        dy = 1;
    }
}

draw関数の最初にchangeDirectionという関数を追加して、addEventListener内でkeyCodeに入れたキーの番号を元にdxとdyの数字を決定させ、その後に四角い箱用のmxとmyを変更して四角を描きます。


このコードを追加したことにより、キーに対応して箱が動くアニメーションになります。

2014年10月27日月曜日

Canvas上で動かしている箱が端に到達した時に移動の向きを逆にする

前回のコードだと、箱がCanvasの右端に到達後、フェードアウトで消えていきます。せっかくなので、端に到達したら逆向きに移動するようにコードを変更してみます。前回のコードでは、draw関数の最後の行でmx変数に1足して、次の描写で1px右で箱を描写しています。逆向きに移動するにはmx変数に1引くことで実現でいます。向きの数字が変更ということで、新たに向きに関するdxという変数を追加して、右端に到達したら、向きについてを変更してみます。

<script>
var canvas;
var ctx;

//X軸の位置が変更できるようにXの値を入れる変数を用意する
var mx = 20;

//移動の向きに関する値を入れる変数
var dx = 1;

window.onload = function(){
    canvas = document.querySelector("#canvas");
    ctx = canvas.getContext("2d");
    setInterval(draw, 20);
};

function draw(){
    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, 400, 300);

    ctx.beginPath();
    ctx.moveTo(mx, 20);
    ctx.lineTo(mx, 40);
    ctx.lineTo(mx + 20, 40);
    ctx.lineTo(mx + 20, 20);
    ctx.lineTo(mx, 20);
  
    ctx.strokeStyle = "blue";
    ctx.stroke();
  
    //mxにdxを加える
    mx = mx + dx;

    //右端は400pxになるので、mxが400よりも大きければ右端にいると見なす
    if(mx > 400){
        //向きの値に-1をかけて、逆向きの指示にする。次のdrawの実行から有効
        dx = -1 * dx;
    }
}
</script>

mxの中の値を見て、箱が右端にいることが分かれば、向きdxの値の符号を逆にする。この処理により、


箱が右端に到達したら、向きを逆にして、移動を開始します。次に左端に到達したら先ほどと同様にフェードアウトしてしまうので、左端でも折り返す処理を追加します。

function draw(){
    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, 400, 300);

    ctx.beginPath();
    ctx.moveTo(mx, 20);
    ctx.lineTo(mx, 40);
    ctx.lineTo(mx + 20, 40);
    ctx.lineTo(mx + 20, 20);
    ctx.lineTo(mx, 20);
  
    ctx.strokeStyle = "blue";
    ctx.stroke();
  
    //mxにdxを加える
    mx = mx + dx;

    if(mx < 0 || mx > 400){
        //向きの値に-1をかけて、逆向きの指示にする。次のdrawの実行から有効
        dx = -1 * dx;
    }
}

mxが左端に到達、つまりは0以下になった時も逆向きにするという処理を追加して


箱が往復し続けるアニメーションができました。Y軸でも同様の処理を追加すると箱が縦横無尽に移動するアニメーションになります。

<script>
var canvas;
var ctx;

var mx = 20;
var my = 20;

var dx = 1;
var dy = 1;

window.onload = function(){
    canvas = document.querySelector("#canvas");
    ctx = canvas.getContext("2d");
    setInterval(draw, 20);
};

function draw(){
    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, 400, 300);

    ctx.beginPath();
    ctx.moveTo(mx, 20);
    ctx.lineTo(mx, 40);
    ctx.lineTo(mx + 20, 40);
    ctx.lineTo(mx + 20, 20);
    ctx.lineTo(mx, 20);
  
    ctx.strokeStyle = "blue";
    ctx.stroke();
  
    mx = mx + dx;
    my = my + dy;

    if(mx < 0 ||mx > 400){
        //向きの値に-1をかけて、逆向きの指示にする。次のdrawの実行から有効
        dx = -1 * dx;
    }

    //Y軸の下の端は300なので、0と300を判断する
    if(my < 0 ||my > 300){
        //向きの値に-1をかけて、逆向きの指示にする。次のdrawの実行から有効
        dy = -1 * dy;
    }
}
</script>

Canvasで箱が横に移動するアニメーションを作る

Canvasに今まで使ってきたコードを組み合わせるとアニメーションを作成することができます。今回はCanvas内で描写した青い箱を横に移動したいと思います。はじめにアニメーションの仕組みですが、Canvas上で箱を描いて、すぐにその箱を消す。消した直後に少しずらした位置にサイズの同じ箱を描写すれば動いている様に見えます。setIntervalで指定した時間毎に作成したdraw関数を実行する様にしましょう。

<script>
var canvas;
var ctx;

window.onload = function(){
    canvas = document.querySelector("#canvas");
    ctx = canvas.getContext("2d");
  
    //0.02秒毎にdraw関数を実行する
    setInterval(draw, 20);
};

function draw(){
    ctx.beginPath();

    ctx.moveTo(20, 20);

    ctx.lineTo(20, 40);
    ctx.lineTo(40, 40);
    ctx.lineTo(40, 20);
    ctx.lineTo(20, 20);
  
    ctx.strokeStyle = "blue";
    ctx.stroke();  
}
</script>

前回のdraw関数をsetInterval関数で0.02秒(指定は20)毎に実行する様にします。この状態では、同じ箇所に重ねる様に何度もdraw関数を実行するという指示しかありません。次に箱の位置を少しずらすコードを追加します。

<script>
var canvas;
var ctx;

//X軸の位置が変更できるようにXの値を入れる変数を用意する
var mx = 20;

window.onload = function(){
    canvas = document.querySelector("#canvas");
    ctx = canvas.getContext("2d");
  
    setInterval(draw, 20);
};

function draw(){
    ctx.beginPath();
  
    ctx.moveTo(mx, 20);
  
    ctx.lineTo(mx, 40);
    ctx.lineTo(mx + 20, 40);
    ctx.lineTo(mx + 20, 20);
    ctx.lineTo(mx, 20);
  
    ctx.strokeStyle = "blue";
    ctx.stroke();
  
    //mxに1加える
    mx = mx + 1;
}
</script>

X軸の値用にmxという変数を用意して、drawを実行する度にmxに1を足す様にして、mxの値を利用して四角を描写します。(mx + 20にすることで右辺の位置を指定できる様になる)


しかし、現時点でのコードだと、描写した四角を消さずに少しずらした点で再度四角を書くという処理が続くため、この画像の様にバー状の描写になります。次に四角を描写する度に四角を削除するという処理を追加します。

function draw(){

    //塗りつぶしの指示(パスを使っていないので、beginPathはいらない)
    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, 400, 300);

    //ここから四角の描写
    ctx.beginPath();

    ctx.moveTo(mx, 20);

    ctx.lineTo(mx, 40);
    ctx.lineTo(mx + 20, 40);
    ctx.lineTo(mx + 20, 20);
    ctx.lineTo(mx, 20);

    ctx.strokeStyle = "blue";
    ctx.stroke();

    //mxに1加える
   mx = mx + 1;
}

draw関数の最初にfillStyleで白にしていて、fillRect関数でcanvasを塗りつぶしています。fillRect関数の引数は左から(Xの開始点, Yの開始点, 幅, 高さ)の指定なので、左端の0, 0からcanvasタグの属性に入れたwidthの400pxとheightの300pxを指定してcanvasタグを元のまっさらな状態に戻して、その後に青い四角を描写します。


これで四角が右側に移動するアニメーションができました。

<script>
var canvas;
var ctx;

var mx = 20;
var my = 20;

window.onload = function(){
    canvas = document.querySelector("#canvas");
    ctx = canvas.getContext("2d");
  
    //0.02秒毎にdraw関数を実行する
    setInterval(draw, 20);
};

function draw(){
    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, 400, 300);

    //ここから四角の描写
    ctx.beginPath();

    ctx.moveTo(mx, my);

    ctx.lineTo(mx, my + 20);
    ctx.lineTo(mx + 20, my + 20);
    ctx.lineTo(mx + 20, my);
    ctx.lineTo(mx, my);

    ctx.strokeStyle = "blue";
    ctx.stroke();

    //mxに1加える
    mx = mx + 1;

    //myに1加える
    my = my + 1;
}
</script>

こうすることで、箱が右下に移動するアニメーションが作成できます。