拍球

点击小球使之不落地

源代码

window.onload = function () {
    // 全局变量
    var canvas, ctx, w, h, zoom, world, ballBody, bottomPlaneBody;
    var ballRadius = 1, gameWidth = 6, gameHeight = 8;

    // 初始化并开始渲染
    init();
    resetGame();                        
    requestAnimationFrame(animate);
                
    // 初始化画布、物理引擎和鼠标事件
    function init() {
        // 初始化画布
        canvas = document.getElementById("renderCanvas");
        w = canvas.width;
        h = canvas.height;
        ctx = canvas.getContext("2d");
        ctx.lineWidth = 0.05;
        ctx.fillStyle = "white";
       
        // 初始化物理引擎
        world = new p2.World();

        // 使物理世界的摩擦为零、恢复系数为0.5(表现为反弹时的弹性大小)
        world.defaultContactMaterial.friction = 0;
        world.defaultContactMaterial.restitution = 0.5;

        // 创建一个body对象,用于要控制的球
        ballBody = new p2.Body({
            mass: 1,
            position: [0, -2]
        });
        ballBody.addShape(new p2.Circle({ 
            radius: ballRadius
        }));
        world.addBody(ballBody);

        // 在四周添加墙
        bottomPlaneBody = createPlane([0, -gameHeight / 2], 0); // 底部
        createPlane([0, gameHeight / 2 + ballRadius * 2], Math.PI); // 顶部
        createPlane([-gameWidth / 2, 0], -Math.PI / 2); // 左边
        createPlane([gameWidth / 2, 0], Math.PI / 2); // 右边

        // 如果球碰到底部地板,则游戏失败
        world.on('beginContact', function (evt) {
            if ((evt.bodyA === ballBody && evt.bodyB === bottomPlaneBody) || 
                    evt.bodyA === bottomPlaneBody && evt.bodyB === ballBody) {
                resetGame();
            }
        });

        // 对画布进行变换操作
        // 因为画布的像素坐标系统的y轴向下,但物理系统的y轴向上,所以需要翻转画布的y方向
        ctx.save();
        ctx.translate(w / 2, h / 2);  // 将画布原点移动到画布中央(默认在左上角)
        zoom = w < h ? w / gameWidth : h / gameHeight;
        ctx.scale(zoom, -zoom); // 对画布进行缩放并反转y轴

        // 监听鼠标事件
        canvas.addEventListener('mousedown', onKeyDown);
        canvas.addEventListener('touchstart', onKeyDown);
    }

    var inputType;
    function onKeyDown(event) {
        if (inputType && event.type !== inputType) {
            return;
        }
        inputType = event.type;

        // 将画布坐标转换为物理系统坐标
        var position = getPhysicsCoord(event);

        // 检测鼠标是否点击到小球
        var didHitBall = world.hitTest(position, [ballBody]).length !== 0;
        if (didHitBall) {
            var count = world.gravity[1] === 0 ? 0 : parseInt(counter.innerHTML) + 1;
            counter.innerHTML = count;
            // 如果点击到小球,则对小球施加一个冲量
            var applyPoint = [0, 0];
            var dx = ballBody.position[0] - position[0];
            var dy = 2;
            var len = Math.sqrt(dx * dx + dy * dy);
            var impulseSize = 15 + count / 3;
            var impulse = [
              dx / len * impulseSize,
              dy / len * impulseSize
            ];
            ballBody.applyImpulse(impulse, applyPoint);
            // 增加重力加速度,从而可以增加难度
            world.gravity[1] = -10 - count;
        }
    }

    // 重置游戏:将重力加速度设置为零,并重新将小球放置到屏幕中央
    function resetGame() {
        world.gravity[0] = world.gravity[1] = 0;
        ballBody.position[0] = 0;
        ballBody.position[1] = -2;
        ballBody.velocity[0] = ballBody.velocity[1] = 0;
    }

    // 将画布坐标转换为物理系统坐标
    function getPhysicsCoord(mouseEvent) {
        var rect = canvas.getBoundingClientRect();
        var clientX = mouseEvent.touches ? mouseEvent.touches[0].clientX : mouseEvent.clientX;
        var clientY = mouseEvent.touches ? mouseEvent.touches[0].clientY : mouseEvent.clientY;
        var x = (clientX - rect.left) * window.devicePixelRatio;
        var y = (clientY - rect.top) * window.devicePixelRatio;
        x = (x - w / 2) / zoom;
        y = -(y - h / 2) / zoom;
        return [x, y];
    }

    // 在指定位置创建墙壁
    function createPlane(position, angle) {
        var planeBody = new p2.Body({
            position: position,
            angle: angle
        });
        planeBody.addShape(new p2.Plane());
        world.addBody(planeBody);
        return planeBody;
    }

    // 动画循环
    var lastTime;
    var maxSubSteps = 5; 
    var fixedDeltaTime = 1 / 30;
    function animate(time) {
        requestAnimationFrame(animate);

        // 获取一帧所需的时间
        var deltaTime = lastTime ? (time - lastTime) / 1000 : 0;
        // 确保一帧的时间不致太大(如果用户切换浏览器标签便会发生这种情况)
        deltaTime = Math.min(1 / 10, deltaTime);

        // 进行物理模拟
        world.step(fixedDeltaTime, deltaTime, maxSubSteps);
        lastTime = time;
        // 绘制场景
        render();
    }

    function render() {

        // 清除画布
        ctx.fillRect(
          -gameWidth, -gameHeight,
          gameWidth * 2, gameHeight * 2
        );

        // 绘制小球
        ctx.beginPath();
        ctx.arc(
          ballBody.interpolatedPosition[0],
          ballBody.interpolatedPosition[1],
          ballRadius,
          0,
          2 * Math.PI
        );
        ctx.fill();
        ctx.stroke();

        // 绘制周围墙壁
        ctx.beginPath();
        ctx.moveTo(-gameWidth / 2, -gameHeight / 2);
        ctx.lineTo(gameWidth / 2, -gameHeight / 2);
        ctx.lineTo(gameWidth / 2, gameHeight / 2);
        ctx.lineTo(-gameWidth / 2, gameHeight / 2);
        ctx.lineTo(-gameWidth / 2, -gameHeight / 2);
        ctx.stroke();
    }
}

发布时间:2017/1/1 下午9:56:59  阅读次数:4646

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号