跳到主要内容

力与冲量

使用方向键施加冲量,使用 WASD 键施加力矩以旋转立方体。按住 F 键施加持续向上的力,以抵消重力。 按 R 键重置立方体。

试着让立方体在一个顶角上保持平衡并旋转! 完整代码位于页面底部。

本教程将演示如何使用力控制动态刚体,实现上面的效果。我们将简要介绍力、冲量、力矩,以及如何在刚体组件界面中自定义行为。

通过脚本施力​

施加恒力​

if (app.keyboard.isPressed(pc.KEY_F) ) {
this.entity.rigidbody.applyForce(0, 9.8, 0);
}

用户按 F 键时,通过 applyForce(x, y, z) 向目标实体施加沿全局 Y 轴的力。还可以指定力矢量的作用点。

冲量​

if (app.keyboard.isPressed(pc.KEY_LEFT) ) {
this.entity.rigidbody.applyImpulse(-1, 0, 0);
}

通过 applyImpulse(x, y, z) 向立方体施加沿 X 轴的冲量,使速度瞬间改变。

力矩​

if (app.keyboard.isPressed(pc.KEY_W) ) {
this.entity.rigidbody.applyTorque(-this.torque, 0, 0);
}

通过 applyTorque(x, y, z) 向实体施加力矩,即产生旋转的力。

角冲量​

this.entity.rigidbody.applyTorqueImpulse(x, y, z)

通过 applyTorqueImpulse(x, y, z) 瞬间改变角速度。上面的演示代码未使用此方法。

移动动态刚体​

移动刚体时,应使用上述方法施加线性力和旋转力(力矩)。通常应避免直接修改刚体的位置或速度,因为这会覆盖物理模拟,尤其在发生碰撞时可能产生异常效果。

不过,如有需要,可以将新的 pc.Vec3 值直接赋给 entity.rigidbody.linearVelocity 或 entity.rigidbody.angularVelocity,覆盖速度。

有关刚体类型的更多信息,请参阅碰撞 API、pc 命名空间、第一人称控制器教程和碰撞教程。

基本配置​

创建包含聚光灯、立方体和地面的基本场景。立方体实体具有模型、刚体、碰撞和脚本组件,地面具有模型、刚体和碰撞组件。立方体刚体设为动态,地面刚体设为静态。为每个箱体创建材质并调整漫反射颜色,使画面更易辨认,同时为 SpotLight 和 DynamicBody 实体启用 Cast Shadows(投射阴影)。完整的 usingForces 场景和代码见此 PlayCanvas 应用。

限制与控制​

我们在编辑器中进行了一些设置,防止不平衡力持续作用导致刚体不断加速而失控。立方体启用了角阻尼,立方体和地面都设置了摩擦力。这里未使用线性阻尼,但它可以模拟空气阻力;也可以根据需要通过代码施加减速。

刚体设置

传送刚体​

要将刚体瞬间传送到新位置,不能使用 pc.Entity API 的 setPosition 函数,因为物理引擎仍会认为刚体位于原处。应改用刚体组件的 teleport 函数:

//code within the update function
this.playerPos = this.entity.getLocalPosition();

// Keeping the cube on screen - cube moves off of one screen edge then appears from the opposite edge.
if (this.playerPos.x < -9.0) {
this.entity.rigidbody.teleport(8.8, this.playerPos.y, this.playerPos.z);
}
if (this.playerPos.x > 9.0) {
this.entity.rigidbody.teleport(-8.8, this.playerPos.y, this.playerPos.z);
}

当立方体在 X 方向超出可见区域时,会调用 teleport 将其传送到屏幕另一侧。目标位置会稍微靠内,避免连续触发 if() 语句。

重置立方体的代码​

if (app.keyboard.wasPressed(pc.KEY_R)) {
this.reset();
}
reset: function () {
this.entity.rigidbody.teleport(0, 2, 0);
this.entity.rigidbody.linearVelocity = pc.Vec3.ZERO;
this.entity.rigidbody.angularVelocity = pc.Vec3.ZERO;
}

我们提供了重置函数,将立方体移回初始位置,并按上述方法同步刚体与传送后实体的位置。函数最后两行将刚体的线速度和角速度归零。也可以重置对象朝向,但此代码未执行该操作。

完整代码​

var DynamicBody = pc.createScript('dynamicBody');

// initialize code called once per entity
DynamicBody.prototype.initialize = function() {
this.torque = 7;
this.app.keyboard.on(pc.EVENT_KEYDOWN, this.onKeyDown, this);

this.on('destroy', function() {
this.app.keyboard.off(pc.EVENT_KEYDOWN, this.onKeyDown, this);
}, this);
};

DynamicBody.prototype.onKeyDown = function (event) {
event.event.preventDefault();
};

// update code called every frame
DynamicBody.prototype.update = function(dt) {
//update player's position
this.playerPos = this.entity.getLocalPosition();

var app = this.app;

//keyboard controls and applying forces and moments.
if (app.keyboard.isPressed(pc.KEY_LEFT) ) {
this.entity.rigidbody.applyImpulse(-1, 0, 0);
}
if (app.keyboard.isPressed(pc.KEY_RIGHT) ) {
this.entity.rigidbody.applyImpulse(1, 0, 0);
}
if (app.keyboard.isPressed(pc.KEY_UP) ) {
this.entity.rigidbody.applyImpulse(0, 1, 0);
}
if (app.keyboard.isPressed(pc.KEY_A) ) {
this.entity.rigidbody.applyTorque(0, this.torque, 0);
}
if (app.keyboard.isPressed(pc.KEY_D) ) {
this.entity.rigidbody.applyTorque(0, -this.torque, 0);
}
if (app.keyboard.isPressed(pc.KEY_W) ) {
this.entity.rigidbody.applyTorque(-this.torque, 0, 0);
}
if (app.keyboard.isPressed(pc.KEY_S) ) {
this.entity.rigidbody.applyTorque(this.torque, 0, 0);
}
if (app.keyboard.isPressed(pc.KEY_F) ) {
this.entity.rigidbody.applyForce(0, 9.8, 0);
}

// Keeping the cube on screen - cube moves off of one screen edge then appears from the opposite edge.
if (this.playerPos.x < -9.0) {
this.entity.rigidbody.teleport(8.8, this.playerPos.y, this.playerPos.z);
}
if (this.playerPos.x > 9.0) {
this.entity.rigidbody.teleport(-8.8, this.playerPos.y, this.playerPos.z);
}

// cube reset control
if (app.keyboard.wasPressed(pc.KEY_R) ) {
this.reset();
}
};

DynamicBody.prototype.reset = function () {
this.entity.rigidbody.teleport(0, 2, 0);
this.entity.rigidbody.linearVelocity = pc.Vec3.ZERO;
this.entity.rigidbody.angularVelocity = pc.Vec3.ZERO;
};