/*---------------------------------------- * moveball.js * Copyright (C) 2020 StraightApps.com, All Rights Reserved * September 16, 2020 初期版 * September 18, 2020 実用版 *-----------------------------------------*/ //---------------------------------------- // canvas の設定 // この部分は、id "canvas-main" の定義後でないと、失敗します。 var canvas = document.getElementById('canvas-main'); var w = scr.innerWidth; // クライアント領域の幅 var h = scr.innerHeight; // クライアント領域の高さ var rect = canvas.getBoundingClientRect(); // Canvas の絶対座標位置を取得 w -= Math.floor( rect.left ); // rectが小数で返されている w = Math.min( 480, w ); // 十分広い場合は 480 ピクセルまでとします var blockSize = Math.floor( w / 24 ); // 24コマ表示可能とします w = blockSize * 24; if (h > blockSize * 24){ // 高さ方向が十分なサイズなら h = blockSize * 24; // 24コマ表示できればOK } else if (h < blockSize * 24){ // 高さが不足の場合 blockSize = Math.floor( h / 24 ); // 縦に24コマ表示可能とします h = blockSize * 24; w = blockSize * 24; // 横も24コマサイズに合わせます } canvas.width = w; // canvas の幅設定 canvas.height = h; // canvas の高さ設定 //---------------------------------------- // コンテキストを取得 var ctx = canvas.getContext('2d'); //---------------------------------------- // ボールイメージの読み込み var ball = new Object(); ball.img = new Image(); ball.img.src = 'jsimg/pin.png'; ball.cx = 0; // キャラクタベースの座標 ball.cy = 0; ball.x = 0; // ピクセルベースの座標 ball.y = 0; ball.width = 100; // pin.png のサイズ ball.height = 100; ball.move = 0; var goal = new Object(); goal.cx = 0; goal.cy = 0; goal.point = 0; // ゴールしたかどうかのフラグ //---------------------------------------- // キー入力用オブジェクトを作成 var key = new Object(); key.up = false; key.down = false; key.right = false; key.left = false; key.push = ''; //---------------------------------------- // マップ用配列を初期化します。 var map = [ "000000000000000000000000", // 1 "000000000000000000000000", "000000000000000000000000", "000000000000000000000000", "000000000000000000000000", // 5 "000000000000000000000000", "000000000000000000000000", "000000000000000000000000", "000000000000000000000000", "000000000000000000000000", // 10 "000000000000000000000000", "000000000000000000000000", "000000000000000000000000", "000000000000000000000000", "000000000000000000000000", // 15 "000000000000000000000000", "000000000000000000000000", "000000000000000000000000", "000000000000000000000000", "000000000000000000000000", // 20 "000000000000000000000000", "000000000000000000000000", "000000000000000000000000", "000000000000000000000000", // 24 ]; //---------------------------------------- // 開発者モードの設定 var developerMode = false; //---------------------------------------- // 新しい問題を作成します。 makeMap(); /*---------------------------------------- * メインループ *-----------------------------------------*/ function main() { //---------------------------------------- // canvas を指定の色で塗りつぶします。 ctx.fillStyle = "rgb( 230, 255, 230 )"; ctx.fillRect(0, 0, canvas.width, canvas.height); for (var y = 0; y < 24; y ++){ for (var x = 0; x < 24; x ++){ if (map[y][x] == 1){ ctx.fillStyle = '#fee'; ctx.fillRect(x * blockSize, y * blockSize, blockSize, blockSize); } if (map[y][x] == 2){ // とりあえずゴールとしている ctx.fillStyle = '#f00'; ctx.fillRect(x * blockSize, y * blockSize, blockSize, blockSize); } } } //---------------------------------------- addEventListener("keydown", keydownfunc, false); addEventListener("keyup", keyupfunc, false); // 方向キーが押されている場合は、ボールを移動します。 // ※ ボールが動いていない時のみ判定します。 if ( ball.move === 0 ) { var dx = 0, dy = 0; if ( key.left === true ) { dx = -1; key.push = 'left'; key.left = false; } else if ( key.up === true ) { dy = -1; key.push = 'up'; key.up = false; } else if ( key.right === true ) { dx = 1; key.push = 'right'; key.right = false; } else if ( key.down === true ) { dy = 1; key.push = 'down'; key.down = false; } if (dx != 0 || dy != 0){ ball.cx += dx; ball.cy += dy; ball.move = blockSize; var n = map[ball.cy][ball.cx]; // 進む先の情報 if (n == 2){ // ゴール位置 var elem = document.getElementById('message'); elem.innerHTML = 'おめでとう!'; goal.point = 1; } else if (n != 0){ // 空欄以外 ball.cx -= dx; ball.cy -= dy; ball.move = 0; key.push = ""; } } } // ball.move が 0 より大きい場合は、4px ずつ移動を続ける if (ball.move > 0) { // 最大4ピクセル移動します。 var nowMove = 4; if (ball.move < 4){ nowMove = ball.move; } ball.move -= nowMove; if ( key.push === 'left' ) ball.x -= nowMove; if ( key.push === 'up' ) ball.y -= nowMove; if ( key.push === 'right' ) ball.x += nowMove; if ( key.push === 'down' ) ball.y += nowMove; // ぶつかるまで継続します。 if (ball.move == 0 && goal.point == 0){ var px = ball.cx; var py = ball.cy; if ( key.push === 'left' ){ px --; } else if ( key.push === 'up' ){ py --; } else if ( key.push === 'right' ){ px ++; } else if ( key.push === 'down' ){ py ++; } if (px != ball.cx || py != ball.cy){ if (map[py][px] == 0){ // 進む先が 0(空欄) ball.cx = px; ball.cy = py; ball.move = blockSize; } else if (map[py][px] == 2){ // 進む先がゴール ball.cx = px; ball.cy = py; ball.move = blockSize; var elem = document.getElementById('message'); elem.innerHTML = 'おめでとう!'; goal.point = 1; } } } } //---------------------------------------- // マウスクリック/タッチイベントを処理 canvas.addEventListener('click', onClick, false); //---------------------------------------- // ボールを表示 ctx.drawImage( ball.img, ball.x, ball.y, blockSize, blockSize ); //ctx.drawImage( ball.img, ball.x, ball.y ); requestAnimationFrame( main ); } /*---------------------------------------- * ページと、依存している全てのデータが読み込まれたら、 * メインループを開始します。 *-----------------------------------------*/ addEventListener('load', main(), false); /*---------------------------------------- * キーが押されたときに呼び出される関数 *-----------------------------------------*/ function keydownfunc( event ) { var key_code = event.keyCode; if( key_code === 37 ) key.left = true; if( key_code === 38 ) key.up = true; if( key_code === 39 ) key.right = true; if( key_code === 40 ) key.down = true; goal.point = 0; // ゴールしたあとの再ムーブ可能 event.preventDefault(); } /*---------------------------------------- * キーが放されたときに呼び出される関数 *-----------------------------------------*/ function keyupfunc( event ) { var key_code = event.keyCode; if( key_code === 37 ) key.left = false; if( key_code === 38 ) key.up = false; if( key_code === 39 ) key.right = false; if( key_code === 40 ) key.down = false; } /*---------------------------------------- * クリックされたときに呼び出される関数 *-----------------------------------------*/ var mx = 0; var my = 0; function onClick( e ) { var rect = e.target.getBoundingClientRect(); // Canvas の絶対座標位置を取得 mx = Math.floor( e.clientX - rect.left ); // rectが小数で返されている my = Math.floor( e.clientY - rect.top ); var cx = Math.floor(mx / blockSize); var cy = Math.floor(my / blockSize); // 開発者モードの場合は、クリック(タップ)で壁をトグルさせます。 if (developerMode == true){ var toggle = true; if (cx == 1 && cy == 1){ // ボールの初期位置は除外 toggle = false; } if (cx == 0 || cy == 0 || cx == 23 || cy == 23){ // ふちは除外 toggle = false; } if (toggle == true){ if (map[cy][cx] == 0){ map[cy] = replaceValue( map[cy], cx, '1'); } else if (map[cy][cx] == 1){ map[cy] = replaceValue( map[cy], cx, '0'); } } // クリック(タップ)による移動は行いません。 return; } // スマホやタブレットだと矢印キーがないのでタップで操作します。 var xdiff = cx - ball.cx; var ydiff = cy - ball.cy; key.left = false; key.right = false; key.up = false; key.down = false; if (Math.abs(xdiff) > Math.abs(ydiff)){ // 横に移動 if (xdiff < 0) key.left = true; else if( xdiff > 0 ) key.right = true; goal.point = 0; // ゴールしたあとの再ムーブ可能 } else if (Math.abs(xdiff) < Math.abs(ydiff)){ // 縦に移動 if (ydiff < 0) key.up = true; else if (ydiff > 0) key.down = true; goal.point = 0; // ゴールしたあとの再ムーブ可能 } } /*---------------------------------------- * 新しいマップの作成 *-----------------------------------------*/ function makeMap() { for (var y = 0; y < 24; y ++){ if (y == 0 || y == 23){ map[y] = "111111111111111111111111"; // すべて壁 } else{ map[y] = "100000000000000000000001"; // クリア } } // 引数で「面」を受け、マップを初期化します。 ball.cx = 1; // キャラクタベース座標 ball.x = ball.cx * blockSize; // ピクセルベース座標 ball.cy = 1; ball.y = ball.cy * blockSize; // 1次元の場合、map.length で要素数を取れる。 // 2次元配列の場合、map.length で行数を取れる map[1] = replaceValue( map[1], 12, 1); map[1] = replaceValue( map[1], 17, 1); map[2] = replaceValue( map[2], 2, 1); map[2] = replaceValue( map[2], 3, 1); map[3] = replaceValue( map[3], 22, 1); map[8] = replaceValue( map[8], 6, 1); map[8] = replaceValue( map[8], 18, 1); map[9] = replaceValue( map[9], 1, 1); map[9] = replaceValue( map[9], 11, 1); map[10] = replaceValue( map[10], 2, 1); map[10] = replaceValue( map[10], 4, 1); map[11] = replaceValue( map[11], 11, 1); map[12] = replaceValue( map[12], 21, 1); map[17] = replaceValue( map[17], 5, 1); map[18] = replaceValue( map[18], 12, 1); map[19] = replaceValue( map[19], 2, 1); map[20] = replaceValue( map[20], 17, 1); //map[12][2] = '1'; // 参照はできるが設定はできない。 // ゴールの座標 goal.cx = 10; goal.cy = 10; map[goal.cy] = replaceValue( map[goal.cy], goal.cx, 2); } /*---------------------------------------- * 文字列のうち1文字を置き換える関数 * mapstr 文字列の pos 位置の文字を value に置き換えます。 *-----------------------------------------*/ function replaceValue( mapstr, pos, value ) { var m = mapstr.substr(0,pos) + value + mapstr.substr(pos + 1); return m; } /*---------------------------------------- * リセットボタンが押されたときの処理 *-----------------------------------------*/ function reset(flag) { // 動いていない時のみ有効とします if ( ball.move === 0 ) { ball.cx = 1; // キャラクタベースの座標 ball.cy = 1; ball.x = blockSize; // ピクセルベースの座標 ball.y = blockSize; var elem = document.getElementById('message'); elem.innerHTML = ""; } // flag がセットされているときは、マップも初期化します。 if (flag == 1){ makeMap(); // マップのリセット } } /*---------------------------------------- * 開発者モードボタンが押されたときの処理 *-----------------------------------------*/ function devMode() { var elem = document.getElementById('dev-button'); if (developerMode == true){ developerMode = false; elem.innerText = ' 開発者モードにする '; } else{ developerMode = true; elem.innerText = ' 開発者モードを終わる '; } }