高企达建设有限公司网站,成都住建局官网官网,网站推广基本方法,百度 网站描述游戏中的对象按照物理规律移动#xff0c;体现重力、引力、反作用力、加速度等物体特性#xff0c;实现自由落体、摇摆运动、抛物线运动#xff0c;以及物理碰撞现象的模拟。用于模拟物理碰撞、物理运动的引擎称为物理引擎。 来自瑞典斯德哥尔摩大学的Stefan Hedman基于Java…游戏中的对象按照物理规律移动体现重力、引力、反作用力、加速度等物体特性实现自由落体、摇摆运动、抛物线运动以及物理碰撞现象的模拟。用于模拟物理碰撞、物理运动的引擎称为物理引擎。 来自瑞典斯德哥尔摩大学的Stefan Hedman基于JavaScript开发了一款面向HTML游戏的2D物理引擎P2物理引擎。P2和Box2D物理引擎一样集成了各种复杂的物理公式和算法帮助实现碰撞、加速、自由落体等物理对象的模拟。 P2是一个开源项目可在GitHub下载使用build中的p2.min.js文件就可以开发物理应用。 1. 创建P2物理项目 使用P2物理引擎创建物理应用的过程和Box2D类型步骤是创建world、创建shape、创建body刚体、实时调用step()函数更新物理模拟计算基于形状、刚体使用Egret或其他HTML渲染以显示物理模拟效果。 1世界worldworld是P2物理引擎入口对应World类用于承载所有物理模拟对象。world类的构造函数为 function World([options]){options?:{gravity?:number[][0,-9.81];}} 其中gravity是重力加速度这是一个Vec2类型的向量对象默认为垂直向上的向量[0,-9.81]。将gravity设置为[0,0]可以取消重力模拟太空失重状态。 2形状Shape形状是物理模拟计算的基础。任何物体都要有对应的形状才可以基于P2进行物理碰撞检测和模拟。所有形状对象都需要通过addShape()添加到刚体中才可以进行碰撞模拟计算 var body:p2.Bodynew p2.Body(); var shape:p2.Shapenew p2.Shape(); body.addShape(shape); P2中的Shape类是一个抽象的父类要使用Box、Circle等子类。 3刚体Body刚体是P2物理引擎的核心概念和对象拥有速度、角度、质量等物理属性同时包含了形状对象使刚体拥有具体的形状。将刚体添加到world中World将以刚体为单位循环遍历进行物理模拟计算并将模拟的结果保存在刚体属性中使刚体成为碰撞对象的原型。所有的刚体都必须通过addBody()添加到P2的world中才会进行物理模拟 var body:p2.Bodynew p2.Body(); var shape:p2.Shapenew p2.Shape(); body.addShape(shape); world.addBody(body); 4贴图EgretP2只是一个算法库以刚体为对象模型模拟并输出物理碰撞、运动结果。这个过程通过持续调用world中的step()方法来实现 function step(dt:number, timeSinceLastCalled?:number0,maxSubSteps?:number10) 其中参数dt是step方法执行的时间间隔单位秒通常取值为游戏帧频的倒数 当游戏帧频降低时计算两帧之间的时间差作为timeSinceLastCalled参数值此时P2会在一次step()中进行count timeSinceLastCalled/dt次计算以保证物理模拟的真实性默认值为0参数maxSubSteps是单次step()进行物理模拟计算的最大次数当timeSinceLastCalled不等于0时单次step()中进行计算的次数count最大为maxSubSteps默认值为10。 但P2本身不具备渲染功能无法显示模拟结果需要借助JavaScript渲染引擎如Egret、Cocos2d-js、Pixi、phaser等通过绘制或贴图来渲染物理模拟结果。在Egret中 class SampleP2APP extends egret.DisplayObjectContainer{ public constructor(){ super(); this.createP2App(); } private world:p2.World; private factor:number30; public createP2App():void{ this.worldnew p2.World(); var worldthis.world; world.gracity[0,0]; var shape:p2.Boxnew p2.Box({width:100/this.factor, height:50/this.factor}); var body:p2.Bodynew p2.Body({mass:1}); body.position[275/this.factor, 100/this.factor]; body.addShape(shape); worldBody(body); this.addEventListener(egret.Event.ENTER_FRAME, this.loop, this); } private loop(e.egret.Event):void{ this.world.step(1/60); } } 代码中创建了一个重力加速度gravity[0,0]的失重环境world然后使用Box形状创建尺寸100x50像素矩形通过addShape()方法将其添加到位于(275,100)位置的刚体body中最后通过world的addBody()方法将刚体添加到世界中完成一个基本的P2物理应用创建。在游戏的loop更新方法中每帧持续调用step()方法实现P2物理模拟计算的持续更新。 P2物理引擎中的坐标单位是米而大部分的游戏引擎在屏幕上渲染游戏时都是基于像素的所以在创建刚体对象、设置形状尺寸时需要将像素转换成米后再进行赋值。游戏中1米通常看作30px所以代码中声明了一个值为30的factor变量在设置形状尺寸或刚体时都是使用像素坐标除以factor变量转换成米之后再赋值的。 2. 用p2DebugDraw实现模拟视图 因为P2只是进行了物理模拟计算没有对模拟结果进行渲染可以基于Egret引擎编写渲染绘图的类如p2DebugDraw。p2DebugDraw类的构造函数为 function p2DebugDraw(world:p2.World, sprite:egret.Sprite) 其中world是P2中创建的world对象sprite是Stage中一个Sprite对象。p2DebugDraw将使用基本绘图的API在sprite对象中绘制所有刚体、关节等物理对象。修改后的代码 class SampleP2APPWithDebugDraw extends egret.DisplayObjectContainer{ public constructor(){ super(); this.createP2App(); } private world:p2.World; private factor:number30; private debugDraw:p2DebugDraw; public createP2App():void{ this.worldnew p2.World(); var worldthis.world; world.gracity[0,0]; var shape:p2.Boxnew p2.Box({width:100/this.factor, height:50/this.factor}); var body:p2.Bodynew p2.Body({mass:1}); body.position[275/this.factor, 100/this.factor]; body.addShape(shape); worldBody(body); var sprite:egret.Spritenew egret.Sprite(); this.addChild(sprite); this.debugDrawnew p2DebugDraw(world, sprite); this.addEventListener(egret.Event.ENTER_FRAME, this.loop, this); } private loop(e.egret.Event):void{ this.world.step(1/60); this.debugDraw.drawDebug(); } } p2DebugDraw仿照Box2d中的b2DebugDraw用不同颜色和形状表示不同类型的刚体、关节等对象粉色为动态刚体、紫色为可动刚体、绿色为静态刚体、黑色线段为关节、红色线段为弹簧、绿色线段为刚体与关节点的偏移量、圆的为关节节点。 p2DebugDraw的属性和方法 p2DebugDraw提供了很多属性和方法 1isDrawAABB:boolean是否绘制刚体的AABB最小包围框默认false 2drawDebug()绘制P2物理引擎中的所有刚体、关节等对象需要实时调用实时更新 3drawShape()绘制P2世界中的任意形状使用时要确保形状对应刚体已添加到world drawShape()方法的结构为 function drawShape(shape:p2.Shape, color?:number, fillColor?:boolean):void 其中shape为要绘制的Shape对象color为要绘制的颜色缺省时根据类型设置颜色fillColor指示是否填充颜色默认true填充色与边框色相同。 4drawConvex()将vertices数组中保存的任意多个顶点坐标逐个使用线段连接起来绘制出多边形。此方法不受P2刚体约束可在任意需要情况下使用。 function drawConvex( vertices:number[][], color:number, alpha:number1, fillColor:boolean true ):void 其中vertices保存了需要绘制的多边形顶点数组color为多边形的边框颜色alpha为多边形填充颜色透明度fillColor为是否填充颜色默认true填充色与边框色相同。 5drawCircle()在指定位置以指定的半径绘制圆形该方法不受P2刚体约束可在任意需要情况下使用。 function drawCircle( pos:number[], radius:number, color:number, alpha:number1, fillColor?:boolean ):void 其中pos为圆形绘制的目标位置radius为圆形半径。 6drawSegment()在两点之间绘制线段结构为 function drawSegment( start:number[], end:number[], color:number):void 其中start为线段起点end为线段终点color为线段颜色。 7drawVecAt()在指定点at处绘制向量v。用来显示出刚体速度结构为 function drawVecAt( v:number[],at:number[], color:number, markStart:booleanfalse ):void 其中v为要绘制的向量at为开始绘制向量的起点color为向量颜色markStart为是否用圆点表示出向量的起点默认false。 8drawVecTo()绘制向量v指向指定点to。 function drawVecTo( v:number[],to:number[], color:number, markStart:booleanfalse ):void 其中v为要绘制的向量to为向量的终点color为绘制的颜色markStart为是否用圆点表示出向量的终点默认false。 3. P2中的形状 形状是物理引擎进行碰撞模拟计算的依据是刚体最基本的属性。P2中使用Shape类来表示形状通过刚体的addShape()方法将形状添加到刚体中之后就可以随着刚体的移动、旋转不断更新并进行碰撞检测了。为刚体添加形状的示例代码为 var shape:p2.Shapenew p2.Shape(); var body:p2.Bodynew p2.Body(); body.addShape(shape); Shape类本身并不参与刚体的创建而是由其几个子类完成一些常见形状的模拟。这些形状包括圆形Circle、矩形Box、胶囊Capsule、粒子Particle、线段Line、平面Plane、海拔形状HightField、多边形Convex等。 1形状游戏中人物形状各种各样但在碰撞检测时出于效率考虑大都被简化为简单的圆形、矩形等形状。P2预置了包括圆形、矩形在内的一些常用的、简单的形状。 ⑴圆形Circle圆形是P2中的基本形状构造函数为 function Circle({options?:{radius:number}) 其中options是包含所有属性的集合对象radius是圆形的半径默认值为1。 ⑵矩形Box矩形通过width和height属性来创建构造函数为 function Box(options:{width?:number, height?:number}) 其中width为矩形宽度默认为1height为矩形高度默认值为1。 ⑶胶囊Capsule可以认为是一种圆角矩形长度length高度2*Radius两端是半径为Radius的半圆形。 function Capsule(options?:{length?:number, radius?:number}) 其中length为胶囊长度radius为胶囊形状两端半圆的半径同时也是胶囊的高度。 ⑷粒子Particle粒子就是微小的颗粒P2物理引擎中的粒子半径和面积为0。粒子参与碰撞检测效果与半径为1px的圆形一样。构造函数简单不需要任何参数 function Particle() ⑸线段Line长度为length的线段看上去与高度为1的Box形状一样但算法上省去对高度的检测。 function Line(options?:{length?:number}) ⑹平面Plane平面沿y轴负方向无限扩展同时在x轴方向的宽度无限像地平面。构造函数为 function Plane() 创建时没有任何参数初始情况下是一个倒置的穹实际应用时一般要通过调整刚体的角度angle使plane平面朝向不同的方向来模拟墙体。示例代码 var shape:p2.Planenew p2.Plane(); var body:p2.Bodynew p2.Body({mass:1, position:[274/this.factor, 200/this.factor]}); body.addShape(shape); body.angleMath.PI/4; this.world.addBody(body); 代码中的平面会随着刚体的角度angle绕坐标点[274,200]顺时针旋转45°。 ⑺海拔形状Heightfield这是一个类似于Plane的形状但不是平的而是一组y坐标组成的高低不平的山形这些山丘之间的间隔都是固定的elementWidth。HeightField形状也是朝y轴负方向无限扩展的水平方向的宽度是elementWidth与山丘数量的乘积。构造函数为 function Heightfield(options?:{heights:number[], minValue?:number,maxValue?:number, elementWidth?:number}) 其中heights是保存每个山丘高度的数组minValue是山丘高度的最小值设置后heights中的小于minValue的值设置为minValuemaxValue为heights中高度的最大值设置此值后heights中大于maxValue的值设置为maxHeightelementWidth为每个山丘之间的间隔默认为1。 定义Heightfield形状就是定义heights属性中y坐标数组同时x坐标以elementWidth为步长逐一增加。目前Heightfield不支持旋转始终朝y轴负方向扩展碰撞检测也不精确边缘位置容易出现穿透现象。 ⑻多边形ConvexConvex是一个多边形形状可以根据一组定义好的顶点坐标创建对应的多边形形状。 function Convex(options?:{vertice?:number[][], axes?:number[][[]}) 其中vertices保存了顶点坐标的数组这是一个二维数组每个元素是由x和y坐标组成的一维数组如vertics[[-1,-1],[1,-1],[1,1],[-1,-1]]axes表示多边形各个边的对称轴同样是一个二维数组且长度应与vertices一致此参数通常可以使用默认值系统根据vertices中的顶点自动计算得出垂直于各个边的法向量。使用时首先要将多边形的顶点坐标保存到数组中然后将其作为vertices参数传递给Convex var points[[-1,-1],[1,-1],[1,1],[-1,-1]]; var shape:p2.Convexnew p2.Convex({vertices:points}); var body:p2.Bodynew p2.Body({mass:1}); body.addShape(shape); this.world.addBody(body); 不推荐使用顶点坐标直接创建多边形因为可能出现凹多边形而P2中的碰撞检测是基于凸多边形的需要将凹多边形分解成多个小的凸多边形还需要重新计算分解后的重心、形状偏移这些计算并不在Convex类中。建议使用Body类的fromPolygon()实现。 2P2中形状碰撞关系P2中形状碰撞不完善一些形状之间无法实现碰撞各个形状之间的碰撞关系见表 Circle Plane Box Convex Particle Line Capsule Heightfield Ray Circle Yes - - - - - - - - Plane Yes - - - - - - - - Box Yes Yes Yes - - - - - - Convex Yes Yes Yes Yes - - - - - Particle Yes Yes Yes Yes - - - - - Line Yes Yes ? ? - - - - - Capsule Yes Yes Yes Yes Yes ? Yes - - Heightfield Yes - Yes Yes ? ? ? - - Ray Yes Yes Yes Yes - Yes Yes Yes - 其中部分是有待完善的。 3形状属性 p2的几种内置形状各有自己的属性但都继承自Shape类所以有共同的属性包含在Shape的构造函数内 function Shape(options?:{ position?:number[], angle?:number, collisionGroup?:number, collisionMask?:number, sensor?:boolean, collisionResponse?:boolean, type?:number, material?:Material}); 其中position是形状相对于本地坐标中心的偏移量这个偏移量会影响刚体的重心angle为形状在刚体本地坐标系统中倾斜的角度collisionGroup为碰撞分组与collisionMask一起使用限制当前形状只与指定条件的形状发生碰撞collisionMask为碰撞筛选与collisionGroup一起使用限制当前形状只与指定条件的形状发生碰撞sensor设置形状是否为敏感区域默认false如果设置为true则该形状不参与碰撞模拟只作为感应区域collisionResponse设置当与其他刚体发生碰撞时当前形状是否进行碰撞模拟默认true如果设为false碰撞发生时当前形状会穿过碰撞刚体type为刚体类型取值范围是Shape. CIRCLE和Shape.BOX等常量之一不需要设置该参数系统会自动设置。 使用collisionGroup和collisionMask属性时有两个形状si和sj对si的collisionGroup属性与sj的collisionMask属性按位与运算再对sj的collisionGroup属性与si的collisionMask属性按位与运算如果两个结果均不为0则忽略此次si与sj的碰撞检测。因为要按位运算所以一般设置成2的倍数。 material为材质是一个Material对象用来设置形状发生碰撞时表现出的响应特性如摩擦力、弹性系数等 实际上Material类中并不包含摩擦力、弹性系数等属性它只是一个标志类只是一个id真正实现碰撞响应特性的是ContactMaterial类。ContactMaterial类用来为添加了merialA和merialB标识的两个形状设置独特的碰撞响应特性构造函数为 function ContectMaterial( materialA:Material, materialB:Material, options?:{ friction:number, restitution:number, stiffness:number, relaxation:number, frictionStuffness:number, frictionRelaxation:number, sufaceVelocity:number}) 其中materialA是形状shapeA的材质标识materialB是形状shapeB的材质标识options是两个形状发生碰撞时响应特性的设置friction是两个形状接触面的摩擦系数默认0.3restitution是两个形状碰撞时的弹性系数默认0stiffness等同于ContactEquation的stiffness属性只碰撞时形状表面的硬度默认1000000当stiffness较小时形状之间可以重叠并有一个排斥力促使分离形成海绵或水平的弹性表面效果relaxation等同于ContactEquation的relaxation指当形状之间重叠后在一次碰撞模拟计算过程中实施排斥力的次数可以想象成粘稠度取值大时刚体速度衰减快sufaceVelocity是两个刚体接触时接触面方向上两个刚体的相对速度如果其中一个刚体静止则sufaceVelocity表示另一个刚体的速度常用来模拟传送带效果。 4形状贴图游戏中物理对象需要对应的游戏图像素材来呈现需要使用图片对刚体形状贴图。贴图的过程实际上是根据物理模拟后刚体的信息实时更新图片素材的坐标和角度。步骤为 ⑴保存刚体与素材图片的对应关系 Egret游戏中的图片素材通常是通过RES对象加载进来需要为刚体添加一个自定义属性用来保存对应素材的引用。示例 body.userDataimage; ⑵遍历world中所有刚体查找拥有自定义素材属性的刚体 world中所有的刚体都保存在bodies数组中通过数组的foreach()方法可以遍历其中的每一个body如果定义的body.userData属性不为null则进行贴图 this.world.bodies.forEach(function(body:p2.Body){ if(body.userData!null){ //更新素材坐标和角度 } } ⑶根据刚体信息实时跟新对应图像素材的坐标和角度 在ENTERFRAME事件处理函数中将刚体坐标和角度属性赋值给素材实时更新》 body.userData.xbody.positon[0]*this.factor; body.userData.ybody.positon[1]*this.factor; body.userData.rotationbody.angle*180/Math.PI; Egret加载的图片原点默认为左上角而刚体的原点处于其重心根据形状不同重心位置也不同。在更新素材图片前需要根据刚体重心的坐标偏移量(offsetX,offsetY)设置图片的anchorOffsetX和anchorOffsetY属性。示例代码 private bindAsset(body:p2.Body, assetName:string):void{ var offset:number[][]; body.updateAABB(); var bodyWidth:numberbody.aabb.upperBoumd[0]-body.aabb.lowerBound[0]; var bodyHeight:numberbody.aabb.upperBoumd[1]-body.aabb.lowerBound[1]; var asset:egret.Bitmapnew egret.Bitmap(); asset.textureRES.getRes(assetName); asset.scaleXbodyWidth*this.factor/asset.width; asset.scaleYbodyHeight*this.factor/asset.height; this.addChild(asset); p2.vec2.subtract(offset, body.position, body.aabb.lowerBound); asset.anchorOffsetXoffset[0]/assets.scaleX*this.factor; asset.anchorOffsetYoffset[1]/assets.scaleY*this.factor; body.userDataasset; } 通过body.updateAABB()更新刚体的aabb属性然后才能获取到正确的upperBoumd和lowerBound数组的值。通过向量的subtract()方法计算刚体坐标position是相对于刚体最小包围盒左上角aabb.lowerBound的偏移量保存在offset变量中。代码中按照缩放比例将offset偏移量赋值asset的anchorOffsetX和anchorOffsetY属性调整图片控制点实现图片与刚体的完全贴合。 4. 刚体属性 P2可以创建多种形状的刚体刚体除了形状外还有各种属性有数十种大概分为4类速度相关、角度相关、对象相关还有其他属性。 1速度相关属性⑴position:number[][0,0]; 表示刚体在全局坐标系的位置是一个二维向量Vec2默认[0,0]。任何物体都需要通过position属性进行精确定位和移动p2在进行物理模拟时会自动更新刚体位置坐标也可以在需要时直接修改其属性值body.position[100,100]; ⑵velocity:number[][0,0]; 表示刚体的线性速度单位像素/秒是一个二维向量。P2使用包含两个元素的数组表示速度第1个元素表示x分量第2个元素表示y分量。 ⑶damping:number0; 表示线性速度阻尼也称速度衰减系数取值在0~1之间默认0。假设刚体当前线速度为v经过一次模拟计算后受阻尼影响刚体线性速度变为v*(1-damping)也就是刚体的线速度会随时间的以1-damping为倍数下降。damping可以用来模拟刚体与地面之间的摩擦力。 ⑷fixedX:booleanfalse; 设定是否固定刚体的x坐标默认false。当设置为true时刚体只能在y轴方向上下移动。 ⑸fixedY:booleanfalse; 设定是否固定刚体的y坐标默认false。当设置为true时刚体只能在x轴方向左右移动。 设定了fixedX及fixedY属性后需要调用updateMassProperties()方法才能使其发挥作用。 ⑹force:number[][0,0]; 表示刚体当前受到的作用力大小是一个二维向量。P2通过刚体的force来模拟外力的作用施加的外力作用于刚体的中心位置不会引起旋转。如果模拟施加一个外力到物体顶部而推倒物体P2通过Body类的applyForce()方法可以自定义外力作用的位置模拟类似旋转效果。 因为world调用step()方法后force属性将被置零如果需要模拟持续施加作用力需要在step()方法前持续设置force属性。 ⑺gravityScale:number1; 设置当前刚体的重力加速度缩放比例默认为1。如果当前重力加速度为gravity[x,y]那么设置gravityScale后重力加速度为[x*gravityScale, y*gravityScale]。 如果设置gravityScale为0当前刚体将不受重力加速度影响若gravityScale设为负值那么刚体将沿重力加速度反方向移动。 2角度相关属性⑴angle:number0; 表示刚体角度为弧度值正值增大为顺时针旋转。 ⑵angularVelocity:number0; 表示刚体角速度即旋转速度默认为0。如果angularVelocity0刚体顺时针旋转如果angularVelocity0刚体逆时针旋转。angularVelocity单位是弧度/秒因为P2更新step()方法是每帧调用需要转换成弧度/帧。假设游戏帧频为fps角速度为a1转换后的每帧弧度为a2a1/fps。 ⑶angularDamping:number0.1; 表示刚体角速度阻尼取值范围0~1默认0.1。假设当前角速度为rP2进行一次模拟计算后受阻尼影响刚体角速度将变为r*(1-damping)。 ⑷angularForce:number0; 表示刚体在角速度方向上受到扭力大小单位N默认0。angularForce作用的结果是使刚体旋转取值越大刚体角速度变化越大。如果angularForce0扭力会促使刚体顺时针旋转如果angularForce0扭力使刚体逆时针旋转。实际上在刚体上施加angularForce扭力后形成一个旋转加速度a单位弧度/平方秒如果刚体质量为m它们之间的关系为 aangularForce/m 也即刚体加速度以每秒弧度为a的变化量递增。如果使用角度/帧为单位公式相应转换为 adt*(angularForce/m)*(180/Math.PI) ⑸fixedRotation:booleanfalse; 设置是否固定刚体角度默认false。当fixedRotation为true时刚体角度不会因碰撞或运动而发生变化比如保持人一直处于垂直站立状态。设置fixedRotation属性后也需要调用updateMassProperties()方法才能起作用。 3对象相关属性⑴shapes;Shape[]; 表示刚体中包含的所有形状均保存在这个shapes数组中。通过遍历数组可以对刚体中的每个形状单独进行操作。如果要计算刚体整体形状的面积就需要遍历shapes属性中的所有形状通过area属性将这些形状的面积累加起来得到整体形状的面积代码为 var totalArea0; for(var i0; ithis.shapes.length;i){ titalAreathis.shapes[i].area; } 这个功能实际上已经集成到了Body类的getArea()方法中可以直接调用这个方法。 ⑵world:World; 表示刚体当前所在的world。通常情况下一个P2应用中只有一个world世界但需要时也可以创建多个world。无论是一个还是多个world都可以通过world属性获取刚体所在的world然后进行addBody()、Raycast()等操作。 4其他属性⑴id:number; 表示刚体的唯一标识。P2中的每一个刚体都是唯一的在碰撞检测等过程中可以通过id属性来对指定的刚体进行识别。 ⑵type:number; 刚体类型。P2中可用的刚体类型有3种静态刚体、可动刚体、动态刚体默认为静态刚体。type的属性值为3个常量 ·静态刚体Body.STATIC静态刚体在world中始终保持静止不受重力影响其坐标、角度不会因为碰撞而发生变化一般使用绿色表示静态刚体。 ·可动刚体Body.KINEMATIC这种刚体是可动的但不受重力影响其坐标、角度不会因为碰撞而发生变化。所谓可动指可通过设置刚体的velocity或angularVelocity等属性使其动起来常使用紫色表示。 ·动态刚体Body.DYNAMIC是最常用的类型在重力作用下可进行自由落体运动通过velocity、angularVelocity、force等属性可使其动起来当碰撞发生时速度和角度相应发生变化进行物理碰撞模拟常常使用粉色表示。 ⑶mass:number; 表示刚体的质量。P2中的刚体没有密度density属性mass充当了密度角色默认为0此时刚体为静态刚体。当mass0刚体类型自动转换为动态刚体数值越大惯性越大。 ⑷ccdIterations:number10; 当刚体高速运动时P2会在一次step()中进行连续多次碰撞检测ccdIterations即表示这个检测次数默认为10。ccdIterations越大碰撞模拟越精确但计算效率也降低。 ⑸ccdSpeedThreshold:number-1; 只CCD碰撞检测的最低速度即速度超过ccdSpeedThreshold门限时P2将对刚体进行连续碰撞检测默认为-1此时不进行连续碰撞检测。 ⑹collisionResponse:booleantrue; 指定当与其他刚体发生碰撞时当前刚体是否会进行碰撞模拟默认为true。如果设为false则碰撞检测发生时当前刚体不进行碰撞模拟会穿过刚体但此时仍会触发碰撞事件并产生ContactEquation。 ⑺allowSleep:booleantrue; 指定是否允许刚体进入睡眠状态默认true。正常情况下P2会遍历world中的每个刚体对其进行纹理模拟计算。当刚体的速度为0处于静止状态时刚体会进入睡眠状态P2将不再对其进行物理模拟计算以提升效率。这个属性起作用world的allowSleeping World.BODY_SLEEPING。 ⑻sleepSpeedLimit:number0.2; 为刚体进入睡眠状态时的最小速度默认0.2即当刚体速度小于sleepSpeedLimit时刚体进入睡眠状态。其前提是刚体的allowSleep为true。 ⑼sleepState:numberBody.AWAKE; 为刚体当前睡眠状态默认为唤醒状态。刚体睡眠状态有3种 ·Body.AWAKE刚体处于唤醒状态P2对刚体进行正常的物理模拟计算更新其属性 ·Body.SLEEPY当刚体的速度为sleepSpeedLimit刚体进入SLEEPY瞌睡状态该状态下P2也对刚体进行物理模拟计算 ·Body.SLEEPING当刚体进入SLEEPY状态超时时间sleepTimeLimit才进入SLEEPING睡眠状态。此时P2在遍历所有刚体将通过当前刚体不对其进行物理模拟计算。 ⑽sleepimeLimit:number1; 为刚体从瞌睡Body.SLEEPY状态进入睡眠状态Body.SLEEPING状态需要的时间默认为1秒。 ⑾idleTime:number; 刚体已经进入睡眠Body.SLEEPING状态的时长。 5. 刚体操作 1addBody和removeBodyWorld类中的addBody()和removeBody()分别用来上P2世界添加和删除刚体。所有创建好的刚体必须通过addBody()添加到P2世界中才可以进行碰撞模拟 var body:p2.Body({mass:1, position:[1,1]}); this.world.addBody(body); 当物体被子弹击中或超出屏幕范围时需要删除刚体可以通过removeBody()将其从P2世界中删除同时还可以避免不必要的计算。示例代码 this.world.removeBody(bodyToRemove); 2addShape和removeShapeBody类中的addShape()和removeShape()分别用来向刚体中添加和删除形状。使用addShape() var shape:p2.Rectanglenew p2.Rectangle(100,50); var body:p2.Bodynew p2.Body({mass:1, position:[1,1]); body.addShape(shape); 其实addShape()中还有其他参数构造函数为 function addShape(shape:p2.Shape, offset:number, angle:number) 其中参数除了被添加的形状shape还有两个参数offset为形状相对于坐标原点的偏移量angle为形状的角度这两个参数都是相对于刚体本地坐标系而言即会随刚体的变化而变化。 可以通过removeShape()方法将指定的形状从刚体中删除。 形状的添加或删除并不会改变刚体的重心坐标因此可用来模拟不倒翁效果。 3adjustCenterOfMass调整重心位置。刚体中增加或移去形状后重心并不会自动改变可以使用adjustCenterOf Mass()方法使刚体重心重新回到中心位置。这个方法不带任何参数也没有返回值。 4applyForce作用力可以让刚体状态改变通过applyForce()方法可以在指定点worldPoint对刚体施加一个作用力形成一个加速度或扭力改变刚体的线速度或角速度。构造函数为 function applyForce(force:number[], worldPoint:number[]) 其中force是要施加的作用力这是一个二维向量 worldPoint是一个全局坐标点表示force在刚体上的作用点当此点不在刚体重心位置时刚体角度也会发生变换。 5applyImpulse如果要瞬间改变刚体的状态如子弹弹出膛效果需要使用applyImpulse()对其施加冲量使刚体的速度和角速度瞬间发生变化。 function applyImpulse(impulse:number[], relativePoint:number[]) 其中impulse为要施加的冲量冲量有大小和方向是一个二维向量 relativePoint是一个本地坐标点表示冲量的作用点当作用点不在刚体中心时刚体角度也会发生变化。 6sleep和wakeup Body的sleep()方法强制使刚体进入睡眠状态此时除了sleepState处于Body.SLEEPING状态外刚体的速度、角速度、受到的作用力、扭力等都会全部清零。 function sleep(){ this.sleepStateBody.SLEEPING; this.angularVelocity0; this.angularForce0; vec2.set(this.velocity, 0, 0); vec2.set(this.force, 0, 0); this.emit(Body.sleepEvent); }; 刚体进入睡眠状态后通过调用wakeup()方法可以将刚体强制唤醒但仅仅是将sleepState设置为Body.AWAKE睡眠状态前的速度、角度等属性不会恢复 function wakeup(){ var sthis.sleepState; this.sleepStateBody.AWAKE; this.idleTime0; if(s!Body.AWAKE){ this.emit(Body.wakeUpEvent); } } 可以使用sleep()方法实现类似冰冻效果。 7emit、on、off、hasP2物理引擎中通过EventEmitter类实现事件派发机制并通过emit()、on()、off()、has()方法分别实现派发、监听、取消监听以及检测是否包含指定事件功能。 ⑴emit()派发自定义事件事件类型使用任意字符串表示 function emit(event:Object) 其中event为要派发的自定义事件是一个Object类型用于包含任何需要通过事件传递的信息。在自定义事件时event对象至少要包含名为type的字符串属性值表示事件名称 body.emit({type:movingUp}); ⑵on()监听对应的自定义函数 监听到事件对象将作为参数传递给事件监听函数示例代码为 var onMyEventfunction(event){ console.log(onMyEvent is fired, because I am moving up.); }; body.on(movingUp, onMyEvent); on()方法的参数是字符串类型的事件名称并在事件触发时调用一个处理函数。 ⑶off()取消监听 当不需要监听某个事件时将事件名称和事件监听函数作为参数传递给off()方法就可以取消监听。示例代码 body.off(movingUp, onMyEvent); ⑷has()检查对象是否包含指定的事件监听 Body类因为继承了EventEmitter类所以也拥有上面4个方法。通过这些方法可以方便地对刚体的某些特定状态进行监听如睡眠或苏醒。Body类内置了睡眠和苏醒事件 ·sleepEvent当刚体进入睡眠状态时派发该事件对应事件名称为sleep监听使用body. on(sleep, onMyEvent); ·wakeupEvent当刚体从睡眠状态恢复至苏醒状态时派发wakeupEvent事件事件名为wake监听使用body.on(wake, onMyEvent); ·sleepyEvent当刚体进入瞌睡状态时派发该事件。Body有一个sleepSpeedLimit属性当刚体的速度小于sleepSpeedLimit值时就进入瞌睡状态如果该状态持续时间超过body. sleepTimeLimit则刚体进入睡眠状态。sleepyEvent对应的事件名为sleepy监听使用body. on(sleepy, onMyEvent); 除了上述内置事件还可以自定义事件比如在step()方法中持续判断body.velocity.y是否小于0来检测刚体向上运动派发自定义moveUp事件 public loop():void{ this.world.step(60/1000); this.debugDraw.drawDebug(); if(this.bodyRef.velocity[1]0){ this.bodyRef.emit({type:myEvent}); } } 8fromPolygonfromPolygon用于将多边形分解成一个个小的形状然后组合成完整的多边形。 function fromPolygon(path:number[][], [options]):boolean 其中path保存了多边形顶点数组options为可选属性定义多边形分解的相关设置。 options包括的选项有 ·optimalDecompfalse是否进行最佳分解默认false开启该选项会降低计算速度 ·skipSimpleCheckfalse是否进行顶点交叉的判断如果确定不存在交叉点可设为true ·removeCollinearPointsfalse是否剔除共线顶点false表示不剔除 如果多边形创建成功返回true否则返回false。导致创建失败有几种原因比如创建的形状中有空洞、顶点和边之间有交叉。可以编程将鼠标光标移动的轨迹中的点作为多边形的顶点。也就是随手创建刚体。 9hitTest用于实现指定坐标点与刚体的碰撞检测并将检测到的碰撞刚体保存到数组中返回。 function hitTest(worldPoint:number[][], bodies:Body[], precision:number):Body[] 其中worldPoint为要检测的坐标点是一个全局坐标点bodies为要检测的刚体清单可以将需要检测的刚体保存到bodies数组中实现有针对性的碰撞检测。如果要对所有的刚体进行检测可以直接设置该参数为world.bodies即hitTest(worldPoint, world.bodies); 参数precision为检测精度默认为0取值越大检测精度越高计算效率会降低一般使用默认值对尺寸极小的物体如particle和line需要设置。方法的返回值为保存了与worldPoint发生碰撞所有刚体的数组。 hitTest()最常用用于检测鼠标光标点击、拖曳效果。P2中没有鼠标光标事件可以通过hitTest()来判断鼠标点是否与刚体发生碰撞然后调整刚体的position至鼠标位置实现对刚体的拖曳。为了便于拖曳在鼠标点击时通过sleep()方法将刚体设为睡眠状态使其不受重力影响可以精确地随鼠标光标移动当拖曳完成弹起时再使用wakeUp()方法重新唤醒刚体恢复重力的作用。 10getAABB获取刚体的最小包围盒AABB(Axis Align Bounding Box)最小包围盒AABB是指包围形状的最小矩形框。可以通过设置刚体的isDrawAABBtrue以显示出刚体的AABB。AABB对象将矩形对象的左下角和右下角坐标分别保存在属性lowerBound和upperBound中。 11getArea 获取刚体的当前所有形状的面积总和。 12setDensity设置刚体的密度结构为 function setDensity(density:number) 其中density为要设置的密度是取值大于0的数值。 实际上P2中的刚体并没有密度属性当调用setDensity()方法后Body会根据刚体当前的面积area计算出对应的质量并保存在mass属性中。 13overlaps检测当前刚体与指定刚体是否有重叠并返回检测结果结构为 function overlaps(body:Body):boolean 其中body为要检测的目标刚体。如果刚体之间有重叠则返回true否则返回false。 目标刚体与被检测刚体需添加到world中才能确保overlaps()的正常运行。通常并不需要使用overlaps()进行重叠检测因为P2在实施碰撞检测和模拟时已经完成了这些内容只有当刚体不进行碰撞模拟时才需要使用overlaps()。比如在界面中放置一个物体当此位置已经存在物体时不能放置。 14toWorldFrame和toLocalFrametoWorldFrame()是将刚体本地坐标系统中的坐标点转换成全局坐标点toLocalFrame()是将全局坐标点转换成刚体本地坐标系统中的坐标点。通过这两个方法可以实现本地坐标和全局坐标之间的转换。 function toWorldFrame(out:number[], localPoint:number[]):void function toLocalFrame(out:number[],worldPoint:number[]):void 其中out为转换后的本地坐标或全局坐标将保存在该属性对应的变量中 localPoint为要转换的本地坐标worldPoint为要转换的全局坐标。 15raycast射线投射技术用于实现线段与形状的碰撞检测通常用来模拟人物的视野、距离探测等。实现射线投射要先从起点from到终点to构建一条射线ray然后检测与该线段发生碰撞的刚体并保存在一个RaycastResult对象中。raycast结构为 function raycast(result:RaycastResult, ray:Ray) 其中result是保存了碰撞刚体、碰撞点、碰撞距离等信息的一个RaycastResult对象ray是用于检测的射线是一个Ray类包含起点from、终点to等属性。使用射线投射需要用到Ray类和RaycastResult类。 ①Ray类Ray类的构造函数为 function Ray([options]) 其中options参数是一个Object对象初始化Ray对象时可以保持options缺省然后再通过Ray属性进行设置。Options对象中包含一些参数 ·from:number[]线段的起点一个二维向量 ·to:number[]线段的终点一个二维向量 ·mode:number射线的碰撞检测模式取值为3个常量分别为Ray.ALL、Ray.CLOSEST、Ray.ANY。Ray.ALL模式下raycast()函数会检测所有与射线ray发生碰撞的刚体Ray.CLOSEST模式下返回检测到的碰撞刚体中距离起点from最近的刚体Ray.ANY模式下当ray检测到第1个碰撞刚体时会立刻停止检测并返回该刚体这时的第1个刚体与创建时的顺序有关。 ·callback:Function回调函数当raycast()检测到碰撞刚体后会立即调用回调函数该属性只适用于rayCastAll() ·collisionMask:number与collisionGroup配合使用对检测刚体进行筛选 ·collisionGroup:number与collisionMask配合使用对检测刚体进行筛选只有满足条件的刚体才参与碰撞检测检测条件为 (this.collisionGroupbody.collisionMask)(body.collisionGroupthis.collisionMask)true ·skipBackface是否忽略射线ray反方向与刚体的碰撞点值为false时只检测正方向 ·checkCollisionResponse配合Body中的collisionResponse属性如果刚体的属性collisionResponse和checkCollisionResponse同时为true则不对该刚体进行检测。 ·direction射线方向可以缺省取决于from和to属性 ·length射线从起点from到终点to的间距 ②RaycastResult类RaycastResult类用于保存射线与刚体的碰撞信息包括几个属性 ·body当前碰撞检测中与射线ray发生碰撞的刚体 ·shape当前碰撞刚体中与射线ray发生重叠的形状 ·fraction射线起点from到碰撞点之间的距离distance与射线长度length的比例 ·normal垂直于射线碰撞边的法向量只读属性默认-1 RaycastResult类还有一些方法 ·hasHit()是否检测到与射线ray发生碰撞的刚体当检测到时返回true ·getHitDistance()当检测到与射线发生碰撞的刚体时碰撞点与射线起点from间的距离 function getHitDistance(ray:Ray):number ·getHitPoint()获取碰撞点位置返回结果是一个全局坐标点 此方法的结构为function getHitPoint(out:number[], ray:Ray):void 其中out为用于保存碰撞点坐标的二维向量ray为当前进行碰撞检测的射线。 ·stop()用于立即停止碰撞检测 因为raycast()方法中的RaycastResult会重复使用所以回调函数中获取到的是最后一次碰撞检测信息。 6. 碰撞处理 P2可以实现物体碰撞模拟同时在碰撞过程中派发一些事件实现碰撞检测将碰撞信息及时反馈以添加相应的特效。 P2中当两个刚体的最小包围盒AABB发生重叠碰撞就开始了然后刚体的形状发生重叠同时P2会对重叠进行修复使刚体朝对方的反方向移动来消除形状重叠当形状不再有重叠时整个碰撞过程结束。可以把碰撞过程分为4个阶段 ·postBroadphaseAABB开始发生重叠但形状并没有发生接触 ·beginContact刚体形状开始发生重叠刚体继续保持原有速度移动 ·preSolve刚体形状发生了重叠但P2还未进行碰撞处理 ·endContactP2已经完成了碰撞处理并为碰撞刚体重新分配了速度同时刚体形状分离不再有重叠 1碰撞事件碰撞过程会产生4个事件postBroadphase、preSolve、beginContact、endContact。在碰撞事件派发后可以通过world类的on方法来监听事件并在监听处理函数中进行对应的处理。on方法的结构为 function on(type:String, listener:functtion) 其中type为监听事件名为一个字符串取值为对应的事件名称listener为事件监听函数当监听到碰撞事件后会自动调用监听函数并将碰撞事件对象作为参数传递给监听处理函数。碰撞监听函数的参数event是一个Object对象对应碰撞事件对象其中保存碰撞信息。 在on事件处理函数中this指的是派发事件的对象context也就是world对象而不再是主类中的this。因此需要在on函数之前将this指针保存到一个局部变量中然后才能在事件处理函数中通过这个局部变量访问主类中的变量和方法。 on()方法监听碰撞事件时会将对应的碰撞事件对象作为参数传递给监听处理函数这些碰撞事件对象为postBroadphaseEvent、preSolveEvent、beginContactEvent、endContactEvent。 ①postBroadphaseEvent当两个刚体的AABB发生重叠时不断派发postBroadphase事件并将碰撞信息保存到对应的postBroadphaseEvent对象中该对象包含属性pairs即保存碰撞刚体对的数组。因为尚未进行刚体形状的碰撞检测所以此时对pairs数组中的碰撞进行删减可以取消对应碰撞刚体之间的碰撞模拟。但删除pairs后将不再进行beginContact和endContact事件派发。 ②preSolveEvent在刚体形状发生重叠时P2会根据动量守恒定律对碰撞对象的速度和角度重新计算实现碰撞模拟这个过程称为solve。preSolve事件在碰撞模拟过程前派发在preSolve事件处理函数中进行一些处理可以取消或干预碰撞的模拟。 preSolveEvent对象的属性有 ·contactEquations保存当前碰撞产生的所有contactEquation对象的数组 ·frictionEquations保存当前碰撞产生的所有frictionEquation对象的数组 preSolve事件会随step()方法不断派发在刚体形状之间没有重叠前contactEquations属性中并不包括contactEquation对象所以代码中要先进行判断contactEquations.length0。 ③beginContactEvent当刚体形状发生重叠时会派发beginContact事件并将碰撞信息保存在对应的beginContact Event对象中其中属性有 ·shapeA发生碰撞的形状A ·shapeB发生碰撞的形状B ·bodyA形状shapeA对应的刚体 ·bodyB形状shapeB对应的刚体 ·contactEquations保存当前碰撞产生的所有contactEquation对象的数组 ④endContactEvent当前两个碰撞刚体的形状分离而不再重叠时会派发endContact事件并将碰撞信息保存在对应的endContactEvent对象中包含的属性有 ·shapeA发生碰撞的形状A ·shapeB发生碰撞的形状B ·bodyA形状shapeA对应的刚体 ·bodyB形状shapeB对应的刚体 2碰撞信息Equation在P2引擎中Equation类用来保存碰撞发生时的碰撞点、碰撞向量等信息。一般用其两个子类ContactEquation和FrictionEquation来保存不同类型的信息。 ①ContactEquation碰撞过程中因接触而产生的碰撞信息如碰撞点、碰撞向量、碰撞刚体等都保存在ContactEquation对象中。在preSolve和beginContact阶段会产生多个ContactEquation对象并分别保存在preSolveEvent和beginContactEvent事件对象的ContactEquation属性中。 ·shapeA发生碰撞的形状A ·shapeB发生碰撞的形状B ·bodyA形状shapeA对应的刚体 ·bodyB形状shapeB对应的刚体 ·contactPointA自bodyA的坐标起到碰撞点的全局向量 ·contactPointB自bodyBA的坐标起到碰撞点的全局向量 ·enabled是否对当前的ContactEquation对象进行碰撞模拟默认true如果在preSolve阶段将其设为false可以取消碰撞模拟 ·firstImpact是否为第1次碰撞当第1次碰撞step()完成后firstImpact立即设为false ·normalA垂直于刚体碰撞边的法向量这是一个全局的单位向量 ·restitution碰撞刚体之间的碰撞弹性系数取值0~1。该系数只影响当前碰撞的模拟如果对shapeA和shapeB设置contactMaterial其restitution属性不受影响。 ②FrictionEquation碰撞过程中因刚体相对运动而产生的摩擦等碰撞信息保存在FrictionEquation对象中。在碰撞的preSolve阶段会产生FrictionEquation对象并保存在preSolveEvent事件对象的FrictionEquation属性中。FrictionEquation对象包含的属性有 ·shapeA发生碰撞的形状A ·shapeB发生碰撞的形状B ·bodyA形状shapeA对应的刚体 ·bodyB形状shapeB对应的刚体 ·frictionCoefficient碰撞时刚体之间的碰撞系数取值越大速度衰减越快只影响当前碰撞模拟。如果有对shapeA和shapeB设置contactMaterial其friction属性不受影响。 ·t碰撞边的切线方向向量 需要注意frictionCoefficient属性需要在world.solver.frictionIterations0情况下才起作用frictionIterations默认是0需要在创建world时设置其值。 7. 关节 P2中使用Constraint及其子类表示关节也就是将两个刚体按照指定的规则约束在一起形成有规律的、相互限制的运动模拟。P2关节模拟中两个刚体没有通过任何刚体连接只是通过算法模拟出关节运动轨迹。为了更加直观p2DebugDraw类中使用黑色的线段表示连接刚体的连杆黑点圆的表示关节节点anchor。 P2中关节有5种每一种都有独特的约束规则包括距离关节DistanceConstraint、齿轮关节GearConstraint、锁定关节LockConstaint、位移关节PrismaticConstraint、旋转关节Revilute Constraint。 1距离关节DistanceConstraint按照指定的距离distance将两个刚体约束在一起其中任何一个刚体的位置发生变化会牵着另一个刚体运动以保证两者的间距为distance。但是两个刚体的角度不受约束可以绕着节点旋转。DistanceConstraint构造函数为 function DistanceConstraint(bodyA:Body, bodyB:Body, options:object) 其中bodyA和bodyB为受约束的刚体options为关节设置选项可以缺省P2以默认值设置其中选项为 ·distance两个刚体受到约束时保持的间距默认为添加关节时两个刚体之间的间距 ·localAnchorA关节点相对于刚体bodyA本地坐标系统的坐标系默认[0,0] ·localAnchorB关节点相对于刚体bodyB本地坐标系统的坐标系默认[0,0] ·maxForce刚体运动中如果距离不等于distance为保持距离而对刚体施加的最大作用力默认为Number.MAX_VALUE 除了上面的构造函数中的参数距离关节还包含一些属性 ·lowerLimit设置距离关节约束范围下限即bodyA到bodyB的距离最小值默认为0该属性必须大于0。只有当lowerLimitEnabled为true时才起作用。 ·lowerLimitEnabled是否设置距离关节约束范围下限默认false。 ·upperLimit设置距离关节约束范围上限即bodyA到bodyB的距离最大值默认为0该属性必须大于0。只有当upperLimitEnabled为true时才起作用。 ·upperLimitEnabled是否设置距离关节约束范围上限默认false ·positionbodyA和bodyB的当前间距 可以通过joint.collideConnected属性为true避免平台和车轮之间的碰撞。创建完成后需要使用world的addConstraint(joint)方法将关节加入世界。 2齿轮关节GearConstraint按照指定的比例ratio将两个刚体的角度angleA和angleB约束为angleangleB*ratio。其中任何一个刚体的角度变换都会牵着另一个刚体的角度变化以确保两个刚体角度的比例为ratio。刚体的坐标位置不受约束可以自由向任意方向移动。构造函数为 function GearConstraint(bodyA:body, bodyB:Body, options:Object) 其中bodyA和bodyB为受关节约束的两个刚体options为关节设置选项可以缺省P2会按默认值进行设置。选项为 ·angle两个刚体的相对角度差齿轮关节会将一个刚体的角度减去该角度差后再保证角度变化量的比例为ratio ·ratio两个刚体的角度变化量的比例当ratio2时bodyB旋转180°bodyA只转90° ·maxForce当两个刚体的角度比例不是ratio时为将其约束为ratio而对刚体施加的最大扭力 齿轮关节还有两种方法 ·setMaxForce(force)当bodyB的角度偏离angle齿轮关节对bodyB施加的最大扭力 ·getMaxForce():number获取setMaxForce()中设置的最大作用力 3锁定关节LockConstraint将两个刚体绑定在一起使其相对坐标位置、角度差保存不变仿佛被钉在一起。此关节中的任何刚体坐标或角度发生变化都会牵着另一个刚体的坐标和角度变化以确保两个刚体相对坐标和角度分别为localOffsetB和localAngleB。构造函数 function LockConstraint(bodyA:Body, bodyB:body, options:Object) 其中bodyA和bodyB为受关节约束的两个刚体options为关节设置选项可以缺省P2会按默认值进行设置。选项为 ·localOffsetB刚体bodyB在关节约束下相对于bodyA本地坐标系的偏移量默认为添加关节时两个刚体的相对位置 ·localAngleB刚体bodyB在关节约束下相对于bodyA本地坐标系统的角度默认时为添加关节时两个刚体的相对角度 ·maxForce当两个刚体未达到关节约束的localOffsetB和localAngleB为使其达到约束指定状态而可以施加的最大作用力默认Number.MAX_VALUE LockConstraint还包含几个方法 ·setMaxForce(force)当bodyB的位置偏离localOffsetB或角度差不等于localAngleB时锁定关节对bodyB施加的最大作用力。 ·getMaxForce():number获取setMaxForce()中设置的最大作用力 4位移关节PrismaticConstraint将刚体bodyB的运动方向限定为在刚体bodyA本地坐标系统中的一个指定向量。构造函数为 function PrismaticContraint(bodyA:Body, bodyB:Body, options:Object) 其中bodyA和bodyB为受约束的刚体options为关节设置选项可以缺省P2以默认值设置其中选项为 ·maxForce当bodyB相对于bodyA的位置偏离localAxisA时为使其恢复到约束位置可以施加的最大作用力默认为Number.MAX_VALUE ·localAnchorA控制点anchorA在bodyA本地坐标系下的坐标默认[0,0] ·localAnchorB控制点anchorB在bodyB本地坐标系下的坐标默认[1,0] ·localAxisA刚体受到约束时只可以在该坐标轴方向上移动这是刚体bodyA坐标系下的一个向量默认[1,0] ·disableRotationalLock是否禁止bodyB绕节点旋转默认false即bodyB不能自由旋转此值只有在构造函数中设置才起作用。 ·upperLimitEnabled是否开启bodyB移动方向上限默认false此时可以沿localAxisA正方向无限移动 ·upperLimit设置bodyB沿localAxisA正方向可以移动的最大距离默认为1 ·lowerLimitEnabled是否开启bodyB移动方向下限默认false此时可以沿localAxisA负方向无限移动 ·lowerLimit设置bodyB沿localAxisA负方向可以移动的最大距离默认为0该属性值要小于upperLimit 除了上述在构造函数中的参数PrismaticConstraint还有其他一些属性 ·motorEnabled是否开启马达属性与motorSpeed配合使用。开启后关节会对bodyB施加作用力使其线速度达到motorSpeed并在约束范围内一直保持该速度。开启或关闭马达属性要用enableMotor()和disableMotor()方法。 ·motorSpeed开启马达属性后bodyB的目标速度值 ·position在localAxisA上bodyB相对于bodyA的当前位置 PrismaticConstraint还有一些方法用于调整关节的相关属性 ·setLimits(lower, upper)设置位移关节的上下限其中lower一定要小于upper ·disableMotor()关闭马达属性 ·enableMotor()开启马达属性 可以创建一个空刚体来固定关节。所谓空刚体就是没有包含任何形状对象的刚体所以不会与任何刚体发生碰撞模拟。 5旋转关节RevoluteConstraint限制两个刚体只能绕指定的控制点旋转该控制点是刚体bodyA本地坐标系下的坐标。其中一个刚体的位置或角度发生变化时为了确保控制点和刚体的相对位置不变另一个刚体也会被牵制发生位置和角度的变化。构造函数为 function RevoluteConstraint(bodyA:Body, bodyB:Body, options:Object) 其中bodyA和bodyB为受关节约束的两个刚体options为关节设置选项可以缺省P2会按默认值进行设置。选项为 ·worldPivot全局坐标系下的关节节点bodyA和bodyB均受约束只能绕该节点旋转。设置该节点后旋转关节会自动计算localPivotA和localPivotB本地节点。 ·localPivotA节点worldPivot在bodyA刚体本地坐标系统下的坐标默认[0,0] ·localPivotB节点worldPivot在bodyB刚体本地坐标系统下的坐标默认[0,0] ·maxForce当刚体坐标偏离节点时为使其恢复到节点位置可以施加的最大作用力默认为Number.MAX_VALUE RevoluteConstraint还包含几个方法 ·setLimits(lower:number, upper:number)设置bodyB绕节点旋转角度的上下限值为弧度 ·enableMotor()开启马达属性与setMotorSpeed()配合使用关节会对bodyB施加扭力使其达到setMotorSpeed() ·disableMotor()关闭马达属性 ·setMotorSpeed(speed)设置bodyB的目标角速度只有开启马达属性后才其作用。 ·getMotorSpeed():number读取马达的当前速度 旋转关节常用于模拟小车运动。 8. 弹簧Spring P2中用来约束刚体运动的还有弹簧Spring。弹簧除约束两个刚体之间的运动轨迹外通过damping阻尼和stiffness刚度系数等属性使得刚体在向目标移动时出现类似弹簧的简谐运动。Spring只是抽象的父类参与运动模拟的是两个子类LinearSpring和RotationalSpring。 1LinearSpringLinearSpring是线性弹簧对刚体的约束行为和距离关节DistanceConstraint相同按照指定的距离restLength将两个刚体约束在一起其中任何一个刚体的位置发生变化会牵制着另一个刚体运动以保证两者的间距为distance。在运动过程中刚体bodyB呈现简谐运动。两个刚体的角度不受约束可以绕节点旋转。构造函数 function LinearSpring(bodyA:Body, bodyB:Body, options:Object) 其中bodyA和bodyB为受弹簧约束的两个刚体options为关节设置选项可以缺省P2会按默认值进行设置。选项为 ·stiffness弹簧的刚度系数默认100。 ·damping弹簧做简谐运动过程中的阻尼系数默认1 ·restLength弹簧不受力状态下的长度默认为worldAnchorA和worldAnchorB间的距离 ·localAnchorA刚体bodyA本地坐标系下的节点坐标默认[0,0] ·localAnchorB刚体bodyB本地坐标系下的节点坐标默认[0,0] ·worldAnchorA弹簧节点在全局坐标系下的坐标设置后将自动转换并覆盖localAnchorA ·worldAnchorB弹簧节点在全局坐标系下的坐标设置后将自动转换并覆盖localAnchorB 2RotationalSpringRotationalSpring是扭力弹簧对刚体的约束类似齿轮关节按照指定的restAngle约束两个刚体之间的角度差。当刚体的角度不等于restAngle时bodyB会进行简谐运动旋转直至角度差恢复至restAngle。两个刚体的坐标位置不受约束可以自由移动。 function LinearSpring(bodyA:Bodt, bodyB:Body, options:Object) 其中bodyA和bodyB为受弹簧约束的两个刚体options为关节设置选项可以缺省P2会按默认值进行设置。选项为 ·restAngle弹簧不受力无简谐运动下刚体bodyA和bodyB间的角度差默认为创建扭力弹簧时两个刚体之间的角度差 ·stiffness弹簧的刚度系数默认100。 ·damping弹簧做简谐运动过程中的阻尼系数默认1转载于:https://www.cnblogs.com/jacksplwxy/p/9763769.html