ボール同士の衝突と跳ね返り|HTML5

ボール同士の衝突と跳ね返り|HTML5

ボール同士の衝突と跳ね返り|HTML5(HTML5, JavaScript, CSS3) : デモ

ボール同士の衝突と跳ね返り|HTML5(HTML5, JavaScript, CSS3) : ZIPファイル(3kb)

1.HTML

<!DOCTYPE html>
<html lang="jp">
    <head>
        <meta charset="UTF-8" />
        <title>ボール同士の衝突</title>
        <link href="css/base.css" rel="stylesheet" type="text/css">
        <script src="js/base.js"></script>
    </head>
    <body>
        <div id="contents">
            <canvas id="canvas" width="275" height="275"></canvas>
        </div>
    </body>
</html>

2.CSS

@charset "utf-8";
  
body
{
    margin: 0;
    padding: 0;
    background-color: #fff;
}
  
#contents
{
    position: absolute;
    top: 0;
    left: 0;
    width: 275px;
    height: 275px;
    border: 1px solid #000;
    overflow:hidden;
}
  
#canvas
{
    position: absolute;
    top: 0;
    left: 0;
    width: 275px;
    height: 275px;
}

3.JavaScript

base.js

window.addEventListener("load", init, false);
  
function init()
{
    var offsetX = 0,
    offsetY = 0,
    ctx,
    bounce = -1,
    theCanvas,
    theContents;
     
    theContents = document.getElementById("contents");
    offsetX = (theContents.currentStyle || document.defaultView.getComputedStyle(theContents,'')).width;
    offsetX = Number(offsetX.replace('px', ''));
  
    offsetY = (theContents.currentStyle || document.defaultView.getComputedStyle(theContents,'')).height;
    offsetY = Number(offsetY.replace('px', ''));
     
    ctx = document.getElementById('canvas').getContext("2d");
     
    var ball0 = new Object();
    ball0.red = Math.floor(Math.random() * 256);
    ball0.green = Math.floor(Math.random() * 256);
    ball0.blue = Math.floor(Math.random() * 256);
    ball0.x = 200;
    ball0.y = 200;
    ball0.vx = Math.random() * 10 - 5;
    ball0.vy = Math.random() * 10 - 5;
    ball0.radius = 50;
    ball0.mass = 2;
     
    var ball1 = new Object();
    ball1.red = Math.floor(Math.random() * 256);
    ball1.green = Math.floor(Math.random() * 256);
    ball1.blue = Math.floor(Math.random() * 256);
    ball1.x = 0;
    ball1.y = 0;
    ball1.vx = Math.random() * 10 - 5;
    ball1.vy = Math.random() * 10 - 5;
    ball1.radius = 25;
    ball1.mass = 1;
     
    setBallAnimation();
     
    function setBallAnimation()
    {
        animation();
        requestAnimationFrame(setBallAnimation);
    }
     
    function animation()
    {
        ctx.clearRect(0, 0, 300, 300);
        ctx.strokeStyle ="rgb(" + ball0.red + "," + ball0.green + "," + ball0.blue + ")";
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.arc(ball0.x, ball0.y, ball0.radius, 0, Math.PI * 2, true);
        ctx.fillStyle = "rgb(" + ball0.red + "," + ball0.green + "," + ball0.blue + ")";
        ctx.fill();
        ctx.stroke();
         
        ctx.strokeStyle ="rgb(" + ball1.red + "," + ball1.green + "," + ball1.blue + ")";
        ctx.lineWidth = 1;
        ctx.beginPath();
        ctx.arc(ball1.x, ball1.y, ball1.radius, 0, Math.PI * 2, true);
        ctx.fillStyle = "rgb(" + ball1.red + "," + ball1.green + "," + ball1.blue + ")";
        ctx.fill();
        ctx.stroke();
         
        move();
    }
     
    /*
     * ボールの運動
     */
    function move()
    {
        ball0.x += ball0.vx;
        ball0.y += ball0.vy;
        ball1.x += ball1.vx;
        ball1.y += ball1.vy;
        checkCollision(ball0, ball1);
        checkWall(ball0);
        checkWall(ball1);
    }
     
    /*
     * 壁の衝突、跳ね返り
     */
    function checkWall(ball)
    {
        if(ball.x + ball.radius > offsetX)
        {
            ball.x = offsetX - ball.radius;
            ball.vx *= bounce;
        }
        else
        if(ball.x - ball.radius < 0)
        {
            ball.x = ball.radius;
            ball.vx *= bounce;
        }
         
        if(ball.y + ball.radius > offsetY)
        {
            ball.y = offsetY- ball.radius;
            ball.vy *= bounce;
        }
        else
        if(ball.y - ball.radius < 0)
        {
            ball.y = ball.radius;
            ball.vy *= bounce;
        }
    }
     
    /*
     * ボール同士の衝突
     */
    function checkCollision(ball0, ball1)
    {
        /*
         * 衝突判定予測
         */
        var dx = ball1.x - ball0.x;
        var dy = ball1.y - ball0.y;
        var dist = Math.sqrt(dx * dx + dy * dy);
        if(dist < ball0.radius + ball1.radius)
        {
            var radian = Math.atan2(dy, dx);
            var sin = Math.sin(radian);
            var cos = Math.cos(radian);
            //位置の回転
            var point0 = {x:0, y:0};
            //位置の回転
            var point1 = rotate(dx, dy, sin, cos, true);
            //速度の回転
            var velocity0 = rotate(ball0.vx, ball0.vy, sin, cos, true);
            //速度の回転
            var velocity1 = rotate(ball1.vx, ball1.vy, sin, cos, true);
            //衝突反応
            var vxTotal = velocity0.x - velocity1.x;
            //運動量の保存
            velocity0.x = ((ball0.mass - ball1.mass) * velocity0.x + 2 * ball1.mass * velocity1.x) / (ball0.mass + ball1.mass);
            velocity1.x = vxTotal + velocity0.x;
             
            //位置の更新
            var absV = Math.abs(velocity0.x) + Math.abs(velocity1.x);
            var overlap = (ball0.radius + ball1.radius) - Math.abs(point0.x - point1.x);
            point0.x += (velocity0.x / absV) * overlap;
            point1.x += (velocity1.x / absV) * overlap;
             
            //位置の回転、元へ戻す
            var point0Final = rotate(point0.x, point0.y, sin, cos, false);
            var point1Final = rotate(point1.x, point1.y, sin, cos, false);
             
            //速度の回転、元の位置へ戻す
            var velocity0Final = rotate(velocity0.x, velocity0.y, sin, cos, false);
            var velocity1Final = rotate(velocity1.x, velocity1.y, sin, cos, false);
             
            /*
             * 衝突判定の予測分だけ速度を加算する
             */
            ball1.x = ball0.x + point1Final.x;
            ball1.y = ball0.y + point1Final.y;
            ball0.x = ball0.x + point0Final.x;
            ball0.y = ball0.y + point0Final.y;
             
            ball0.vx = velocity0Final.x;
            ball0.vy = velocity0Final.y;
            ball1.vx = velocity1Final.x;
            ball1.vy = velocity1Final.y;
        }
    }
     
    /*
     * 回転行列
     */
    function rotate(x, y, sin, cos, reverse)
    {
        var result = {x:0, y:0}
        if(reverse)
        {
            result.x = x * cos + y * sin;
            result.y = y * cos - x * sin;
        }
        else
        {
            result.x = x * cos - y * sin;
            result.y = y * cos + x * sin;
        }
        return result;
    }
}
 
// 各ブラウザ対応
window.requestAnimationFrame = (function()
{
    return window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame ||
    window.oRequestAnimationFrame ||
    window.msRequestAnimationFrame ||
    function(callback)
    {
        window.setTimeout(callback, 1000/60);
    };
}());

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です