[お題にTRY] Canvas三目並べ #2 : イベントセット

2020/06/24

Javascript テクノロジー プログラミング 特集

t f B! P L
eyecatch プログラミングを勉強している人が、最も面白みを感じるのは、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イベントを追加しないといけないんですが、これも後回しにすることにします。 そして次回はいよいよ、コンピュータの指し手を反映する処理になります。 お楽しみに。

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ