拍球
点击小球使之不落地
源代码
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 阅读次数:5377
