实验10 疯狂的大炮
简介
水平抛出的物体的运动是平抛运动,与水平有一定仰角抛出的物体的运动是斜抛运动。这两种运动都是曲线运动,统称抛物运动。
平抛运动
物体以一定的初速度沿水平方向抛出,如果物体仅受重力作用,这样的运动叫做平抛运动。平抛运动可视为水平方向的匀速直线运动以及竖直方向的自由落体运动的合运动。平抛运动的物体,由于所受的合外力为恒力,所以平抛运动是匀变速曲线运动,平抛物体的运动轨迹为一抛物线。平抛运动是曲线运动,平抛运动的时间仅与抛出点的竖直高度有关;物体落地的水平位移与时间(竖直高度)及水平初速度有关。平抛运动的实验及频闪照片如图2-5所示。
图2-5 平抛运动
如图2-5(a)所示,小球A的运动是平抛运动,小球B的运动是自由落体运动,图2-5(b)为两个小球的运动轨迹图。根据运动的独立性,小球A和B应当是同时落地。现在开始在Canvas中模拟这个过程。
第一步,首先需要两个小球,一个小球的速度为(x∶0,y∶0),另一个小球的速度为(x∶400,y∶0):
var balls=[]; balls.length=0; var ball1={ x: 70, y: 50, r: 10, vx: 400, vy: 0 }; var ball2={ x: 50, y: 50, r: 10, vx: 0, vy: 0 }; balls.push(ball1); balls.push(ball2);
第二步,利用Jscex循环,让小球运动起来。
var canvas=document.getElementById("myCanvas");
var cxt=canvas.getContext("2d");
var cyc=110;
var a=80;
cxt.fillStyle="#030303";
cxt.fillRect(0, 0, canvas.width, canvas.height);
var moveAsync=eval(Jscex.compile("async", function () {
while (true) {
cxt.fillStyle="rgba(0, 0, 0, .3)";
cxt.fillRect(0, 0, canvas.width, canvas.height);
cxt.fillStyle="#fff";
for (var i in balls) {
cxt.beginPath(); \注:根据速度,重绘所有小球的位置。\
cxt.arc(balls[i].x, balls[i].y, balls[i].r, 0, Math.PI * 2, true);
cxt.closePath();
cxt.fill();
balls[i].y +=balls[i].vy * cyc / 1000;
balls[i].x +=balls[i].vx * cyc / 1000;
if (balls[i].r+balls[i].y >=canvas.height) {
if (balls[i].vy > 0) {
balls[i].vy *=-0.7;
}
}
else {
balls[i].vy +=a;
}
}
$await(Jscex.Async.sleep(cyc));
}
}))
moveAsync().start();
其效果如图2-6所示。
图2-6 在Canvas中模拟平抛运动
斜抛运动之疯狂的大炮
斜抛运动在很多游戏中都碰到过,如“愤怒的小鸟”,小鸟经过弹弓的作用与水平有一仰角抛出。下面制作一个无限开炮的大炮来模拟斜抛运动。
第一步,需要一张大炮的图片,如图2-7所示。
图2-7 大炮素材
通过如下方式加载图片:
var img=new Image(); img.src="image/artillery.png"; img.onload=function () { cxt.drawImage(img, 0, 325); }
第二步,模拟炮弹。在每次循环时生成一个炮弹:
var ball={ x: 185, y: 470, r: getRandomNumber(0, 20), vx: getRandomNumber(190, 3000), vy: getRandomNumber(-3000, 0) }; balls.push(ball); if (balls.length > 100) { balls.shift(); }
当炮弹射出数量大于100时,从balls中移除一个小球,以防止浏览器堆栈溢出。其中getRandomNumber方法是自定义方法,可以获得一定范围内的随机数:
function getRandomNumber(min, max) { return (min+Math.floor(Math.random() * (max-min+1))) }
第三步,Jscex循环实现大炮的猛烈轰炸:
var fireAsync=eval(Jscex.compile("async", function () { while (true) { var ball={ x: 185, y: 470, r: getRandomNumber(0, 20), vx: getRandomNumber(190, 3000), vy: getRandomNumber(-3000, 0) }; balls.push(ball); if (balls.length > 100) { balls.shift(); } cxt.fillStyle="rgba(0, 0, 0, .3)"; cxt.fillRect(0, 0, canvas.width, canvas.height); cxt.fillStyle="#fff"; cxt.drawImage(img, 0, 425); for (i in balls) { cxt.beginPath(); cxt.arc(balls[i].x, balls[i].y, balls[i].r, 0, Math.PI * 2, true); cxt.closePath(); cxt.fill(); balls[i].y +=balls[i].vy * cyc / 1000; balls[i].x +=balls[i].vx * cyc / 1000; if (balls[i].r+balls[i].y >=canvas.height) { if (balls[i].vy > 0) { balls[i].vy *=-0.7; } } else { balls[i].vy +=a; } } $await(Jscex.Async.sleep(cyc)); } })) fireAsync().start();
以上代码的运动效果如图2-8所示。
图2-8 大炮的猛烈轰炸
总结
通过这个实验,可以更加深刻地理解运动的独立性,了解Canvas中绘制图片的基本方法。但是这还不够,因为运动不仅仅在线性运动方面是独立的,它与物体的转动也是相互独立的。绘制图片时不建议直接使用drawImage,当图片没有加载完成,调用drawImage方法会在一些浏览器中抛异常。所以要在drawImage之前进行图片的预加载。