physijs文档

2,473 阅读12分钟

基础设置

开发Physijs的主要目标是使其尽可能简单和用户友好。物理引擎可能令人生畏,难以设置,有如此多的选项和配置,很容易让人不知所措。Physijs将所有额外的逻辑抽象出来,这样您就可以专注于项目的其余部分。

它是一个插件,一个非常友好的WebGL框架。因此,您需要使用three.js进行场景管理才能使用Physijs。如果你不熟悉three.js,那么花几天时间学习它然后回来;如果您了解three.js的基础知识,那么您也已经了解了Physijs的基础知识。

如何将Physijs添加到项目中?只需六个简单步骤:

  • <script>标记添加到页面并将其指向physi.js文件。
  • 配置Physijs.scripts.workerPhysijs.scripts.ammoJavascript参数以指向Web worker Ammo.js脚本
  • 用来Physijs.Scene代替THREE.Scene
  • 相反的THREE.Mesh,选择最好的物理网等Physijs.BoxMeshPhysijs.SphereMeshPhysijs.ConvexMesh
  • scene.simulate在渲染时或者想要迭代物理设置时调用方法。

下面您将看到一个非常基本的场景,导致立方体从视图中掉落。总而言之,它只需要4行新代码和2条修改后的行来为一个物理提供一个three.js场景。

查看,了解Physijs可以执行的操作。

注意因为Physijs在主应用程序的一个单独的线程中运行,所以scene.simulate在以前的模拟调用运行时完全可以调用它。当发生这种情况时,scene.simulate将返回false


<!DOCTYPE html>
<html>

<head>
	<script type="text/javascript" src="/js/Three.js"></script>
	<script type="text/javascript" src="/js/physi.js"></script>
	
	
</head>

<body>
	<div id="viewport"></div>

<script type="text/javascript">
	
	'use strict';
	
	Physijs.scripts.worker = '/js/physijs_worker.js';
	Physijs.scripts.ammo = '/js/ammo.js';
	
	var initScene, render, renderer, scene, camera, box;
	
	initScene = function() {
		renderer = new THREE.WebGLRenderer({ antialias: true });
		renderer.setSize( window.innerWidth, window.innerHeight );
		document.getElementById( 'viewport' ).appendChild( renderer.domElement );
		// 场景创建
		scene = new Physijs.Scene;
		
		camera = new THREE.PerspectiveCamera(
			35,
			window.innerWidth / window.innerHeight,
			1,
			1000
		);
		camera.position.set( 60, 50, 60 );
		camera.lookAt( scene.position );
		scene.add( camera );
		
		// Box
		box = new Physijs.BoxMesh(
			new THREE.CubeGeometry( 5, 5, 5 ),
			new THREE.MeshBasicMaterial({ color: 0x888888 })
		);
		scene.add( box );
		
		requestAnimationFrame( render );
	};
	
	render = function() {
		scene.simulate(); // run physics
		renderer.render( scene, camera); // render the scene
		requestAnimationFrame( render );
	};
	
	window.onload = initScene();
	
	</script>
</body>
</html>

基本形状

Physijs目前支持9种形状:

  • Physijs.PlaneMesh- 无限零厚度平面
  • Physijs.BoxMesh-匹配THREE.CubeGeometry
  • Physijs.SphereMesh- 匹配THREE.SphereGeometry
  • Physijs.CylinderMesh- 匹配THREE.CylinderGeometry
  • Physijs.ConeMesh- 匹配THREE.CylinderGeometry(锥形)
  • Physijs.CapsuleMesh- 匹配THREE.CylinderGeometry,除了在两端有两个半球
  • Physijs.ConvexMesh- 匹配您拥有的任何凸几何
  • Physijs.ConcaveMesh- 匹配您拥有的任何凹几何,即任意网格(比如人行)
  • Physijs.HeightfieldMesh- 匹配z坐标中给定的高度值的规则网格

使用任何这些形状就THREE.Mesh在代码中替换最适合几何体的Physijs网格一样简单
您可以像平常一样将这些网格物体添加到场景中

scene.add( mesh_object )

您可以侦听一个事件,在添加对象时进行了描述,是在将对象添加到物理世界时触发的。

查看形状示例,了解如何创建和添加形状。

笔记

  • 使用Physijs.ConcaveMesh谨慎,它表现最差
  • THREE.PlaneMesh和Physijs.PlaneMesh不严格类似,因为后者是无限的。非常薄BoxMesh可以更好地表示有限平面。
  • Physijs.CapsuleMesh通常适合人形玩家角色


回调和事件

场景更新(scene.simulate):

因为Physijs运行在与主应用程序不同的线程上,所以无法保证每次调用时它都能迭代场景scene.simulate因此,您可以将事件侦听器附加到物理模拟运行时触发的场景。
var scene = new Physijs.scene;
scene.addEventListener( 'update', function() {
    // the scene's physics have finished updating
    scene.simulate( undefined, 1 );
    physics_stats.update();
});

碰撞(Collisions):

要在对象遇到与另一个对象的冲突时收到通知,您可以将collision事件侦听器附加到单个对象。
var mesh = new Physijs.BoxMesh( geometry, material );
mesh.addEventListener( 'collision', function( other_object, linear_velocity, angular_velocity ) {
    // `this` 监听事件的网格对象
    // other_object 另一个跟‘this’碰撞的对象
    // linear_velocity 碰撞的速度Vector3对象
    // angular_velocity 碰撞的角度Vector3 对象 
});

添加对象:

如果场景很复杂或者有很多对象,物理模拟可能会减慢到添加新对象对应用程序来说很多工作的程度。为了帮助减轻这可能导致的一些痛苦,对象具有ready在将对象添加到物理模拟之后触发事件。可以在许多示例中,以避免在模拟速度减慢时使用新对象充满场景。
var readyHandler = function() {
    // 对象添加到场景成功
};
var mesh = new Physijs.SphereMesh( geometry, material );
mesh.addEventListener( 'ready', readyHandler );
scene.add( mesh );


Collisions碰撞

目前使用对象级别的碰撞事件处理冲突。这些事件通过4个参数:其他相撞对象、在两个物体之间的速度差(冲击的实质上的速度)、在角速度的差、和接触之间的正常thisother_object
var mesh = new Physijs.SphereMesh(
    new THREE.SphereGeometry( 3 ),
    new THREE.MeshBasicMaterial({ color: 0x888888 })
);
mesh.addEventListener( 'collision', function( other_object, relative_velocity, relative_rotation, contact_normal ) {
    // `this`与`other_object`相撞,冲击速度为`relative_velocity`,
    // 旋转力为'relative_rotation`,正常'contact_normal`
});


运动夹紧(Motion Clamping

当物体具有高速度时,如果在模拟步骤之间移动并经过其他物体,则可能错过碰撞。
要解决此问题,请启用CCD动作夹紧。
对于大小为1的立方体,请尝试:
// Enable CCD if the object moves more than 1 meter in one simulation frame
// 如果对象在一个模拟框架中移动超过1米,则启用CCD
mesh.setCcdMotionThreshold(1);

// Set the radius of the embedded sphere such that it is smaller than the object
// 设置嵌入球体的半径,使其小于对象
mesh.setCcdSweptSphereRadius(0.2);


复合形状Compound Shapes

复合形状是向场景添加复杂几何体的有效方法,通过将Physijs中可用形状拼接在一起以创建更大,更复杂的几何体来创建。

在three.js中,您可以通过将这些子几何添加到父对象来将它们添加到一起,例如:
var parent = new THREE.Mesh( new THREE.CubeGeometry( 5, 5, 5 ), new THREE.MeshBasicMaterial({ color: 0x888888 }) );

var child = new THREE.Mesh( new THREE.SphereGeometry( 2.5 ), new THREE.MeshBasicMaterial({ color: 0x888888 }) );
child.position.z = 5;

parent.add( child );
scene.add( parent );

上面的代码将创建一个5x5x5的立方体,并在z轴上距离它5个单位处附加一个2.5半径的球体。移动和旋转父对象会直接影响子对象。为了使Physijs插件保持简单和简单,这正是其复合形状的工作原理。唯一需要改变的是替换THREE.Mesh Physijs等价:

var parent = new Physijs.BoxMesh( new THREE.CubeGeometry( 5, 5, 5 ), new THREE.MeshBasicMaterial({ color: 0x888888 }) );

var child = new Physijs.SphereMesh( new THREE.SphereGeometry( 2.5 ), new THREE.MeshBasicMaterial({ color: 0x888888 }) );
child.position.z = 5;

parent.add( child );
scene.add( parent );
在处理复合对象时,您唯一需要记住的是,在将父项添加到场景之前,
必须
所有子项添加到父项。将父级放入场景时,其形状将最终确定并且无法添加到其中。


约束Constraints

Point to Point(点到点)

点约束允许对象绑定到场景中的某个点,并且可以用于将两个对象约束在它们之间的某个点上。

var constraint = new Physijs.PointConstraint(
    physijs_mesh_a, // First object to be constrained  第一个被约束的对象
    physijs_mesh_b, // OPTIONAL second object - if omitted then physijs_mesh_1 will be constrained to the scene
                    // 可选的第二个对象 - 如果省略,则physijs_mesh_1将被约束到场景
    new THREE.Vector3( 0, 10, 0 ) // point in the scene to apply the constraint
);
scene.addConstraint( constraint );


Hinge Constraint(链约束)

铰链约束限制物体的运动,就像它在铰链(例如门)上一样。与点约束一样,铰链可以使用1或2个对象创建。

var constraint = new Physijs.HingeConstraint(
    physijs_mesh_a, // First object to be constrained
    physijs_mesh_b, // OPTIONAL second object - if omitted then physijs_mesh_1 will be constrained to the scene
    new THREE.Vector3( 0, 10, 0 ), // point in the scene to apply the constraint
    new THREE.Vector3( 1, 0, 0 ) // Axis along which the hinge lies - in this case it is the X axis 铰链所在的轴
);
scene.addConstraint( constraint );
constraint.setLimits(
    low, // minimum angle of motion, in radians 最小运动角度,以弧度为单位
    high, // maximum angle of motion, in radians 最大运动角度,以弧度为单位
    bias_factor, // applied as a factor to constraint error 作为约束错误的一个因素
    relaxation_factor, // controls bounce at limit (0.0 == no bounce) 控件在极限处反弹(0.0 ==无反弹)
);
constraint.enableAngularMotor( target_velocity, acceration_force );
constraint.disableMotor();


Slider Constraint

滑块约束将约束对象的线性移动以与给定轴对齐。例如推拉门或叉车上的提升机构。与点和铰链约束一样,第二个对象是可选参数。
var constraint = new Physijs.SliderConstraint(
    physijs_mesh_a, // First object to be constrained
    physijs_mesh_b, // OPTIONAL second object - if omitted then physijs_mesh_1 will be constrained to the scene
    new THREE.Vector3( 0, 10, 0 ), // point in the scene to apply the constraint
    new THREE.Vector3( 1, 0, 0 ) // Axis along which the hinge lies - in this case it is the X axis
);
scene.addConstraint( constraint );
constraint.setLimits(
    linear_lower, // lower limit of linear movement, expressed in world units 线性运动的下限,以世界单位表示 
    linear_upper, // upper limit of linear movement, expressed in world units 线性运动的上限,以世界单位表示 
    angular_lower, // lower limit of angular movement, expressed in radians 线性运动的上限,以世界单位表示 
    angular_upper // upper limit of angular movement, expressed in radians 角运动的上限,以弧度表示
);
constraint.setRestitution(
    linear, // amount of restitution when reaching the linear limits 归还的量到达所述线性限制时 
    angular // amount of restitution when reaching the angular limits 恢复原状的量到达角限制时
);
constraint.enableLinearMotor( target_velocity, acceration_force );
constraint.disableLinearMotor();
constraint.enableAngularMotor( target_velocity, acceration_force );
constraint.disableAngularMotor();


ConeTwist Constraint

锥形扭曲约束以类似于肩关节或踝关节等人体关节的方式限制两个物体之间的运动和旋转。
在此约束中,两个对象都是必需的。
var constraint = new Physijs.ConeTwistConstraint(
    physijs_mesh_a, // First object to be constrained
    physijs_mesh_b, // Second object to be constrained
    new THREE.Vector3( 0, 10, 0 ), // point in the scene to apply the constraint
);
scene.addConstraint( constraint );
constraint.setLimit( x, y, z ); // rotational limit, in radians, for each axis
constraint.setMotorMaxImpulse( max_impulse ); // float value of the maximum impulse the motor can apply toward its target
constraint.setMotorTarget( target ); // target is the desired rotation for the constraint and can be expressed by a THREE.Vector3, THREE.Matrix4, or THREE.Quaternion
constraint.enableMotor();
constraint.disableMotor();


Degree Of Freedom Constraint

自由度约束可以完全控制对象如何在线性和角度运动中受到约束。

var constraint = new Physijs.DOFConstraint(
    physijs_mesh_a, // First object to be constrained
    physijs_mesh_b, // OPTIONAL second object - if omitted then physijs_mesh_1 will be constrained to the scene
    new THREE.Vector3( 0, 10, 0 ), // point in the scene to apply the constraint
);
scene.addConstraint( constraint );
constraint.setLinearLowerLimit( new THREE.Vector3( -10, -5, 0 ) ); // sets the lower end of the linear movement along the x, y, and z axes.
constraint.setLinearUpperLimit( new THREE.Vector3( 10, 5, 0 ) ); // sets the upper end of the linear movement along the x, y, and z axes.
constraint.setAngularLowerLimit( new THREE.Vector3( 0, -Math.PI, 0 ) ); // sets the lower end of the angular movement, in radians, along the x, y, and z axes.
constraint.setAngularUpperLimit( new THREE.Vector3( 0, Math.PI, 0 ) ); // sets the upper end of the angular movement, in radians, along the x, y, and z axes.
constraint.configureAngularMotor(
    which, // which angular motor to configure - 0,1,2 match x,y,z
    low_limit, // lower limit of the motor
    high_limit, // upper limit of the motor
    velocity, // target velocity
    max_force // maximum force the motor can apply
);
constraint.enableAngularMotor( which ); // which angular motor to configure - 0,1,2 match x,y,z
constraint.disableAngularMotor( which ); // which angular motor to configure - 0,1,2 match x,y,z


您可以使用两种方法使对象冻结或不可移动。如果物体总是会是静态的,如地面,你可以设置它的质量0,当你创建一个使用第三个参数网:new Physijs.BoxMesh( geometry, material, 0 )具有质量的任何对象0将始终是静态的。

第二种方法可以用于对象,当它们在某些时候是静态的而在其他时候是动态的,就像在THREE.Vector3( 0, 0, 0 )则没有能量将被应用到该对象。您可以使用object.setAngularVelocityobject.setLinearVelocity以相同的方式清除对象可能已具有的任何速度。稍后您可以将对象的线性和角度因子重置为( 1, 1, 1 )
// 设置为静止的
_vector.set( 0, 0, 0 );
selected_block.setAngularFactor( _vector );
selected_block.setAngularVelocity( _vector );
selected_block.setLinearFactor( _vector );
selected_block.setLinearVelocity( _vector );

// 设置受重力影响
_vector.set( 0, 0, 0 );
selected_block.setAngularFactor( _vector );
selected_block.setAngularVelocity( _vector );


Materials材质

您可以在不触及其材料系统的情况下使用Physijs。但是,在这样做时,您将无法自定义对象具有多少摩擦力和恢复力(“弹性”)。

材料非常容易使用,但需要一点点习惯。您必须Physijs.createMaterial使用您希望Physijs材料具有的three.js材质,摩擦力和恢复值来调用这种新材料可以像任何其他three.js材料一样使用。

注意:friction(摩擦)restitution(恢复值或反弹)在0.0和1.0之间给出

var friction = 0.8; // high friction 摩擦
var restitution = 0.3; // low restitution 

var material = Physijs.createMaterial(
    new THREE.MeshBasicMaterial({ color: 0x888888 }),
    friction,
    restitution
);

// Create a cube with friction of .8 and restitution of .3
var mesh = new Physijs.BoxMesh(
    new THREE.CubeGeometry( 5, 5, 5 ),
    material
);


Object scaling对象缩放

在将对象添加到场景之前,可以缩放对象。将它们添加到场景后,您无法修改它们的比例。这使您可以在Three.js中创建和重用不同大小的相同几何对象。缩放是对象矩阵的一个属性,它不能是动态的。


Pausing Simulation暂停模拟

停止模拟只需要在渲染时停止调用模拟函数(如下所示)。

var render = function() {
    if (!isPaused) {
        scene.simulate();
    }
    renderer.render();
};

恢复模拟需要调用场景的onSimulationResume方法,该方法将最后的模拟时间设置为等于当前时间,从而防止在模拟未暂停时发生的所有模拟。如下所示。

var unpauseSimulation = function() {
    isPaused = false;
    scene.onSimulationResume();
};


Scene Configuration场景配置

在创建场景时,您可以做出一些选择。当您创建它们时,大多数都会传递到场景中:

var scene = new Physijs.Scene({ reportsize: 50, fixedTimeStep: 1 / 60 });

场景构造函数参数

  • fixedTimeStep default 1 / 60 此数字确定模拟一个模拟步骤的时间。数字越小,模拟越准确(越慢)。
  • broadphasedefault dynamic指定将使用哪个broadphase,选择是dynamicsweepprune
  • reportsize default 50 作为优化,包含对象位置的世界报告将根据此编号进行预初始化。最好将其设置为场景所具有的对象数量。

场景配置方法:

  • setGravity default ( 0, -10, 0 )设定重力的量和方向的方法。用法:scene.setGravity(new THREE.Vector3( x_force, y_force, z_force ))
  • setFixedTimeStep default 1 / 60 重置 fixedTimeStep 构造函数中给定的值。该方法用于允许动态物理精度配置。
    不要每帧都调用它!

自定义模拟步骤:

您可以将两个参数传递给scene.simulate
方法:timeStepmaxStepstimeStep指定要模拟的时间量,默认为自上次迭代以来的时间量。maxSteps是模拟可以执行的迭代次数的上限。
告诉模拟器运行的时间(
timeStep
)可以通过允许的迭代次数(
maxSteps
来限制
例如:如果fixedTimeStep(见上文)是1 / 6016毫秒)并且maxSteps是5,则可以模拟的最大时间是maxSteps*fixedTimeStep= 0.08秒或80毫秒。如果给出timeStep小于或等于80ms,则将模拟所有内容,但如果timeStep超过80ms则不会模拟所有时间。这种方式maxSteps可用于防止物理模拟在开始过载时磨削到完全停止。在示例scene.simulate中调用参数,( undefined, 2 )参数告诉Physijs尽可能多地模拟2次迭代。

        scene = new Physijs.Scene;        scene.setGravity(new THREE.Vector3( 0, -30, 0 )); // 设定重力的量和方向        scene.addEventListener(            'update',            function() {                scene.simulate( undefined, 1 );            }        );


Updating an object's position & rotation更新对象的位置和旋转

有一个方面是无法完成与three.js的无缝集成:更改对象的位置和/或旋转。如果这样做,则必须将该对象__dirtyPosition__dirtyRotation标志设置为true,否则将从模拟中的最后已知值覆盖。

var mesh = new Physijs.BoxMesh( geometry, material );
scene.add( mesh );

var render = function() {
    // Change the object's position
    mesh.position.set( 0, 0, 0 );
    mesh.__dirtyPosition = true;

    // Change the object's rotation
    mesh.rotation.set(0, 90, 180);
    mesh.__dirtyRotation = true;
    
    // You may also want to cancel the object's velocity
    mesh.setLinearVelocity(new THREE.Vector3(0, 0, 0));
    mesh.setAngularVelocity(new THREE.Vector3(0, 0, 0));
    
    scene.simulate();
    renderer.render();
};