プログラミングを勉強している人が、最も面白みを感じるのは、GUIの表示処理だと感じた、ユゲタです。
一昔前のCUIだけのプログラミングは、文字を入力して、文字が返ってくるか、デバイスに何かしらの処理をさせるだけだったんですが、少なくとも、ゲームをプログラミングするときには、その結果をグラフィカルに表示しないといけません。
webアプリの場合は、インターネットブラウザがその役割を担ってくれるので、便利なのですが、プログラム言語によっては、webブラウザに結果表示するのもしんどいものもあります。
それぞれの言語でGUIライブラリなどがあるものもあれば、自分でイチから作らないと行けないものも有り、こうした点で、有利なプログラム言語と、不利な言語があります。
この点で言えば、javascriptは、webブラウザに限定されてしまいますが、GUIの初期操作を省ける点が便利ですね。
そんなjavascriptでのcanvasを使った、三目並べの2日目、今回は操作イベントをセットしてみます。
本日のIT謎掛け
「クリックイベント」と、かけまして・・・
「犬の動詞系」と、ときます。
そのココロは・・・
ポチる。
Eventセット前準備
プレイヤーは、○からスタートする仕様で進めようと思います。
あとで、開始前に先攻と後攻を選択するモードを付ける予定ですが、とりあえず、ゲーム部分の作り込みを先に行っておきます。
三目並べに必要なイベントは、9つのマスに、クリック(またはタッチ)することで、そのイチに「○」または「×」の記号を設置していきます。
canvasでのクリックイベントは、座標を取得して、判定すればいいのでしょうか?
とりあえず、クリックイベントでの情報取得をして判定してみたいと思います。
クリックイベントのセット
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>TicTacToe</title>
<style>
#mycanvas{
border:1px solid #ccc;
}
</style>
<script src="tictactoe.js"></script>
</head>
<body>
<canvas id="mycanvas" width="400" height="200">not canvas</canvas>
</body>
</html>
(function(){
var LIB = function(){};
LIB.prototype.event = function(target, mode, func , flg){
flg = (flg) ? flg : false;
if (target.addEventListener){target.addEventListener(mode, func, flg)}
else{target.attachEvent('on' + mode, function(){func.call(target , window.event)})}
};
LIB.prototype.construct = function(){
switch(document.readyState){
case "complete" : new MAIN();break;
case "interactive" : this.event(window , "DOMContentLoaded" , (function(){new MAIN()}).bind(this));break;
default : this.event(window , "load" , (function(){new MAIN()}).bind(this));break;
}
};
var MAIN = function(){
// 棋譜格納用のバッファ
this.buffer = [];
this.drawBase();
this.setEvent();
};
MAIN.prototype.drawBase = function(){
var ctx = document.getElementById("mycanvas").getContext("2d");
// 背景
ctx.fillStyle = "#14bdac";
ctx.fillRect(0,0,400,200);
// 枠線
ctx.strokeStyle = "#0da192";
ctx.lineWidth = 6;
ctx.beginPath();
ctx.moveTo(170,20);
ctx.lineTo(170,180);
ctx.stroke();
ctx.fill();
ctx.beginPath();
ctx.moveTo(230,20);
ctx.lineTo(230,180);
ctx.stroke();
ctx.fill();
ctx.beginPath();
ctx.moveTo(120,70);
ctx.lineTo(280,70);
ctx.stroke();
ctx.fill();
ctx.beginPath();
ctx.moveTo(120,130);
ctx.lineTo(280,130);
ctx.stroke();
ctx.fill();
};
MAIN.prototype.setEvent = function(){
var ctx = document.getElementById("mycanvas");
new LIB().event(ctx , "click" , (function(e){this.clickCtx(e)}).bind(this));
};
MAIN.prototype.clickCtx = function(e){
var canvas = e.currentTarget;
// canvas内のクリックされた座標
var posX = e.clientX - canvas.getBoundingClientRect().left;
var posY = e.clientY - canvas.getBoundingClientRect().top;
// 座標から枠判定 [1-9]
var cell = this.checkCell(posX , posY);
if(!cell){return;}
// バッファに登録(登録済みのセルの場合は、処理しない)
if(this.buffer.indexOf(cell) !== -1){return;}
this.buffer.push(cell);
// 記号を表示する
this.drawBuffer();
};
MAIN.prototype.checkCell = function(posX , posY){
if(118 <= posX && posX <= 118 + 47
&& 18 <= posY && posY <= 18 + 47){
return 1;
}
if(177 <= posX && posX <= 177 + 47
&& 18 <= posY && posY <= 18 + 47){
return 2;
}
if(236 <= posX && posX <= 236 + 47
&& 18 <= posY && posY <= 18 + 47){
return 3;
}
if(118 <= posX && posX <= 118 + 47
&& 76 <= posY && posY <= 76 + 47){
return 4;
}
if(177 <= posX && posX <= 177 + 47
&& 76 <= posY && posY <= 76 + 47){
return 5;
}
if(236 <= posX && posX <= 236 + 47
&& 76 <= posY && posY <= 76 + 47){
return 6;
}
if(118 <= posX && posX <= 118 + 47
&& 134 <= posY && posY <= 134 + 47){
return 7;
}
if(177 <= posX && posX <= 177 + 47
&& 134 <= posY && posY <= 134 + 47){
return 8;
}
if(236 <= posX && posX <= 236 + 47
&& 134 <= posY && posY <= 134 + 47){
return 9;
}
return null;
};
MAIN.prototype.drawBuffer = function(){
var canvas = document.getElementById("mycanvas");
var ctx = canvas.getContext("2d");
// 描画をクリアする
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 基盤を表示
this.drawBase();
// バッファ全ての座標を指定
for(var i in this.buffer){
// セルに記号を書き込む
this.drawMark(this.buffer[i] , 0);
}
};
// mark @ [0:○ , 1:×]
MAIN.prototype.drawMark = function(cell , mark){
var ctx = document.getElementById("mycanvas").getContext("2d");
var x = y = null , r=16;
switch(cell){
case 1:
x = 118 + 24;
y = 18 + 24;
break;
case 2:
x = 177 + 24;
y = 18 + 24;
break;
case 3:
x = 236 + 24;
y = 18 + 24;
break;
case 4:
x = 118 + 24;
y = 76 + 24;
break;
case 5:
x = 177 + 24;
y = 76 + 24;
break;
case 6:
x = 236 + 24;
y = 76 + 24;
break;
case 7:
x = 118 + 24;
y = 134 + 24;
break;
case 8:
x = 177 + 24;
y = 134 + 24;
break;
case 9:
x = 236 + 24;
y = 134 + 24;
break;
}
if(x === null && y === null){return;}
console.log(cell+"/"+ x +"/"+ y);
ctx.strokeStyle = "white";
ctx.beginPath();
ctx.arc(x,y,r, 0/180*Math.PI , 360/180*Math.PI);
ctx.stroke();
};
new LIB().construct();
})();
※今回は、index.htmlは前回と同じで変更は、ありません。
カンタン解説
イベントのセットと、○印を表示する処理を追加しました。
効率を考えない、プログラムですが、リファクタリングは、完成後に行うことにしたいと思います。
今回のポイントは、canvasタグにクリックイベントをセットして、そのクリックされたcanvas内の座標を取得するという点です。
var canvas = e.currentTarget;
// canvas内のクリックされた座標
var posX = e.clientX - canvas.getBoundingClientRect().left;
var posY = e.clientY - canvas.getBoundingClientRect().top;
この箇所ですが、マウス座標を取得した際に、canvasタグの座標を差し引いてあげなければ、マウス座標は、document.bodyの0起点からになるので、誤差が発生してしまいます。
慣れればなんてことない処理ですが、知らないと、結構パニクります。
そして、記号描画のところでは、基盤をいちどクリアしてから、再描画する手順になります。
単純に追記するような処理でもいいんですが、canvasアニメーションなどをしようとすると、この手順を標準化しておいたほうがよさそうです。
そういえばという問題点
スマホ表示がまだできていません。
viewportの設定とtouchイベントを追加しないといけないんですが、これも後回しにすることにします。
そして次回はいよいよ、コンピュータの指し手を反映する処理になります。
お楽しみに。
0 件のコメント:
コメントを投稿