20、《每周一点canvas动画》——桌球运动(2)

2023-11-23 00:41

本文主要是介绍20、《每周一点canvas动画》——桌球运动(2),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

每周一点canvas动画代码文件

在上一节,《每周一点canvas动画》——桌球运动(1)中我们介绍了如何运用动量守恒能量守恒的概念,最为真实的模拟了物体与物体之间发生碰撞后的情况。那么这一节,我们在上一节的基础上我们继续深入研究,非水平和垂直的情况下如何处理物体与物体之间碰撞后的情况,以及更为普适的多物体运动。本节主要内容:

  1. 二维碰撞解析

  2. 代码实现

  3. 粒子系统

  4. 总结

1.二维碰撞解析

深呼吸一下,接下来可能是本系列文章最为难理解的一部分(我是不是以前说过这话。。。),好吧!其实并不是多难,只要你看过前面的章节,你就会发现我们所有的东西都是循序渐进的,之所以说难,是因为复杂度的确有所提高。但是,所用到的知识绝对在前面做过铺垫。言归正传,接下来我们来分析这个从一维到二维的运动该如何处理。

我们上一节讲的一维碰撞运动如下图所示:

二维的概念很简单,即物体的运动方向并不是某个单一的方向。如下图中一图所示(左上第一幅),因为物体的速度方向并不是沿着某一个轴。所以,你不能直接把它带入动量守恒与能量守恒的公式中去计算碰撞后的速度,下面我们就来分析图中所示的解决方法。

首先,我们把物体的位置,速度全部都旋转到了水平位置。是不是有种似曾相识的感觉,没错,这与角度反弹那章其实是一个东西!下面我们继续分析,既然转到了水平面,一切就好说了。速度是矢量,我们把它沿着水平和竖直分解。

现在,我们将速度分解成了两部分,你可以直接忽视竖直方向的速度,而只考虑水平方向。为什么呢,你可以自己想想。然后,我们对物体做碰撞处理,因为只考虑改变水平方向,所以竖直方向不变,处理完水平的碰撞后,将竖直方向的速度与水平方向的合成。最后,你应该猜的到我们要做什么了,把所有的东西在旋转回去。

好了,这就是我们的整体思路,其实你应该发现,这与角度反弹那一章简直如出一辙。

2.代码优化

有了上面的解析,我们对上一节的代码做些优化。在桌球运动(1)中,我们这样计算碰撞后两个物体的速度vx0Final, vx1Final分别是多少。

...
var vx0Final = ((ball0.mass - ball1.mass)*ball0.vx + 2 * ball1.mass * ball1.vx)/(ball0.mass +ball1.mass);
var vx1Final = ((ball1.mass - ball0.mass)*ball1.vx + 2 * ball0.mass * ball0.vx)/(ball0.mass +ball1.mass);
...

但是,现在我们现在可以只考虑水平方向的速度。所以上述代码可以优化成如下形式:


var vxTotal = ball0.vx - ball1.vx;         //速度方向相反所以相减
var vx0Final = ((ball0.mass - ball1.mass) * ball0.vx + 2 * ball1.mass * ball1.vx)/(ball0.mass + ball1.mass);      //只计算其中的一个速度
var vx1Final = vxTotal + vx0Final;        //另一个速度

3.代码实现

还是老规矩,我们先上效果图:

下面我们来具体分析一下核心代码的实现,完整代码看源文件:

function ballCollsion(ball0, ball1){
第一部分         var dx = ball1.x - ball0.x,dy = ball1.y - ball0.y,dist = Math.sqrt(dx*dx + dy*dy); //用于距离的碰撞检测if(dist < ball0.radius + ball1.radius){ //如果发生碰撞//计算物体间的夹角,并得出坐标旋转所需要的sin,cos值var angle = Math.atan2(dy, dx),sin = Math.sin(angle),cos = Math.cos(angle),//ball0旋转后的坐标x0 = 0,y0 = 0,//ball1旋转后的坐标x1 = dx * cos + dy * sin,y1 = dy * cos - dx * sin,//ball0 旋转后的速度vx0 = ball0.vx * cos + ball0.vy * sin,vy0 = ball0.vy * cos - ball0.vx * sin,//ball1 旋转后的速度vx1 = ball1.vx * cos + ball1.vy * sin,vy1 = ball1.vy * cos - ball1.vx * sin,第二部分               //不用考虑竖直方向的速度vxTotal = vx0 - vx1;//带入公式计算碰撞后的速度vx0 = ((ball0.mass - ball1.mass) * vx0 + 2 * ball1.mass * vx1) /(ball0.mass + ball1.mass);vx1 = vxTotal + vx0;x0 += vx0;x1 += vx1;第三部分               //位置旋转回去var x0Final = x0 * cos - y0 * sin,y0Final = y0 * cos + x0 * sin,x1Final = x1 * cos - y1 * sin,y1Final = y1 * cos + x1 * sin;//调整球体实际上位于屏幕的位置ball1.x = ball0.x + x1Final;ball1.y = ball0.y + y1Final;ball0.x = ball0.x + x0Final;ball0.y = ball0.y + y0Final;//素的旋转回去ball0.vx = vx0 * cos - vy0 * sin;ball0.vy = vy0 * cos + vx0 * sin;ball1.vx = vx1 * cos - vy1 * sin;ball1.vy = vy1 * cos + vx1 * sin;}}

核心代码一共分为三个部分,在第一部分当中我们使用ball0作为整个系统旋转的中心点,所以它的位置为(0, 0),即使旋转后也不会发生变化。ball1的位置是相对于ball0,所以可以直接通过dx,dy获取ball1旋转后相对于ball0的坐标,最后是旋转ball0, ball1的速度。

第二部分,因为旋转到水平位置,所以不需要考虑竖直方向的速度。将水平速度带入公式,计算得到碰撞后的速度大小。同时我盟让x0,x1加上碰撞后的速度,让两物体分开。

第三部分,我们把球体的位置再旋转回去,由于ball0的坐标为(0,0),所以你可以注意到旋转后的坐标x0Final,y0Final仍旧为0。然后具体调整它们位于画布中的位置。这里你要稍微理解一下,在上面的坐标旋转,速度旋转中,我们都是以ball0为中心点做的。所以,最后的结果都是相对的,而我们需要的是他们位于画布中的实际位置。所以,需要加上ball0在画布中的坐标。最后,再把速度也旋转回去。ok,大功告成!

4.粒子系统

有了前面的具体分析我们很容易就可以实现多物体的应用。先上效果图:

是不是很炫啊!其实这个效果很早就能做出来了,只是当时并没有讲碰撞检测,所以你看到物体之间相遇后并没有发生相互作用。所以,一直到这我才放上这个效果。核心的代码,就是上面的部分,我们已经讲完了。这里我们就分析如何实现多物体处理。具体代码请看bolliard-multi.html

那么,我们是如何实现的呢?还是老东西,在动画循环中:

   for(var ballA, i=0, len = numBalls - 1; i<len; i++){ballA = balls[i];for(var ballB, j=i+1; j<numBalls; j++){ballB = balls[j];checkCollision(ballA, ballB); //物体间的碰撞处理drawLine(ballA, ballB);  //物体间连线画线}}

眼熟吧,其实它就是《每周一点canvas动画》——碰撞检测(2)中的多物体碰撞检测策略。那么物体之间的连线是怎么做的呢?

function drawLine(ball0, ball1){var dx = ball1.x - ball0.x,dy = ball1.y - ball0.y,dist = Math.sqrt(dx*dx + dy*dy); //计算物体间的距离if(dist < long){ //小于long画线context.save();context.strokeStyle = "rgba(255,255,255,0.3)";context.beginPath();context.moveTo(ball0.x, ball0.y);context.lineTo(ball1.x, ball1.y);context.closePath()context.stroke()context.restore();}}

最后在动画循环中调用,一个粒子系统就这样完成了,你甚至可以控制粒子的运动速度,大小,颜色,距离多远连线等等,拿去放心使用吧。这里还是要声明一点,在源码中的checkCollision函数中,为了便于使用做了些简单的封装,比如这样的:

function rotate(x, y, sin, cos, reverse){return {x: (reverse)?(x*cos + y*sin):(x*cos - y*sin),y: (reverse)?(y*cos - x*sin):(y*cos + x*sin)}}

应该不会影响你的理解。

5.总结

这一章到这就结束了,整个简单的碰撞体系到这基本就结束了。回望一下,其实我们的内容覆盖了,如何判断两个物体发生碰撞?如何处理碰撞后的情况?如何将其运用于多物体系统?这里面有简单和复杂之分。如果,你追寻的是简单效果,那么你只需要简单的做速度的反向处理等即可。如果,你想要更精确,更自然的效果。那你就需要使用本章所介绍的动量守恒和能量守恒等概念,来做精确的处理,所有的一切都取决于你的具体需求。

本章重要公式如下:

//遵循动量守恒和能量守恒的碰撞后的速度大小
v0Final = ((m0 - m1)*v0 + 2*m1*v1) / (m0 + m1);
v1Final = ((m1 - m0)*v1 + 2*m0*v0) / (m0 + m1);//精简版
var vxTotal = vx0 - vx1;
vx0 = ((ball0.mass - ball1.mass)*vx0 + 2*ball1.mass*vx1)/(ball0.mass + ball1.mass);
vx1 = vxTotal + vx0;

下一章,我们讲万有引力的应用。敬请期待!!!

这篇关于20、《每周一点canvas动画》——桌球运动(2)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/414092

相关文章

基于Canvas的Html5多时区动态时钟实战代码

《基于Canvas的Html5多时区动态时钟实战代码》:本文主要介绍了如何使用Canvas在HTML5上实现一个多时区动态时钟的web展示,通过Canvas的API,可以绘制出6个不同城市的时钟,并且这些时钟可以动态转动,每个时钟上都会标注出对应的24小时制时间,详细内容请阅读本文,希望能对你有所帮助...

最好用的WPF加载动画功能

《最好用的WPF加载动画功能》当开发应用程序时,提供良好的用户体验(UX)是至关重要的,加载动画作为一种有效的沟通工具,它不仅能告知用户系统正在工作,还能够通过视觉上的吸引力来增强整体用户体验,本文给... 目录前言需求分析高级用法综合案例总结最后前言当开发应用程序时,提供良好的用户体验(UX)是至关重要

基于Python实现PDF动画翻页效果的阅读器

《基于Python实现PDF动画翻页效果的阅读器》在这篇博客中,我们将深入分析一个基于wxPython实现的PDF阅读器程序,该程序支持加载PDF文件并显示页面内容,同时支持页面切换动画效果,文中有详... 目录全部代码代码结构初始化 UI 界面加载 PDF 文件显示 PDF 页面页面切换动画运行效果总结主

Qt QWidget实现图片旋转动画

《QtQWidget实现图片旋转动画》这篇文章主要为大家详细介绍了如何使用了Qt和QWidget实现图片旋转动画效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、效果展示二、源码分享本例程通过QGraphicsView实现svg格式图片旋转。.hpjavascript

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

【C++学习笔记 20】C++中的智能指针

智能指针的功能 在上一篇笔记提到了在栈和堆上创建变量的区别,使用new关键字创建变量时,需要搭配delete关键字销毁变量。而智能指针的作用就是调用new分配内存时,不必自己去调用delete,甚至不用调用new。 智能指针实际上就是对原始指针的包装。 unique_ptr 最简单的智能指针,是一种作用域指针,意思是当指针超出该作用域时,会自动调用delete。它名为unique的原因是这个

用Unity2D制作一个人物,实现移动、跳起、人物静止和动起来时的动画:中(人物移动、跳起、静止动作)

上回我们学到创建一个地形和一个人物,今天我们实现一下人物实现移动和跳起,依次点击,我们准备创建一个C#文件 创建好我们点击进去,就会跳转到我们的Vision Studio,然后输入这些代码 using UnityEngine;public class Move : MonoBehaviour // 定义一个名为Move的类,继承自MonoBehaviour{private Rigidbo

【JavaScript】LeetCode:16-20

文章目录 16 无重复字符的最长字串17 找到字符串中所有字母异位词18 和为K的子数组19 滑动窗口最大值20 最小覆盖字串 16 无重复字符的最长字串 滑动窗口 + 哈希表这里用哈希集合Set()实现。左指针i,右指针j,从头遍历数组,若j指针指向的元素不在set中,则加入该元素,否则更新结果res,删除集合中i指针指向的元素,进入下一轮循环。 /*** @param