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

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

ただいまの
回答率

87.59%

CreateJSで複数のシェイプを作成したときの当たり判定やドラッグ&ドロップの書き方

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,029
退会済みユーザー

退会済みユーザー

前提・実現したいこと

私は、今までHTML・CSSを中心に1年間程コーディングしてきて、javascriptはjqueryなどライブラリを使用して簡単な実装をするくらいしか、使ってこなかったのですが、最近javascriptの学習をし始めている者です。

CreateJSというライブラリを使用して、以下のようなことを実現させたいと思っているのですが、複数のシェイプを作成したときの書き方がわからず困っております。
コードは色々なサイトで説明されているのを調べたりして書きました。

ー実現させたいこと(すべてを満たしたい条件)ー
・テキストを重ねた円のシェイプを複数(2つ以上)
・それぞれドラッグ&ドロップができる
・上下左右の端に壁があるようにしたい(canvasの枠の中にシェイプの全体がいつでも収まる状態?)
・当たり判定で、シェイプ同士が衝突したときに弾き合うようにしたい
・マウス操作での勢いに基づいて速さをつけたい(現段階でのコードだと、ドラッグしたところとドロップしたところの距離に比例してドロップ時の速さが決まってしまっていると思います。)

javascriptの基本もまだまだわかっておらず、見当違いのことを書いているかもしれませんが、どなたかご教示いただけますと幸いです。

発生している問題

現段階でのコードは「該当のソースコード」の通りです。

シェイプとテキストはセットで1つのコンテナに入れています。

・ドラッグ&ドロップでそれぞれ動かせるようにしたい
のですが、コンテナ1とコンテナ2のドラッグ時(クリック時)に取得する、それぞれの座標がうまく切り分けられていない?せいか、コンテナ1とコンテナ2が一緒に移動してしまう状況です。
おそらく、stage.mouseXとstage.mouseYをコンテナ1でもコンテナ2にも
書いているからだとは思っているのですが、どのように書けばよいか分かりません。

・当たり判定で、シェイプ同士が衝突したときに弾き合うようにしたい
のですが、以下のやり方で、できると調べてでてきたのですが、あたっているときの処理はどのように書けばシェイプ同士が衝突したとき弾くようになるのかが分かりません。

// ●と△の相対座標を求める
var point = △.localToLocal(0, 0, ●);
// △と●があたっているかを調べる
var isHit = ●.hitTest(point.x, point.y);

if(isHit == true){
  // あたっている時の処理
}else{
  // 離れている時の処理
}

・マウス操作での勢いに基づいて速さをつけたい
のですが、ここはどう書けばよいのでしょうか。

vx = (stage.mouseX - old1X) / 8;
vy = (stage.mouseY - old1Y) / 8;


上記のコードは元々は、

vx = stage.mouseX - old1X;
vy = stage.mouseY - old1Y;


でしたが、速度を落としたいと思い、「/ 8」を加えました。

該当のソースコード

<!DOCTYPE html>
<html>

<head>
    <title>サンプル</title>
    <meta http-equiv="content-type" charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="sample.css" rel="stylesheet">
    <script src="https://code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
    <script src="https://code.createjs.com/1.0.0/createjs.min.js"></script>
</head>

<body>
    <canvas id="myCanvas" width="1000" height="700" style="background-color: #ffe500;"></canvas>
</body>
<script>
// 読み込みが終わってから初期化
window.addEventListener("load", init);

function init() {
    // Stageオブジェクトを作成。
    // 初期化
    var stage = new createjs.Stage("myCanvas");
    // 速度情報
    var vx = 0; // 初期の速さ
    var vy = 0;
    var flag = false;
    var old1X;
    var old1Y;
    var old2X;
    var old2Y;
    var radius = 120; // 円の半径

    // コンテナー1(グループの親)を作成
    var container1 = new createjs.Container();
    container1.x = 200;
    container1.y = 200;
    stage.addChild(container1); // 画面に追加

    // コンテナー2(グループの親)を作成
    var container2 = new createjs.Container();
    container2.x = 800;
    container2.y = 400;
    stage.addChild(container2); // 画面に追加

    // コンテナ1の円を作成します
    var circle1 = new createjs.Shape();
    circle1.graphics.beginFill("darkorange"); // オレンジで描画するように設定
    circle1.graphics.drawCircle(0, 0, radius); //半径120pxの円を描画
    container1.addChild(circle1); // 表示リストに追加

     // コンテナ2の円を作成します
    var circle2 = new createjs.Shape();
    circle2.graphics.beginFill("darkorange"); // オレンジで描画するように設定
    circle2.graphics.drawCircle(0, 0, radius); //半径120pxの円を描画
    container2.addChild(circle2); // 表示リストに追加

     // コンテナ1の円の中のテキストを作成します
    var text1 = new createjs.Text("CreateJS", "28px Gothic", "black");
    text1.textAlign = "center";
    text1.textBaseline = "middle";
    container1.addChild(text1);

     // コンテナ2の円の中のテキストを作成します
    var text2 = new createjs.Text("プログラム言語", "28px Gothic", "black");
    text2.textAlign = "center";
    text2.textBaseline = "middle";
    container2.addChild(text2);


    // コンテナ1のドラッグした場所を保存する変数
    var dragPoint1X;
    var dragPoint1Y;

    // コンテナ1のインタラクティブの設定
    container1.addEventListener("mousedown", handleDown);
    container1.addEventListener("pressmove", handleMove);
    container1.addEventListener("pressup", handleUp);

    // コンテナ1を押したときの処理です
    function handleDown(event) {
        // ドラッグを開始した座標を覚えておく
        dragPoint1X = stage.mouseX - container1.x;
        dragPoint1Y = stage.mouseY - container1.y;
        // マウスの位置を保存
        old1X = stage.mouseX;
        old1Y = stage.mouseY;
    }

    // コンテナ1を押した状態で動かしたときの処理です
    function handleMove(event) {
        container1.x = stage.mouseX - dragPoint1X;
        container1.y = stage.mouseY - dragPoint1Y;
    }
    // コンテナ1からマウスを離したときの処理です
    function handleUp(event) {
        // コンテナ1の速度を有効にする(ドラッグした距離に応じて、速度を設定)
        vx = (stage.mouseX - old1X) / 8;
        vy = (stage.mouseY - old1Y) / 8;

        // コンテナ1の速度を無効にする
        flag = false;
    }

    // コンテナ2のドラッグした場所を保存する変数
    var dragPoint2X;
    var dragPoint2Y;

    container2.addEventListener("mousedown", handleDown);
    container2.addEventListener("pressmove", handleMove);
    container2.addEventListener("pressup", handleUp);

    // コンテナ2を押したときの処理です
    function handleDown(event) {
        // ドラッグを開始した座標を覚えておく
        dragPoint2X = stage.mouseX - container2.x;
        dragPoint2Y = stage.mouseY - container2.y;
        // マウスの位置を保存
        old2X = stage.mouseX;
        old2Y = stage.mouseY;
    }

    // コンテナ2を押した状態で動かしたときの処理です
    function handleMove(event) {
        container2.x = stage.mouseX - dragPoint2X;
        container2.y = stage.mouseY - dragPoint2Y;
    }
    // コンテナ2からマウスを離したときの処理です
    function handleUp(event) {
         // コンテナ2の速度を有効にする(ドラッグした距離に応じて、速度を設定)
        vx = (stage.mouseX - old2X) / 8;
        vy = (stage.mouseY - old2Y) / 8;


        // コンテナ2の速度を無効にする
        flag = false;
    }

    createjs.Ticker.addEventListener("tick", handleTick);

    function handleTick() {
        // 重力計算
        vy += 0.01;

        // コンテナ1をドラッグ中でないときだけ物理演算
        if (flag == false) {
            //重力
            vy += 0.01;

            // 摩擦
            vx *= 0.85;
            vy *= 0.85;

            // コンテナ1に物理演算を適用
            container1.x += vx;
            container1.y += vy;

            // コンテナ1が画面の端からはみ出さないようにする処理
            if (container1.x + radius > stage.canvas.width) {
                container1.x = stage.canvas.width - radius;
                vx *= -0.8;
            } else if (container1.x - radius < 0) {
                container1.x = radius;
                vx *= -0.8;
            }
            if (container1.y + radius > stage.canvas.height) {
                container1.y = stage.canvas.height - radius;
                vy *= -0.8;
            } else if (container1.y - radius < 0) {
                container1.y = radius;
                vy *= -0.8;
            }
        } else {
            if (container1.x + radius > stage.canvas.width) {
                container1.x = stage.canvas.width - radius;
            } else if (container1.x - radius < 0) {
                container1.x = radius;
            }
            if (container1.y + radius > stage.canvas.height) {
                container1.y = stage.canvas.height - radius;
            } else if (container1.y - radius < 0) {
                container1.y = radius;
            }
        }

        // コンテナ2をドラッグ中でないときだけ物理演算
        if (flag == false) {
            //重力
            vy += 0.01;

            // 摩擦
            vx *= 0.85;
            vy *= 0.85;

             // コンテナ2に物理演算を適用
            container2.x += vx;
            container2.y += vy;

            // コンテナ2が画面の端からはみ出さないようにする処理
            if (container2.x + radius > stage.canvas.width) {
                container2.x = stage.canvas.width - radius;
                vx *= -0.8;
            } else if (container2.x - radius < 0) {
                container2.x = radius;
                vx *= -0.8;
            }
            if (container2.y + radius > stage.canvas.height) {
                container2.y = stage.canvas.height - radius;
                vy *= -0.8;
            } else if (container2.y - radius < 0) {
                container2.y = radius;
                vy *= -0.8;
            }
        } else {
            if (container2.x + radius > stage.canvas.width) {
                container2.x = stage.canvas.width - radius;
            } else if (container2.x - radius < 0) {
                container2.x = radius;
            }
            if (container2.y + radius > stage.canvas.height) {
                container2.y = stage.canvas.height - radius;
            } else if (container2.y - radius < 0) {
                container2.y = radius;
            }
        }

        // 画面更新
        stage.update();
    }
}
</script>

</html>

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

OS: windows

よろしくお願いいたします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+1

<!DOCTYPE html>
<title></title>
<meta charset="utf-8">

<style>
canvas { background: #fd0; }
</style>

<body>
    <canvas width="700" height="700"></canvas>

<script>
{
  const
    GAIN = 0.99,
    PI2 = Math.PI * 2,
    DEF_COLOR = 'rgba(255,0,0,.5)';

  //__
  class Point {
    constructor (x = 0, y = 0) {
      this.x = x; this.y = y;
    }
  }

  //__
  class Vector extends Point {
    constructor (x, y) {
      super (x, y);
    }
  }


  //__
  class Ball extends Point  {
    constructor (name, x, y, r = 100, color = DEF_COLOR, direction = new Vector) {
      super (x, y);
      this.name = name;
      this.r = r;
      this.color = color;
      this.direction = direction;
      this.touched = false;
      this.offset = null;
    }

    setTouch ({x: px, y: py}) {
      let {x, y, r} = this;
      if ((px - x)**2 + (py - y)**2 < r**2) {
        this.touched = true;
        this.offset = new Point (x - px, y - py);
        this.direction = new Vector;
      }
    }

    isCollision ({x: px, y: py, r: pr}) {
      let {x, y, r} = this;
      return (x - px)**2 + (y - py)**2 <= (r + pr)**2;
    }
  }


  //__
  class Mouse extends Point {
    constructor (target = document, ary = [ ]) {
      super (0, 0);
      this.target = target;
      this.ary = ary;
      this.mx = 0;
      this.my = 0;

      ['mousedown', 'mousemove', 'mouseup']
        .forEach (etype => target.addEventListener (etype, this, false));
    }

    handleEvent (event) {
      this[event.type](event);
    }

    mousedown (event) {
      this.ary.forEach (b => b.setTouch (this));
    }

    mousemove (event) {
      let {x, y, ary} = this;

      ary.forEach (b => {
        if (b.touched) {
          let o = b.offset;
          b.x = x + o.x;
          b.y = y + o.y;
          b.direction = new Vector;
        }
      });

      this.mx = x;
      this.my = y;
      this.x = event.clientX;
      this.y = event.clientY;
    }

    mouseup (event) {
      let
        {mx, my, ary} = this,
        v = new Vector (event.clientX - mx, event.clientY - my);

      ary.forEach (b => {
        if (b.touched) {
          b.touched = false;
          b.direction = v;
          b.offset = null;
        }
      });
    }

  }


  //__
  class Field {
    constructor (target, ary = [ ]) {
      this.target = target;
      this.ary = ary;
      this.w = target.width;
      this.h = target.height;
    }

    add (obj) {
      this.ary.push (obj);
    }

    progress () {
      let { w, h, ary } = this;

      ary.forEach (b => {
        let
          {x, y, r} = b,
          d = b.direction;

        x += d.x *= GAIN;
        y += d.y *= GAIN;

        if (x < r) {
          x = r;
          d.x *= -1;
        }
        else if (w - r < x) {
          x = w - r;
          d.x *= -1;
        }

        if (y < r) {
          y = r;
          d.y *= -1;
        }
        else if (h - r < y) {
          y = h - r;
          d.y *= -1;
        }

        //当り判定後が雑すぎる
        let tmp = new Ball ('tmp', x, y, r);
        ary.forEach (tb => {
          if (tb != b) {
            if (tmp.isCollision (tb)) {
              d.x *= -1;
              d.y *= -1;
              x = b.x;
              y = b.y;
            }
          }
        })

        b.x = x;
        b.y = y;
      });
    }

  }


  //__
  class Canvas {
    constructor (canvas) {
      this.canvas = canvas;
      this.ctx = canvas.getContext ('2d');
      this.w = canvas.width;
      this.h = canvas.height;
    } 

    cls () {
      this.ctx.clearRect (0, 0, this.w, this.h);
    }

    draw (balls) {
      let ctx = this.ctx;

      balls.forEach (b => {
        let {name, x, y, r, color} = b;

        ctx.fillStyle = color;
        ctx.beginPath ();
        ctx.arc (x, y, r, 0, PI2, false);
        ctx.fill ();

        ctx.font = "18px 'MS Pゴシック'";
        ctx.fillStyle = 'rgba(0,0,0,.5)';
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText (name, x, y);
      });
    }
  }


  //__
  this.Point = Point;
  this.Vector = Vector;
  this.Ball = Ball;
  this.Field = Field;
  this.Mouse = Mouse;
  this.Canvas = Canvas;
}



const
  target = document.querySelector ('canvas'),
  canvas = new Canvas (target),
  balls = [
    new Ball ('Hello', 200, 300),
    new Ball ('こんにちは', 500, 300, 80),
    new Ball ('当り判定が雑', 700, 500, 70, 'rgba(0,255,255,.5)')
  ],
  field = new Field (target, balls),
  ctrl = new Mouse (target, balls);

function demo () {
  field.progress ();
  canvas.cls ();
  canvas.draw (balls);
  window.requestAnimationFrame (demo);
}

demo ();

</script>

</html>

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/01/23 13:21

    ご回答ありがとうございます!!
    コードを理解するところから頑張ります。。。

    キャンセル

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

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

関連した質問

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