Files
cocos-engine/cocos/physics/framework/physics-system.ts
Santy-Wang 464c9dd044 optimize api doc i18n (#10965)
* optimize api doc i18n

* refine docs

* refine

* refine

* adjust grammar error
2022-05-07 10:52:56 +08:00

473 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
import { EDITOR } from 'internal:constants';
import { Vec3 } from '../../core/math';
import { IRaycastOptions } from '../spec/i-physics-world';
import { director, Director } from '../../core/director';
import { System } from '../../core/components';
import { PhysicsMaterial } from './assets/physics-material';
import { RecyclePool, game, Enum } from '../../core';
import { Ray } from '../../core/geometry';
import { PhysicsRayResult } from './physics-ray-result';
import { IPhysicsConfig, ICollisionMatrix } from './physics-config';
import { CollisionMatrix } from './collision-matrix';
import { PhysicsGroup } from './physics-enum';
import { constructDefaultWorld, IWorldInitData, selector } from './physics-selector';
import { legacyCC } from '../../core/global-exports';
legacyCC.internal.PhysicsGroup = PhysicsGroup;
/**
* @en
* Physics system.
* @zh
* 物理系统。
*/
export class PhysicsSystem extends System implements IWorldInitData {
public static get PHYSICS_NONE () {
return !selector.id;
}
public static get PHYSICS_BUILTIN () {
return selector.id === 'builtin';
}
public static get PHYSICS_CANNON () {
return selector.id === 'cannon.js';
}
public static get PHYSICS_BULLET () {
return selector.id === 'bullet';
}
public static get PHYSICS_PHYSX () {
return selector.id === 'physx';
}
/**
* @en
* Gets the ID of the system.
* @zh
* 获取此系统的ID。
*/
public static readonly ID = 'PHYSICS';
/**
* @en
* Gets the predefined physics groups.
* @zh
* 获取预定义的物理分组。
*/
public static get PhysicsGroup () {
return PhysicsGroup;
}
/**
* @en
* Gets the physical system instance.
* @zh
* 获取物理系统实例。
*/
public static get instance (): PhysicsSystem {
return PhysicsSystem._instance!;
}
/**
* @en
* Gets or sets whether the physical system is enabled, which can be used to pause or continue running the physical system.
* @zh
* 获取或设置是否启用物理系统,可以用于暂停或继续运行物理系统。
*/
public get enable (): boolean {
return this._enable;
}
public set enable (value: boolean) {
this._enable = value;
}
/**
* @zh
* Gets or sets whether the physical system allows automatic sleep, which defaults to true.
* @zh
* 获取或设置物理系统是否允许自动休眠,默认为 true。
*/
public get allowSleep (): boolean {
return this._allowSleep;
}
public set allowSleep (v: boolean) {
this._allowSleep = v;
if (this.physicsWorld) {
this.physicsWorld.setAllowSleep(v);
}
}
/**
* @en
* Gets or sets the maximum number of simulated substeps per frame.
* @zh
* 获取或设置每帧模拟的最大子步数。
*/
public get maxSubSteps () {
return this._maxSubSteps;
}
public set maxSubSteps (value: number) {
this._maxSubSteps = value;
}
/**
* @en
* Gets or sets the fixed delta time consumed by each simulation step in seconds.
* @zh
* 获取或设置每步模拟消耗的固定时间(以 s 为单位)。
*/
public get fixedTimeStep () {
return this._fixedTimeStep;
}
public set fixedTimeStep (value: number) {
this._fixedTimeStep = value;
}
/**
* @en
* Gets or sets the value of gravity in the physical world, which defaults to (0, -10, 0).
* @zh
* 获取或设置物理世界的重力数值,默认为 (0, -10, 0)。
*/
public get gravity (): Vec3 {
return this._gravity;
}
public set gravity (gravity: Vec3) {
this._gravity.set(gravity);
if (this.physicsWorld) {
this.physicsWorld.setGravity(gravity);
}
}
/**
* @en
* Gets or sets the default speed threshold for going to sleep.
* @zh
* 获取或设置进入休眠的默认速度临界值。
*/
public get sleepThreshold (): number {
return this._sleepThreshold;
}
public set sleepThreshold (v: number) {
this._sleepThreshold = v;
}
/**
* @en
* Turn on or off the automatic simulation.
* @zh
* 获取或设置是否自动模拟。
*/
public get autoSimulation () {
return this._autoSimulation;
}
public set autoSimulation (value: boolean) {
this._autoSimulation = value;
}
/**
* @en
* Gets the global default physical material.
* @zh
* 获取全局的默认物理材质。
*/
public get defaultMaterial (): PhysicsMaterial {
return this._material;
}
/**
* @en
* Gets the wrappered object of the physical world through which you can access the actual underlying object.
* @zh
* 获取物理世界的封装对象,通过它你可以访问到实际的底层对象。
*/
public get physicsWorld () {
return selector.physicsWorld!;
}
/**
* @en
* Gets the raycastClosest test result.
* @zh
* 获取 raycastClosest 的检测结果。
*/
public readonly raycastClosestResult = new PhysicsRayResult();
/**
* @en
* Gets the raycast test results.
* @zh
* 获取 raycast 的检测结果。
*/
public readonly raycastResults: PhysicsRayResult[] = [];
/**
* @en
* Gets the collision matrix that used for initialization only.
* @zh
* 获取碰撞矩阵,它仅用于初始化。
*/
public readonly collisionMatrix: ICollisionMatrix = new CollisionMatrix(1) as ICollisionMatrix;
/**
* @en
* The minimum size of the collision body.
* @zh
* 碰撞体的最小尺寸。
*/
public minVolumeSize = 1e-5;
public readonly useNodeChains: boolean = false;
private _enable = true;
private _allowSleep = true;
private _maxSubSteps = 1;
private _subStepCount = 0;
private _fixedTimeStep = 1.0 / 60.0;
private _autoSimulation = true;
private _accumulator = 0;
private _sleepThreshold = 0.1;
private readonly _gravity = new Vec3(0, -10, 0);
private readonly _material = new PhysicsMaterial();
private static readonly _instance: PhysicsSystem | null = null;
private readonly raycastOptions: IRaycastOptions = {
group: -1,
mask: -1,
queryTrigger: true,
maxDistance: 10000000,
}
private readonly raycastResultPool = new RecyclePool<PhysicsRayResult>(() => new PhysicsRayResult(), 1);
private constructor () {
super();
this._material.on(PhysicsMaterial.EVENT_UPDATE, this._updateMaterial, this);
}
postUpdate (deltaTime: number) {
if (EDITOR && !this._executeInEditMode && !selector.runInEditor) return;
if (!this.physicsWorld) return;
if (!this._enable) {
this.physicsWorld.syncSceneToPhysics();
return;
}
if (this._autoSimulation) {
this._subStepCount = 0;
this._accumulator += deltaTime;
director.emit(Director.EVENT_BEFORE_PHYSICS);
while (this._subStepCount < this._maxSubSteps) {
if (this._accumulator >= this._fixedTimeStep) {
this.physicsWorld.syncSceneToPhysics();
this.physicsWorld.step(this._fixedTimeStep);
this.physicsWorld.emitEvents();
this.physicsWorld.syncAfterEvents();
this._accumulator -= this._fixedTimeStep;
this._subStepCount++;
} else {
this.physicsWorld.syncSceneToPhysics();
break;
}
}
director.emit(Director.EVENT_AFTER_PHYSICS);
}
}
/**
* @en
* Reset the physics configuration.
* @zh
* 重置物理配置。
*/
resetConfiguration (config?: IPhysicsConfig) {
const con = config || (game.config ? game.config.physics : null);
if (con) {
if (typeof con.allowSleep === 'boolean') this._allowSleep = con.allowSleep;
if (typeof con.fixedTimeStep === 'number') this._fixedTimeStep = con.fixedTimeStep;
if (typeof con.maxSubSteps === 'number') this._maxSubSteps = con.maxSubSteps;
if (typeof con.sleepThreshold === 'number') this._sleepThreshold = con.sleepThreshold;
if (typeof con.autoSimulation === 'boolean') this.autoSimulation = con.autoSimulation;
if (con.gravity) Vec3.copy(this._gravity, con.gravity);
if (con.defaultMaterial) {
this._material.setValues(
con.defaultMaterial.friction,
con.defaultMaterial.rollingFriction,
con.defaultMaterial.spinningFriction,
con.defaultMaterial.restitution,
);
}
if (con.collisionMatrix) {
for (const i in con.collisionMatrix) {
this.collisionMatrix[`${1 << parseInt(i)}`] = con.collisionMatrix[i];
}
}
if (con.collisionGroups) {
const cg = con.collisionGroups;
if (cg instanceof Array) {
cg.forEach((v) => { PhysicsGroup[v.name] = 1 << v.index; });
Enum.update(PhysicsGroup);
}
}
}
if (this.physicsWorld) {
this.physicsWorld.setGravity(this._gravity);
this.physicsWorld.setAllowSleep(this._allowSleep);
this.physicsWorld.setDefaultMaterial(this._material);
}
}
/**
* @en
* Reset the accumulator of time to given value.
* @zh
* 重置时间累积总量为给定值。
*/
resetAccumulator (time = 0) {
this._accumulator = time;
}
/**
* @en
* Perform simulation steps for the physics world.
* @zh
* 执行物理世界的模拟步进。
* @param fixedTimeStep
*/
step (fixedTimeStep: number, deltaTime?: number, maxSubSteps?: number) {
if (this.physicsWorld) this.physicsWorld.step(fixedTimeStep, deltaTime, maxSubSteps);
}
/**
* @en
* Sync the scene world transform changes to the physics world.
* @zh
* 同步场景世界的变化信息到物理世界中。
*/
syncSceneToPhysics () {
if (this.physicsWorld) this.physicsWorld.syncSceneToPhysics();
}
/**
* @en
* Emit trigger and collision events.
* @zh
* 触发`trigger`和`collision`事件。
*/
emitEvents () {
if (this.physicsWorld) this.physicsWorld.emitEvents();
}
/**
* @en
* Collision detect all collider, and record all the detected results, through PhysicsSystem.Instance.RaycastResults access to the results.
* @zh
* 检测所有的碰撞盒,并记录所有被检测到的结果,通过 PhysicsSystem.instance.raycastResults 访问结果。
* @param worldRay @zh 世界空间下的一条射线 @en A ray in world space
* @param mask @zh 掩码,默认为 0xffffffff @en Mask, default value is 0xffffffff
* @param maxDistance @zh 最大检测距离,默认为 10000000目前请勿传入 Infinity 或 Number.MAX_VALUE
* @en Maximum detection distance, default value is 10000000, do not pass Infinity or Number.MAX_VALUE for now
* @param queryTrigger @zh 是否检测触发器 @en Whether to detect triggers
* @return {boolean} @zh 表示是否有检测到碰撞 @en Indicates whether a collision has been detected
*/
raycast (worldRay: Ray, mask = 0xffffffff, maxDistance = 10000000, queryTrigger = true): boolean {
if (!this.physicsWorld) return false;
this.raycastResultPool.reset();
this.raycastResults.length = 0;
this.raycastOptions.mask = mask >>> 0;
this.raycastOptions.maxDistance = maxDistance;
this.raycastOptions.queryTrigger = queryTrigger;
return this.physicsWorld.raycast(worldRay, this.raycastOptions, this.raycastResultPool, this.raycastResults);
}
/**
* @en
* Collision detect all collider, and record and ray test results with the shortest distance
* by PhysicsSystem.Instance.RaycastClosestResult access to the results.
* @zh
* 检测所有的碰撞盒,并记录与射线距离最短的检测结果,通过 PhysicsSystem.instance.raycastClosestResult 访问结果。
* @param worldRay @zh 世界空间下的一条射线 @en A ray in world space
* @param mask @zh 掩码,默认为 0xffffffff @en Mask, default value is 0xffffffff
* @param maxDistance @zh 最大检测距离,默认为 10000000目前请勿传入 Infinity 或 Number.MAX_VALUE
* @en Maximum detection distance, default value is 10000000, do not pass Infinity or Number.MAX_VALUE for now
* @param queryTrigger @zh 是否检测触发器 @en Whether to detect triggers
* @return {boolean} @zh 表示是否有检测到碰撞 @en Indicates whether a collision has been detected
*/
raycastClosest (worldRay: Ray, mask = 0xffffffff, maxDistance = 10000000, queryTrigger = true): boolean {
if (!this.physicsWorld) return false;
this.raycastOptions.mask = mask >>> 0;
this.raycastOptions.maxDistance = maxDistance;
this.raycastOptions.queryTrigger = queryTrigger;
return this.physicsWorld.raycastClosest(worldRay, this.raycastOptions, this.raycastClosestResult);
}
private _updateMaterial () {
if (this.physicsWorld) this.physicsWorld.setDefaultMaterial(this._material);
}
/**
* @en
* Construct and register the system singleton.
* If the module is pre-loaded, it will be executed automatically.
* @zh
* 构造并注册系统单例。
* 预先加载模块的情况下,会自动执行。
*/
static constructAndRegister () {
if (!PhysicsSystem._instance) {
// Construct physics world and physics system only once
const sys = new PhysicsSystem();
sys.resetConfiguration();
constructDefaultWorld(sys);
(PhysicsSystem._instance as unknown as PhysicsSystem) = sys;
director.registerSystem(PhysicsSystem.ID, sys, sys.priority);
}
}
}
/**
* By registering the initialization event, the system can be automatically
* constructed and registered when the module is pre-loaded
*/
director.once(Director.EVENT_INIT, () => { PhysicsSystem.constructAndRegister(); });