大家好,咱么又见面了,我是能说会道,技术一流的一灯小雨。
本人最近在用Javascript写一个小游戏,其中涉及到抛物线算法,在这里和大家分享一下。我好像听到有人说,什么是抛物线算法?呃,这个。。。你一定玩过愤怒的小鸟这款游戏吧?就是“呜。。。呜。。。砰。。砰。。砰。。”。嗯,对,没错,这就是抛物线算法。要完成抛物线算法呢,得需要从简单的自由落体算法开始,虽然公式简单,谁都会算,但用代码实现起来,也不是那么简单呢?那么,咱么现在就开始吧!
自由落体
1.先通过CSS构建一个球体模型。代码如下:
.ball{
width:30px;height:30px;
background-color: black;
border-radius: 15px;
position: absolute;
left:100px;
top:0;
transform: translateY(180px);
}
2.创建球体。想必你通过上述代码已经脑补出球体的模样了吧!那么,接下来咱们要做的就是把球往下扔咯!通过button的click事件创建球体。代码如下:
body:
<button onclick="shoot()">发射</button>
javascript:
function shoot()
{
var ball = document.createElement('div');
ball.className = 'ball';
document.body.appendChild(ball);
ball.style.top = `${vy * t + 0.5 * g * t * t }px`;
}
3.添加垂直加速度。继续改造shoot函数,代码如下:
function shoot()
{
var t = 8, g = 10, vy = 0;
var ball = document.createElement('div');
ball.className = 'ball';
document.body.appendChild(ball);
ball.style.top = `${vy * t + 0.5 * g * t * t }px`;
}
我们声明了几个变量,t表示运动时间, g表示垂直加速度,vy表示垂直方向的初始速度,因为自由落体的初始状态为静止,那么它初始速度为0注意,代码中ball.style.top的属性值用的模板字符串,模板字符串是ES6的字符串新特性。可将它视为普通字符串拼接的简化形式。最终会产生类似’...px‘的普通字符串结果。请注意字符串中的计算公式,这是高一的物理知识,这是整个代码的核心噢!那么,咱们先运行看看效果吧!呃。。。好像哪里不对呢,小球从从空中以迅雷不及掩耳之势直接掉在地上,说好的自由落体呢?实际上上述的计算公式计算出来的是小球最终的落地位置,我们直接这样赋值,可不就瞬移到地上了。不过,还好,落地的位置非常准。现在的情况是知道起点和终点,那么我们怎么模拟中间的运动过程呢?答案是:通过插值。
4.贝塞尔插值算法
贝塞尔插值算法广泛应用于工业领域。最初是用来设计汽车造型的,大家有没有注意到,汽车造型中的那些边角处理都非常圆滑,过渡非常流畅。这些圆滑处理就是用贝塞尔插值算法做到的。当然贝塞尔插值不是一种单一的算法,而是一些列算法,每个算法公式都和具体问题密切相关,汽车设计又汽车设计的插值公式,自由落体也有自由落体的插值公式。很多插值公式甚至需要一篇论文来对它进行理论阐释。那么自由落体的插值公式是怎样的呢?代码如下:
function generateCubicBezier(v, g, t)
{
var a = v / g;
var b = t + v / g;
return [[(a/3+(a+b)/3-a)/(b-a),(a*a/3+a*b*2/3-a*a)/(b*b-a*a)],
[(b/3+(a+b)/3-a)/(b-a),(b*b/3+a*b*2/3-a*a)/(b*b-a*a)]];
}
传入初始速度,垂直加速度,运动时间,返回一个二维的插值数组。该函数在插值演算时调用。很遗憾,这段算法我无法解释,这是某大神分享的。虽然大神有对算法进行具体阐释,不过本人没有理解,自己都不能理解的东西,最好不要向别人解释,免得误人子弟。源码在此,你大可自己好好参详,也许你天资聪颖,能参透一二也未可知。我呢,就用它就好了,那么,咱们就用它进行插值吧。继续改造shoot函数。代码如下:
function shoot()
{
var t = 8, g = 10, vy = 0;
var ball = document.createElement('div');
ball.className = 'ball';
document.body.appendChild(ball);;
ball.style.transition = `top cubic-bezier(${generateCubicBezier(vy, g, t)}) ${t}s`;
ball.style.top = `${vy * t + 0.5 * g * t * t }px`
}
同样需要注意的是ball.style.transition值用的也是模板字符串,这里对top进行了插值演算。咱么看看效果吧!呃。。。还是以迅雷不及掩耳之势直接掉地上。。接续改造。。
function shoot()
{
var t = 8, g = 10, vy = 0;
var ball = document.createElement('div');
ball.className = 'ball';
document.body.appendChild(ball);
setTimeout(()=>{
ball.style.top = `${vy * t + 0.5 * g * t * t }px`;
}, 100);
ball.style.transition = `top cubic-bezier(${generateCubicBezier(vy, g, t)}) ${t}s`;
}
我不想解释什么,插值演算有时候就是这么变态!呃。。。还是强行解释一波吧,在插值演算的时候,你不能直接一开始就把最终值
告诉它,否则它直接就插入最终值了,这里给它延时100毫秒。看效果,OK!!
5.清除资源。
小球运动时间完了,就清除它。继续改造代码,最终代码如下:
function shoot()
{
var t = 8, g = 10, vy = 0;
var ball = document.createElement('div');
ball.className = 'ball';
document.body.appendChild(ball);
setTimeout(()=>{
ball.style.top = `${vy * t + 0.5 * g * t * t }px`;
}, 100);
setTimeout(() => {
document.body.removeChild(ball);
}, 1000 * t);
ball.style.transition = `top cubic-bezier(${generateCubicBezier(vy, g, t)}) ${t}s`;
}
好了,自由落体大功告成!注意这里的单位时px,个人觉得下落速度有点慢,可以调整垂直加速度g。那么抛物线就简单了!
抛物线算法
实际上抛物线算法就时在自由落体的基础上加一个水平匀速直线运动就好了,那么,继续改造代码。。。代码如下:
function shoot()
{
var t = 8, g = 10, vy = 0, vx = 50;
var ball = document.createElement('div');
ball.className = 'ball';
document.body.appendChild(ball);
setTimeout(() => {
ball.style.top = `${vy * t + 0.5 * g * t * t }px`;
ball.style.left += `${vx * t}px`;//加入一个水平初速度
}, 100);
setTimeout(() => {
document.body.removeChild(ball);
}, 1000 * t);
//此处加入水平插值
ball.style.transition = `left linear ${t}s, top cubic-bezier(${generateCubicBezier(vy, g, t)}) ${t}s`
}
这里需要注意的,水平位置因为初始位置为100px,所以这里做的是累加,否则最终计算值水平位置就不对,小球运动轨迹会让人莫名其妙。
大功告成,是不是很简单呢?
好了,就到这里吧,希望你有所收获,如果你有疑问或想法,欢迎给我留言吧,再会吧。。。