質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.77%

(JavaScriptゲーム)虫をクリックしたらTrueを返したい

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 763

mercredi

score 26

JavaScriptで虫をクリックすると加点するゲームを作っております。画像を挿入する前にcontext.arcでサークルを描いてクリックした時はTrueが帰ってきておりましたが、サークルをImageに変えると全てがFalseで返ってきてきてしまいます。

*変えた部分は、虫の画像とBackgroud画像を挿入しました。
Trueを返したいBoolean ValueはisOnCircleです。

常にFalseが返ってきてしまう(虫の画像を挿入した場合)

var Bug = (function () {
    function Bug() {
    }
    return Bug;
}());
var jumpInterval;
var interval = 4000;
var bug = new Bug();
var canvas = document.getElementById('canvas');
canvas.width =727
    canvas.height = 483;
var context = canvas.getContext('2d');

var background = new Image();
background.src = "images/lawn.jpg";

background.onload = function () {
    context.drawImage(background, 0, 0);
}

function handleClick(evt) {
    console.log(evt.x + ',' + evt.y);
    var x = evt.x - 100;
    var y = evt.y - 100;
    // (x - center_x)^2 + (y - center_y)^2 < radius^2
    var isOnCircle = Math.pow(x - bug.x, 2) + Math.pow(y - bug.y, 2) < Math.pow(bug.r, 2);

    console.log(isOnCircle);

    if (interval > 500) {
        interval -= 300;
    } else {
        interval = 500;
    }
    console.log(interval);
}
function jump() {
    bug.x = ((Math.random() * 10324897) % 500) + 1;
    bug.y = ((Math.random() * 10324897) % 500) + 1;
    bug.r = 32;
    context.clearRect(0, 0, canvas.width, canvas.height);

    var background = new Image();
    background.src = "images/lawn.jpg";

    background.onload = function () {
        context.drawImage(background, 0, 0);
    }


    console.log(bug.x);
    console.log(bug.y);



    context.beginPath();
    var bugImage = new Image();
    bugImage.src = "images/bug.PNG";

    bugImage.onload = function () {
        context.drawImage(bugImage, bug.x, bug.y);
    }
    context.stroke();

    clearInterval(jumpInterval);
    jumpInterval = setInterval(jump, interval);

}
jumpInterval = setInterval(jump, interval);
canvas.addEventListener("click", handleClick);

クリックするとTrueがきちんと返ってくる(サークルで試した場合)

var Bug = (function () {
    function Bug() {
    }
    return Bug;
}());
var jumpInterval;
var interval = 4000;
var bug = new Bug();
var canvas = document.getElementById('canvas');
canvas.width =727
    canvas.height = 483;
var context = canvas.getContext('2d');

function handleClick(evt) {
    console.log(evt.x + ',' + evt.y);
    var x = evt.x - 100;
    var y = evt.y - 100;
    //(x - center_x)^2 + (y - center_y)^2 < radius^2
    var isOnCircle = Math.pow(x - bug.x, 2) + Math.pow(y - bug.y, 2) < Math.pow(bug.r, 2);

    console.log(isOnCircle);

    if (interval > 500) {
        interval -= 300;
    } else {
        interval = 500;
    }
    console.log(interval);
}
function jump() {
    bug.x = ((Math.random() * 10324897) % 500) + 1;
    bug.y = ((Math.random() * 10324897) % 500) + 1;
    bug.r = 32;
    context.clearRect(0, 0, canvas.width, canvas.height);

    console.log(bug.x);
    console.log(bug.y);



    context.beginPath();
     context.arc(bug.x, bug.y, bug.r, 0, 2 * Math.PI);
    context.stroke();

    clearInterval(jumpInterval);
    jumpInterval = setInterval(jump, interval);

}
jumpInterval = setInterval(jump, interval);
canvas.addEventListener("click", handleClick);

補足情報(言語/FW/ツール等のバージョンなど)

ちなみにr(半径)の32はちゃんと虫の画像を計って得た値です。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

バグがあるようですね…虫だけにな!(ドヤ顔)

さて、真面目に回答します。
見た所、問題の本質はここにあるように感じました。

var bugImage = new Image();
bugImage.src = "images/bug.PNG";

bugImage.onload = function () {
    context.drawImage(bugImage, bug.x, bug.y);
}


setIntervalでジャンプ処理するたびにここを通っていますが、onloadメソッドはイメージソースの読み込み完了時に非同期で呼び出されるのです。
うまく成功したとしても、非同期描画になり、描画された頃には虫は次の場所へ移動している可能性があります(「残像だ」)。
また、この記述方式だと、おそらく2回目以降はbug.png読み込みがキャッシュ経由して一瞬で終わり、onload = funct...と悠長に登録したものが、呼び出されもしないということも多々発生しているはずです。

var bugImage = new Image();
bugImage.onload = function () {
    context.drawImage(bugImage, bug.x, bug.y);
}
bugImage.src = "images/bug.PNG";


キャッシュで呼び出しミスが発生しないためには、先にonload登録をして、その後読み込むように書きます。
これは単純にImageの読み込みの定跡なので、まる覚えして、今後気をつけて下さい。

で、ゲームが成立するためには、頻繁に呼び出すsetInterval中にこの非同期呼び出しをしないような工夫が必要です。
backgroundbugImageは最初に読み込んで、後は使いまわしましょう。大体の流れはこんな感じです。

var image_count = 2;// 2個読み込むよ
var load_image = function(){
    image_count--;
    if (image_count == 0){
        game_start();// 画像2ファイルの読み込みが完了したらゲームスタートです。
    }
};
var bugImage = new Image();
bugImage.onload = load_image;
bugImage.src = "images/bug.PNG";

var background = new Image();
background.onload = load_image;
background.src = "images/lawn.jpg";

var game_start = function(){

    // この中にいままで書いてた本体部分を記述してください。
}

こうすると、画像読み込み完了ごとにload_imageが呼び出されることで、image_countが減っていき、0になった時点(=すべての画像読み込みが完了した時点)でゲームスタートです。
ゲームスタート時点で画像の読み込みは事前に完了していることが保証されるので、jumpの中身は

    context.clearRect(0, 0, canvas.width, canvas.height);
    context.drawImage(background, 0, 0);
    context.drawImage(bugImage, bug.x, bug.y)

こういう風に、寄り道なしでまっすぐに記述できます。
これでバグは消え(むしろ現れ?)、判定もうまくいくようになるはずです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/08/01 11:12

    なるほど!素晴らしいです。とてもわかりやすく何度も読み返しました、ありがとうございました。

    Jumpの中身は
    --------------
    function jump() {
    bug.x = (Math.random() * 500 + 1) + 1; //ここの計算式はモジュールを使う必要ありませんでした。
    bug.y = (Math.random() * 500 + 1) + 1;
    bug.r = 32;

    context.clearRect(0, 0, canvas.width, canvas.height);
    context.drawImage(background, 0, 0);
    context.drawImage(bugImage, bug.x-32, bug.y-32)//Imageだとサークルと違って座標が左上にくるらしいので、半径分(32)マイナスしました。

    clearInterval(jumpInterval);
    jumpInterval = setInterval(jump, interval);
    }
    -----------------------------
    (すみません、コメント欄だとコードも文章も一緒になっちゃうみたいで。。)

    このように書き、言われた通りにすべて直したら成功しました。ありがとうございました!

    キャンセル

0

bug.x = (Math.random() * 500 + 1) + 1;


これって、カッコを外して

bug.x = Math.random() * 500 + 2;


ということになります。虫の座標範囲だから多少間違ってても問題ありませんが、今後ゲームを作る上でランダムを使いこなせないと致命的です。
例えば、サイコロの1~6を出す場合は、こう記述します。

var n = Math.floor(Math.random() * 6) + 1;


Math.floor(Math.random() * 6) で0~5の6つの数字が出ます。それに1を足して、1~6になります。
一定範囲の整数値であれば、数字が違うだけで、このパターンになりますので、覚えておきましょう。
考え方としては、出したい数字の範囲がいくつあるのか数えて、その数字が「かけるいくつ」の部分になります。
そして、出したい数字の最小値が、「たすいくつ」の部分になります。
範囲の数字を考えるときは、最小値・最大値を「含むのか含まないのか」に注意しましょう。

もうひとつ、ランダムな成功判定のやり方です。
命中率のパラメータがパーセント単位でhitに入ってるとして、このようになります。

if (Math.floor(Math.random()*100)) < hit){
    // 判定成功
} else {
    // 判定失敗
}


ランダム値は0~99の100パターン出現します。
hitに20が入っていたとしたら、0~19の20パターンでは判定成功します。20~99の80パターンでは失敗します。
つまり、成功率20%で、成功判定側の処理に行きます。

このふたつを覚えておけば、あとはなんとでも応用できるかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/08/13 01:58

    お礼が遅くなってしまってすみません。丁寧な解説本当にありがとうございます。
    ランダムの使い方勉強になりました。ランダムの計算式を作るときに理屈で覚えておけばわかりやすいですね。数学の先生みたいです(笑)ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.77%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る