【整理】HTML5游戏开发学习笔记(3)- 抛物线运动

1.预备知识
(1)Canvas旋转的实现过程

			window.onload = function(){
				
				var ctx = document.getElementById('canvas1').getContext('2d')

				//旋转
				ctx.save()
				ctx.translate(200,200)//把(200,200)点作为临时的(0,0)点
				ctx.rotate(30*Math.PI/180)//顺时针旋转30度所对应的弧度
				ctx.fillRect(0,0,100,150)
				ctx.restore()

			}


(2)抛物线运动中的重力加速度的模拟实现模型

			/*
				抛物线运动
				这个版本做了修改,为了减少一次角度和弧度之间的转换计算(通过Math.atan2可以直接算出弧度),
				构造参数中的角度改成了弧度
				deleted:
				angle = angle,								//初始角度
				radians = angle*Math.PI/180,	//初始弧度

				object ParabolicMotion
					@velocity:初始速度
					@radians:初始弧度
					@gravity:初始加速度{x:0,y:2}
			*/
			function ParabolicMotion(velocity,radians,gravity){
				var velocity = velocity, 
						radians = radians, 
						gravity = gravity	 

				var offsetX = velocity*Math.cos(radians),
						offsetY = -velocity*Math.sin(radians)

				function next(offset,gravity){
					var offset1 = offset
					var offset2 = offset+gravity

					return {offset:offset2,dv:(offset1+offset2)*.5}
				}

				return {

					/*
						获取运动轨迹到下一个时间点,x,y轴所偏移的距离
					*/
					moveNext : function(){
						var offsetXD = next(offsetX,gravity.x)
						var offsetYD = next(offsetY,gravity.y)
						
						offsetX = offsetXD.offset
						offsetY = offsetYD.offset

						//console.log(offsetX+','+offsetY)
						
						return {x:offsetXD.dv,y:offsetYD.dv}
					}

				}
			}


2.实现思路
涉及的对象,包括球(Ball),弹弓(Slingshot),抛物线运动(ParabolicMotion)。
操作过程是,鼠标键按下拖拽小球,和弹弓的作用点成一定的角度,鼠标键按起后,小球做抛物线运动

3.主要代码

			/*弹弓*/
			function Slingshot(){

				var opts,
						ctx,
						crtBall,
						ballSelected = false

				function refresh(){
					ctx.clearRect(0,0,opts.width,opts.height)

					drawSling()
					crtBall.draw()					
				}

				function drawSling(){
					var point = opts.actionPoint

					ctx.beginPath()
					ctx.moveTo(point.x,point.y)
					ctx.lineTo(point.x,opts.height)
					ctx.closePath()
					ctx.stroke()

					if(crtBall!=null&&ballSelected){
						//绘制连接球和弹弓的"橡皮筋"
						ctx.beginPath()
						ctx.moveTo(point.x,point.y)
						ctx.lineTo(crtBall.x,crtBall.y)
						ctx.closePath()
						ctx.stroke()
					}
				}

				function isBallSelected(offsetX,offsetY){
					var point = opts.actionPoint
					var a = Math.abs(point.x-offsetX)
					var b = Math.abs(point.y-offsetY)
					//var c = Math.sqrt(a*a+b*b)

					return (a*a+b*b)<=crtBall.radius*crtBall.radius
				}

				// 添加拉弹弓事件
				function initEvents(){
						var canvas = opts.canvas
						/*
							addEventListener函数的第3个参数,
							false表示内层元素事件先触发,ture则表示外层的事件先触发
							
							alert(e.offsetX+','+e.offsetY)
							必须使用offsetX,offsetX是相对于canvas画布的距离(但firefox不支持)

							事件参数e没有考虑浏览器兼容问题
						*/
						canvas.addEventListener('mousedown',function(e){														
							// 判断球是否被选中
							ballSelected = isBallSelected(e.offsetX,e.offsetY)
							
							if(ballSelected){
								crtBall.locate(e.offsetX,e.offsetY)
								refresh()								
							}
						},false)

						canvas.addEventListener('mousemove',function(e){
							if(ballSelected){
								crtBall.locate(e.offsetX,e.offsetY)
								refresh()
							}
						},false)

						canvas.addEventListener('mouseup',function(e){
							if(ballSelected){
								ballSelected = false

								crtBall.locate(e.offsetX,e.offsetY)
								refresh()			

								//发射炮弹
								fire(function(x,y){
									//TODO 判断是否打中目标

								})								
							}
						},false)
				}

				function fire(onCompleted){
					// 获取当前的炮弹发射速度和弧度
					var velocity = ($.square(opts.actionPoint.y-crtBall.y)+$.square(opts.actionPoint.x-crtBall.x))/100
					var radians = -Math.atan2(opts.actionPoint.y-crtBall.y,opts.actionPoint.x-crtBall.x)//30*Math.PI/180
					var gravity = {x:0,y:2}

					var parabolicMotion = new ParabolicMotion(velocity,radians,gravity)
					var completed = false

					var timer =	setInterval(function(){
						// 在当前抛物线轨迹下,获取炮弹下一次单位时间内x,y轴需要偏移的单位长度
						var offset = parabolicMotion.moveNext()

						crtBall.move(offset.x,offset.y)
						refresh()
						
						// 检查是否超出画布边界
						completed = (crtBall.x>=opts.width||crtBall.y>=opts.height)

						if(completed){
							clearInterval(timer)

							if(typeof onCompleted=='function'){
								onCompleted(crtBall.x,crtBall.y)
							}			
						}					
					},100)					
				}

				return {

					init : function(options){
						opts = $.extend(options,{
							canvas : null,//画布
							width : 1000,//画布长
							height : 300,//画布高
							actionPoint : {x:150,y:200}/*作用力点坐标*/
						})

						ctx = opts.canvas.getContext('2d')
						drawSling()

						// 添加拉弹弓事件
						initEvents()

						return this
					},

					loadBall : function(ball){
						var point = opts.actionPoint

						crtBall = ball.init({ctx:ctx,x:point.x,y:point.y})
													.draw()
	
						return this
					}

				}
			}
			// app			
			window.onload = function(){
				
				var canvas = document.getElementById('canvas1')
				var ball = new Ball(10)
				var slingshot = new Slingshot().init({canvas:canvas})			

				// 装载一个炮弹
				slingshot.loadBall(ball)
			}



4.优化和完善
(1)需实现球打中目标物体后,目标物体进行旋转
(2)可以实现同时有多个小球发射,就像游戏里发射子弹的效果

原文地址:https://www.cnblogs.com/Benoly/p/4045487.html