/*----------------------------------------
* 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 = ' 開発者モードを終わる ';
}
}