`
换个号韩国红果果
  • 浏览: 48096 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类

桌面上有多个球在同时运动,怎么实现球之间不交叉,即碰撞?

阅读更多
稍微想了一下,然后解决了很多bug,最后终于把它实现了。其实原理很简单。在每改变一个小球的x y坐标后,遍历整个在dom树中的其他小球,看一下它们与当前小球的距离是否小于球半径的两倍?若小于说明下一次绘制该小球(设为a)前要把他的方向变为原来相反方向(与a要碰撞的小球设为b),即假如当前小球的距离小于球半径的两倍的话,马上改变当前小球方向。那么下一次绘制也是先绘制b,再绘制a,由于a的方向已经改变,所以距离越来越远,说明一下,可以考虑两种极端情况1.a b同向平行运动,这时假如要碰了(实际不会碰撞因为平行),a的速度方向变了,越来越远。2.a,b垂直运动马上相交,快碰撞时a方向改变,则越来越远。
写程序时可能出现的bug有
1方向相反直接写成angle=-angle;这是错的,因为一定要规定angle不能小于0,因为小球对于浏览器边框的检测是按照angle在0到360之间来检测的,一旦为0,出现对浏览器的越界则无法将其检测出来。
2,由于angle是存在于particle中的私有变量,而判断是否碰撞的检测函数define是需要改变angle的,又由于函数animate中的绘制函数
for(var particle=0;particle<particles.length ;particle++){

			particles[particle].draw(timeDelta);
			particles[particle].define();
			document.getElementsByClassName('tag')[0].className='';
			
		}
需要在绘制完一个点后,马上调用碰撞检测函数define检测该点所以需要把它放在animate当中来调用,particle与animate都是全局变量这就陷入了一个两难的境地,既要全局变量animate能调用define也要让define能够访问全局变量particle下的私有变量,这正符合闭包的定义,闭包是即可以访问私有变量也可以成为全局变量被其他全局变量调用。所以可以把define作为particle下的闭包。
3当球碰到下边框时,若角度太小,接近于0或者太大接近180度时可能会无法出来,一直在边框上。所以应该有一定限制,我只限制了上边框,代码
if(nexty<0){//if  angle is  between  3 o'clock  and  9 o'clock 
				if((angle-0.08)<=0) {
					angle=0.1;

			}
			if(angle+0.08>= Math.PI){
				angle=Math.PI-0.1
			}
			angle=Math.PI*2-angle;
		}

取5度即0.08的阈值。一旦小于这个值取为0.1.下边框我没有处理。
4 左下角是我绘制的fps图,一旦绘制到#fps中点时,擦除重绘。
完整代码如下
css

body{
		padding: 0px;margin: 0px;
		overflow: hidden;
	}
	span{
		width: 16px;height: 16px;display: inline-block;border-radius: 50%;position: absolute;
	}
	#fps{
		position: fixed;bottom: 10px;right: 10px;font-size: 30px;font-weight: bold;
	}

html
<div id="fps"></div>

js引用的是color.js
它是一个1匿名函数

如下

(function(){

	var MAX_PARTICLES = 50;
	var MAX_VELOCITY = 500;
	var PARTICLE_RADIUS = 8;
	var STAGE_WIDTH = 1024;
	var STAGE_HEIGHT = 768;
	var COLORS = ["#cc0000", "#ffcc00", "#aaff00", "#0099cc", "#194c99", "#661999"];
	var FRAME_TIMES = [];
	var fpsArr=[];
	var timer;
	var max=0;
	var min=60;
	var average=0;
	var time=new Date().getTime();
	var particles = [];
	var wrap=document.createElement('div');//fps图表包含块初始化
		wrap.className='wrap';
		wrap.style.position='absolute';
		wrap.style.bottom=10+'px';
		wrap.style.left=10+'px';
		wrap.style.width=500+'px';
		wrap.style.height=120+'px';
		document.body.appendChild(wrap);
	var labelx=1;
	init();
	
	// debugger();

	function init(){

		STAGE_WIDTH=document.documentElement.clientWidth;//获取窗口可见宽高
		STAGE_HEIGHT=document.documentElement.clientHeight;

		for(var i=0 ;i<MAX_PARTICLES;i++)
			particles.push( new particle() );
		timer=setInterval(animate
	,30);
		
	}
	function  paint(fps){//绘制图表上的每一个点
		
		if(labelx>wrap.offsetWidth/2){//一旦大于宽的一半擦掉重绘
			var delDot=document.getElementsByClassName('dot');
			while(delDot[0])
				wrap.removeChild(delDot[0]);
			labelx=1;
		}
		var dot=document.createElement('div');
		dot.className='dot';
		dot.style.position='absolute';
		dot.style.bottom=3*fps+'px';
		dot.style.left=labelx+'px';
		dot.style.backgroundColor='red';
		dot.style.zIndex=-5;
		dot.style.width=2+'px';
		dot.style.height=2+'px';
		wrap.appendChild(dot);
		labelx+=2;
	}
	function animate(){//动画
		function sum(arr){
			var i=0;
			var sum=0;
			for(;i<arr.length;i++)
				sum+=arr[i];
				
			
			return sum;
		}
		if(FRAME_TIMES.length>30) //保持计算fps的存储时间的数组长度为30
			FRAME_TIMES.splice(0,1);	
		var curr=new Date().getTime();
		FRAME_TIMES.push(curr);
		// console.log(curr-FRAME_TIMES[FRAME_TIMES.length-2]);
		var fps=0;
		if(FRAME_TIMES.length>1)
		fps= 1000 / ((curr - FRAME_TIMES[0]) / (FRAME_TIMES.length - 1));

		fps=Number(fps.toFixed(2));
		paint(fps);//绘制fps图表
		if(fpsArr.length>30) 
			fpsArr.splice(0,1);
		fpsArr.push(fps);//存储每个fps方便计算平均值
		// console.log(sum(fpsArr))
		average=(sum(fpsArr)/fpsArr.length).toFixed(2);//计算平均值
	
		if(fps>max)
			max=fps;

		if(fps&&fps<min)
			min=fps;

		//fpsArr.push
		var fshow=document.getElementById('fps');
		fshow.innerHTML="average: "+average+  " max: "+max+" min: "+min+" fps: "+fps+'fps';
		var timeDelta = curr - FRAME_TIMES[FRAME_TIMES.length - 2];
		
		for(var particle=0;particle<particles.length ;particle++){//绘制每一个点(实际上是改变每一个小球的坐标)

			particles[particle].draw(timeDelta);
			particles[particle].define(); /*当前小球绘制完后马上进行碰撞检测*/
			document.getElementsByClassName('tag')[0].className='';//检测完回来后置为空,以便下一个元素继续标记自己
			
		}
	}

	function  distance(a,b){
		return Math.sqrt( Math.pow(a,2)+ Math.pow(b,2) )
	}
	function particle(){//小球位置颜色速度角度初始化并添加到dom树
		var velocity=(MAX_VELOCITY-MAX_VELOCITY/5)*Math.random()+MAX_VELOCITY/5;
		var x=STAGE_WIDTH/2-PARTICLE_RADIUS;
		var y=STAGE_HEIGHT/2-PARTICLE_RADIUS;
		var color=COLORS[Math.floor( COLORS.length*Math.random() )];
		var node=document.createElement('span');
		var angle=2*Math.PI*Math.random();
		
		node.style.backgroundColor=color;
		node.style.left=x+'px';
		node.style.top=y+'px';
		document.body.appendChild(node);

	function define(){//碰撞检测

		if(new Date().getTime()-time<5000)  return 
			var xx=x;
			var yy=y;
			var eles=document.getElementsByTagName('span');  //获取此时桌面上的所有小球,因为当前小球已经绘制完,故会连自己也会获取进来。而它自己与自己是永远相碰的故在draw中设置该小球的className,以便下面进行区分。
			for(var i=0;i<eles.length;i++){

				var dirtax=eles[i].offsetLeft-xx;
				var dirtay=eles[i].offsetTop-yy;
				if( distance( dirtax,dirtay) <16 && eles[i].className!='tag'){//检测是否是自己

					if(angle>=0&&angle<Math.PI){//在0 到180时变成大于180度角,并且break,不然进入下一条逻辑,又把角度变回来了!这相当于两种情况只执行一种,用if else即可
						angle=Math.PI+angle;
						break;
					}
					if(angle>=Math.PI&&angle<Math.PI*2){//

						angle=angle-Math.PI;
						break;
					}
				}
			}
	}

		function draw(){//每一个球坐标获取以及绘制
			var nextx=x + velocity*Math.cos(angle)/40;//(x,y) the current positon . (nextx,nexty) the  next position
			var nexty=y - velocity*Math.sin(angle)/40;
			// for(var i=0;i<10000000;i++){}
			if(nextx>=STAGE_WIDTH-PARTICLE_RADIUS*2-13	){
				// nextx=nextx-10;
				//if  angle is  between  3 o'clock  and  12  o'clock 
				if (angle>=0&&angle<Math.PI/2) 
					angle=Math.PI-angle;
				//if  angle is  between  6 o'clock  and  3  o'clock 
				if(angle>3*Math.PI/2&&angle<Math.PI*2)
					angle=3*Math.PI-angle;	
				}

			if(nexty<0){//if  angle is  between  3 o'clock  and  9 o'clock 
				if((angle-0.08)<=0) {
					angle=0.1;

			}
			if(angle+0.08>= Math.PI){
				angle=Math.PI-0.1
			}
			angle=Math.PI*2-angle;
		}

			if(nextx<0){
				if(angle>=Math.PI&&angle<Math.PI*1.5)//if  angle is  between  9 o'clock  and  6  o'clock 
					angle=3*Math.PI-angle;

				if(angle>Math.PI/2&&angle<Math.PI)//if  angle is  between  12 o'clock  and  9  o'clock 
					angle=Math.PI-angle;
			}

			if(nexty>STAGE_HEIGHT-PARTICLE_RADIUS*2	-13)//if  angle is  between  9 o'clock  and  3  o'clock 

					angle=Math.PI*2-angle;

				//refresh  the  postition  of the particle
			node.style.left=nextx+'px';
			node.style.top=nexty+'px';
			node.className='tag';
			x=nextx;
			y=nexty;

		}
		return {//作为闭包返回。
			draw:draw,
			define:define
		}

	}
	var  t=[];
	document.body.onresize=function(){//调整窗口大小时重新绘制
		//alert('1')
		// console.log('1')
		// var current=new Date().getTime();
		
		var spanlist=document.getElementsByTagName('span');
		while(spanlist.length!=0){document.body.removeChild(spanlist[0])}
			FRAME_TIMES=[];//重置
		clearInterval(timer);//清除定时器
		 max=0;
		min=60;
		particles=[];//将存闭包的particles清空
		init();//重绘开始
	}

})()


分享到:
评论

相关推荐

    基于MFC实现的单摆碰撞程序

    程序可能先加载或初始化一个或多个单摆,然后根据物理规则模拟它们的运动和碰撞。碰撞后,系统会更新每个单摆的状态,可能包括位置、速度和角度。所有这些动态过程可能通过图形界面实时显示,给用户提供直观的观察...

    最小凸多边形生成算法——Qt实现

    这个算法在很多领域都有应用,例如数据简化、形状识别、碰撞检测等。Qt是一个跨平台的应用程序开发框架,常用于创建用户界面和桌面应用程序。 在"最小凸多边形生成算法——Qt实现"这个项目中,开发者利用Qt的图形...

    c语言实现的超级玛丽游戏源码.zip

    6. **游戏状态管理**:游戏有多个状态,如游戏开始、游戏进行、游戏结束等,源码会有一个系统来管理这些状态的切换。 7. **键盘输入处理**:通过读取用户键盘输入,控制马里奥的移动、跳跃等动作。 8. **文件I/O**...

    多线段求交

    在IT领域,几何算法是计算机图形学中的一个重要分支,它涉及到如何处理图形对象之间的相互关系。...通过结合这两个文件,我们可以构建一个完整的解决方案,用于处理和分析具有多个线段的几何数据。

    js 水果忍者源码

    总的来说,使用JS实现“水果忍者”游戏是一个涉及多方面技术的综合性项目,需要开发者具备扎实的编程基础、良好的问题解决能力以及对游戏机制的深入理解。通过这个项目,开发者不仅可以提升编程技能,还能体验到游戏...

    C++ MFC实现飞机大战游戏

    本程序中使用多个定时器,分别控制不同的功能。在MFC的API函数中使用SetTimer()函数设置定时器,设置系统间隔时间,在OnTimer()函数中实现响应定时器的程序。 2.2 透明贴图实现技术  绘制透明位图的关键就是创建一...

    虚拟现实技术考试题和答案解析.doc

    分布式虚拟现实(Distributed VR)进一步扩展了虚拟现实的应用范围,使得多地用户可以同时参与同一个虚拟环境,实现远程协作。 总的来说,虚拟现实技术涉及众多交叉学科,从人机交互设计到计算机图形学,从传感器...

    餐桌礼仪之筷子文化十五忌.doc

    在中国的传统文化中,餐桌礼仪是体现一个人教养与文化素养的重要方面,而筷子作为中国人日常饮食的主要工具,其使用方式更是礼仪中不可忽视的一部分。"餐桌礼仪之筷子文化十五忌"详细列举了在使用筷子时应该避免的...

    DS428高性能的4进8出数字扬声器系统(音箱)处理器.pdf

    用户还可以选择桌面防振传声器座,将其固定安装在桌面或其他板面上,以进一步减少由于碰撞平面产生的敲击声和振动声。 上海绒三角视讯科技有限公司提供的U857QSU超指向性鹅颈式传声器及其相关技术,不仅提升了音频...

    西餐餐桌服务工作标准.pdf

    6. 任何时候端上盘碟,手部应远离客人,以防碰撞,且不应从客人面前越过或直接在客人前方操作,以减少意外发生的可能性。 上菜方式: 1. 按照女士、男士、老人、儿童的顺序进行正式服务,体现尊重和照顾。 2. 食品...

    扫地机器人的原理大概是这样.docx

    ### 扫地机器人的工作原理及其关键技术 #### 一、环境识别技术 扫地机器人的高效运作离不开其...总之,扫地机器人的发展正在朝着更加智能、高效和人性化的方向迈进,未来的产品将会在功能和用户体验上带来更多的惊喜。

    AugmentingVirtualCharactersfo

    这涉及到人机交互、计算机图形学、人工智能等多个IT领域的交叉应用。 【描述】:“AugmentingVirtualCharactersforMoreNaturalInteraction共10页.pdf.zip” 提示这是一个10页的PDF文档,压缩在ZIP文件中。文档很...

    usb.zip_通讯编程_Visual_C++_

    而通讯编程则涉及到如何设计和实现两个或多个设备之间的数据交换,例如通过USB接口。 压缩包中的文件提供了关于USB通信的实用工具和文档: 1. **PeakOemDrv.exe**:这可能是一个驱动程序安装程序,用于安装与PCAN-...

    2021-2022年收藏的精品资料人机工程学在室内家具的应用设计.doc

    内一般认为,人机工程学是一门综合性的交叉学科,主要研究人在操作机器或使用产品时,人、机器和工作环境三者之间的相互关系,旨在提高工作效率,保证作业安全,创造舒适、健康的工作环境,以适应不同人群的需求。...

    实时道路交通系统:在Unity中创建的实时道路交通系统。 为荣誉项目而开发

    - **场景搭建**:首先,我们需要在 Unity 中建立一个城市道路的3D模型,包括道路、交叉口、车道线等元素,这可以通过导入自定义模型或者使用Unity的内置几何体来实现。 - **车辆模型**:创建可复用的车辆模型,...

Global site tag (gtag.js) - Google Analytics