mirror of
https://github.com/galacean/engine.git
synced 2026-06-19 20:37:21 +08:00
Merge branch 'main' of github.com:galacean/engine
This commit is contained in:
@@ -59,6 +59,12 @@
|
||||
"collapse": true
|
||||
}
|
||||
},
|
||||
"audio": {
|
||||
"title": "Audio",
|
||||
"theme": {
|
||||
"collapse": true
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"title": "Input",
|
||||
"theme": {
|
||||
|
||||
29
docs/en/audio/assets.mdx
Normal file
29
docs/en/audio/assets.mdx
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
order: 1
|
||||
title: Audio Assets
|
||||
type: Audio
|
||||
label: Audio
|
||||
---
|
||||
|
||||
|
||||
Audio assets are the core components in Galacean used for storing and managing audio files. Through audio assets, developers can conveniently upload, manage, and utilize various audio files.
|
||||
|
||||
## Supported Audio Formats
|
||||
|
||||
Galacean Engine supports a variety of commonly used audio formats, including but not limited to:
|
||||
- MP3
|
||||
- WAV
|
||||
- OGG
|
||||
|
||||
## Uploading and Managing Audio Files
|
||||
|
||||
Developers can easily upload audio files using the asset selector. Uploaded audio files are automatically stored in the asset library for subsequent use.
|
||||
|
||||
<video src="https://gw.alipayobjects.com/v/huamei_edbbqz/afts/video/dXVsQboyhYwAAAAAAAAAAAAADoY9AQFr" autoPlay loop muted />
|
||||
|
||||
## Audio Preview
|
||||
|
||||
In the asset library, developers can preview audio files to ensure they meet the game's requirements.
|
||||
|
||||
<video src="https://gw.alipayobjects.com/v/huamei_edbbqz/afts/video/sZwaQr6CuAMAAAAAAAAAAAAADoY9AQFr" autoPlay loop muted />
|
||||
|
||||
28
docs/en/audio/component.mdx
Normal file
28
docs/en/audio/component.mdx
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
order: 2
|
||||
title: Audio Component
|
||||
type: Audio
|
||||
label: Audio
|
||||
---
|
||||
|
||||
The Audio Component is a core component in Galacean used to add audio playback functionality to entities. Through the audio component, developers can control the playback, pausing, stopping, and volume adjustment of audio files.
|
||||
|
||||
## Adding the Audio Component
|
||||
|
||||
Developers can follow these steps to add the audio component to an entity:
|
||||
|
||||
1. **Select the Target Entity**: Choose the [Entity](/en/docs/core/entity) where you want to add the audio component.
|
||||
2. **Access the Inspector Panel**: Navigate to the Inspector panel for the selected entity.
|
||||
3. **Add the Component**: At the bottom of the Inspector panel, click on "Add Component" and select "AudioSource".
|
||||
|
||||
<video src="https://gw.alipayobjects.com/v/huamei_edbbqz/afts/video/JJ5QSKZKhzgAAAAAAAAAAAAADoY9AQFr" autoPlay loop muted />
|
||||
|
||||
## Adjusting Volume and Mute
|
||||
|
||||
The audio component provides features to adjust the volume and mute the audio. Developers can control these properties either through scripts or directly in the Inspector panel.
|
||||
|
||||
<video src="https://gw.alipayobjects.com/v/huamei_edbbqz/afts/video/lDyIRbwv5nAAAAAAAAAAAAAADoY9AQFr" autoPlay loop muted />
|
||||
|
||||
## Controlling Playback Timing
|
||||
|
||||
The audio component allows developers to precisely control the timing of audio playback. By using scripts, developers can invoke methods to play, pause, and stop audio files as needed.
|
||||
21
docs/en/audio/overview.mdx
Normal file
21
docs/en/audio/overview.mdx
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
order: 0
|
||||
title: Audio Overview
|
||||
type: Audio
|
||||
label: Audio
|
||||
---
|
||||
|
||||
Galacean Engine provides comprehensive audio functionality, which is an essential part of creating an immersive gaming experience. The audio system in Galacean supports the import of various standard audio formats, allowing developers to add audio components to [Entities](/en/docs/core/entity) and precisely play sounds in 3D space, including background music and sound effects, thereby providing a more complete and realistic experience for the game.
|
||||
|
||||
## Audio Assets
|
||||
|
||||
Audio assets are the core components in Galacean used for storing and managing audio files. Through audio assets, developers can conveniently upload, manage, and utilize various audio files.
|
||||
|
||||
## Audio Component
|
||||
|
||||
The audio component is the core component in Galacean used to add audio playback functionality to entities. Through the audio component, developers can control the playback, pausing, stopping, and volume adjustment of audio files.
|
||||
|
||||
In this section, you can learn about:
|
||||
|
||||
- [Audio Assets](/en/docs/audio/assets/): Information about audio files and their supported formats
|
||||
- [Audio Component](/en/docs/audio/component/): Details about the audio component and its properties
|
||||
22
docs/en/physics/_meta.json
Normal file
22
docs/en/physics/_meta.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"overall": {
|
||||
"title": "Physics Overview",
|
||||
"order": 0
|
||||
},
|
||||
"collider": {
|
||||
"title": "Collider",
|
||||
"order": 1
|
||||
},
|
||||
"joint": {
|
||||
"title": "Joint",
|
||||
"order": 2
|
||||
},
|
||||
"manager": {
|
||||
"title": "Physics Scene",
|
||||
"order": 3
|
||||
},
|
||||
"debug": {
|
||||
"title": "Physics Debug",
|
||||
"order": 4
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
---
|
||||
order: 3
|
||||
title: Collider Component
|
||||
type: Physics
|
||||
label: Physics
|
||||
---
|
||||
|
||||
The biggest advantage of introducing a physics engine is that it allows objects in the scene to have physical responses. Colliders ([Collider](/apis/core/#Collider)) are a type of component in the engine, and there are currently two types. Before using them, we need to understand these two types of colliders:
|
||||
|
||||
1. [StaticCollider](/apis/core/#StaticCollider): Static collider, mainly used for stationary objects in the scene;
|
||||
2. [DynamicCollider](/apis/core/#DynamicCollider): Dynamic collider, used for objects in the scene that need to be controlled by scripts or respond to physical feedback.
|
||||
|
||||
## Editor Usage
|
||||
|
||||
### Adding Collider Component
|
||||
|
||||
Before adding a physics component to an object, the first thing to consider is whether the collider is static or dynamic, and then add the corresponding collider component, either a static collider [StaticCollider](/apis/core/#StaticCollider) or a dynamic collider [DynamicCollider](/apis/core/#DynamicCollider).
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*-E4USbdiH6sAAAAAAAAAAAAADsJ_AQ/original" />
|
||||
|
||||
### Selecting the Shape of the Collider
|
||||
|
||||
Next, we need to add a [ColliderShape](/apis/core/#ColliderShape) to the collider component. In fact, each `Collider` is a collection of [ColliderShape](/apis/core/#ColliderShape), meaning each `Collider` can be set to a composite collider shape by combining [ColliderShape](/apis/core/#ColliderShape).
|
||||
|
||||
Currently, four types of `ColliderShape` are supported, but the support varies among different backend physics packages, as detailed below:
|
||||
|
||||
| Name | Description | Supported Backend Physics Packages |
|
||||
| :--- |:---------|:----------------------------|
|
||||
| [BoxColliderShape](/apis/core/#BoxColliderShape) | Box-shaped collider | physics-lite, physics-physx |
|
||||
| [SphereColliderShape](/apis/core/#SphereColliderShape) | Sphere-shaped collider | physics-lite, physics-physx |
|
||||
| [PlaneColliderShape](/apis/core/#PlaneColliderShape) | Unbounded plane collider | physics-physx |
|
||||
| [CapsuleColliderShape](/apis/core/#CapsuleColliderShape) | Capsule-shaped collider | physics-physx |
|
||||
|
||||
The engine supports composite collider shapes, meaning the collider itself can be composed of `BoxColliderShape`, `SphereColliderShape`, and `CapsuleColliderShape`.
|
||||
|
||||
It is particularly emphasized here the positional relationship between `Collider` and `ColliderShape`. The posture of each `Collider` is consistent with the `Entity` it is attached to, and they are synchronized every frame. The `ColliderShape` can set an offset **relative to** the `Entity` through the `position` property.
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_vvspai/afts/img/A*erlGRKk7dNMAAAAAAAAAAAAADsqFAQ/original" />
|
||||
|
||||
After adding the collider component, the collider shape is not added by default, so you need to click Add Item to add it. After adding, you will see the auxiliary rendering of the collider appear in the viewport.
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*OUr-SIejEkoAAAAAAAAAAAAADsJ_AQ/original" />
|
||||
|
||||
For each collider shape, you can design corresponding size properties. For example:
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*d4MCRbuHeMsAAAAAAAAAAAAADsJ_AQ/original" alt="alt text" style={{ zoom: "67%" }} />
|
||||
|
||||
No matter which collider shape is used, you can set the Local Position, which is the local offset relative to the Entity coordinates.
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*p8UcRJ9Q0EIAAAAAAAAAAAAADsJ_AQ/original" />
|
||||
|
||||
The `ColliderShape` also has a noteworthy property called `Trigger`, which can switch this `ColliderShape` from `collider mode` to `trigger mode`.
|
||||
|
||||
Trigger mode: The object does not have a rigid body shape, but can trigger specific script functions when contact occurs.
|
||||
Collider mode: The object has a rigid body shape, and when contact occurs, it can not only trigger script functions but also change its original motion according to physical laws.
|
||||
|
||||
### Dynamic Collider Settings
|
||||
Unlike static colliders, dynamic colliders are subject to physical laws, so there are many additional physical properties to set.
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*7rzqSKtjULMAAAAAAAAAAAAADsJ_AQ/original" alt="alt text" style={{ zoom: "67%" }} />
|
||||
|
||||
After modifying these parameters, the viewport will not change because dynamic colliders are subject to gravity by default, so you need to observe them in `preview mode`.
|
||||
|
||||
### Note
|
||||
- The determined collision area should be as simple as possible to improve the performance of the physics engine detection.
|
||||
- The reference coordinate system of the collider is the coordinate system of the subordinate Entity.
|
||||
- PlaneColliderShape represents a full plane, so there are no auxiliary lines displayed, and it is generally used as a floor.
|
||||
|
||||
## Script Usage
|
||||
|
||||
### Adding a Collider
|
||||
``` typescript
|
||||
// 添加静态碰撞器
|
||||
const boxCollider = boxEntity.addComponent(StaticCollider);
|
||||
// 添加动态碰撞器
|
||||
const sphereCollider = sphereEntity.addComponent(DynamicCollider);
|
||||
```
|
||||
|
||||
### Adding a ColliderShape
|
||||
``` typescript
|
||||
const boxCollider = boxEntity.getComponent(StaticCollider);
|
||||
const physicsBox = new BoxColliderShape();
|
||||
physicsBox.size = new Vector3(1, 1, 1);
|
||||
boxCollider.addShape(physicsBox);
|
||||
|
||||
//设置 Trigger
|
||||
physicsBox.isTrigger = true;
|
||||
```
|
||||
|
||||
For these two types, corresponding functions are provided in the script.
|
||||
|
||||
### Trigger Script Functions
|
||||
|
||||
For trigger mode, you first need to add a `Collider` to the `Entity` in the scene; when these components come into contact, three functions in the script component will be automatically triggered:
|
||||
|
||||
1. [onTriggerEnter](/en/docs/script#$1-ontriggerenter): Called when contact occurs.
|
||||
2. [onTriggerStay](/en/docs/script#$1-ontriggerstay): *Loop* called during contact.
|
||||
3. [onTriggerExit](/en/docs/script#$1-ontriggerexit): Called when contact ends.
|
||||
|
||||
You can enable trigger mode through the `isTrigger` property on `ColliderShape`, but it is particularly emphasized that **trigger events will not be called between two StaticColliders**, unless one of them is a `DynamicCollider`.
|
||||
|
||||
<Playground href="/embed/physx-collision-detection" />
|
||||
|
||||
### Collider Script Functions
|
||||
|
||||
For collider mode, when `DynamicColliders` interact, three collision-related script functions will be triggered:
|
||||
1. [onCollisionEnter](/en/docs/script#$1-oncollisionenter): Called when a collision occurs.
|
||||
2. [onCollisionStay](/en/docs/script#$1-oncollisionstay): *Loop* called during the collision.
|
||||
3. [onCollisionExit](/en/docs/script#$1-oncollisionexit): Called when the collision ends.
|
||||
|
||||
<Playground href="/embed/physx-compound" />
|
||||
174
docs/en/physics/collider/characterController.mdx
Normal file
174
docs/en/physics/collider/characterController.mdx
Normal file
@@ -0,0 +1,174 @@
|
||||
---
|
||||
order: 3
|
||||
title: Character Controller
|
||||
type: Physics
|
||||
label: Physics
|
||||
---
|
||||
|
||||
Character Controller ([CharacterController](/apis/core/#CharacterController)) is a special type of collider component specifically designed for handling character movement. It provides specialized movement algorithms and collision detection, capable of handling steps, slopes, and other complex terrains, making it particularly suitable for first-person or third-person game character control.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Select the target entity and click the Add Component button in the inspector to add the CharacterController component.
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*nEMXRKiqpy8AAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
2. Set the collision shape of the controller to match the character's appearance as closely as possible. For detailed instructions on collision shapes, please refer to the [Collision Shape](/docs/physics/collider/colliderShape) documentation.
|
||||
|
||||
<Callout type="positive">
|
||||
Unlike other colliders, the character controller can only add one collision shape. It is generally recommended to use a capsule shape (CapsuleColliderShape) as the character's collision shape.
|
||||
</Callout>
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*4QvUTI4D89EAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*aRGqSIMqDmsAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
3. Adjust the properties of the collider as needed to modify the physical behavior of the object. The meaning and function of each property are explained below.
|
||||
|
||||
## Property Explanation
|
||||
|
||||
|
||||
### Inherited from Collider
|
||||
| Property | Description |
|
||||
| ----------------------------------------- | ----------------- |
|
||||
| [**shapes**](/apis/core/#Collider-shapes) | Collection of collision shapes |
|
||||
|
||||
### CharacterController Specific Properties
|
||||
| Property | Description | Default Value |
|
||||
| ---- | ---- | ------ |
|
||||
| [**stepOffset**](/apis/core/#CharacterController-stepOffset) | The maximum step height the character can automatically step over. <ul><li>Must be greater than or equal to 0</li><li>Actual step height = stepOffset + contact offset of the collision shape</li></ul> | 0.5 |
|
||||
| [**slopeLimit**](/apis/core/#CharacterController-slopeLimit) | The maximum slope angle (degrees) the character can walk on. <ul><li>Slopes steeper than this angle will be treated as walls</li><li>Affects the character's ability to climb slopes</li></ul> | 45° |
|
||||
| [**nonWalkableMode**](/apis/core/#CharacterController-nonWalkableMode) | Defines how to handle non-walkable surfaces. <ul><li>PreventClimbing: Prevents the character from climbing non-walkable slopes but does not force other movements (default)</li><li>PreventClimbingAndForceSliding: Prevents the character from climbing non-walkable slopes and forces the character to slide down the slope</li></ul> | PreventClimbing |
|
||||
| [**upDirection**](/apis/core/#CharacterController-upDirection) | Defines the upward direction of the character. The default is (0, 1, 0), which is the Y-axis in world space. Affects the direction determination for movement and collision detection | (0, 1, 0) |
|
||||
|
||||
## Methods
|
||||
|
||||
|
||||
### Inherited from Collider
|
||||
| Method Name | Description |
|
||||
| --------------------------------------------------- | ----------------- |
|
||||
| [**addShape**](/apis/core/#Collider-addShape) | Add a collision shape |
|
||||
| [**removeShape**](/apis/core/#Collider-removeShape) | Remove a specified collision shape |
|
||||
| [**clearShapes**](/apis/core/#Collider-clearShapes) | Clear all collision shapes |
|
||||
|
||||
### CharacterController Specific Methods
|
||||
| Method Name | Description |
|
||||
| ---- | ---- |
|
||||
| [**move**](/apis/core/#CharacterController-move) | Moves the character controller. Returns a collision flag value indicating the collision state. <ul><li>displacement: Movement vector</li><li>minDist: Minimum movement distance</li><li>elapsedTime: Elapsed time</li></ul> |
|
||||
|
||||
### Collision Flags
|
||||
|
||||
The `move()` function returns a collision flag value that indicates the collision state of the character controller with the environment. These flags can be checked using bitwise AND operations (&):
|
||||
|
||||
| Flag Name | Value | Description |
|
||||
|---------|----|----|
|
||||
| None | 0 | No collision occurred |
|
||||
| Sides | 1 | Collided with sides |
|
||||
| Up | 2 | Collided with the top (e.g., ceiling) |
|
||||
| Down | 4 | Collided with the bottom (e.g., ground) |
|
||||
|
||||
## Script Usage
|
||||
|
||||
### Basic Configuration
|
||||
```typescript
|
||||
// Create character controller
|
||||
const controller = entity.addComponent(CharacterController);
|
||||
|
||||
// Add capsule shape
|
||||
const capsule = new CapsuleColliderShape();
|
||||
capsule.radius = 0.5;
|
||||
capsule.height = 2;
|
||||
controller.addShape(capsule);
|
||||
|
||||
// Configure controller properties
|
||||
controller.stepOffset = 0.5; // Set step height
|
||||
controller.slopeLimit = 45; // Set maximum walkable slope angle
|
||||
controller.upDirection = new Vector3(0, 1, 0); // Set upward direction
|
||||
```
|
||||
|
||||
### Using the Move Function
|
||||
```typescript
|
||||
class CharacterMovement extends Script {
|
||||
private _velocity = new Vector3();
|
||||
|
||||
onUpdate() {
|
||||
const controller = this.entity.getComponent(CharacterController);
|
||||
const deltaTime = engine.deltaTime;
|
||||
|
||||
// Create displacement vector
|
||||
const displacement = new Vector3();
|
||||
Vector3.scale(this._velocity, deltaTime, displacement);
|
||||
|
||||
// Execute movement and get collision flags
|
||||
// minDist: Minimum movement distance, usually set to 0
|
||||
// deltaTime: Elapsed time for physics calculations
|
||||
const collisionFlags = controller.move(displacement, 0, deltaTime);
|
||||
|
||||
// Handle collision response
|
||||
if (collisionFlags & ControllerCollisionFlag.Down) {
|
||||
// Character is on the ground
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Collision Flags
|
||||
|
||||
The `move()` function returns a collision flag value indicating the character controller's collision state with the environment. These flags can be checked using bitwise AND (&) operations:
|
||||
|
||||
| Flag Name | Value | Description |
|
||||
|---------|----|----|
|
||||
| None | 0 | No collision occurred |
|
||||
| Sides | 1 | Collision with sides |
|
||||
| Up | 2 | Collision with ceiling |
|
||||
| Down | 4 | Collision with ground |
|
||||
|
||||
Usage example:
|
||||
```typescript
|
||||
const flags = controller.move(displacement, 0, deltaTime);
|
||||
|
||||
// Check if touching ground
|
||||
if (flags & ControllerCollisionFlag.Down) {
|
||||
// Character is on ground
|
||||
this._isGrounded = true;
|
||||
}
|
||||
|
||||
// Check if hitting ceiling
|
||||
if (flags & ControllerCollisionFlag.Up) {
|
||||
// Character hit ceiling
|
||||
this._velocity.y = 0;
|
||||
}
|
||||
|
||||
// Check if hitting walls
|
||||
if (flags & ControllerCollisionFlag.Sides) {
|
||||
// Character hit wall
|
||||
this._handleWallCollision();
|
||||
}
|
||||
|
||||
// Check multiple flags
|
||||
if ((flags & ControllerCollisionFlag.Down) &&
|
||||
(flags & ControllerCollisionFlag.Sides)) {
|
||||
// Character is touching both ground and wall
|
||||
}
|
||||
```
|
||||
|
||||
### Walking on Slopes/Steps
|
||||
|
||||
1. **Walking on Slopes**
|
||||
```typescript
|
||||
// Control the walkable slope angle by setting slopeLimit
|
||||
controller.slopeLimit = 60; // Allow steeper slopes
|
||||
|
||||
// Set the handling method for non-walkable slopes
|
||||
controller.nonWalkableMode = ControllerNonWalkableMode.PreventClimbingAndForceSliding; // Slide down steep slopes
|
||||
```
|
||||
|
||||
2. **Adjusting Step Height**
|
||||
```typescript
|
||||
// Adjust stepOffset to control the maximum step height
|
||||
controller.stepOffset = 0.3; // Lower steps
|
||||
controller.stepOffset = 0.5; // Higher steps
|
||||
```
|
||||
|
||||
For a complete example, refer to:
|
||||
<Playground href="/embed/physx-controller" />
|
||||
290
docs/en/physics/collider/colliderShape.mdx
Normal file
290
docs/en/physics/collider/colliderShape.mdx
Normal file
@@ -0,0 +1,290 @@
|
||||
---
|
||||
order: 4
|
||||
title: Collision Shapes
|
||||
type: Physics
|
||||
label: Physics
|
||||
---
|
||||
|
||||
Collision shapes ([ColliderShape](/apis/core/#ColliderShape)) define the physical form of colliders. Galacean provides several basic collision shapes that can be combined to create complex collision areas.
|
||||
|
||||
## Supported Shape Types
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*jsEBTIs9C_MAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
| Shape Type | Features | Backend Support |
|
||||
| -------- | ---- | -------- |
|
||||
| [**BoxColliderShape**](/apis/core/#BoxColliderShape) | Most fundamental collision shape<br/>Ideal for box-like objects<br/>Size adjustable along all three axes using size property | All physics backends |
|
||||
| [**SphereColliderShape**](/apis/core/#SphereColliderShape) | Perfect for spherical collision detection<br/>Single radius parameter for simple configuration | All physics backends |
|
||||
| [**PlaneColliderShape**](/apis/core/#PlaneColliderShape) | Infinite plane collision surface<br/>Ideal for ground planes | PhysX physics backend only |
|
||||
| [**CapsuleColliderShape**](/apis/core/#CapsuleColliderShape) | Combination of cylinder and hemispheres<br/>Perfect for character controllers<br/>Configurable radius and height | PhysX physics backend only |
|
||||
|
||||
## Basic Properties
|
||||
|
||||
### Transform Properties
|
||||
- [**position**](/apis/core/#ColliderShape-position)
|
||||
Local position offset relative to the collider entity.
|
||||
|
||||
- [**rotation**](/apis/core/#ColliderShape-rotation)
|
||||
Local rotation relative to the collider entity.
|
||||
|
||||
### Physics Material
|
||||
- [**material**](/apis/core/#ColliderShape-material)
|
||||
Defines the physical material properties of the shape. Each collision shape needs a physical material to define its physical characteristics:
|
||||
|
||||
| Property | Description | Default Value |
|
||||
| ---- | ---- | ------ |
|
||||
| [**staticFriction**](/apis/core/#PhysicsMaterial-staticFriction) | Friction when the object is stationary. Higher values increase starting resistance | 0.6 |
|
||||
| [**dynamicFriction**](/apis/core/#PhysicsMaterial-dynamicFriction) | Friction when the object is moving. Higher values increase movement resistance | 0.6 |
|
||||
| [**bounciness**](/apis/core/#PhysicsMaterial-bounciness) | Controls collision restitution. Range: 0-1 (0 = no bounce, 1 = perfect bounce) | 0 |
|
||||
| [**bounceCombine**](/apis/core/#PhysicsMaterial-bounceCombine) | How bounce combines between objects: <ul><li>Average: Mean of both values (default)</li><li>Minimum: Lower of both values</li><li>Maximum: Higher of both values</li><li>Multiply: Product of both values</li></ul> | Average |
|
||||
| [**frictionCombine**](/apis/core/#PhysicsMaterial-frictionCombine) | How friction combines between objects: <ul><li>Average: Mean of both values (default)</li><li>Minimum: Lower of both values</li><li>Maximum: Higher of both values</li><li>Multiply: Product of both values</li></ul> | Average |
|
||||
|
||||
### Trigger Settings
|
||||
- [**isTrigger**](/apis/core/#ColliderShape-isTrigger)
|
||||
Enable trigger mode:
|
||||
- true: Event triggers only, no physics
|
||||
- false: Normal physical collisions (default)
|
||||
|
||||
|
||||
For detailed information about collision and trigger events, please refer to the [Collision Events](/docs/physics/collider/event) documentation.
|
||||
|
||||
## Script Usage
|
||||
|
||||
### Creating Basic Shapes
|
||||
|
||||
```typescript
|
||||
// Create box collider
|
||||
const boxShape = new BoxColliderShape();
|
||||
boxShape.size = new Vector3(1, 1, 1);
|
||||
boxShape.position = new Vector3(0, 0.5, 0);
|
||||
|
||||
// Create sphere collider
|
||||
const sphereShape = new SphereColliderShape();
|
||||
sphereShape.radius = 0.5;
|
||||
sphereShape.position = new Vector3(0, 1, 0);
|
||||
|
||||
// Create capsule collider
|
||||
const capsuleShape = new CapsuleColliderShape();
|
||||
capsuleShape.radius = 0.5;
|
||||
capsuleShape.height = 2;
|
||||
|
||||
// Create plane collider
|
||||
const planeShape = new PlaneColliderShape();
|
||||
```
|
||||
|
||||
### Shape Management
|
||||
|
||||
```typescript
|
||||
// Get all shapes
|
||||
const shapes = staticCollider.shapes;
|
||||
|
||||
// Add a shape
|
||||
const boxShape = new BoxColliderShape();
|
||||
staticCollider.addShape(boxShape);
|
||||
|
||||
// Remove a specific shape
|
||||
staticCollider.removeShape(boxShape);
|
||||
|
||||
// Clear all shapes
|
||||
staticCollider.clearShapes();
|
||||
```
|
||||
|
||||
### Setting Physics Material
|
||||
|
||||
```typescript
|
||||
// Create and configure physics material
|
||||
const material = new PhysicsMaterial();
|
||||
material.staticFriction = 0.6; // Static friction coefficient
|
||||
material.dynamicFriction = 0.4; // Dynamic friction coefficient
|
||||
material.bounciness = 0.5; // Restitution coefficient
|
||||
|
||||
// Set material combine modes
|
||||
material.frictionCombine = PhysicsMaterialCombineMode.Average; // Friction combine mode
|
||||
material.bounceCombine = PhysicsMaterialCombineMode.Maximum; // Bounce combine mode
|
||||
|
||||
// Apply material to collision shape
|
||||
const shape = new BoxColliderShape();
|
||||
shape.material = material;
|
||||
|
||||
// Set different materials for different shapes
|
||||
const iceShape = new BoxColliderShape();
|
||||
const iceMaterial = new PhysicsMaterial();
|
||||
iceMaterial.staticFriction = 0.1;
|
||||
iceMaterial.dynamicFriction = 0.05;
|
||||
iceMaterial.bounciness = 0;
|
||||
iceShape.material = iceMaterial;
|
||||
|
||||
const bounceShape = new SphereColliderShape();
|
||||
const bounceMaterial = new PhysicsMaterial();
|
||||
bounceMaterial.bounciness = 0.8;
|
||||
bounceMaterial.bounceCombine = PhysicsMaterialCombineMode.Maximum;
|
||||
bounceShape.material = bounceMaterial;
|
||||
|
||||
// Destroy materials when no longer needed
|
||||
material.destroy();
|
||||
iceMaterial.destroy();
|
||||
bounceMaterial.destroy();
|
||||
```
|
||||
|
||||
### Creating Compound Shapes
|
||||
|
||||
```typescript
|
||||
// Create an entity with multiple collision shapes
|
||||
function createCompoundCollider(entity: Entity) {
|
||||
const collider = entity.addComponent(DynamicCollider);
|
||||
|
||||
// Main shape
|
||||
const mainShape = new BoxColliderShape();
|
||||
mainShape.size = new Vector3(2, 1, 1);
|
||||
collider.addShape(mainShape);
|
||||
|
||||
// Top shape
|
||||
const topShape = new BoxColliderShape();
|
||||
topShape.size = new Vector3(1, 1, 1);
|
||||
topShape.position = new Vector3(0, 1, 0);
|
||||
collider.addShape(topShape);
|
||||
|
||||
return collider;
|
||||
}
|
||||
```
|
||||
|
||||
#### Example Reference
|
||||
<Playground href="/embed/physx-compound" />
|
||||
|
||||
### Common Scenarios
|
||||
|
||||
1. **Creating Ground**
|
||||
```typescript
|
||||
// Create a simple ground
|
||||
function createGround(width: number, length: number): Entity {
|
||||
const entity = new Entity();
|
||||
const collider = entity.addComponent(StaticCollider);
|
||||
|
||||
const groundShape = new BoxColliderShape();
|
||||
groundShape.size = new Vector3(width, 0.1, length);
|
||||
|
||||
// Set ground material
|
||||
const material = new PhysicsMaterial();
|
||||
material.staticFriction = 0.6;
|
||||
material.dynamicFriction = 0.6;
|
||||
material.bounciness = 0.0;
|
||||
groundShape.material = material;
|
||||
|
||||
collider.addShape(groundShape);
|
||||
return entity;
|
||||
}
|
||||
|
||||
// Create an infinite plane ground
|
||||
function createInfinitePlane(): Entity {
|
||||
const entity = new Entity();
|
||||
const collider = entity.addComponent(StaticCollider);
|
||||
|
||||
const planeShape = new PlaneColliderShape();
|
||||
// Set ground material
|
||||
const material = new PhysicsMaterial();
|
||||
material.staticFriction = 0.6;
|
||||
material.dynamicFriction = 0.6;
|
||||
planeShape.material = material;
|
||||
|
||||
collider.addShape(planeShape);
|
||||
return entity;
|
||||
}
|
||||
```
|
||||
|
||||
2. **Creating Walls**
|
||||
```typescript
|
||||
// Create a configurable wall
|
||||
function createWall(width: number, height: number, depth: number, friction: number = 0.6): Entity {
|
||||
const entity = new Entity();
|
||||
const collider = entity.addComponent(StaticCollider);
|
||||
|
||||
const wallShape = new BoxColliderShape();
|
||||
wallShape.size = new Vector3(width, height, depth);
|
||||
|
||||
// Set wall material
|
||||
const material = new PhysicsMaterial();
|
||||
material.staticFriction = friction;
|
||||
material.dynamicFriction = friction;
|
||||
material.bounciness = 0.0;
|
||||
wallShape.material = material;
|
||||
|
||||
collider.addShape(wallShape);
|
||||
return entity;
|
||||
}
|
||||
```
|
||||
|
||||
3. **Creating Slopes**
|
||||
```typescript
|
||||
// Create a slope with friction
|
||||
function createSlope(width: number, height: number, angle: number): Entity {
|
||||
const entity = new Entity();
|
||||
const collider = entity.addComponent(StaticCollider);
|
||||
|
||||
const slopeShape = new BoxColliderShape();
|
||||
slopeShape.size = new Vector3(width, 0.1, height);
|
||||
slopeShape.rotation = new Vector3(0, 0, angle);
|
||||
|
||||
// Set slope material
|
||||
const material = new PhysicsMaterial();
|
||||
material.staticFriction = 0.8; // Higher friction to prevent sliding
|
||||
material.dynamicFriction = 0.8;
|
||||
material.bounciness = 0.0;
|
||||
slopeShape.material = material;
|
||||
|
||||
collider.addShape(slopeShape);
|
||||
return entity;
|
||||
}
|
||||
```
|
||||
|
||||
4. **Creating Compound Shapes with Different Materials**
|
||||
```typescript
|
||||
// Create a mixed-material platform
|
||||
function createMixedPlatform(): Entity {
|
||||
const entity = new Entity();
|
||||
const collider = entity.addComponent(StaticCollider);
|
||||
|
||||
// Create main platform (normal friction)
|
||||
const platformShape = new BoxColliderShape();
|
||||
platformShape.size = new Vector3(10, 0.5, 10);
|
||||
const normalMaterial = new PhysicsMaterial();
|
||||
normalMaterial.staticFriction = 0.6;
|
||||
normalMaterial.dynamicFriction = 0.6;
|
||||
platformShape.material = normalMaterial;
|
||||
collider.addShape(platformShape);
|
||||
|
||||
// Add ice area (low friction)
|
||||
const iceShape = new BoxColliderShape();
|
||||
iceShape.size = new Vector3(3, 0.51, 3);
|
||||
iceShape.position = new Vector3(3, 0, 0);
|
||||
const iceMaterial = new PhysicsMaterial();
|
||||
iceMaterial.staticFriction = 0.1;
|
||||
iceMaterial.dynamicFriction = 0.05;
|
||||
iceShape.material = iceMaterial;
|
||||
collider.addShape(iceShape);
|
||||
|
||||
// Add bouncy area
|
||||
const bounceShape = new BoxColliderShape();
|
||||
bounceShape.size = new Vector3(3, 0.51, 3);
|
||||
bounceShape.position = new Vector3(-3, 0, 0);
|
||||
const bounceMaterial = new PhysicsMaterial();
|
||||
bounceMaterial.bounciness = 0.8;
|
||||
bounceMaterial.bounceCombine = PhysicsMaterialCombineMode.Maximum;
|
||||
bounceShape.material = bounceMaterial;
|
||||
collider.addShape(bounceShape);
|
||||
|
||||
return entity;
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Shape Selection**
|
||||
- Use multiple basic shapes for complex objects
|
||||
- Avoid using too many collision shapes (affects performance)
|
||||
- Choose capsule shapes for character controllers
|
||||
|
||||
2. **Performance Optimization**
|
||||
- Use the simplest possible collision shapes
|
||||
- Set reasonable local offsets to avoid excessive overlap of collision shapes
|
||||
- Use triggers instead of actual physical collisions when appropriate
|
||||
- Destroy unused collision shapes and physical materials in time
|
||||
293
docs/en/physics/collider/dynamicCollider.mdx
Normal file
293
docs/en/physics/collider/dynamicCollider.mdx
Normal file
@@ -0,0 +1,293 @@
|
||||
---
|
||||
order: 2
|
||||
title: Dynamic Collider
|
||||
type: Physics
|
||||
label: Physics
|
||||
---
|
||||
|
||||
Dynamic Collider ([DynamicCollider](/apis/core/#DynamicCollider)) is used to simulate objects that can move freely and are affected by physical forces. It can respond to gravity, forces, collisions, and other physical effects, suitable for game objects that require realistic physical simulation.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Select the target entity and click the Add Component button in the inspector to add the DynamicCollider component.
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*Ep4xTqWpligAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
2. Add a collision shape to the collider. Dynamic colliders support adding multiple collision shapes. For detailed instructions on collision shapes, please refer to the [Collision Shape](/docs/physics/collider/colliderShape) documentation. Currently, the following types are supported:
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*np90QZkKqXUAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
3. Adjust the position, size, and other properties of the collision shape to match the scene elements.
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*QthGTLWFSh8AAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
4. Adjust the properties of the collider as needed to modify the physical behavior of the object. The meaning and function of each property are explained below.
|
||||
|
||||
## Property Explanation
|
||||
|
||||
|
||||
### Inherited from Collider
|
||||
| Property | Description |
|
||||
| ----------------------------------------- | ----------------- |
|
||||
| [**shapes**](/apis/core/#Collider-shapes) | Collection of collision shapes |
|
||||
|
||||
### DynamicCollider Specific Properties
|
||||
|
||||
| Property | Description | Default Value |
|
||||
| --------------- | --------------------------------------------------------- | ------ |
|
||||
| [**mass**](/apis/core/#DynamicCollider-mass) | Mass of the collider. The larger the mass, the harder it is to change the object's motion state | 1.0 |
|
||||
| [**useGravity**](/apis/core/#DynamicCollider-useGravity) | Whether to be affected by gravity | true |
|
||||
| [**isKinematic**](/apis/core/#DynamicCollider-isKinematic) | Whether it is a kinematic object. Kinematic objects are not affected by physics but can affect other objects | false |
|
||||
|
||||
### Velocity Settings
|
||||
|
||||
| Property | Description | Default Value |
|
||||
| ---------------------------- | ------------------------------------------- | ------- |
|
||||
| [**linearVelocity**](/apis/core/#DynamicCollider-linearVelocity) | Linear velocity vector (world units/second) | (0,0,0) |
|
||||
| [**angularVelocity**](/apis/core/#DynamicCollider-angularVelocity) | Angular velocity vector (degrees/second) | (0,0,0) |
|
||||
| [**maxAngularVelocity**](/apis/core/#DynamicCollider-maxAngularVelocity) | Maximum angular velocity limit (degrees/second) | 18000/π |
|
||||
| [**maxDepenetrationVelocity**](/apis/core/#DynamicCollider-maxDepenetrationVelocity) | Maximum separation velocity when colliders overlap, used to prevent objects from penetrating | 1e32 |
|
||||
|
||||
### Damping
|
||||
|
||||
| Property | Description | Default Value |
|
||||
| ------------------ | ---------------- | ------ |
|
||||
| [**linearDamping**](/apis/core/#DynamicCollider-linearDamping) | Linear motion damping coefficient | 0 |
|
||||
| [**angularDamping**](/apis/core/#DynamicCollider-angularDamping) | Angular velocity damping coefficient | 0.05 |
|
||||
|
||||
### Mass/Inertia Tensor
|
||||
|
||||
| Property | Description | Default Value |
|
||||
| -------------------------- | ------------------------ | ------- |
|
||||
| [**centerOfMass**](/apis/core/#DynamicCollider-centerOfMass) | Position of the center of mass relative to the transform origin | (0,0,0) |
|
||||
| [**automaticCenterOfMass**](/apis/core/#DynamicCollider-automaticCenterOfMass) | Whether to automatically calculate the center of mass | true |
|
||||
| [**inertiaTensor**](/apis/core/#DynamicCollider-inertiaTensor) | Inertia tensor of the object relative to the center of mass | (1,1,1) |
|
||||
| [**automaticInertiaTensor**](/apis/core/#DynamicCollider-automaticInertiaTensor) | Whether to automatically calculate the inertia tensor | true |
|
||||
|
||||
### Performance Optimization Settings
|
||||
|
||||
| Property | Description | Default Value |
|
||||
| -------------------- | --------------------------------------- | ------ |
|
||||
| [**sleepThreshold**](/apis/core/#DynamicCollider-sleepThreshold) | Sleep threshold. Objects enter sleep mode when their motion energy falls below this value | 0.005 |
|
||||
| [**solverIterations**](/apis/core/#DynamicCollider-solverIterations) | Constraint solver iteration count | 4 |
|
||||
|
||||
<Callout type="info">
|
||||
Constraint solving is the process used by the physics engine to resolve collisions and constraints between objects. Each iteration attempts to adjust the positions and velocities of objects to satisfy all physical constraints (such as collisions, joints, etc.).
|
||||
- More iterations result in more accurate physical behavior but higher computational cost
|
||||
- Too few iterations may cause objects to jitter or penetrate
|
||||
- It is recommended to balance accuracy and performance based on actual needs:
|
||||
- General objects: Use the default value of 4
|
||||
- Precise physics: Increase to 6-8
|
||||
- Performance priority: Reduce to 2-3
|
||||
</Callout>
|
||||
|
||||
### Motion Constraints
|
||||
|
||||
- [**constraints**](/apis/core/#DynamicCollider-constraints)
|
||||
Used to restrict the object's movement along specific axes. You can lock position and rotation along the X, Y, and Z axes.
|
||||
```typescript
|
||||
// Lock Y-axis position and all rotations
|
||||
collider.constraints =
|
||||
DynamicColliderConstraints.FreezePositionY |
|
||||
DynamicColliderConstraints.FreezeRotationX |
|
||||
DynamicColliderConstraints.FreezeRotationY |
|
||||
DynamicColliderConstraints.FreezeRotationZ;
|
||||
```
|
||||
|
||||
### Collision Detection Mode
|
||||
|
||||
- [**collisionDetectionMode**](/apis/core/#DynamicCollider-collisionDetectionMode)
|
||||
Controls the precision of collision detection:
|
||||
|
||||
| Mode | Description | Applicable Scenarios | Performance Cost |
|
||||
| ---- | ---- | -------- | -------- |
|
||||
| **Discrete** | Basic detection mode, detects collisions at fixed time steps, may cause tunneling at high speeds | Low-speed objects | Lowest |
|
||||
| **Continuous** | Continuous detection for static colliders, prevents high-speed objects from tunneling through static objects | High-speed projectiles | Moderate |
|
||||
| **ContinuousDynamic** | Continuous detection for all colliders, prevents high-speed objects from tunneling through each other | Precise physical simulations | High |
|
||||
| **ContinuousSpeculative** | Uses speculative algorithms for continuous detection, performance cost between Discrete and Continuous | General game scenarios | Moderate |
|
||||
|
||||
#### Example of Setting Collision Detection Mode
|
||||
|
||||
```typescript
|
||||
// 1. Use discrete detection for normal objects
|
||||
normalObject.collisionDetectionMode = CollisionDetectionMode.Discrete;
|
||||
|
||||
// 2. Use continuous detection for projectiles to prevent tunneling
|
||||
projectile.collisionDetectionMode = CollisionDetectionMode.Continuous;
|
||||
|
||||
// 3. Use fully continuous detection for important physical interactions
|
||||
importantPhysicsObject.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
|
||||
|
||||
// 4. Use speculative detection for general game objects
|
||||
gameObject.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
|
||||
```
|
||||
|
||||
#### Selection Recommendations
|
||||
|
||||
1. **Based on Object Speed**
|
||||
|
||||
- Low-speed objects: Use Discrete
|
||||
- Medium-speed objects: Use ContinuousSpeculative
|
||||
- High-speed objects: Use Continuous or ContinuousDynamic
|
||||
|
||||
2. **Based on Importance**
|
||||
|
||||
- Normal scene objects: Use Discrete
|
||||
- Critical game objects: Use ContinuousSpeculative
|
||||
- Precise physical interactions: Use ContinuousDynamic
|
||||
|
||||
3. **Based on Performance**
|
||||
- Performance priority: Use Discrete
|
||||
- Balance between performance and precision: Use ContinuousSpeculative
|
||||
- Precision priority: Use ContinuousDynamic
|
||||
|
||||
## Methods
|
||||
|
||||
### Inherited from Collider
|
||||
| Method Name | Description |
|
||||
| --------------------------------------------------- | ----------------- |
|
||||
| [**addShape**](/apis/core/#Collider-addShape) | Add a collision shape |
|
||||
| [**removeShape**](/apis/core/#Collider-removeShape) | Remove a specified collision shape |
|
||||
| [**clearShapes**](/apis/core/#Collider-clearShapes) | Clear all collision shapes |
|
||||
|
||||
### DynamicCollider Specific Methods
|
||||
| Method Name | Description |
|
||||
| --------------- | ------------------ |
|
||||
| [**applyForce**](/apis/core/#DynamicCollider-applyForce) | Apply force |
|
||||
| [**applyTorque**](/apis/core/#DynamicCollider-applyTorque) | Apply torque |
|
||||
| [**move**](/apis/core/#DynamicCollider-move) | Kinematic move |
|
||||
| [**sleep**](/apis/core/#DynamicCollider-sleep) | Force sleep |
|
||||
| [**wakeUp**](/apis/core/#DynamicCollider-wakeUp) | Wake up the object |
|
||||
| [**isSleeping**](/apis/core/#DynamicCollider-isSleeping) | Check if the object is sleeping |
|
||||
|
||||
## Script Usage
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
```typescript
|
||||
// Create dynamic collider
|
||||
const dynamicCollider = entity.addComponent(DynamicCollider);
|
||||
|
||||
// Add collision shape
|
||||
const boxShape = new BoxColliderShape();
|
||||
boxShape.size = new Vector3(1, 1, 1);
|
||||
dynamicCollider.addShape(boxShape);
|
||||
|
||||
// Configure basic physical properties
|
||||
dynamicCollider.mass = 1.0; // Set mass
|
||||
dynamicCollider.useGravity = true; // Enable gravity
|
||||
dynamicCollider.isKinematic = false; // Set to dynamic mode
|
||||
```
|
||||
|
||||
### Motion Control
|
||||
|
||||
```typescript
|
||||
class PhysicsController extends Script {
|
||||
private _collider: DynamicCollider;
|
||||
|
||||
onAwake() {
|
||||
// Get dynamic collider reference
|
||||
this._collider = this.entity.getComponent(DynamicCollider);
|
||||
|
||||
// Configure motion damping
|
||||
this._collider.linearDamping = 0.1; // Set linear damping
|
||||
this._collider.angularDamping = 0.1; // Set angular damping
|
||||
|
||||
// Set motion constraints
|
||||
this._collider.constraints =
|
||||
DynamicColliderConstraints.FreezeRotationX | // Lock X-axis rotation
|
||||
DynamicColliderConstraints.FreezeRotationZ; // Lock Z-axis rotation
|
||||
}
|
||||
|
||||
onUpdate() {
|
||||
// Get current velocity
|
||||
const velocity = this._collider.linearVelocity;
|
||||
|
||||
// Set velocity
|
||||
this._collider.linearVelocity = new Vector3(5, velocity.y, 0);
|
||||
|
||||
// Apply continuous force (e.g., thrust)
|
||||
if (this.engine.inputManager.isKeyHeldDown(Keys.W)) {
|
||||
this._collider.applyForce(new Vector3(0, 0, 10));
|
||||
}
|
||||
|
||||
// Apply instantaneous force (e.g., jump)
|
||||
if (this.engine.inputManager.isKeyDown(Keys.Space)) {
|
||||
this._collider.applyForce(new Vector3(0, 500, 0));
|
||||
}
|
||||
|
||||
// Apply torque (e.g., rotation)
|
||||
if (this.engine.inputManager.isKeyHeldDown(Keys.R)) {
|
||||
this._collider.applyTorque(new Vector3(0, 10, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Kinematic Control
|
||||
|
||||
```typescript
|
||||
class KinematicController extends Script {
|
||||
private _collider: DynamicCollider;
|
||||
|
||||
onAwake() {
|
||||
this._collider = this.entity.getComponent(DynamicCollider);
|
||||
this._collider.isKinematic = true; // Set to kinematic mode
|
||||
}
|
||||
|
||||
// Implement elevator movement
|
||||
onUpdate() {
|
||||
const time = this.engine.time.elapsedTime;
|
||||
const position = new Vector3(0, Math.sin(time) * 2, 0);
|
||||
this._collider.move(position);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Sleep Management
|
||||
|
||||
```typescript
|
||||
class SleepController extends Script {
|
||||
private _collider: DynamicCollider;
|
||||
|
||||
onAwake() {
|
||||
this._collider = this.entity.getComponent(DynamicCollider);
|
||||
|
||||
// Configure sleep parameters
|
||||
this._collider.sleepThreshold = 0.005; // Set sleep threshold
|
||||
}
|
||||
|
||||
onUpdate() {
|
||||
// Check if the object is sleeping
|
||||
if (this._collider.isSleeping()) {
|
||||
console.log("Object is sleeping");
|
||||
}
|
||||
|
||||
// Manually control sleep
|
||||
if (this.engine.inputManager.isKeyDown(Keys.S)) {
|
||||
this._collider.sleep(); // Force sleep
|
||||
}
|
||||
|
||||
if (this.engine.inputManager.isKeyDown(Keys.W)) {
|
||||
this._collider.wakeUp(); // Wake up the object
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Mass/Inertia Tensor Settings
|
||||
|
||||
```typescript
|
||||
// Automatic calculation
|
||||
const collider = entity.addComponent(DynamicCollider);
|
||||
collider.mass = 1.0;
|
||||
collider.automaticCenterOfMass = true; // Automatically calculate center of mass
|
||||
collider.automaticInertiaTensor = true; // Automatically calculate inertia tensor
|
||||
|
||||
// Manual settings
|
||||
const customCollider = entity.addComponent(DynamicCollider);
|
||||
customCollider.automaticCenterOfMass = false;
|
||||
customCollider.automaticInertiaTensor = false;
|
||||
customCollider.centerOfMass = new Vector3(0, 0.5, 0); // Manually set center of mass
|
||||
customCollider.inertiaTensor = new Vector3(1, 1, 1); // Manually set inertia tensor
|
||||
```
|
||||
89
docs/en/physics/collider/event.mdx
Normal file
89
docs/en/physics/collider/event.mdx
Normal file
@@ -0,0 +1,89 @@
|
||||
---
|
||||
order: 5
|
||||
title: Collision Events
|
||||
type: Physics
|
||||
label: Physics
|
||||
---
|
||||
|
||||
Collision events are an important part of the physics system, allowing scripts to respond to physical interactions between objects. The Galacean physics system provides two types of events: collision events and trigger events.
|
||||
|
||||
## Event Types
|
||||
|
||||
### Collision Events
|
||||
Collision events are triggered when two colliders in collider mode physically interact with each other. These events provide information about the collision, such as the contact point, normal, and the other collider involved.
|
||||
|
||||
### Trigger Events
|
||||
Trigger events are triggered when a collider enters, stays in, or exits a trigger area. Triggers are collision shapes with the `isTrigger` property set to true. They detect overlaps without causing physical reactions.
|
||||
|
||||
## Event Trigger Relationship
|
||||
|
||||
The detailed event trigger relationship between colliders and triggers is illustrated below:
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_vvspai/afts/img/A*erlGRKk7dNMAAAAAAAAAAAAADsqFAQ/original" />
|
||||
|
||||
## Collider Mode VS Trigger Mode
|
||||
|
||||
| Mode | Description | Applicable Scenarios |
|
||||
| ---- | ---- | -------- |
|
||||
| **Collider Mode** | Triggers collider events and has actual physical collision effects | Objects requiring real physical interaction |
|
||||
| **Trigger Mode** | Only triggers event callbacks, no physical collision | Area detection, trigger mechanisms, etc. |
|
||||
|
||||
<Callout type="positive">
|
||||
Collider mode and trigger mode are properties of collision shapes. A collider can contain multiple collision shapes, each of which can be independently set as a trigger. This means:
|
||||
A collider can have both modes simultaneously, with some shapes set to collider mode and others to trigger mode, allowing for both physical collisions and area detection
|
||||
</Callout>
|
||||
|
||||
## Event Callbacks
|
||||
|
||||
When two colliders collide or trigger overlaps occur, callback functions in [scripts](/docs/script/script) attached to the entity owning the collider will be triggered.
|
||||
|
||||
### Collider Event Callbacks
|
||||
| Event Name | Trigger Timing |
|
||||
| --- | --- |
|
||||
| [**onCollisionEnter**](/apis/core/#Script-onCollisionEnter) | Triggered when collision starts |
|
||||
| [**onCollisionExit**](/apis/core/#Script-onCollisionExit) | Triggered when collision ends |
|
||||
| [**onCollisionStay**](/apis/core/#Script-onCollisionStay) | Triggered when collision continues |
|
||||
|
||||
### Trigger Event Callbacks
|
||||
| Event Name | Trigger Timing |
|
||||
| --- | --- |
|
||||
| [**onTriggerEnter**](/apis/core/#Script-onTriggerEnter) | Triggered when entering the trigger |
|
||||
| [**onTriggerExit**](/apis/core/#Script-onTriggerExit) | Triggered when exiting the trigger |
|
||||
| [**onTriggerStay**](/apis/core/#Script-onTriggerStay) | Triggered when staying inside the trigger |
|
||||
|
||||
|
||||
## Script Usage
|
||||
|
||||
```typescript
|
||||
class EventHandler extends Script {
|
||||
// Collider events
|
||||
onCollisionEnter(other: Collider) {
|
||||
console.log("Collision started, object:", other.entity.name);
|
||||
}
|
||||
|
||||
onCollisionStay(other: Collider) {
|
||||
console.log("Collision ongoing, object:", other.entity.name);
|
||||
}
|
||||
|
||||
onCollisionExit(other: Collider) {
|
||||
console.log("Collision ended, object:", other.entity.name);
|
||||
}
|
||||
|
||||
// Trigger events
|
||||
onTriggerEnter(other: Collider) {
|
||||
console.log("Trigger started, object:", other.entity.name);
|
||||
}
|
||||
|
||||
onTriggerStay(other: Collider) {
|
||||
console.log("Trigger ongoing, object:", other.entity.name);
|
||||
}
|
||||
|
||||
onTriggerExit(other: Collider) {
|
||||
console.log("Trigger ended, object:", other.entity.name);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
<Playground href="/embed/physx-collision-detection" />
|
||||
101
docs/en/physics/collider/overview.mdx
Normal file
101
docs/en/physics/collider/overview.mdx
Normal file
@@ -0,0 +1,101 @@
|
||||
---
|
||||
order: 1
|
||||
title: Collider
|
||||
type: Physics
|
||||
label: Physics
|
||||
---
|
||||
|
||||
Collider ([Collider](/apis/core/#Collider)) is a component used to detect and respond to physical collisions. The engine provides the following types of colliders:
|
||||
|
||||
| Type | Description | Applicable Scenarios |
|
||||
| --- | --- | --- |
|
||||
| [**StaticCollider**](/apis/core/#StaticCollider) | Static collider, does not move but can collide with other objects | Ground, walls, and other static objects |
|
||||
| [**DynamicCollider**](/apis/core/#DynamicCollider) | Dynamic collider, affected by the physics engine and can move freely | Movable objects, projectiles, etc. |
|
||||
| [**CharacterController**](/apis/core/#CharacterController) | Collider specifically for character control | Player characters, NPCs, etc. |
|
||||
|
||||
Collider components are used to define the physical properties and collision behavior of objects. The Galacean physics system provides three types of colliders:
|
||||
|
||||
1. [Dynamic Collider](/docs/physics/collider/dynamicCollider)
|
||||
Dynamic colliders can move freely and are affected by physical forces, suitable for movable objects that require physical simulation, such as projectiles and pushable boxes.
|
||||
|
||||
2. [Static Collider](/docs/physics/collider/staticCollider)
|
||||
Static colliders are fixed in the scene and do not move, typically used to create fixed physical obstacles such as ground and walls.
|
||||
|
||||
3. [Character Controller](/docs/physics/collider/characterController)
|
||||
Character controllers are colliders specifically designed for character movement, supporting features such as walking on slopes and climbing steps, suitable for character control in first-person or third-person games.
|
||||
|
||||
## Basic Concepts
|
||||
|
||||
All collider types have the following common features:
|
||||
|
||||
1. **Shape Management**: Each collider can contain multiple [collision shapes](/docs/physics/collider/colliderShape) (character controllers can only add one). For detailed instructions on collision shapes, please refer to the [Collision Shape](/docs/physics/collider/colliderShape) documentation.
|
||||
|
||||
Currently, four types of `collision shapes` are supported, but the support varies among different backend physics packages, as shown below:
|
||||
|
||||
| Name | Description | Supported Backend Physics Packages |
|
||||
| :------------------------------------------------------- | :--------------- | :--------------------------- |
|
||||
| [BoxColliderShape](/apis/core/#BoxColliderShape) | Box-shaped collision shape | physics-lite, physics-physx |
|
||||
| [SphereColliderShape](/apis/core/#SphereColliderShape) | Sphere-shaped collision shape | physics-lite, physics-physx |
|
||||
| [PlaneColliderShape](/apis/core/#PlaneColliderShape) | Infinite plane collision shape | physics-physx |
|
||||
| [CapsuleColliderShape](/apis/core/#CapsuleColliderShape) | Capsule-shaped collision shape | physics-physx |
|
||||
|
||||
2. **Transform Synchronization**:
|
||||
- Colliders automatically synchronize the world transform of the entity, including position, rotation, and scale
|
||||
- `Collision shapes` can be set with local offsets and rotations relative to the entity
|
||||
|
||||
3. **Collision/Trigger Events**: Colliders can generate collision and trigger events when interacting with other colliders. For detailed information on these events and how to handle them, please refer to the [Collision Events](/docs/physics/collider/event) documentation.
|
||||
## Best Practices
|
||||
|
||||
1. **Reasonable Use of Shapes**
|
||||
|
||||
- Minimize the number of collision shapes, adding additional shapes only when necessary to reduce performance overhead
|
||||
- Avoid using too many collision shapes
|
||||
- Use simplified collision shapes instead of precise meshes for complex models
|
||||
- Capsule shapes are recommended for character controllers as they handle steps and slopes better
|
||||
|
||||
2. **Performance Optimization**
|
||||
|
||||
- Set immovable objects as static colliders
|
||||
- Merge adjacent static colliders
|
||||
- Adjust the solverIterations of dynamic colliders appropriately to balance performance and accuracy
|
||||
- Use CollisionDetectionMode wisely, enabling continuous collision detection only when necessary
|
||||
|
||||
3. **Dynamic Collider Settings**
|
||||
|
||||
- Adjust linearDamping and angularDamping based on the object's mass to avoid unnatural movement
|
||||
- Use constraints to restrict unnecessary degrees of freedom and improve stability
|
||||
- Set sleepThreshold appropriately to let stationary objects enter sleep mode in time
|
||||
|
||||
4. **Character Controller Configuration**
|
||||
|
||||
- Set an appropriate stepOffset based on the game type to avoid getting stuck or crossing unreasonable steps
|
||||
- Set an appropriate slopeLimit considering the slope angles in the game scene
|
||||
- Choose a suitable nonWalkableMode considering whether sliding effects are needed
|
||||
- Ensure the collision shape matches the character's visual model reasonably
|
||||
|
||||
5. **Usage Scenarios**
|
||||
- Static Colliders: Scene boundaries, terrain, buildings, and other fixed objects
|
||||
- Dynamic Colliders: Pushable objects, physical props, destructible objects, etc.
|
||||
- Character Controllers: Player characters, NPCs, and other objects requiring special movement control
|
||||
|
||||
## Script Usage
|
||||
|
||||
### Adding Colliders
|
||||
|
||||
```typescript
|
||||
// Add a static collider
|
||||
const staticCollider = entity.addComponent(StaticCollider);
|
||||
|
||||
// Add a dynamic collider
|
||||
const dynamicCollider = entity.addComponent(DynamicCollider);
|
||||
|
||||
// Add a character controller
|
||||
const characterController = entity.addComponent(CharacterController);
|
||||
```
|
||||
|
||||
## More Information
|
||||
|
||||
- [Dynamic Collider Documentation](/docs/physics/collider/dynamicCollider)
|
||||
- [Static Collider Documentation](/docs/physics/collider/staticCollider)
|
||||
- [Character Controller Documentation](/docs/physics/collider/characterController)
|
||||
- [Collision Events Documentation](/docs/physics/collider/event)
|
||||
109
docs/en/physics/collider/staticCollider.mdx
Normal file
109
docs/en/physics/collider/staticCollider.mdx
Normal file
@@ -0,0 +1,109 @@
|
||||
---
|
||||
order: 1
|
||||
title: Static Collider
|
||||
type: Physics
|
||||
label: Physics
|
||||
---
|
||||
|
||||
Static Collider ([StaticCollider](/apis/core/#StaticCollider)) is used to create physical objects that are fixed in the scene and do not move. It is not affected by forces applied by the physics engine but can collide and trigger responses with other dynamic objects. It is commonly used to create static scene elements such as ground and walls.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Select the target entity and click the Add Component button in the inspector to add the StaticCollider component.
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*g5RMQJwnSZsAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
2. Add a collision shape to the collider. For detailed instructions on collision shapes, please refer to the [Collision Shape](/docs/physics/collider/colliderShape) documentation.
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*UcaRSb0nahcAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
3. Adjust the position, size, and other properties of the collision shape to match the scene elements.
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*g5RMQJwnSZsAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Description |
|
||||
| ----------------------------------------- | ----------------- |
|
||||
| [**shapes**](/apis/core/#Collider-shapes) | Collection of collision shapes |
|
||||
|
||||
## Methods
|
||||
|
||||
| Method Name | Description |
|
||||
| --------------------------------------------------- | ----------------- |
|
||||
| [**addShape**](/apis/core/#Collider-addShape) | Add a collision shape |
|
||||
| [**removeShape**](/apis/core/#Collider-removeShape) | Remove a specified collision shape |
|
||||
| [**clearShapes**](/apis/core/#Collider-clearShapes) | Clear all collision shapes |
|
||||
|
||||
|
||||
## Script Usage
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
```typescript
|
||||
// Create a static collider
|
||||
const staticCollider = entity.addComponent(StaticCollider);
|
||||
|
||||
// Add a box collision shape
|
||||
const boxShape = new BoxColliderShape();
|
||||
boxShape.size = new Vector3(1, 1, 1);
|
||||
boxShape.position = new Vector3(0, 0.5, 0);
|
||||
staticCollider.addShape(boxShape);
|
||||
```
|
||||
|
||||
### Shape Management
|
||||
|
||||
```typescript
|
||||
// Get all shapes
|
||||
const shapes = staticCollider.shapes;
|
||||
|
||||
// Add a shape
|
||||
const boxShape = new BoxColliderShape();
|
||||
staticCollider.addShape(boxShape);
|
||||
|
||||
// Remove a specific shape
|
||||
staticCollider.removeShape(boxShape);
|
||||
|
||||
// Clear all shapes
|
||||
staticCollider.clearShapes();
|
||||
```
|
||||
|
||||
### Trigger Settings
|
||||
|
||||
```typescript
|
||||
// Set as a trigger
|
||||
const triggerShape = new BoxColliderShape();
|
||||
triggerShape.isTrigger = true;
|
||||
staticCollider.addShape(triggerShape);
|
||||
|
||||
// Add a trigger response script
|
||||
class TriggerHandler extends Script {
|
||||
onTriggerEnter(other: Collider) {
|
||||
console.log("Trigger activated");
|
||||
}
|
||||
}
|
||||
entity.addComponent(TriggerHandler);
|
||||
```
|
||||
|
||||
### Composite Shapes
|
||||
|
||||
```typescript
|
||||
// Create an L-shaped wall
|
||||
function createLWall() {
|
||||
const entity = new Entity();
|
||||
const collider = entity.addComponent(StaticCollider);
|
||||
|
||||
// Main wall
|
||||
const wall1 = new BoxColliderShape();
|
||||
wall1.size = new Vector3(5, 3, 0.5);
|
||||
collider.addShape(wall1);
|
||||
|
||||
// Side wall
|
||||
const wall2 = new BoxColliderShape();
|
||||
wall2.size = new Vector3(0.5, 3, 3);
|
||||
wall2.position = new Vector3(2.25, 0, 1.25);
|
||||
collider.addShape(wall2);
|
||||
|
||||
return entity;
|
||||
}
|
||||
```
|
||||
@@ -1,51 +0,0 @@
|
||||
---
|
||||
order: 4
|
||||
title: Character Controller Component
|
||||
type: Physics
|
||||
label: Physics
|
||||
---
|
||||
|
||||
The character controller is a very important functional component provided by the physics engine. With the character controller, it is easy to add physical effects to the movement of animated characters. For example, you can set parameters to prevent the character from climbing slopes of a certain angle, or avoid collision feedback with other colliders during the character's movement. In fact, the character controller is just an advanced encapsulation of the [collider](/en/docs/physics/collider), implementing various advanced character control behaviors through collision detection. Therefore, the creation and use of the character controller component are very similar to the collider component.
|
||||
```typescript
|
||||
const physicsCapsule = new CapsuleColliderShape();
|
||||
physicsCapsule.radius = radius;
|
||||
physicsCapsule.height = height;
|
||||
const characterController = capsuleEntity.addComponent(CharacterController);
|
||||
characterController.addShape(physicsCapsule);
|
||||
```
|
||||
Like the collider component, it is constructed by creating a `ColliderShape` and adding it to the component, giving the character controller a specific shape. However, two points need to be emphasized here:
|
||||
1. The character controller does not support compound shapes, so only one `ColliderShape` can be added.
|
||||
2. The character controller currently only supports `CapsuleColliderShape` and `BoxColliderShape`, with `CapsuleColliderShape` being the most commonly used.
|
||||
|
||||
Subsequent behaviors of the character controller are controlled through various parameters and methods of `CharacterController`, with the most important being the `move` function:
|
||||
|
||||
```typescript
|
||||
class Controller extends Script {
|
||||
onPhysicsUpdate() {
|
||||
const fixedTimeStep = this.engine.physicsManager.fixedTimeStep;
|
||||
const character = this._character;
|
||||
const flag = character.move(this._displacement, 0.1, fixedTimeStep);
|
||||
if (flag | ControllerCollisionFlag.Down) {
|
||||
character.move(new Vector3(0, -0.2, 0), 0.1, fixedTimeStep);
|
||||
}
|
||||
this._displacement.setValue(0, 0, 0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can specify the character's displacement in the `move` method, and this method returns a composite value of an enumeration type. Through this enumeration type `ControllerCollisionFlag`, you can determine whether the character controller has collided with other collider components:
|
||||
|
||||
```typescript
|
||||
export enum ControllerCollisionFlag {
|
||||
/** Character is colliding to the sides. */
|
||||
Sides = 1,
|
||||
/** Character has collision above. */
|
||||
Up = 2,
|
||||
/** Character has collision below. */
|
||||
Down = 4
|
||||
}
|
||||
```
|
||||
|
||||
This determines how the character's next animation and movement will proceed. In the example below, you can control the character's movement via the keyboard, allowing it to climb or jump over specific obstacles.
|
||||
|
||||
<Playground href="/embed/physx-controller" />
|
||||
@@ -1,16 +1,14 @@
|
||||
---
|
||||
order: 6
|
||||
title: Physics Debugging
|
||||
title: Physics Debug
|
||||
type: Physics
|
||||
label: Physics
|
||||
---
|
||||
|
||||
Physical colliders are composed of basic physical shapes, including spheres, boxes, capsules, and infinite planes. In practical applications, these collider shapes rarely perfectly overlap with the rendered objects, which brings significant difficulties for visual debugging.
|
||||
There are two debugging methods:
|
||||
1. Using the PhysX Visual Debugger (PVD), an official debugging tool developed by Nvidia. However, using this tool requires compiling the debug version of PhysX yourself and connecting the browser and the debugging tool via WebSocket.
|
||||
For specific usage methods, you can refer to the introduction in the [physx.js](https://github.com/galacean/physX.js) Readme.
|
||||
2. We also provide a lightweight [auxiliary line tool](https://github.com/galacean/engine-toolkit/tree/main/packages/auxiliary-lines), which draws corresponding wireframes based on the configuration of physical components to assist in configuring and debugging physical components.
|
||||
It is also very easy to use, just mount the `WireframeManager` script and then set it to associate with various physical components, or directly associate with nodes:
|
||||
Physical colliders are composed of basic physical shapes, including spheres, boxes, capsules, and infinite planes. In practical applications, these collider shapes rarely align perfectly with the rendered objects, which creates significant challenges for visual debugging.
|
||||
|
||||
We provide a lightweight [auxiliary lines tool](https://github.com/galacean/engine-toolkit/tree/main/packages/auxiliary-lines), which draws wireframes according to the physics component configuration to assist in configuring and debugging physics components.
|
||||
It's very easy to use - just mount the `WireframeManager` script and set its association with various physics components, or directly associate it with nodes:
|
||||
```typescript
|
||||
const wireframe = rootEntity.addComponent(WireframeManager);
|
||||
wireframe.addCollideWireframe(collider);
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
---
|
||||
order: 5
|
||||
title: Basic Physics Constraint Components
|
||||
type: Physics
|
||||
label: Physics
|
||||
---
|
||||
|
||||
Physics constraint components are very important physical components. By using constraints, you can better control the movement of dynamic collider components and add interesting interactive responses to the scene. This article mainly introduces the three most basic physics constraint components:
|
||||
|
||||
1. Fixed Constraint Component
|
||||
|
||||
<Image src="https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/_images/fixedJoint.png" />
|
||||
2. Spring Constraint Component
|
||||
|
||||
<Image src="https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/_images/distanceJoint.png" />
|
||||
3. Hinge Constraint Component
|
||||
|
||||
<Image src="https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/_images/revoluteJoint.png" />
|
||||
|
||||
All physics constraints have two acting objects. One represents the dynamic collider affected by the physical constraint (the physics constraint component is mounted on this node), and the other is the position where the constraint is mounted or another dynamic collider (set through component configuration).
|
||||
Therefore, the usage of these components is similar. Taking the fixed constraint component `FixedJoint` as an example:
|
||||
|
||||
```typescript
|
||||
const fixedJoint = currentEntity.addComponent(FixedJoint);
|
||||
fixedJoint.connectedCollider = prevCollider;
|
||||
```
|
||||
|
||||
## Local Coordinates and World Coordinates
|
||||
|
||||
Understanding the use of physics constraint components, one key point is to understand **local coordinates** and **world coordinates**. All physics constraints can configure the `connectedCollider` property.
|
||||
In addition, some physics constraint components can also set the position where the physical constraint is mounted by configuring the `connectedAnchor` property.
|
||||
|
||||
**It is particularly important to note that when `connectedCollider` is set, `connectedAnchor` represents the local coordinates relative to that collider. When `connectedCollider` is null,
|
||||
`connectedAnchor` represents the world coordinates.**
|
||||
|
||||
## Hinge Constraint
|
||||
|
||||
Among the above three physics constraints, the hinge constraint is relatively more complex because, in addition to configuring `connectedCollider` and `connectedAnchor`, it also requires specifying the direction of the hinge's rotation axis and the rotation radius.
|
||||
These two properties can be specified by configuring `axis` (the default direction is towards the positive x-axis) and `swingOffset`.
|
||||
The `swingOffset` is also a vector and can be understood as the offset from the rotation center determined by `connectedAnchor` and `connectedCollider`, where the dynamic collision is moved to this point to start rotating around the rotation axis.
|
||||
|
||||
The usage of the above physics constraint components can be referred to:
|
||||
<Playground href="/embed/physx-joint-basic" />
|
||||
94
docs/en/physics/joint/fixedJoint.mdx
Normal file
94
docs/en/physics/joint/fixedJoint.mdx
Normal file
@@ -0,0 +1,94 @@
|
||||
---
|
||||
order: 1
|
||||
title: Fixed Joint
|
||||
type: Physics
|
||||
label: Physics
|
||||
---
|
||||
|
||||
[Fixed Joint](/apis/core/#FixedJoint) is a rigid constraint component that completely restricts the relative motion between two colliders. When two objects are connected by a fixed joint, they maintain their relative position and orientation as if glued together rigidly.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Select the target entity and click the add component button in the inspector to add the FixedJoint component.
|
||||
<Callout type="info">
|
||||
When adding a joint component, make sure the target entity already has a [Dynamic Collider](/docs/physics/collider/dynamicCollider) component attached. If not, the editor will automatically add a `Dynamic Collider Component` for you.
|
||||
</Callout>
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*x3SBT4XKDUcAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
2. Use the connectedCollider property of the component to set the target collider to connect to (if not needed, keep it as null, which means connecting to a point in world space).
|
||||
<Callout type="positive">
|
||||
If the target is a collider, the target entity needs to have a collider component ([Dynamic Collider](/docs/physics/collider/dynamicCollider), [Static Collider](/docs/physics/collider/staticCollider), [Character Controller](/docs/physics/collider/characterController)) attached.
|
||||
</Callout>
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*ARu_S7PhgiMAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
3. Configure the joint's properties as needed. See below for detailed property descriptions.
|
||||
|
||||
## Property Description
|
||||
|
||||
### Collider Settings
|
||||
- **connectedCollider**
|
||||
Specifies the target collider to connect to. When set to null, the joint connects to a fixed point in world space, allowing you to fix objects at specific positions in space.
|
||||
|
||||
### Anchor Settings
|
||||
- **anchor**
|
||||
Defines the anchor point position on the main collider, using local coordinates. This point defines the joint's connection position.
|
||||
|
||||
- **connectedAnchor**
|
||||
Defines the connection point position. Its meaning depends on the connectedCollider setting:
|
||||
- When connectedCollider is null, represents an absolute position in world space
|
||||
- When connectedCollider is not null, represents a relative position in the target collider's local space
|
||||
|
||||
- **automaticConnectedAnchor**
|
||||
Whether to automatically calculate the connectedAnchor value. When enabled, the system automatically sets the connection point to maintain initial relative positions. Set to false for manual precise control of the connection point.
|
||||
|
||||
### Break Thresholds
|
||||
- **breakForce**
|
||||
Maximum force the joint can withstand before breaking. Set to Infinity for an unbreakable joint. This property can be used to simulate destructible connections between objects.
|
||||
|
||||
- **breakTorque**
|
||||
Maximum torque the joint can withstand before breaking. Set to Infinity for an unbreakable joint. Used in conjunction with breakForce to simulate more realistic connection destruction.
|
||||
|
||||
### Physics Calculation Intervention
|
||||
- **connectedMassScale** and **massScale**
|
||||
Used to adjust the mass influence of the connected and main colliders respectively. These scaling values affect joint constraint calculations, allowing fine-tuning of joint physics behavior. Default value is 1.0, increasing values increases the corresponding collider's "importance" in constraint solving.
|
||||
|
||||
## Script Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```typescript
|
||||
// Add fixed joint component
|
||||
const fixedJoint = entity.addComponent(FixedJoint);
|
||||
|
||||
// Set the connected collider
|
||||
fixedJoint.connectedCollider = targetEntity.getComponent(Collider);
|
||||
|
||||
// Set anchor point
|
||||
fixedJoint.anchor.setValue(0, 1, 0);
|
||||
|
||||
// Set connection point
|
||||
fixedJoint.automaticConnectedAnchor = false;
|
||||
fixedJoint.connectedAnchor.setValue(0, 0, 0);
|
||||
```
|
||||
|
||||
### Break Settings
|
||||
|
||||
```typescript
|
||||
// Set break conditions
|
||||
fixedJoint.breakForce = 1000; // Break force
|
||||
fixedJoint.breakTorque = 1000; // Break torque
|
||||
|
||||
// Set as unbreakable
|
||||
fixedJoint.breakForce = Infinity;
|
||||
fixedJoint.breakTorque = Infinity;
|
||||
```
|
||||
|
||||
### Mass Influence
|
||||
|
||||
```typescript
|
||||
// Adjust mass influence
|
||||
fixedJoint.massScale = 1.5; // Increase own mass influence
|
||||
fixedJoint.connectedMassScale = 0.5; // Decrease connected object's mass influence
|
||||
```
|
||||
145
docs/en/physics/joint/hingeJoint.mdx
Normal file
145
docs/en/physics/joint/hingeJoint.mdx
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
order: 3
|
||||
title: Hinge Joint
|
||||
type: Physics
|
||||
label: Physics
|
||||
---
|
||||
|
||||
Hinge joints are used to simulate the connection of two objects through an axis, allowing them to rotate freely around this axis. This type of joint is commonly used to implement door hinges, wheels, pendulums, and other physical effects that require rotation around a fixed axis.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Select the target entity and click the add component button in the inspector to add the HingeJoint component.
|
||||
<Callout type="info">
|
||||
When adding a joint component, make sure the target entity already has a `Dynamic Collider Component` attached. If not, the editor will automatically add a `Dynamic Collider Component` for you.
|
||||
</Callout>
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*SL8YQLNbsJwAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
2. Use the connectedCollider property of the component to set the target collider to connect to (if not needed, keep it as null, which means connecting to a point in world space).
|
||||
<Callout type="positive">
|
||||
If the target is a collider, the target entity needs to have a collider component (Dynamic Collider, Static Collider, Character Controller) attached.
|
||||
</Callout>
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*SwsmS572tcAAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
3. Adjust the joint's properties as needed to modify its behavior. Refer to the descriptions below for the meaning and function of each property.
|
||||
|
||||
## Property Descriptions
|
||||
|
||||
### Collider Settings
|
||||
- [**connectedCollider**](/apis/core/#HingeJoint-connectedCollider)
|
||||
Specifies the target collider to connect to. When set to null, the joint connects to a fixed point in world space.
|
||||
|
||||
### Anchor Settings
|
||||
- [**anchor**](/apis/core/#HingeJoint-anchor)
|
||||
Defines the anchor point on the main collider (local coordinates). This point is the rotation center of the hinge.
|
||||
|
||||
- [**connectedAnchor**](/apis/core/#HingeJoint-connectedAnchor)
|
||||
Defines the connection point position:
|
||||
- When connectedCollider is null, it represents a fixed point in world space
|
||||
- When connectedCollider is not null, it represents the connection point in the local space of the target collider
|
||||
|
||||
- [**automaticConnectedAnchor**](/apis/core/#HingeJoint-automaticConnectedAnchor)
|
||||
Whether to automatically calculate the connection point position. When enabled, it automatically maintains the initial relative position of the objects.
|
||||
|
||||
### Rotation Settings
|
||||
- [**axis**](/apis/core/#HingeJoint-axis)
|
||||
Defines the direction of the hinge's rotation axis. Objects will rotate along this axis.
|
||||
|
||||
### Motion Limits
|
||||
- [**useLimits**](/apis/core/#HingeJoint-useLimits)
|
||||
Whether to enable angle limits. When enabled, the rotation range of the hinge can be limited.
|
||||
|
||||
- [**limits**](/apis/core/#HingeJoint-limits)
|
||||
Sets the rotation range of the hinge:
|
||||
|
||||
| Property | Description |
|
||||
| --------------- | -------------------------------------------------------------------- |
|
||||
| **min** | Minimum angle limit (degrees) |
|
||||
| **max** | Maximum angle limit (degrees) |
|
||||
| **contactDistance** | Contact distance, defines the distance at which the limit starts to take effect |
|
||||
| **stiffness** | Spring stiffness (only effective when useSpring is true) |
|
||||
| **damping** | Damping coefficient (only effective when useSpring is true) |
|
||||
|
||||
### Motor Drive
|
||||
- [**useMotor**](/apis/core/#HingeJoint-useMotor)
|
||||
Whether to enable the motor function. When enabled, the hinge can be actively driven to rotate.
|
||||
|
||||
- [**motor**](/apis/core/#HingeJoint-motor)
|
||||
Motor parameter settings:
|
||||
|
||||
| Property | Description |
|
||||
| --------------- | -------------------------------------------------------------------- |
|
||||
| **targetVelocity** | Target angular velocity (degrees/second) |
|
||||
| **forceLimit** | Maximum torque limit |
|
||||
| **freeSpin** | Whether to allow free spin |
|
||||
| **gearRatio** | Gear ratio, used to adjust the actual output angular velocity |
|
||||
|
||||
### Spring Settings
|
||||
- [**useSpring**](/apis/core/#HingeJoint-useSpring)
|
||||
Whether to enable the spring effect. When enabled, the limiter will exhibit elastic characteristics.
|
||||
|
||||
### Break Thresholds
|
||||
- [**breakForce**](/apis/core/#HingeJoint-breakForce)
|
||||
The maximum force the joint can withstand before breaking.
|
||||
|
||||
- [**breakTorque**](/apis/core/#HingeJoint-breakTorque)
|
||||
The maximum torque the joint can withstand before breaking.
|
||||
|
||||
### Mass Calculation Intervention
|
||||
- [**connectedMassScale**](/apis/core/#HingeJoint-connectedMassScale) and [**massScale**](/apis/core/#HingeJoint-massScale)
|
||||
Adjust the mass influence of the connected collider and the main collider. The default value is 1.0.
|
||||
|
||||
## Script Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```typescript
|
||||
// Add Hinge Joint component
|
||||
const hingeJoint = entity.addComponent(HingeJoint);
|
||||
|
||||
// Set the connected object
|
||||
hingeJoint.connectedCollider = targetEntity.getComponent(Collider);
|
||||
|
||||
// Set the rotation axis
|
||||
hingeJoint.axis.setValue(0, 1, 0); // Rotate around Y-axis
|
||||
```
|
||||
|
||||
### Motion Limits
|
||||
|
||||
```typescript
|
||||
// Enable angle limits
|
||||
hingeJoint.useLimits = true;
|
||||
hingeJoint.limits = new JointLimits();
|
||||
hingeJoint.limits.min = -45; // Minimum angle
|
||||
hingeJoint.limits.max = 45; // Maximum angle
|
||||
hingeJoint.limits.contactDistance = 5; // Contact distance
|
||||
|
||||
// Enable elastic limits
|
||||
hingeJoint.useSpring = true;
|
||||
hingeJoint.limits.stiffness = 100; // Spring stiffness
|
||||
hingeJoint.limits.damping = 0.2; // Damping coefficient
|
||||
```
|
||||
|
||||
### Motor Drive
|
||||
|
||||
```typescript
|
||||
// Enable motor
|
||||
hingeJoint.useMotor = true;
|
||||
hingeJoint.motor = new JointMotor();
|
||||
hingeJoint.motor.targetVelocity = 180; // Target angular velocity (degrees/second)
|
||||
hingeJoint.motor.forceLimit = 500; // Maximum torque
|
||||
hingeJoint.motor.freeSpin = false; // Free spin
|
||||
hingeJoint.motor.gearRatio = 1; // Gear ratio
|
||||
```
|
||||
|
||||
### Get Motion Information
|
||||
|
||||
```typescript
|
||||
// Get current angle
|
||||
const currentAngle = hingeJoint.angle;
|
||||
|
||||
// Get current angular velocity
|
||||
const angularVelocity = hingeJoint.velocity;
|
||||
```
|
||||
99
docs/en/physics/joint/overview.mdx
Normal file
99
docs/en/physics/joint/overview.mdx
Normal file
@@ -0,0 +1,99 @@
|
||||
---
|
||||
order: 0
|
||||
title: Overview
|
||||
type: Physics
|
||||
label: Physics
|
||||
---
|
||||
|
||||
Joint components are used to simulate connections between colliders or between a collider and an arbitrary point in world space. By applying forces to restrict degrees of freedom, they achieve specific physical effects. Currently, Galacean supports three types of joint components:
|
||||
|
||||
1. [Fixed Joint](/docs/physics/joint/fixedJoint)
|
||||
A fixed joint completely restricts the relative motion between two colliders, maintaining their fixed relative position and orientation. This type of joint is particularly useful when you need separable objects or synchronized motion without a hierarchical structure.
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*h050RoMxg8sAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
2. [Spring Joint](/docs/physics/joint/springJoint)
|
||||
A spring joint uses spring force and damping to maintain the distance between two objects. It allows objects to move freely within a certain range and applies elastic constraint forces when they exceed that range, similar to having a spring between them.
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*eWoxQ57nWeEAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
3. [Hinge Joint](/docs/physics/joint/hingeJoint)
|
||||
A hinge joint allows objects to rotate freely around a fixed axis, commonly used for implementing door hinges, wheels, pendulums, and other physical effects requiring rotation around a fixed axis.
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*yZPrRohs-VcAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
## Basic Concepts
|
||||
|
||||
All joint types share the following common characteristics:
|
||||
|
||||
1. **Joint Endpoints**: Each joint involves two acting objects:
|
||||
- Main collider: The collider with the joint component attached
|
||||
- Connected object: The target collider specified by the `connectedCollider` property or a point in world space
|
||||
|
||||
2. **Anchor Settings**:
|
||||
- `anchor`: The connection point on the main collider
|
||||
- `connectedAnchor`: The connection point on the target object
|
||||
|
||||
3. **Break Thresholds**:
|
||||
- `breakForce`: Maximum force the joint can withstand
|
||||
- `breakTorque`: Maximum torque the joint can withstand
|
||||
|
||||
4. **Physics Calculation Intervention**:
|
||||
- `connectedMassScale` and `massScale`: Used to adjust the mass influence of connected and main colliders
|
||||
|
||||
## Script Usage
|
||||
|
||||
### Creating Joints
|
||||
|
||||
You can dynamically create and configure joint components through scripts:
|
||||
|
||||
```typescript
|
||||
// Create a fixed joint
|
||||
const fixedJoint = entity.addComponent(FixedJoint);
|
||||
|
||||
// Create a spring joint
|
||||
const springJoint = entity.addComponent(SpringJoint);
|
||||
|
||||
// Create a hinge joint
|
||||
const hingeJoint = entity.addComponent(HingeJoint);
|
||||
```
|
||||
|
||||
### Setting Connection Objects
|
||||
|
||||
All joints can set connection targets and anchor points:
|
||||
|
||||
```typescript
|
||||
// Set the target collider to connect to
|
||||
joint.connectedCollider = targetEntity.getComponent(Collider);
|
||||
|
||||
// Set the joint's anchor point on the main collider
|
||||
joint.anchor.setValue(0, 1, 0);
|
||||
|
||||
// Set the joint's connection point on the target collider
|
||||
joint.connectedAnchor.setValue(0, 0, 0);
|
||||
```
|
||||
|
||||
### Common Property Configuration
|
||||
|
||||
All joint types support the following settings:
|
||||
|
||||
```typescript
|
||||
// Set break force and torque
|
||||
joint.breakForce = 1000;
|
||||
joint.breakTorque = 1000;
|
||||
|
||||
// Set mass influence
|
||||
joint.massScale = 1.0;
|
||||
joint.connectedMassScale = 1.0;
|
||||
|
||||
// Set whether to automatically calculate connection points
|
||||
joint.automaticConnectedAnchor = true;
|
||||
```
|
||||
|
||||
For example of using these physical constraint components, see:
|
||||
<playground src="physx-joint-basic.ts"></playground>
|
||||
|
||||
For detailed script usage of each joint type, please refer to their respective documentation:
|
||||
- [FixedJoint](/docs/physics/joint/fixedJoint)
|
||||
- [SpringJoint](/docs/physics/joint/springJoint)
|
||||
- [HingeJoint](/docs/physics/joint/hingeJoint)
|
||||
102
docs/en/physics/joint/springJoint.mdx
Normal file
102
docs/en/physics/joint/springJoint.mdx
Normal file
@@ -0,0 +1,102 @@
|
||||
---
|
||||
order: 2
|
||||
title: Spring Joint
|
||||
type: Physics
|
||||
label: Physics
|
||||
---
|
||||
|
||||
Spring joints are used to maintain distance constraints between two objects by controlling their relative motion through spring force and damping. They can set minimum and maximum distance ranges and apply elastic constraint forces when objects exceed these ranges.
|
||||
|
||||
## Usage
|
||||
|
||||
1. Select the target entity and click the add component button in the inspector to add the SpringJoint component.
|
||||
<Callout type="info">
|
||||
When adding a joint component, make sure the target entity already has a `Dynamic Collider Component` attached. If not, the editor will automatically add a `Dynamic Collider Component` for you.
|
||||
</Callout>
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*Jy9kSaMDsJoAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
2. Use the connectedCollider property of the component to set the target collider to connect to (if not needed, keep it as null, which means connecting to a point in world space).
|
||||
<Callout type="positive">
|
||||
If the target is a collider, the target entity needs to have a collider component (Dynamic Collider, Static Collider, Character Controller) attached.
|
||||
</Callout>
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*JRtfRoM1vS4AAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
3. Adjust the joint's properties as needed to modify its behavior. Refer to the descriptions below for the meaning and function of each property.
|
||||
|
||||
## Property Descriptions
|
||||
|
||||
### Collider Settings
|
||||
- [**connectedCollider**](/apis/core/#SpringJoint-connectedCollider)
|
||||
Specifies the target collider to connect to. When set to null, the joint connects to a fixed point in world space, allowing you to fix the object at a specific position in space.
|
||||
|
||||
### Anchor Settings
|
||||
- [**anchor**](/apis/core/#SpringJoint-anchor)
|
||||
Defines the anchor point on the main collider using local coordinates. This point defines the connection position of the joint.
|
||||
|
||||
- [**connectedAnchor**](/apis/core/#SpringJoint-connectedAnchor)
|
||||
Defines the connection point position. Its meaning depends on the connectedCollider setting:
|
||||
- When connectedCollider is null, it represents an absolute position in world space
|
||||
- When connectedCollider is not null, it represents a relative position in the local space of the target collider
|
||||
|
||||
- **automaticConnectedAnchor**
|
||||
Whether to automatically calculate the value of connectedAnchor. When enabled, the system automatically sets the connection point to ensure the initial position relationship between objects. If you need precise manual control of the connection point, set this property to false.
|
||||
|
||||
### Distance Constraints
|
||||
- [**minDistance**](/apis/core/#SpringJoint-minDistance)
|
||||
The minimum allowed distance. When the distance between objects is less than this value, the joint applies a pushing force.
|
||||
|
||||
- [**maxDistance**](/apis/core/#SpringJoint-maxDistance)
|
||||
The maximum allowed distance. When the distance between objects exceeds this value, the joint applies a pulling force.
|
||||
|
||||
- [**tolerance**](/apis/core/#SpringJoint-tolerance)
|
||||
The tolerance range value, default is 0.25. This value determines when the joint starts to apply constraint forces:
|
||||
- The joint starts to apply constraint forces only when the actual distance between objects exceeds the allowed range by the tolerance value
|
||||
- For example: if maxDistance = 10 and tolerance = 0.25, the joint starts to apply pulling force only when the distance exceeds 10.25
|
||||
- A larger tolerance value makes the joint's constraint behavior softer, while a smaller value makes the constraint stricter
|
||||
|
||||
### Spring Characteristics
|
||||
- [**stiffness**](/apis/core/#SpringJoint-stiffness)
|
||||
The spring stiffness coefficient. A higher value produces a stronger restoring force, making the spring behave "stiffer".
|
||||
|
||||
- [**damping**](/apis/core/#SpringJoint-damping)
|
||||
The damping coefficient. Used to suppress spring oscillations, a higher value makes the motion stop faster.
|
||||
|
||||
### Break Thresholds
|
||||
- [**breakForce**](/apis/core/#SpringJoint-breakForce)
|
||||
The maximum force the joint can withstand before breaking. Setting it to Infinity means the joint will never break due to force. This property can be used to simulate destructible connections between objects.
|
||||
|
||||
- [**breakTorque**](/apis/core/#SpringJoint-breakTorque)
|
||||
The maximum torque the joint can withstand before breaking. Setting it to Infinity means the joint will never break due to torque. Used in conjunction with breakForce to more realistically simulate the destruction of connections.
|
||||
|
||||
### Mass Calculation Intervention
|
||||
- [**connectedMassScale**](/apis/core/#SpringJoint-connectedMassScale) and [**massScale**](/apis/core/#SpringJoint-massScale)
|
||||
Used to adjust the mass influence of the connected collider and the main collider, respectively. These scaling values affect the joint constraint calculations, allowing you to fine-tune the joint's physical behavior. The default value is 1.0. Increasing the value increases the "importance" of the corresponding collider in constraint solving.
|
||||
|
||||
## Script Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```typescript
|
||||
// Add Spring Joint component
|
||||
const springJoint = entity.addComponent(SpringJoint);
|
||||
|
||||
// Set the connected object
|
||||
springJoint.connectedCollider = targetEntity.getComponent(Collider);
|
||||
|
||||
// Set distance range
|
||||
springJoint.minDistance = 1;
|
||||
springJoint.maxDistance = 5;
|
||||
```
|
||||
|
||||
### Spring Settings
|
||||
|
||||
```typescript
|
||||
// Configure spring properties
|
||||
springJoint.stiffness = 50; // Spring stiffness
|
||||
springJoint.damping = 0.2; // Damping coefficient
|
||||
|
||||
// Set tolerance
|
||||
springJoint.tolerance = 0.25;
|
||||
```
|
||||
@@ -5,79 +5,110 @@ type: Physics
|
||||
label: Physics
|
||||
---
|
||||
|
||||
The Physics Manager (PhysicsManager) is used to manage all the physical components in the scene and is responsible for communicating with the physics backend to perform global operations related to the physical scene, such as updates and raycasting. In multi-scene projects, each Scene has its own PhysicsManager, and the physical systems between Scenes are isolated and do not affect each other.
|
||||
The PhysicsScene ([PhysicsScene](/apis/core/#PhysicsScene)) manages all physics components in the scene and communicates with the physics backend, implementing global operations related to the PhysicsScene, such as updates and ray casting. In multi-scene projects, each [Scene](/apis/core/Scene) has its own PhysicsScene, and physics systems between Scenes are isolated and do not affect each other.
|
||||
|
||||
## Physics Update
|
||||
|
||||
The physical scene and the rendering scene are independent of each other but continuously synchronize their data during the program's execution. Therefore, like scripts, the timing of synchronization is very important. Generally speaking, the update frequency of the physical scene is different from that of the rendering scene, and it can be set in the physics manager:
|
||||
|
||||
The PhysicsScene and rendering scene are independent but continuously synchronize their data during program execution. Therefore, like [scripts](/docs/script/script), the timing of synchronization is also very important. The update frequency of the PhysicsScene differs from the rendering scene and is controlled by the following parameter:
|
||||
```typescript
|
||||
/** The fixed time step in seconds at which physics are performed. */
|
||||
/** The fixed update time step (seconds) of the PhysicsScene */
|
||||
fixedTimeStep: number = 1 / 60;
|
||||
|
||||
/** The max sum of time step in seconds one frame. */
|
||||
maxSumTimeStep: number = 1 / 3;
|
||||
```
|
||||
|
||||
In each rendering frame, the physics engine updates at a fixed time step `fixedTimeStep`.
|
||||
### Update Mechanism
|
||||
|
||||
If the time interval is greater than `fixedTimeStep`, the maximum time step for a single simulation is determined by `maxSumTimeStep`. At this time, if the default parameters listed above are used, frame chasing may occur.
|
||||
In this case, you can reduce the number of physics simulation updates per frame by adjusting `maxSumTimeStep`.
|
||||
- In each rendering frame, the physics engine updates according to the fixed time step [fixedTimeStep](/apis/core/#PhysicsScene-fixedTimeStep)
|
||||
- If the actual frame interval is greater than `fixedTimeStep`:
|
||||
- Multiple physics updates will be performed until catching up with the actual time
|
||||
- The maximum update time per frame is limited by `engine.time.maximumDeltaTime`
|
||||
- If the actual frame interval is less than `fixedTimeStep`, it accumulates to the next frame
|
||||
|
||||
If it is less than a `fixedTimeStep`, it will be postponed to the next frame for processing. Therefore, in each rendering frame, the physical scene may be updated multiple times or only once, so all updates related to physical components need to be placed in a specific update function, which is provided by `Script`:
|
||||
### Physics Update Callback
|
||||
|
||||
For physics component updates, you need to use a dedicated callback function in [scripts](/docs/script/script):
|
||||
```typescript
|
||||
export class Script extends Component {
|
||||
/**
|
||||
* Called before physics calculations, the number of times is related to the physical update frequency.
|
||||
* Called before physics calculations, the number of calls depends on the physics update frequency
|
||||
*/
|
||||
onPhysicsUpdate(): void {
|
||||
}
|
||||
onPhysicsUpdate(): void {}
|
||||
}
|
||||
```
|
||||
|
||||
When the physical scene is updated, in addition to calling this function, it will also synchronize the Collider and the posture of the Entity it is attached to. The timing of the physics update is as follows:
|
||||
The position of the physics update in the entire update process can be referred to in the figure below
|
||||
|
||||
1. Call the user logic in `onPhysicsUpdate`
|
||||
2. `callColliderOnUpdate` synchronizes the new posture of the modified Entity to the physical collider
|
||||
3. Update the physical scene
|
||||
4. `callColliderOnLateUpdate` synchronizes the updated positions of all DynamicColliders to the corresponding Entities
|
||||
<Image src="https://gw.alipayobjects.com/mdn/rms_7c464e/afts/img/A*_8C-TJP2UIgAAAAAAAAAAAAAARQnAQ" />
|
||||
|
||||
### Internal Update Process of the Physics System
|
||||
|
||||
The execution order when the PhysicsScene is updated:
|
||||
|
||||
1. Execute user logic in `onPhysicsUpdate`
|
||||
2. `callColliderOnUpdate` synchronizes the modified [Entity](/apis/core/#Entity) `Transform` data to the physics collider
|
||||
3. Update the PhysicsScene
|
||||
4. `callColliderOnLateUpdate` synchronizes the updated positions of all [colliders](/docs/physics/collider/overview) to the corresponding `Entity`
|
||||
|
||||
## Setting Scene Gravity
|
||||
|
||||
The PhysicsScene allows customization of gravity direction and magnitude. Gravity affects all [dynamic colliders](/docs/physics/collider/dynamicCollider) with gravity enabled in the scene.
|
||||
|
||||
```typescript
|
||||
// Get the gravity value of the PhysicsScene
|
||||
const gravity = scene.physics.gravity;
|
||||
|
||||
// Modify gravity - set gravity to 0
|
||||
scene.physics.gravity.set(0, 0, 0);
|
||||
|
||||
// Modify gravity - set to Earth's gravitational acceleration (default value)
|
||||
scene.physics.gravity.set(0, -9.81, 0);
|
||||
```
|
||||
|
||||
## Using Raycasting
|
||||
|
||||
<Playground href="/embed/physx-raycast" />
|
||||
|
||||
A ray can be understood as an infinite line emitted from a point in a certain direction in the 3D world. Raycasting is very widely used in 3D applications. Through raycasting, you can pick objects in the 3D scene when the user clicks the screen; it can also be used in shooting games to determine whether a bullet can hit the target.
|
||||
A ray can be understood as an infinite line emitted from a point in a certain direction in the 3D world. Raycasting is widely used in 3D applications:
|
||||
|
||||
<Image src="https://gw.alipayobjects.com/mdn/rms_7c464e/afts/img/A*SHM1RI49Bd4AAAAAAAAAAAAAARQnAQ" />
|
||||
(_Image source: Internet_)
|
||||
- Used to pick objects in the 3D scene when the user clicks the screen
|
||||
- Used in shooting games to determine if a bullet can hit the target
|
||||
- Detect visibility and occlusion relationships between objects
|
||||
|
||||
To use raycasting, you first need to import the [Ray](/apis/math/#Ray) module in the code; then generate a ray, which can be custom-generated or converted from screen input through the camera ([camera](/apis/core/#Camera-viewportPointToRay)); finally, call the [PhysicsManager.raycast](/apis/core/#PhysicsManager-raycast) method to detect the collision body hit by the ray. The code is as follows:
|
||||
<Image src="https://gw.alipayobjects.com/mdn/rms_7c464e/afts/img/A*SHM1RI49Bd4AAAAAAAAAAAAAARQnAQ)
|
||||
(_Image source: Internet_" />
|
||||
|
||||
### Raycasting Example
|
||||
|
||||
To use raycasting, follow these steps:
|
||||
|
||||
1. Import necessary modules such as [Ray](/apis/math/Ray)
|
||||
2. Create a ray (can be customized or generated through [camera.screenPointToRay](/apis/core/#Camera-screenPointToRay))
|
||||
3. Call the [`raycast`](/apis/core/#PhysicsScene-raycast) method to detect collisions
|
||||
```typescript
|
||||
// 加载 Raycast 模块
|
||||
import {WebGLEngine, HitResult, Ray} from "@galacean/engine";
|
||||
import {LitePhysics} from "@galacean/engine-physics-lite";
|
||||
// Load the Raycast module
|
||||
import { WebGLEngine, HitResult, Ray } from "@galacean/engine";
|
||||
import { LitePhysics } from "@galacean/engine-physics-lite";
|
||||
|
||||
const engine = await WebGLEngine.create({
|
||||
canvas: "canvas",
|
||||
physics: new LitePhysics(),
|
||||
physics: new LitePhysics()
|
||||
});
|
||||
engine.canvas.resizeByClientSize();
|
||||
|
||||
// 将屏幕输入转换成Ray
|
||||
document.getElementById('canvas').addEventListener('click', (e) => {
|
||||
const scene = engine.scenes[0];
|
||||
// Convert screen input to Ray
|
||||
document.getElementById("canvas").addEventListener("click", (e) => {
|
||||
const ratio = window.devicePixelRatio;
|
||||
let ray = new Ray();
|
||||
camera.screenPointToRay(new Vector2(e.offsetX, e.offsetY).scale(ratio), ray);
|
||||
const hit = new HitResult();
|
||||
result = engine.physicsManager.raycast(ray, Number.MAX_VALUE, Layer.Everything, hit);
|
||||
result = scene.physics.raycast(ray, Number.MAX_VALUE, Layer.Everything, hit);
|
||||
if (result) {
|
||||
console.log(hit.entity.name);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
It should be particularly noted that if you want to enable raycasting for an Entity, the Entity must have a **Collider**, otherwise it cannot be triggered. If the Shapes of the Colliders hit by the ray are at the same distance, the Shape that was added first will be returned (for example: if two Entities with the same Collider completely overlap, the Entity with the Collider added first, or more accurately, the Shape added first, will be returned).
|
||||
### Notes
|
||||
|
||||
At the same time, in Galacean, an InputManager is also provided. This manager encapsulates the input sources and provides more user-friendly logic. You can refer to [here](/en/docs/input) for usage.
|
||||
- Entity must add [collider](/docs/physics/collider/overview) components to be detected by raycasting
|
||||
- When a ray hits multiple [collider shapes](/docs/physics/collider/colliderShape) at the same distance, it will return the Entity of the collider shape that was added first
|
||||
- It is recommended to use [InputManager](/docs/input/input/) to handle input, as it provides a more convenient way to handle input
|
||||
|
||||
@@ -44,3 +44,121 @@ Choosing a physics backend needs to consider three factors: functionality, perfo
|
||||
1. Functionality: For complete physics engine functionality and high-performance physics simulation, it is recommended to choose the PhysX backend. The Lite backend only supports collision detection.
|
||||
2. Performance: PhysX will automatically downgrade to pure JavaScript code on platforms that do not support WebAssembly, so performance will also decrease. However, due to the built-in data structures for scene search, the performance is still better than the Lite backend.
|
||||
3. Package Size: Choosing the PhysX backend will additionally introduce nearly 2.5mb of wasm files (the size of the pure JavaScript version is similar), increasing the package size while reducing the application's initialization speed.
|
||||
|
||||
---
|
||||
order: 0
|
||||
title: Physics Overview
|
||||
type: Physics
|
||||
label: Physics
|
||||
---
|
||||
|
||||
The physics engine is a crucial component of game engines, primarily responsible for the following functions:
|
||||
- Physical collision detection
|
||||
- Rigid body dynamics simulation
|
||||
- Joint constraint system
|
||||
- Physics event response
|
||||
|
||||
To meet the needs of different application scenarios, Galacean adopts a multi-backend design:
|
||||
|
||||
- **Lite**: Optimized for simple interaction scenarios, lightweight
|
||||
- **PhysX**: Provides complete physics features based on PhysX physics engine
|
||||
|
||||
Both backends implement a unified [Physics System API](https://github.com/galacean/engine/tree/main/packages/design/src/physics), allowing developers to flexibly choose based on project requirements. For detailed design specifications of the physics system, please refer to the [Design Document](https://github.com/galacean/engine/wiki/Physical-system-design).
|
||||
|
||||
<Callout type="info">
|
||||
For scenarios requiring physics components or Raycast picking like `InputManager`, the physics engine needs to be initialized before use.
|
||||
</Callout>
|
||||
|
||||
Currently, Galacean Engine provides two built-in physics engine backend implementations:
|
||||
|
||||
1. [Lite](/apis/physics-lite)([physics-lite](https://github.com/galacean/engine/tree/main/packages/physics-lite))
|
||||
- Lightweight physics engine implementation
|
||||
- Only supports basic collision detection
|
||||
- Suitable for simple interaction scenarios
|
||||
|
||||
2. [PhysX](/apis/physics-physx)([physics-physx](https://github.com/galacean/engine/tree/main/packages/physics-physx))
|
||||
- Based on PhysX physics engine, compiled through WebAssembly
|
||||
- Supports advanced physics features and accurate simulation
|
||||
- Suitable for complex physical interaction scenarios
|
||||
|
||||
## Choosing a Physics Backend
|
||||
|
||||
When choosing a physics backend, three factors need to be considered: functionality, performance, and package size:
|
||||
|
||||
### 1. Feature Support
|
||||
|
||||
| Feature | physics-lite | physics-physx |
|
||||
|---------|-------------|---------------|
|
||||
| Collision Detection | ✓ | ✓ |
|
||||
| Physics Effects and Feedback | × | ✓ |
|
||||
| Continuous Collision Detection | × | ✓ |
|
||||
| Joint System | × | ✓ |
|
||||
|
||||
### 2. Performance
|
||||
- **PhysX**:
|
||||
- Best performance on WebAssembly platform
|
||||
- Automatically falls back to JavaScript implementation
|
||||
- Built-in scene acceleration structure
|
||||
|
||||
- **Lite**:
|
||||
- Lightweight implementation, low performance overhead
|
||||
- Suitable for simple scenarios
|
||||
|
||||
### 3. Package Size
|
||||
- **PhysX**: About 2.5MB (wasm/js)
|
||||
- **Lite**: Lightweight, almost no additional overhead
|
||||
|
||||
## Usage
|
||||
|
||||
### Configuration in Editor
|
||||
|
||||
Developers can set the physics backend in the **Project Settings** panel accessed through the [Main Menu](/docs/interface/menu).
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*LO_FRIsaIzIAAAAAAAAAAAAADsJ_AQ/original" />
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*ZvWdQqEfIKoAAAAAAAAAAAAADsJ_AQ/original" />
|
||||
|
||||
### Script Usage
|
||||
|
||||
When initializing the engine through scripts, simply pass the physics backend object to the `Engine`:
|
||||
|
||||
#### Using Lite Physics Engine
|
||||
```typescript
|
||||
import { WebGLEngine } from "@galacean/engine-rhi-webgl";
|
||||
import { LitePhysics } from "@galacean/engine-physics-lite";
|
||||
|
||||
const engine = await WebGLEngine.create({ canvas: "canvas" });
|
||||
engine.physicsManager.initialize(LitePhysics);
|
||||
```
|
||||
|
||||
#### Using PhysX Physics Engine
|
||||
```typescript
|
||||
import { WebGLEngine } from "@galacean/engine-rhi-webgl";
|
||||
import { PhysXPhysics } from "@galacean/engine-physics-physx";
|
||||
|
||||
const engine = await WebGLEngine.create({ canvas: "canvas" });
|
||||
await engine.physicsManager.initialize(PhysXPhysics);
|
||||
```
|
||||
|
||||
## More Information
|
||||
|
||||
For detailed usage of the physics system, please refer to the following documentation:
|
||||
|
||||
### Colliders
|
||||
- [Collider Overview](/docs/physics/collider/overview)
|
||||
- [DynamicCollider](/docs/physics/collider/dynamicCollider) - Freely moving physical objects
|
||||
- [StaticCollider](/docs/physics/collider/staticCollider) - Fixed physical objects in the scene
|
||||
- [CharacterController](/docs/physics/collider/characterController) - Collider dedicated to character control
|
||||
- [ColliderShape](/docs/physics/collider/colliderShape) - Shape definition for colliders
|
||||
|
||||
### Joint System
|
||||
- [Joint System Overview](/docs/physics/joint/overview)
|
||||
- [FixedJoint](/docs/physics/joint/fixedJoint) - Completely restricts relative motion between objects
|
||||
- [SpringJoint](/docs/physics/joint/springJoint) - Spring-like distance constraints
|
||||
- [HingeJoint](/docs/physics/joint/hingeJoint) - Axial rotation constraints
|
||||
|
||||
### PhysicsScene
|
||||
- [PhysicsScene](/docs/physics/manager) - Physics system manager in the scene
|
||||
|
||||
|
||||
### Physics Debugging
|
||||
- [Physics Debug](/docs/physics/debug) - Physics system debugging tools
|
||||
|
||||
@@ -59,6 +59,12 @@
|
||||
"collapse": true
|
||||
}
|
||||
},
|
||||
"audio": {
|
||||
"title": "音频",
|
||||
"theme": {
|
||||
"collapse": true
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"title": "交互",
|
||||
"theme": {
|
||||
|
||||
28
docs/zh/audio/assets.mdx
Normal file
28
docs/zh/audio/assets.mdx
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
order: 1
|
||||
title: 音频资产
|
||||
type: 音频
|
||||
label: Audio
|
||||
---
|
||||
|
||||
音频资产是 Galacean 中用于存储和管理音频文件的核心组件。通过音频资产,开发者可以方便地上传、管理和使用各种音频文件。
|
||||
|
||||
## 支持的音频格式
|
||||
|
||||
Galacean引擎支持多种常见的音频格式,包括但不限于:
|
||||
- MP3
|
||||
- WAV
|
||||
- OGG
|
||||
|
||||
## 上传和管理音频文件
|
||||
|
||||
开发者可以通过资产选择器轻松上传音频文件,上传的音频文件会自动存储在资产库中,方便后续使用。
|
||||
|
||||
<video src="https://gw.alipayobjects.com/v/huamei_edbbqz/afts/video/dXVsQboyhYwAAAAAAAAAAAAADoY9AQFr" autoPlay loop muted />
|
||||
|
||||
|
||||
## 音频预览
|
||||
|
||||
在资产库中,开发者可以预览播放音频文件,确保音频文件符合游戏需求。
|
||||
|
||||
<video src="https://gw.alipayobjects.com/v/huamei_edbbqz/afts/video/sZwaQr6CuAMAAAAAAAAAAAAADoY9AQFr" autoPlay loop muted />
|
||||
29
docs/zh/audio/component.mdx
Normal file
29
docs/zh/audio/component.mdx
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
order: 2
|
||||
title: 音频组件
|
||||
type: 音频
|
||||
label: Audio
|
||||
---
|
||||
|
||||
音频组件是 Galacean 中用于在实体中添加音频播放功能的核心组件。通过音频组件,开发者可以控制音频的播放、暂停、停止以及音量调节等功能。
|
||||
|
||||
|
||||
## 添加音频组件
|
||||
|
||||
开发者可以按照以下步骤在实体中添加音频组件:
|
||||
|
||||
1. **选择目标实体**:在项目面板中选择需要添加音频组件的[实体](/docs/core/entity)。
|
||||
2. **打开Inspector面板**:选中目标实体后,Inspector面板将显示该实体的所有组件。
|
||||
3. **添加组件**:在Inspector面板的底部,点击“添加组件”按钮,并从列表中选择“音频”。
|
||||
|
||||
<video src="https://gw.alipayobjects.com/v/huamei_edbbqz/afts/video/JJ5QSKZKhzgAAAAAAAAAAAAADoY9AQFr" autoPlay loop muted />
|
||||
|
||||
## 调节音量和静音
|
||||
|
||||
音频组件提供了调节音量和静音的功能。开发者可以通过脚本或 Inspector 面板来控制这些属性。
|
||||
|
||||
<video src="https://gw.alipayobjects.com/v/huamei_edbbqz/afts/video/lDyIRbwv5nAAAAAAAAAAAAAADoY9AQFr" autoPlay loop muted />
|
||||
|
||||
## 控制播放时机
|
||||
|
||||
音频组件允许开发者精确控制音频的播放时机。开发者可以通过脚本调用播放、暂停和停止等方法来控制音频的播放。
|
||||
23
docs/zh/audio/overview.mdx
Normal file
23
docs/zh/audio/overview.mdx
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
order: 0
|
||||
title: 音频概述
|
||||
type: 音频
|
||||
label: Audio
|
||||
---
|
||||
|
||||
Galacean引擎提供了音频功能,音频是构建沉浸式体验的重要组成部分。Galacean 的音频功能支持用户导入多种标准格式的音频资产,为 [Entity](/docs/core/entity) 添加音频组件,并能够在3D空间中精准播放声音,包括背景音乐和音效,为游戏提供更加完整和真实的体验。
|
||||
|
||||
|
||||
## 音频资产
|
||||
|
||||
音频资产是 Galacean 中用于存储和管理音频文件的核心组件。通过音频资产,开发者可以方便地上传、管理和使用各种音频文件。
|
||||
|
||||
## 音频组件
|
||||
|
||||
音频组件是 Galacean 中用于在实体中添加音频播放功能的核心组件。通过音频组件,开发者可以控制音频的播放、暂停、停止以及音量调节等功能。
|
||||
|
||||
|
||||
在本章节,您可以了解到:
|
||||
|
||||
- [音频资产](/docs/audio/assets/):有关于音频文件及其支持的格式等参考信息
|
||||
- [音频组件](/docs/audio/component/):有关于音频组件相关属性
|
||||
22
docs/zh/physics/_meta.json
Normal file
22
docs/zh/physics/_meta.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"overall": {
|
||||
"title": "物理总览",
|
||||
"order": 0
|
||||
},
|
||||
"collider": {
|
||||
"title": "碰撞器",
|
||||
"order": 1
|
||||
},
|
||||
"joint": {
|
||||
"title": "关节",
|
||||
"order": 2
|
||||
},
|
||||
"manager": {
|
||||
"title": "物理场景",
|
||||
"order": 3
|
||||
},
|
||||
"debug": {
|
||||
"title": "物理调试",
|
||||
"order": 4
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
---
|
||||
order: 3
|
||||
title: 碰撞器组件
|
||||
type: 物理
|
||||
label: Physics
|
||||
---
|
||||
|
||||
引入物理引擎的最大好处是使得场景中的物体拥有了物理响应。碰撞器([Collider](/apis/core/#Collider))在引擎中是一种组件,目前有两种,在使用前,我们需要先了解下这两种碰撞器:
|
||||
|
||||
1. [StaticCollider](/apis/core/#StaticCollider):静态碰撞器,主要用于场景中静止的物体;
|
||||
2. [DynamicCollider](/apis/core/#DynamicCollider):动态碰撞器,用于场景中需要受到脚本控制,或者响应物理反馈的物体。
|
||||
|
||||
## 编辑器使用
|
||||
|
||||
### 添加碰撞器组件
|
||||
|
||||
为一个物体添加物理组件之前,首先需要考虑的是,碰撞器是静态的还是动态的,然后添加对应的碰撞器组件,静态碰撞器 [StaticCollider](/apis/core/#StaticCollider) 或者 [DynamicCollider](/apis/core/#DynamicCollider)
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*-E4USbdiH6sAAAAAAAAAAAAADsJ_AQ/original" />
|
||||
|
||||
### 选择碰撞器的外形
|
||||
|
||||
之后我们需要为碰撞器组件添加 [ColliderShape](/apis/core/#ColliderShape),事实上,每一种 `Collider` 都是 [ColliderShape](/apis/core/#ColliderShape) 的集合,即每一种 `Collider` 都可以通过组合 [ColliderShape](/apis/core/#ColliderShape) 设置复合的碰撞器外形。
|
||||
|
||||
目前支持了四种 `ColliderShape`,但不同的后端物理包支持程度不同,具体如下:
|
||||
|
||||
| 名称 | 解释 | 支持的后端物理包 |
|
||||
| :--- |:---------|:----------------------------|
|
||||
| [BoxColliderShape](/apis/core/#BoxColliderShape) | 盒形碰撞外形 | physics-lite, physics-physx |
|
||||
| [SphereColliderShape](/apis/core/#SphereColliderShape) | 球形碰撞外形 | physics-lite, physics-physx |
|
||||
| [PlaneColliderShape](/apis/core/#PlaneColliderShape) | 无界平面碰撞外形 | physics-physx |
|
||||
| [CapsuleColliderShape](/apis/core/#CapsuleColliderShape) | 胶囊碰撞外形 | physics-physx |
|
||||
|
||||
引擎支持复合的碰撞器外形,也就是说,碰撞器本身可以由 `BoxColliderShape`,`SphereColliderShape`,`CapsuleColliderShape` 复合而成。
|
||||
|
||||
这里特别强调的是 `Collider` 与 `ColliderShape` 的位置关系。每一个 `Collider` 的姿态和其挂载的 `Entity` 是一致的,每一帧两者都会进行同步。而 `ColliderShape` 上则可以通过 `position` 属性设置 **相对于** `Entity` 的偏移。
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_vvspai/afts/img/A*erlGRKk7dNMAAAAAAAAAAAAADsqFAQ/original" />
|
||||
|
||||
在加入碰撞器组件后,不会默认添加碰撞器外形,因此需要点击 Add Item 进行添加,添加后会在视口中看到碰撞器的辅助渲染出现。
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*OUr-SIejEkoAAAAAAAAAAAAADsJ_AQ/original" />
|
||||
|
||||
对于每一个碰撞器外形,都可以设计对应的一些大小属性。例如
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*d4MCRbuHeMsAAAAAAAAAAAAADsJ_AQ/original" alt="alt text" style={{ zoom: "67%" }} />
|
||||
|
||||
无论哪个碰撞器外形,都可以设置 Local Position,即相对于 Entity 坐标的局部偏移
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*p8UcRJ9Q0EIAAAAAAAAAAAAADsJ_AQ/original" />
|
||||
|
||||
`ColliderShape` 上还有一个值得注意的属性 `Trigger`,它可以将这个 `ColliderShape` 由 `碰撞器模式` 转为 `触发器模式`。
|
||||
|
||||
触发器模式:物体不具备刚体外形,但发生接触时可以触发特定的脚本函数。
|
||||
碰撞器模式:物理具有刚体外形,发生接触时不仅可以触发脚本函数,还可以根据物理规律改变原先的运动。
|
||||
|
||||
### 动态碰撞器设置
|
||||
和静态碰撞器不同,动态碰撞器会受到物理规律的作用,因此有许多附加的物理属性进行设置
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*7rzqSKtjULMAAAAAAAAAAAAADsJ_AQ/original" alt="alt text" style={{ zoom: "67%" }} />
|
||||
|
||||
在修改这些参数后,视口不会发生变化,因为动态碰撞器默认会受到重力的作用,因此需要在`预览模式`下才能进行观察。
|
||||
|
||||
### 注意
|
||||
- 确定的碰撞区域应尽量简单,以提高物理引擎检测的性能
|
||||
- 碰撞器的参照坐标系为从属 Entity 的坐标系
|
||||
- PlaneColliderShape 表示全平面,因此没有辅助线的显示,一般作为地板使用
|
||||
|
||||
## 脚本使用
|
||||
|
||||
### 添加碰撞器
|
||||
``` typescript
|
||||
// 添加静态碰撞器
|
||||
const boxCollider = boxEntity.addComponent(StaticCollider);
|
||||
// 添加动态碰撞器
|
||||
const sphereCollider = sphereEntity.addComponent(DynamicCollider);
|
||||
```
|
||||
|
||||
### 添加ColliderShape
|
||||
``` typescript
|
||||
const boxCollider = boxEntity.getComponent(StaticCollider);
|
||||
const physicsBox = new BoxColliderShape();
|
||||
physicsBox.size = new Vector3(1, 1, 1);
|
||||
boxCollider.addShape(physicsBox);
|
||||
|
||||
//设置 Trigger
|
||||
physicsBox.isTrigger = true;
|
||||
```
|
||||
|
||||
针对这两种类型,脚本中都提供了对应的函数
|
||||
|
||||
### 触发器脚本函数
|
||||
|
||||
对于触发器模式,首先需要给场景中的 `Entity` 添加 `Collider`;该当这些组件相互接触时,会自动触发脚本组件当中的三个函数:
|
||||
|
||||
1. [onTriggerEnter](/docs/script/class/#ontriggerenter):相互接触时调用
|
||||
2. [onTriggerStay](/docs/script/class/#ontriggerstay):接触过程中*循环*调用
|
||||
3. [onTriggerExit](/docs/script/class/#ontriggerexit):接触结束时调用
|
||||
|
||||
可以通过 `ColliderShape` 上的 `isTrigger` 开启触发器模式,但需要特别强调的是,**两个 StaticCollider 之间不会调用触发器事件**,除非其中一个是 `DynamicCollider`。
|
||||
|
||||
<Playground href="/embed/physx-collision-detection" />
|
||||
|
||||
### 碰撞器脚本函数
|
||||
|
||||
对于碰撞器模式,`DynamicCollider` 相互作用时会触发三个碰撞相关的脚本函数:
|
||||
1. [onCollisionEnter](/docs/script/class/#oncollisionenter):碰撞触发时调用
|
||||
2. [onCollisionStay](/docs/script/class/#oncollisionstay):碰撞过程中*循环*调用
|
||||
3. [onCollisionExit](/docs/script/class/#oncollisionexit):碰撞结束时调用
|
||||
|
||||
<Playground href="/embed/physx-compound" />
|
||||
163
docs/zh/physics/collider/characterController.mdx
Normal file
163
docs/zh/physics/collider/characterController.mdx
Normal file
@@ -0,0 +1,163 @@
|
||||
---
|
||||
order: 3
|
||||
title: 角色控制器
|
||||
type: 物理
|
||||
label: Physics
|
||||
---
|
||||
|
||||
角色控制器([CharacterController](/apis/core/#CharacterController))是一种特殊的碰撞器组件,专门用于处理角色的物理运动。它提供了专门的移动算法和碰撞检测,能够处理台阶、斜坡等复杂地形,特别适合第一人称或第三人称游戏中的角色控制。
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 选中目标实体,并在检查器中点击添加组件按钮,添加 CharacterController 组件。
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*nEMXRKiqpy8AAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
2. 设置控制器的碰撞形状,使碰撞形状与角色的外形相尽量匹配。关于碰撞形状的详细说明请参考[碰撞形状](/docs/physics/collider/colliderShape)文档。
|
||||
|
||||
<Callout type="positive">
|
||||
与其他碰撞器不同,角色控制器只能添加一个碰撞形状。通常建议使用胶囊体(CapsuleColliderShape)作为角色的碰撞形状。
|
||||
</Callout>
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*4QvUTI4D89EAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*aRGqSIMqDmsAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
3. 根据需要设置碰撞器的属性调整物体的物理行为,各属性的含义和作用请参考下文。
|
||||
|
||||
|
||||
|
||||
## 属性说明
|
||||
|
||||
### 继承自 Collider 的属性
|
||||
| 属性 | 描述 |
|
||||
| ----------------------------------------- | ------------ |
|
||||
| [**shapes**](/apis/core/#Collider-shapes) | 碰撞形状集合 |
|
||||
|
||||
### 特有属性
|
||||
| 属性 | 描述 | 默认值 |
|
||||
| ---- | ---- | ------ |
|
||||
| [**stepOffset**](/apis/core/#CharacterController-stepOffset) | 角色可以自动跨越的最大台阶高度。<ul><li>必须大于等于 0</li><li>实际可跨越高度 = stepOffset + 碰撞形状的接触偏移</li></ul> | 0.5 |
|
||||
| [**slopeLimit**](/apis/core/#CharacterController-slopeLimit) | 角色可以行走的最大斜坡角度(度)。<ul><li>超过此角度的斜面将被视为不可行走的墙壁</li><li>影响角色的爬坡能力</li></ul> | 45° |
|
||||
| [**nonWalkableMode**](/apis/core/#CharacterController-nonWalkableMode) | 定义如何处理不可行走的表面。<ul><li>PreventClimbing:阻止角色攀爬不可行走的斜坡,但不会强制其他移动(默认)</li><li>PreventClimbingAndForceSliding:阻止角色攀爬不可行走的斜坡,并强制角色沿斜坡滑下</li></ul> | PreventClimbing |
|
||||
| [**upDirection**](/apis/core/#CharacterController-upDirection) | 定义角色的向上方向。默认为 (0, 1, 0),即世界空间的 Y 轴向上。影响移动和碰撞检测的方向判定 | (0, 1, 0) |
|
||||
|
||||
## 公开方法
|
||||
|
||||
### 继承自 Collider 的方法
|
||||
| 方法名 | 描述 |
|
||||
| --------------------------------------------------- | ---------------- |
|
||||
| [**addShape**](/apis/core/#Collider-addShape) | 添加碰撞形状 |
|
||||
| [**removeShape**](/apis/core/#Collider-removeShape) | 移除指定碰撞形状 |
|
||||
| [**clearShapes**](/apis/core/#Collider-clearShapes) | 清空所有碰撞形状 |
|
||||
|
||||
### 特有方法
|
||||
| 方法名 | 描述 |
|
||||
| ---- | ---- |
|
||||
| [**move**](/apis/core/#CharacterController-move) | 移动角色控制器。返回一个碰撞标志值,标识碰撞状态。<ul><li>displacement:移动向量</li><li>minDist:最小移动距离</li><li>elapsedTime:经过的时间</li></ul> |
|
||||
|
||||
### 碰撞标志说明
|
||||
|
||||
移动函数 `move()` 会返回一个碰撞标志值,用于表示角色控制器与环境的碰撞状态。这些标志可以通过按位与运算(&)来检测:
|
||||
|
||||
| 标志名称 | 值 | 说明 |
|
||||
|---------|----|----|
|
||||
| None | 0 | 没有发生任何碰撞 |
|
||||
| Sides | 1 | 与侧面发生碰撞 |
|
||||
| Up | 2 | 与上方发生碰撞(如天花板) |
|
||||
| Down | 4 | 与下方发生碰撞(如地面) |
|
||||
|
||||
## 脚本使用
|
||||
|
||||
### 基础配置
|
||||
```typescript
|
||||
// 创建角色控制器
|
||||
const controller = entity.addComponent(CharacterController);
|
||||
|
||||
// 添加胶囊体形状
|
||||
const capsule = new CapsuleColliderShape();
|
||||
capsule.radius = 0.5;
|
||||
capsule.height = 2;
|
||||
controller.addShape(capsule);
|
||||
|
||||
// 配置控制器属性
|
||||
controller.stepOffset = 0.5; // 设置台阶高度
|
||||
controller.slopeLimit = 45; // 设置最大可行走斜坡角度
|
||||
controller.upDirection = new Vector3(0, 1, 0); // 设置向上方向
|
||||
```
|
||||
|
||||
### 使用移动函数
|
||||
```typescript
|
||||
class CharacterMovement extends Script {
|
||||
private _velocity = new Vector3();
|
||||
|
||||
onUpdate() {
|
||||
const controller = this.entity.getComponent(CharacterController);
|
||||
const deltaTime = engine.deltaTime;
|
||||
|
||||
// 创建位移向量
|
||||
const displacement = new Vector3();
|
||||
Vector3.scale(this._velocity, deltaTime, displacement);
|
||||
|
||||
// 执行移动并获取碰撞标志
|
||||
// minDist: 最小移动距离,通常设为0
|
||||
// deltaTime: 经过的时间,用于物理计算
|
||||
const collisionFlags = controller.move(displacement, 0, deltaTime);
|
||||
|
||||
// 处理碰撞响应
|
||||
if (collisionFlags & ControllerCollisionFlag.Down) {
|
||||
// 角色接触地面
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
使用示例:
|
||||
```typescript
|
||||
const flags = controller.move(displacement, 0, deltaTime);
|
||||
|
||||
// 检查是否接触地面
|
||||
if (flags & ControllerCollisionFlag.Down) {
|
||||
// 角色在地面上
|
||||
this._isGrounded = true;
|
||||
}
|
||||
|
||||
// 检查是否撞到天花板
|
||||
if (flags & ControllerCollisionFlag.Up) {
|
||||
// 角色撞到头部
|
||||
this._velocity.y = 0;
|
||||
}
|
||||
|
||||
// 检查是否撞到墙壁
|
||||
if (flags & ControllerCollisionFlag.Sides) {
|
||||
// 角色撞到墙壁
|
||||
this._handleWallCollision();
|
||||
}
|
||||
|
||||
// 可以同时检查多个标志
|
||||
if ((flags & ControllerCollisionFlag.Down) &&
|
||||
(flags & ControllerCollisionFlag.Sides)) {
|
||||
// 角色同时接触地面和墙壁
|
||||
}
|
||||
```
|
||||
|
||||
### 斜坡/台阶行走
|
||||
|
||||
1. **斜坡行走**
|
||||
```typescript
|
||||
// 通过设置 slopeLimit 控制可行走的斜坡角度
|
||||
controller.slopeLimit = 60; // 允许更陡的斜坡
|
||||
|
||||
// 设置不可行走斜面的处理方式
|
||||
controller.nonWalkableMode = ControllerNonWalkableMode.PreventClimbingAndForceSliding; // 在太陡的斜面上会滑下
|
||||
```
|
||||
|
||||
2. **台阶行走调整**
|
||||
```typescript
|
||||
// 调整 stepOffset 来控制可跨越的台阶高度
|
||||
controller.stepOffset = 0.3; // 较低的台阶
|
||||
controller.stepOffset = 0.5; // 较高的台阶
|
||||
```
|
||||
|
||||
完整的示例效果可以参考:
|
||||
<Playground href="/embed/physx-controller" />
|
||||
296
docs/zh/physics/collider/colliderShape.mdx
Normal file
296
docs/zh/physics/collider/colliderShape.mdx
Normal file
@@ -0,0 +1,296 @@
|
||||
---
|
||||
order: 4
|
||||
title: 碰撞形状
|
||||
type: 物理
|
||||
label: Physics
|
||||
---
|
||||
|
||||
碰撞形状([ColliderShape](/apis/core/#ColliderShape))定义了碰撞器的物理外形。Galacean 提供了多种基础碰撞形状,可以通过组合这些形状来构建复杂的碰撞区域。
|
||||
|
||||
## 支持的形状类型
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*jsEBTIs9C_MAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
| 形状类型 | 特点 | 后端支持 |
|
||||
| -------- | ---- | -------- |
|
||||
| [**BoxColliderShape**](/apis/core/#BoxColliderShape) | 最基础的碰撞形状<br/>适用于方形、矩形物体<br/>可以通过 size 属性调整三个轴向的大小 | 所有物理后端 |
|
||||
| [**SphereColliderShape**](/apis/core/#SphereColliderShape) | 用于球形物体的碰撞检测<br/>通过 radius 属性设置半径 | 所有物理后端 |
|
||||
| [**PlaneColliderShape**](/apis/core/#PlaneColliderShape) | 无限大的平面碰撞体<br/>通常用作地面 | 仅 physics-physx 后端 |
|
||||
| [**CapsuleColliderShape**](/apis/core/#CapsuleColliderShape) | 由圆柱体和两个半球组成<br/>适用于角色碰撞器<br/>可以设置 radius 和 height | 仅 physics-physx 后端 |
|
||||
|
||||
## 基本属性
|
||||
|
||||
### 变换属性
|
||||
- [**position**](/apis/core/#ColliderShape-position)
|
||||
形状相对于碰撞器实体的局部位置偏移。
|
||||
|
||||
- [**rotation**](/apis/core/#ColliderShape-rotation)
|
||||
形状相对于碰撞器实体的局部旋转。
|
||||
|
||||
### 物理材质
|
||||
- [**material**](/apis/core/#ColliderShape-material)
|
||||
定义形状的物理材质属性。每个碰撞形状都需要有一个物理材质,用于定义其物理特性:
|
||||
|
||||
| 属性 | 描述 | 默认值 |
|
||||
| ---- | ---- | ------ |
|
||||
| [**staticFriction**](/apis/core/#PhysicsMaterial-staticFriction) | 物体静止时的摩擦系数,值越大物体越难开始移动 | 0.6 |
|
||||
| [**dynamicFriction**](/apis/core/#PhysicsMaterial-dynamicFriction) | 物体运动时的摩擦系数,值越大物体移动时受到的阻力越大 | 0.6 |
|
||||
| [**bounciness**](/apis/core/#PhysicsMaterial-bounciness) | 碰撞反弹程度,取值范围 0-1,0 表示完全不反弹,1 表示完全弹性碰撞 | 0 |
|
||||
| [**bounceCombine**](/apis/core/#PhysicsMaterial-bounceCombine) | 定义两个碰撞物体的弹性系数如何组合: <ul><li>Average: 取两者平均值(默认)</li><li>Minimum: 取两者中的最小值</li><li>Maximum: 取两者中的最大值</li><li>Multiply: 将两者相乘</li></ul> | Average |
|
||||
| [**frictionCombine**](/apis/core/#PhysicsMaterial-frictionCombine) | 定义两个碰撞物体的摩擦系数如何组合: <ul><li>Average: 取两者平均值(默认)</li><li>Minimum: 取两者中的最小值</li><li>Maximum: 取两者中的最大值</li><li>Multiply: 将两者相乘</li></ul> | Average |
|
||||
|
||||
### 触发器设置
|
||||
- [**isTrigger**](/apis/core/#ColliderShape-isTrigger)
|
||||
是否作为触发器使用:
|
||||
- true:只触发触发器事件,不产生物理反应
|
||||
- false:正常的物理碰撞(默认)
|
||||
|
||||
|
||||
有关碰撞和触发器事件的详细信息,请参阅[碰撞事件](/docs/physics/collider/event)文档。
|
||||
|
||||
## 脚本使用
|
||||
|
||||
### 创建基础形状
|
||||
|
||||
```typescript
|
||||
// 创建盒形碰撞器
|
||||
const boxShape = new BoxColliderShape();
|
||||
boxShape.size = new Vector3(1, 1, 1);
|
||||
boxShape.position = new Vector3(0, 0.5, 0);
|
||||
|
||||
// 创建球形碰撞器
|
||||
const sphereShape = new SphereColliderShape();
|
||||
sphereShape.radius = 0.5;
|
||||
sphereShape.position = new Vector3(0, 1, 0);
|
||||
|
||||
// 创建胶囊体碰撞器
|
||||
const capsuleShape = new CapsuleColliderShape();
|
||||
capsuleShape.radius = 0.5;
|
||||
capsuleShape.height = 2;
|
||||
|
||||
// 创建平面碰撞器
|
||||
const planeShape = new PlaneColliderShape();
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 形状管理
|
||||
|
||||
```typescript
|
||||
// 获取所有形状
|
||||
const shapes = staticCollider.shapes;
|
||||
|
||||
// 增加形状
|
||||
const boxShape = new BoxColliderShape();
|
||||
staticCollider.addShape(boxShape);
|
||||
|
||||
// 移除特定形状
|
||||
staticCollider.removeShape(boxShape);
|
||||
|
||||
// 清空所有形状
|
||||
staticCollider.clearShapes();
|
||||
```
|
||||
|
||||
|
||||
### 设置物理材质
|
||||
|
||||
```typescript
|
||||
// 创建物理材质并配置属性
|
||||
const material = new PhysicsMaterial();
|
||||
material.staticFriction = 0.6; // 静摩擦系数
|
||||
material.dynamicFriction = 0.4; // 动摩擦系数
|
||||
material.bounciness = 0.5; // 弹性系数
|
||||
|
||||
// 设置材质的组合模式
|
||||
material.frictionCombine = PhysicsMaterialCombineMode.Average; // 摩擦力组合方式
|
||||
material.bounceCombine = PhysicsMaterialCombineMode.Maximum; // 弹性组合方式
|
||||
|
||||
// 将材质应用到碰撞形状
|
||||
const shape = new BoxColliderShape();
|
||||
shape.material = material;
|
||||
|
||||
// 为不同的形状设置不同的材质
|
||||
const iceShape = new BoxColliderShape();
|
||||
const iceMaterial = new PhysicsMaterial();
|
||||
iceMaterial.staticFriction = 0.1;
|
||||
iceMaterial.dynamicFriction = 0.05;
|
||||
iceMaterial.bounciness = 0;
|
||||
iceShape.material = iceMaterial;
|
||||
|
||||
const bounceShape = new SphereColliderShape();
|
||||
const bounceMaterial = new PhysicsMaterial();
|
||||
bounceMaterial.bounciness = 0.8;
|
||||
bounceMaterial.bounceCombine = PhysicsMaterialCombineMode.Maximum;
|
||||
bounceShape.material = bounceMaterial;
|
||||
|
||||
// 不再使用时记得销毁材质
|
||||
material.destroy();
|
||||
iceMaterial.destroy();
|
||||
bounceMaterial.destroy();
|
||||
```
|
||||
|
||||
### 创建复合形状
|
||||
|
||||
```typescript
|
||||
// 创建带有多个碰撞形状的物体
|
||||
function createCompoundCollider(entity: Entity) {
|
||||
const collider = entity.addComponent(DynamicCollider);
|
||||
|
||||
// 主体形状
|
||||
const mainShape = new BoxColliderShape();
|
||||
mainShape.size = new Vector3(2, 1, 1);
|
||||
collider.addShape(mainShape);
|
||||
|
||||
// 顶部形状
|
||||
const topShape = new BoxColliderShape();
|
||||
topShape.size = new Vector3(1, 1, 1);
|
||||
topShape.position = new Vector3(0, 1, 0);
|
||||
collider.addShape(topShape);
|
||||
|
||||
return collider;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### 示例参考
|
||||
<Playground href="/embed/physx-compound" />
|
||||
|
||||
### 常用场景示例
|
||||
|
||||
1. **创建地面**
|
||||
```typescript
|
||||
// 创建一个简单的地面
|
||||
function createGround(width: number, length: number): Entity {
|
||||
const entity = new Entity();
|
||||
const collider = entity.addComponent(StaticCollider);
|
||||
|
||||
const groundShape = new BoxColliderShape();
|
||||
groundShape.size = new Vector3(width, 0.1, length);
|
||||
|
||||
// 设置地面的物理材质
|
||||
const material = new PhysicsMaterial();
|
||||
material.staticFriction = 0.6;
|
||||
material.dynamicFriction = 0.6;
|
||||
material.bounciness = 0.0;
|
||||
groundShape.material = material;
|
||||
|
||||
collider.addShape(groundShape);
|
||||
return entity;
|
||||
}
|
||||
|
||||
// 创建一个无限平面地面
|
||||
function createInfinitePlane(): Entity {
|
||||
const entity = new Entity();
|
||||
const collider = entity.addComponent(StaticCollider);
|
||||
|
||||
const planeShape = new PlaneColliderShape();
|
||||
// 设置地面材质
|
||||
const material = new PhysicsMaterial();
|
||||
material.staticFriction = 0.6;
|
||||
material.dynamicFriction = 0.6;
|
||||
planeShape.material = material;
|
||||
|
||||
collider.addShape(planeShape);
|
||||
return entity;
|
||||
}
|
||||
```
|
||||
|
||||
2. **创建墙壁**
|
||||
```typescript
|
||||
// 创建一个可配置的墙壁
|
||||
function createWall(width: number, height: number, depth: number, friction: number = 0.6): Entity {
|
||||
const entity = new Entity();
|
||||
const collider = entity.addComponent(StaticCollider);
|
||||
|
||||
const wallShape = new BoxColliderShape();
|
||||
wallShape.size = new Vector3(width, height, depth);
|
||||
|
||||
// 设置墙壁的物理材质
|
||||
const material = new PhysicsMaterial();
|
||||
material.staticFriction = friction;
|
||||
material.dynamicFriction = friction;
|
||||
material.bounciness = 0.0;
|
||||
wallShape.material = material;
|
||||
|
||||
collider.addShape(wallShape);
|
||||
return entity;
|
||||
}
|
||||
```
|
||||
|
||||
3. **创建斜坡**
|
||||
```typescript
|
||||
// 创建一个带摩擦力的斜坡
|
||||
function createSlope(width: number, height: number, angle: number): Entity {
|
||||
const entity = new Entity();
|
||||
const collider = entity.addComponent(StaticCollider);
|
||||
|
||||
const slopeShape = new BoxColliderShape();
|
||||
slopeShape.size = new Vector3(width, 0.1, height);
|
||||
slopeShape.rotation = new Vector3(0, 0, angle);
|
||||
|
||||
// 设置斜坡的物理材质
|
||||
const material = new PhysicsMaterial();
|
||||
material.staticFriction = 0.8; // 较大的摩擦力防止物体滑落
|
||||
material.dynamicFriction = 0.8;
|
||||
material.bounciness = 0.0;
|
||||
slopeShape.material = material;
|
||||
|
||||
collider.addShape(slopeShape);
|
||||
return entity;
|
||||
}
|
||||
```
|
||||
|
||||
4. **创建不同材质的复合形状**
|
||||
```typescript
|
||||
// 创建一个混合材质的平台
|
||||
function createMixedPlatform(): Entity {
|
||||
const entity = new Entity();
|
||||
const collider = entity.addComponent(StaticCollider);
|
||||
|
||||
// 创建主平台(普通摩擦力)
|
||||
const platformShape = new BoxColliderShape();
|
||||
platformShape.size = new Vector3(10, 0.5, 10);
|
||||
const normalMaterial = new PhysicsMaterial();
|
||||
normalMaterial.staticFriction = 0.6;
|
||||
normalMaterial.dynamicFriction = 0.6;
|
||||
platformShape.material = normalMaterial;
|
||||
collider.addShape(platformShape);
|
||||
|
||||
// 添加冰面区域(低摩擦力)
|
||||
const iceShape = new BoxColliderShape();
|
||||
iceShape.size = new Vector3(3, 0.51, 3);
|
||||
iceShape.position = new Vector3(3, 0, 0);
|
||||
const iceMaterial = new PhysicsMaterial();
|
||||
iceMaterial.staticFriction = 0.1;
|
||||
iceMaterial.dynamicFriction = 0.05;
|
||||
iceShape.material = iceMaterial;
|
||||
collider.addShape(iceShape);
|
||||
|
||||
// 添加弹性区域
|
||||
const bounceShape = new BoxColliderShape();
|
||||
bounceShape.size = new Vector3(3, 0.51, 3);
|
||||
bounceShape.position = new Vector3(-3, 0, 0);
|
||||
const bounceMaterial = new PhysicsMaterial();
|
||||
bounceMaterial.bounciness = 0.8;
|
||||
bounceMaterial.bounceCombine = PhysicsMaterialCombineMode.Maximum;
|
||||
bounceShape.material = bounceMaterial;
|
||||
collider.addShape(bounceShape);
|
||||
|
||||
return entity;
|
||||
}
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **形状选择**
|
||||
- 复杂物体使用多个基础形状组合
|
||||
- 避免使用过多的碰撞形状(影响性能)
|
||||
- 为角色控制器选择胶囊体形状
|
||||
|
||||
2. **性能优化**
|
||||
- 使用尽可能简单的碰撞形状
|
||||
- 合理设置局部偏移,避免碰撞形状过度重叠
|
||||
- 适当使用触发器代替实际的物理碰撞
|
||||
- 及时销毁不再使用的碰撞形状及物理材质
|
||||
|
||||
|
||||
293
docs/zh/physics/collider/dynamicCollider.mdx
Normal file
293
docs/zh/physics/collider/dynamicCollider.mdx
Normal file
@@ -0,0 +1,293 @@
|
||||
---
|
||||
order: 2
|
||||
title: 动态碰撞器
|
||||
type: 物理
|
||||
label: Physics
|
||||
---
|
||||
|
||||
动态碰撞器([DynamicCollider](/apis/core/#DynamicCollider))用于模拟可以自由运动并受物理影响的物体。它可以响应重力、受力、碰撞等物理作用,适用于需要真实物理模拟的游戏物体。
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 选中目标实体,并在检查器中点击添加组件按钮,添加 DynamicCollider 组件。
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*Ep4xTqWpligAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
2. 为碰撞器添加碰撞形状。动态碰撞器支持添加多个碰撞形状,关于碰撞形状的详细说明请参考[碰撞形状](/docs/physics/collider/colliderShape)文档。目前支持以下类型:
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*np90QZkKqXUAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
3. 调整碰撞形状的位置、大小等属性,使其与场景元素匹配。
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*QthGTLWFSh8AAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
4. 根据需要设置碰撞器的属性调整物体的物理行为,各属性的含义和作用请参考下文。
|
||||
|
||||
## 属性说明
|
||||
|
||||
### 继承自 Collider 的属性
|
||||
| 属性 | 描述 |
|
||||
| ----------------------------------------- | ------------ |
|
||||
| [**shapes**](/apis/core/#Collider-shapes) | 碰撞形状集合 |
|
||||
|
||||
### 特有属性
|
||||
|
||||
| 属性 | 描述 | 默认值 |
|
||||
| --------------- | --------------------------------------------------------- | ------ |
|
||||
| [**mass**](/apis/core/#DynamicCollider-mass) | 碰撞器的质量,质量越大物体运动状态越难改变 | 1.0 |
|
||||
| [**useGravity**](/apis/core/#DynamicCollider-useGravity) | 是否受重力影响 | true |
|
||||
| [**isKinematic**](/apis/core/#DynamicCollider-isKinematic) | 是否为运动学物体,运动学物体不受物理影响但可以影响其他物体 | false |
|
||||
|
||||
### 速度设置
|
||||
|
||||
| 属性 | 描述 | 默认值 |
|
||||
| ---------------------------- | ------------------------------------------- | ------- |
|
||||
| [**linearVelocity**](/apis/core/#DynamicCollider-linearVelocity) | 线性速度向量(世界单位/秒) | (0,0,0) |
|
||||
| [**angularVelocity**](/apis/core/#DynamicCollider-angularVelocity) | 角速度向量(度/秒) | (0,0,0) |
|
||||
| [**maxAngularVelocity**](/apis/core/#DynamicCollider-maxAngularVelocity) | 最大角速度限制(度/秒) | 18000/π |
|
||||
| [**maxDepenetrationVelocity**](/apis/core/#DynamicCollider-maxDepenetrationVelocity) | 碰撞体重叠时的最大分离速度,用于防止物体穿透 | 1e32 |
|
||||
|
||||
### 阻尼
|
||||
|
||||
| 属性 | 描述 | 默认值 |
|
||||
| ------------------ | ---------------- | ------ |
|
||||
| [**linearDamping**](/apis/core/#DynamicCollider-linearDamping) | 线性运动阻尼系数 | 0 |
|
||||
| [**angularDamping**](/apis/core/#DynamicCollider-angularDamping) | 角速度阻尼系数 | 0.05 |
|
||||
|
||||
### 质量/惯性张量
|
||||
|
||||
| 属性 | 描述 | 默认值 |
|
||||
| -------------------------- | ------------------------ | ------- |
|
||||
| [**centerOfMass**](/apis/core/#DynamicCollider-centerOfMass) | 质心相对于变换原点的位置 | (0,0,0) |
|
||||
| [**automaticCenterOfMass**](/apis/core/#DynamicCollider-automaticCenterOfMass) | 是否自动计算质心位置 | true |
|
||||
| [**inertiaTensor**](/apis/core/#DynamicCollider-inertiaTensor) | 物体相对于质心的惯性张量 | (1,1,1) |
|
||||
| [**automaticInertiaTensor**](/apis/core/#DynamicCollider-automaticInertiaTensor) | 是否自动计算惯性张量 | true |
|
||||
|
||||
### 性能优化设置
|
||||
|
||||
| 属性 | 描述 | 默认值 |
|
||||
| -------------------- | --------------------------------------- | ------ |
|
||||
| [**sleepThreshold**](/apis/core/#DynamicCollider-sleepThreshold) | 休眠阈值,物体运动能量低于此值时进入休眠 | 0.005 |
|
||||
| [**solverIterations**](/apis/core/#DynamicCollider-solverIterations) | 约束求解迭代次数 | 4 |
|
||||
|
||||
<Callout type="info">
|
||||
约束求解是物理引擎用来解决物体之间碰撞和约束的计算过程。每次迭代都会尝试调整物体的位置和速度,使其满足所有的物理约束(如碰撞、关节等)。
|
||||
- 迭代次数越多,物理表现越准确,但计算开销也越大
|
||||
- 迭代次数过少可能导致物体出现抖动或穿透
|
||||
- 建议根据实际需求平衡精度和性能:
|
||||
- 一般物体:使用默认值 4
|
||||
- 精确物理:可以增加到 6-8
|
||||
- 性能优先:可以降低到 2-3
|
||||
</Callout>
|
||||
|
||||
### 运动约束
|
||||
|
||||
- [**constraints**](/apis/core/#DynamicCollider-constraints)
|
||||
用于限制物体在特定轴向上的运动。可以分别锁定位置和旋转的X、Y、Z轴向运动。
|
||||
```typescript
|
||||
// 限制Y轴位置和所有旋转
|
||||
collider.constraints =
|
||||
DynamicColliderConstraints.FreezePositionY |
|
||||
DynamicColliderConstraints.FreezeRotationX |
|
||||
DynamicColliderConstraints.FreezeRotationY |
|
||||
DynamicColliderConstraints.FreezeRotationZ;
|
||||
```
|
||||
|
||||
### 碰撞检测模式
|
||||
|
||||
- [**collisionDetectionMode**](/apis/core/#DynamicCollider-collisionDetectionMode)
|
||||
用于控制物体碰撞检测的精确程度:
|
||||
|
||||
| 模式 | 描述 | 适用场景 | 性能消耗 |
|
||||
| ---- | ---- | -------- | -------- |
|
||||
| **Discrete** | 最基础的检测模式,按固定的时间步长检测碰撞,可能在高速运动时发生穿透 | 低速物体 | 最低 |
|
||||
| **Continuous** | 对静态碰撞体使用连续碰撞检测,可以防止高速物体穿透静态物体 | 高速投射物 | 中等 |
|
||||
| **ContinuousDynamic** | 对所有碰撞体使用连续碰撞检测,可以防止高速物体之间的相互穿透 | 精确物理模拟 | 较高 |
|
||||
| **ContinuousSpeculative** | 使用推测算法进行连续碰撞检测,性能消耗介于 Discrete 和 Continuous 之间 | 一般游戏场景 | 中等 |
|
||||
|
||||
#### 设置碰撞检测模式示例
|
||||
|
||||
```typescript
|
||||
// 1. 普通物体使用离散检测
|
||||
normalObject.collisionDetectionMode = CollisionDetectionMode.Discrete;
|
||||
|
||||
// 2. 投射物使用连续检测防止穿墙
|
||||
projectile.collisionDetectionMode = CollisionDetectionMode.Continuous;
|
||||
|
||||
// 3. 重要物理交互使用完全连续检测
|
||||
importantPhysicsObject.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
|
||||
|
||||
// 4. 一般游戏物体使用推测性检测
|
||||
gameObject.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative;
|
||||
```
|
||||
|
||||
#### 选择建议
|
||||
|
||||
1. **基于物体速度选择**
|
||||
|
||||
- 低速物体:使用 Discrete
|
||||
- 中速物体:使用 ContinuousSpeculative
|
||||
- 高速物体:使用 Continuous 或 ContinuousDynamic
|
||||
|
||||
2. **基于重要性选择**
|
||||
|
||||
- 普通场景物体:使用 Discrete
|
||||
- 游戏关键物体:使用 ContinuousSpeculative
|
||||
- 必须精确的物理交互:使用 ContinuousDynamic
|
||||
|
||||
3. **基于性能选择**
|
||||
- 性能优先:使用 Discrete
|
||||
- 性能与精度平衡:使用 ContinuousSpeculative
|
||||
- 精度优先:使用 ContinuousDynamic
|
||||
|
||||
|
||||
## 公开方法
|
||||
|
||||
### 继承自 Collider 的方法
|
||||
| 方法名 | 描述 |
|
||||
| --------------------------------------------------- | ---------------- |
|
||||
| [**addShape**](/apis/core/#Collider-addShape) | 添加碰撞形状 |
|
||||
| [**removeShape**](/apis/core/#Collider-removeShape) | 移除指定碰撞形状 |
|
||||
| [**clearShapes**](/apis/core/#Collider-clearShapes) | 清空所有碰撞形状 |
|
||||
|
||||
### 特有方法
|
||||
| 方法名 | 描述 |
|
||||
| --------------- | ------------------ |
|
||||
| [**applyForce**](/apis/core/#DynamicCollider-applyForce) | 施加力 |
|
||||
| [**applyTorque**](/apis/core/#DynamicCollider-applyTorque) | 施加扭矩 |
|
||||
| [**move**](/apis/core/#DynamicCollider-move) | 运动学移动 |
|
||||
| [**sleep**](/apis/core/#DynamicCollider-sleep) | 强制休眠 |
|
||||
| [**wakeUp**](/apis/core/#DynamicCollider-wakeUp) | 唤醒物体 |
|
||||
| [**isSleeping**](/apis/core/#DynamicCollider-isSleeping) | 检查是否在休眠状态 |
|
||||
|
||||
## 脚本使用
|
||||
|
||||
### 基础配置
|
||||
|
||||
```typescript
|
||||
// 创建动态碰撞器
|
||||
const dynamicCollider = entity.addComponent(DynamicCollider);
|
||||
|
||||
// 添加碰撞形状
|
||||
const boxShape = new BoxColliderShape();
|
||||
boxShape.size = new Vector3(1, 1, 1);
|
||||
dynamicCollider.addShape(boxShape);
|
||||
|
||||
// 配置基本物理属性
|
||||
dynamicCollider.mass = 1.0; // 设置质量
|
||||
dynamicCollider.useGravity = true; // 启用重力
|
||||
dynamicCollider.isKinematic = false; // 设置为动力学模式
|
||||
```
|
||||
|
||||
### 运动控制
|
||||
|
||||
```typescript
|
||||
class PhysicsController extends Script {
|
||||
private _collider: DynamicCollider;
|
||||
|
||||
onAwake() {
|
||||
// 获取动态碰撞器引用
|
||||
this._collider = this.entity.getComponent(DynamicCollider);
|
||||
|
||||
// 配置运动阻尼
|
||||
this._collider.linearDamping = 0.1; // 设置线性阻尼
|
||||
this._collider.angularDamping = 0.1; // 设置角速度阻尼
|
||||
|
||||
// 设置运动约束
|
||||
this._collider.constraints =
|
||||
DynamicColliderConstraints.FreezeRotationX | // 锁定X轴旋转
|
||||
DynamicColliderConstraints.FreezeRotationZ; // 锁定Z轴旋转
|
||||
}
|
||||
|
||||
onUpdate() {
|
||||
// 获取当前速度
|
||||
const velocity = this._collider.linearVelocity;
|
||||
|
||||
// 设置速度
|
||||
this._collider.linearVelocity = new Vector3(5, velocity.y, 0);
|
||||
|
||||
// 施加持续力(如推力)
|
||||
if (this.engine.inputManager.isKeyHeldDown(Keys.W)) {
|
||||
this._collider.applyForce(new Vector3(0, 0, 10));
|
||||
}
|
||||
|
||||
// 施加瞬时力(如跳跃)
|
||||
if (this.engine.inputManager.isKeyDown(Keys.Space)) {
|
||||
this._collider.applyForce(new Vector3(0, 500, 0));
|
||||
}
|
||||
|
||||
// 施加扭矩(使物体旋转)
|
||||
if (this.engine.inputManager.isKeyHeldDown(Keys.R)) {
|
||||
this._collider.applyTorque(new Vector3(0, 10, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 运动学控制
|
||||
|
||||
```typescript
|
||||
class KinematicController extends Script {
|
||||
private _collider: DynamicCollider;
|
||||
|
||||
onAwake() {
|
||||
this._collider = this.entity.getComponent(DynamicCollider);
|
||||
this._collider.isKinematic = true; // 设置为运动学模式
|
||||
}
|
||||
|
||||
// 实现电梯运动
|
||||
onUpdate() {
|
||||
const time = this.engine.time.elapsedTime;
|
||||
const position = new Vector3(0, Math.sin(time) * 2, 0);
|
||||
this._collider.move(position);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 休眠管理
|
||||
|
||||
```typescript
|
||||
class SleepController extends Script {
|
||||
private _collider: DynamicCollider;
|
||||
|
||||
onAwake() {
|
||||
this._collider = this.entity.getComponent(DynamicCollider);
|
||||
|
||||
// 配置休眠参数
|
||||
this._collider.sleepThreshold = 0.005; // 设置休眠阈值
|
||||
}
|
||||
|
||||
onUpdate() {
|
||||
// 检查是否处于休眠状态
|
||||
if (this._collider.isSleeping()) {
|
||||
console.log("物体已休眠");
|
||||
}
|
||||
|
||||
// 手动控制休眠
|
||||
if (this.engine.inputManager.isKeyDown(Keys.S)) {
|
||||
this._collider.sleep(); // 强制休眠
|
||||
}
|
||||
|
||||
if (this.engine.inputManager.isKeyDown(Keys.W)) {
|
||||
this._collider.wakeUp(); // 唤醒物体
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 质量/惯性张量设置
|
||||
|
||||
```typescript
|
||||
// 自动计算
|
||||
const collider = entity.addComponent(DynamicCollider);
|
||||
collider.mass = 1.0;
|
||||
collider.automaticCenterOfMass = true; // 自动计算质心
|
||||
collider.automaticInertiaTensor = true; // 自动计算惯性张量
|
||||
|
||||
// 手动设置
|
||||
const customCollider = entity.addComponent(DynamicCollider);
|
||||
customCollider.automaticCenterOfMass = false;
|
||||
customCollider.automaticInertiaTensor = false;
|
||||
customCollider.centerOfMass = new Vector3(0, 0.5, 0); // 手动设置质心
|
||||
customCollider.inertiaTensor = new Vector3(1, 1, 1); // 手动设置惯性张量
|
||||
```
|
||||
88
docs/zh/physics/collider/event.mdx
Normal file
88
docs/zh/physics/collider/event.mdx
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
order: 5
|
||||
title: 碰撞事件
|
||||
type: 物理
|
||||
label: Physics
|
||||
---
|
||||
|
||||
碰撞事件是物理系统的重要组成部分,允许脚本响应物体之间的物理交互。Galacean 物理系统提供了两种类型的事件:碰撞事件和触发器事件。
|
||||
|
||||
## 事件类型
|
||||
|
||||
### 碰撞事件
|
||||
碰撞事件在两个处于碰撞器模式的碰撞器物理交互时触发。这些事件提供了关于碰撞的信息,如接触点、法线和涉及的另一个碰撞器。
|
||||
|
||||
### 触发器事件
|
||||
触发器事件在碰撞器进入、停留或离开触发区域时触发。触发器是将 `isTrigger` 属性设置为 true 的碰撞形状。它们检测重叠而不引起物理反应。
|
||||
|
||||
## 事件触发关系
|
||||
|
||||
碰撞器和触发器之间的详细事件触发关系如下图所示:
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_vvspai/afts/img/A*erlGRKk7dNMAAAAAAAAAAAAADsqFAQ/original" />
|
||||
|
||||
## 碰撞器模式 VS 触发器模式
|
||||
|
||||
| 模式 | 描述 | 使用场景 |
|
||||
| ---- | ---- | -------- |
|
||||
| **碰撞器模式** | 触发碰撞器事件,具有实际的物理碰撞效果 | 需要真实物理交互的物体 |
|
||||
| **触发器模式** | 仅触发事件回调,不产生物理碰撞 | 检测区域、触发机关等 |
|
||||
|
||||
<Callout type="positive">
|
||||
碰撞器模式和触发器模式是碰撞形状的属性,一个碰撞器可以包含多个碰撞形状,每个形状都可以独立设置是否为触发器。这意味着:
|
||||
一个碰撞器可以同时具备两种模式,可以添加多个碰撞形状,有的设为碰撞器模式,有的设为触发器模式,这样可以同时实现物理碰撞和区域检测的功能
|
||||
</Callout>
|
||||
|
||||
## 事件回调
|
||||
|
||||
当两个碰撞器发生碰撞或触发器重叠时,会触发碰撞器所属实体上挂载的[脚本](/docs/script/script)中的回调函数。
|
||||
|
||||
### 碰撞器事件回调
|
||||
| 事件名 | 触发时机 |
|
||||
| --- | --- |
|
||||
| [**onCollisionEnter**](/apis/core/#Script-onCollisionEnter) | 开始碰撞时触发 |
|
||||
| [**onCollisionExit**](/apis/core/#Script-onCollisionExit) | 结束碰撞时触发 |
|
||||
| [**onCollisionStay**](/apis/core/#Script-onCollisionStay) | 碰撞持续时触发 |
|
||||
|
||||
### 触发器事件回调
|
||||
| 事件名 | 触发时机 |
|
||||
| --- | --- |
|
||||
| [**onTriggerEnter**](/apis/core/#Script-onTriggerEnter) | 进入触发器时触发 |
|
||||
| [**onTriggerExit**](/apis/core/#Script-onTriggerExit) | 离开触发器时触发 |
|
||||
| [**onTriggerStay**](/apis/core/#Script-onTriggerStay) | 在触发器内时持续触发 |
|
||||
|
||||
## 脚本使用
|
||||
|
||||
```typescript
|
||||
class EventHandler extends Script {
|
||||
// 碰撞器事件
|
||||
onCollisionEnter(other: Collider) {
|
||||
console.log("碰撞开始,对象:", other.entity.name);
|
||||
}
|
||||
|
||||
onCollisionStay(other: Collider) {
|
||||
console.log("碰撞持续中,对象:", other.entity.name);
|
||||
}
|
||||
|
||||
onCollisionExit(other: Collider) {
|
||||
console.log("碰撞结束,对象:", other.entity.name);
|
||||
}
|
||||
|
||||
// 触发器事件
|
||||
onTriggerEnter(other: Collider) {
|
||||
console.log("触发器开始,对象:", other.entity.name);
|
||||
}
|
||||
|
||||
onTriggerStay(other: Collider) {
|
||||
console.log("触发器持续中,对象:", other.entity.name);
|
||||
}
|
||||
|
||||
onTriggerExit(other: Collider) {
|
||||
console.log("触发器结束,对象:", other.entity.name);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 示例
|
||||
|
||||
<Playground href="/embed/physx-collision-detection" />
|
||||
103
docs/zh/physics/collider/overview.mdx
Normal file
103
docs/zh/physics/collider/overview.mdx
Normal file
@@ -0,0 +1,103 @@
|
||||
---
|
||||
order: 1
|
||||
title: 碰撞器
|
||||
type: 物理
|
||||
label: Physics
|
||||
---
|
||||
|
||||
碰撞器([Collider](/apis/core/#Collider))是一种用于检测和响应物理碰撞的组件。引擎提供以下几种碰撞器:
|
||||
|
||||
| 类型 | 描述 | 适用场景 |
|
||||
| --- | --- | --- |
|
||||
| [**StaticCollider**](/apis/core/#StaticCollider) | 静态碰撞器,不会移动但可与其他物体碰撞 | 地面、墙壁等静态物体 |
|
||||
| [**DynamicCollider**](/apis/core/#DynamicCollider) | 动态碰撞器,受物理引擎影响可自由运动 | 可移动物体、射弹等 |
|
||||
| [**CharacterController**](/apis/core/#CharacterController) | 专门用于角色控制的碰撞器 | 玩家角色、NPC等 |
|
||||
|
||||
碰撞器组件用于定义物体的物理属性和碰撞行为。Galacean 物理系统提供了三种类型的碰撞器:
|
||||
|
||||
1. [动态碰撞器](/docs/physics/collider/dynamicCollider)
|
||||
动态碰撞器可以自由运动并受物理力的影响,适用于需要物理模拟的可移动物体,如投掷物、可推动的箱子等。
|
||||
|
||||
2. [静态碰撞器](/docs/physics/collider/staticCollider)
|
||||
静态碰撞器固定在场景中不会移动,通常用于创建固定的物理障碍物,如地面、墙壁等。
|
||||
|
||||
3. [角色控制器](/docs/physics/collider/characterController)
|
||||
角色控制器是专门为角色移动设计的碰撞器,支持斜坡行走、台阶攀爬等特性,适用于第一人称或第三人称游戏中的角色控制。
|
||||
|
||||
## 基本概念
|
||||
|
||||
所有碰撞器类型都具有以下共同特征:
|
||||
|
||||
1. **形状管理**:每个碰撞器可以包含多个[碰撞形状](/docs/physics/collider/colliderShape) (角色控制器只能添加一个)。关于碰撞形状的详细说明请参考[碰撞形状](/docs/physics/collider/colliderShape)文档。
|
||||
|
||||
目前支持了四种 `碰撞形状`,但不同的后端物理包支持程度不同,具体如下:
|
||||
|
||||
| 名称 | 解释 | 支持的后端物理包 |
|
||||
| :------------------------------------------------------- | :--------------- | :--------------------------- |
|
||||
| [BoxColliderShape](/apis/core/#BoxColliderShape) | 盒形碰撞外形 | physics-lite, physics-physx |
|
||||
| [SphereColliderShape](/apis/core/#SphereColliderShape) | 球形碰撞外形 | physics-lite, physics-physx |
|
||||
| [PlaneColliderShape](/apis/core/#PlaneColliderShape) | 无界平面碰撞外形 | physics-physx |
|
||||
| [CapsuleColliderShape](/apis/core/#CapsuleColliderShape) | 胶囊碰撞外形 | physics-physx |
|
||||
|
||||
2. **变换同步**:
|
||||
- 碰撞器会自动同步实体的世界变换,包括位置、旋转和缩放的同步
|
||||
- `碰撞形状` 可以设置相对于实体的局部偏移和旋转
|
||||
|
||||
3. **碰撞/触发事件**:碰撞器在与其他碰撞器交互时可以生成碰撞和触发器事件。有关这些事件的详细信息以及如何处理它们,请参阅[碰撞事件](/docs/physics/collider/event)文档。
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **合理使用形状**
|
||||
|
||||
- 保持碰撞形状数量最小化,仅在必要时添加额外形状,以减少性能开销
|
||||
- 避免使用过多的碰撞形状
|
||||
- 对于复杂模型,使用简化的碰撞形状代替精确网格
|
||||
- 角色控制器推荐使用胶囊体形状,可以更好地处理台阶和斜坡
|
||||
|
||||
2. **性能优化**
|
||||
|
||||
- 将不会移动的物体设置为静态碰撞器
|
||||
- 合并相邻的静态碰撞器
|
||||
- 适当调整动态碰撞器的 solverIterations,在性能和精度间取得平衡
|
||||
- 合理使用 CollisionDetectionMode,只在必要时启用连续碰撞检测
|
||||
|
||||
3. **动态碰撞器设置**
|
||||
|
||||
- 根据物体质量调整 linearDamping 和 angularDamping,避免物体运动不自然
|
||||
- 使用 constraints 限制不需要的自由度,提高稳定性
|
||||
- 适当设置 sleepThreshold,让静止物体及时进入休眠状态
|
||||
|
||||
4. **角色控制器配置**
|
||||
|
||||
- 根据游戏类型设置合适的 stepOffset,避免卡住或跨越不合理
|
||||
- 设置适当的 slopeLimit,需要考虑游戏场景中的斜坡角度
|
||||
- 选择合适的 nonWalkableMode,考虑是否需要滑落效果
|
||||
- 保持碰撞形状与角色视觉模型的合理匹配
|
||||
|
||||
5. **使用场景**
|
||||
- 静态碰撞器:场景边界、地形、建筑等固定物体
|
||||
- 动态碰撞器:可推动物体、物理道具、破坏物等
|
||||
- 角色控制器:玩家角色、NPC 等需要特殊移动控制的对象
|
||||
|
||||
## 脚本使用
|
||||
|
||||
### 添加碰撞器
|
||||
|
||||
```typescript
|
||||
// 添加静态碰撞器
|
||||
const staticCollider = entity.addComponent(StaticCollider);
|
||||
|
||||
// 添加动态碰撞器
|
||||
const dynamicCollider = entity.addComponent(DynamicCollider);
|
||||
|
||||
// 添加角色控制器
|
||||
const characterController = entity.addComponent(CharacterController);
|
||||
```
|
||||
|
||||
|
||||
## 更多信息
|
||||
|
||||
- [动态碰撞器文档](/docs/physics/collider/dynamicCollider)
|
||||
- [静态碰撞器文档](/docs/physics/collider/staticCollider)
|
||||
- [角色控制器文档](/docs/physics/collider/characterController)
|
||||
- [碰撞事件文档](/docs/physics/collider/event)
|
||||
111
docs/zh/physics/collider/staticCollider.mdx
Normal file
111
docs/zh/physics/collider/staticCollider.mdx
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
order: 1
|
||||
title: 静态碰撞器
|
||||
type: 物理
|
||||
label: Physics
|
||||
---
|
||||
|
||||
静态碰撞器([StaticCollider](/apis/core/#StaticCollider))用于创建固定在场景中不会移动的物理对象。它不受物理引擎施加的力的影响,但可以与其他动态物体产生碰撞和触发响应,常用于创建地面、墙壁等静态场景元素。
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 选中目标实体,并在检查器中点击添加组件按钮,添加 StaticCollider 组件。
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*g5RMQJwnSZsAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
|
||||
2. 为碰撞器添加碰撞形状。关于碰撞形状的详细说明请参考[碰撞形状](/docs/physics/collider/colliderShape)文档。
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*UcaRSb0nahcAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
3. 调整碰撞形状的位置、大小等属性,使其与场景元素匹配。
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*g5RMQJwnSZsAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
## 属性
|
||||
|
||||
| 属性 | 描述 |
|
||||
| ----------------------------------------- | ------------ |
|
||||
| [**shapes**](/apis/core/#Collider-shapes) | 碰撞形状集合 |
|
||||
|
||||
## 方法
|
||||
|
||||
| 方法名 | 描述 |
|
||||
| --------------------------------------------------- | ---------------- |
|
||||
| [**addShape**](/apis/core/#Collider-addShape) | 添加碰撞形状 |
|
||||
| [**removeShape**](/apis/core/#Collider-removeShape) | 移除指定碰撞形状 |
|
||||
| [**clearShapes**](/apis/core/#Collider-clearShapes) | 清空所有碰撞形状 |
|
||||
|
||||
|
||||
## 脚本使用
|
||||
|
||||
### 基础配置
|
||||
|
||||
```typescript
|
||||
// 创建静态碰撞器
|
||||
const staticCollider = entity.addComponent(StaticCollider);
|
||||
|
||||
// 添加盒形碰撞形状
|
||||
const boxShape = new BoxColliderShape();
|
||||
boxShape.size = new Vector3(1, 1, 1);
|
||||
boxShape.position = new Vector3(0, 0.5, 0);
|
||||
staticCollider.addShape(boxShape);
|
||||
```
|
||||
|
||||
### 形状管理
|
||||
|
||||
```typescript
|
||||
// 获取所有形状
|
||||
const shapes = staticCollider.shapes;
|
||||
|
||||
// 增加形状
|
||||
const boxShape = new BoxColliderShape();
|
||||
staticCollider.addShape(boxShape);
|
||||
|
||||
// 移除特定形状
|
||||
staticCollider.removeShape(boxShape);
|
||||
|
||||
// 清空所有形状
|
||||
staticCollider.clearShapes();
|
||||
```
|
||||
|
||||
|
||||
### 触发器设置
|
||||
|
||||
```typescript
|
||||
// 设置为触发器
|
||||
const triggerShape = new BoxColliderShape();
|
||||
triggerShape.isTrigger = true;
|
||||
staticCollider.addShape(triggerShape);
|
||||
|
||||
// 添加触发器响应脚本
|
||||
class TriggerHandler extends Script {
|
||||
onTriggerEnter(other: Collider) {
|
||||
console.log("触发器被触发");
|
||||
}
|
||||
}
|
||||
entity.addComponent(TriggerHandler);
|
||||
```
|
||||
|
||||
### 复合形状
|
||||
|
||||
```typescript
|
||||
// 创建L形墙
|
||||
function createLWall() {
|
||||
const entity = new Entity();
|
||||
const collider = entity.addComponent(StaticCollider);
|
||||
|
||||
// 主墙
|
||||
const wall1 = new BoxColliderShape();
|
||||
wall1.size = new Vector3(5, 3, 0.5);
|
||||
collider.addShape(wall1);
|
||||
|
||||
// 侧墙
|
||||
const wall2 = new BoxColliderShape();
|
||||
wall2.size = new Vector3(0.5, 3, 3);
|
||||
wall2.position = new Vector3(2.25, 0, 1.25);
|
||||
collider.addShape(wall2);
|
||||
|
||||
return entity;
|
||||
}
|
||||
```
|
||||
@@ -1,53 +0,0 @@
|
||||
---
|
||||
order: 4
|
||||
title: 角色控制器组件
|
||||
type: 物理
|
||||
label: Physics
|
||||
---
|
||||
|
||||
角色控制器是物理引擎提供的一种非常重要的功能组件,通过角色控制器可以很容易为动画角色的运动增加物理上的表现,例如可以设定参数使得角色无法爬过一定角度的陡坡,
|
||||
也可以在角色运动的过程中避免角色与其他碰撞器发生碰撞反馈等等。实际上,角色控制器只是[碰撞器](/docs/physics/collider)的一种高级封装,通过碰撞检测来实现各种高级的的角色控制行为。
|
||||
也正因此,角色控制器组件的创建与使用,和碰撞器组件非常类似。
|
||||
```typescript
|
||||
const physicsCapsule = new CapsuleColliderShape();
|
||||
physicsCapsule.radius = radius;
|
||||
physicsCapsule.height = height;
|
||||
const characterController = capsuleEntity.addComponent(CharacterController);
|
||||
characterController.addShape(physicsCapsule);
|
||||
```
|
||||
和碰撞器组件一样,都是通过构造 `ColliderShape`,并且将其添加到组件中,使得角色控制器获得特定的外形。但这里需要特别强调两点:
|
||||
1. 角色控制器不支持复合外形,因此只能添加一个 `ColliderShape`。
|
||||
2. 角色控制器目前只支持 `CapsuleColliderShape` 和 `BoxColliderShape`,且其中 `CapsuleColliderShape` 最为常用。
|
||||
|
||||
后续角色控制器的行为通过 `CharacterController` 的各个参数和方法进行控制,其中最重要的是 `move` 函数:
|
||||
|
||||
```typescript
|
||||
class Controller extends Script {
|
||||
onPhysicsUpdate() {
|
||||
const fixedTimeStep = this.engine.physicsManager.fixedTimeStep;
|
||||
const character = this._character;
|
||||
const flag = character.move(this._displacement, 0.1, fixedTimeStep);
|
||||
if (flag | ControllerCollisionFlag.Down) {
|
||||
character.move(new Vector3(0, -0.2, 0), 0.1, fixedTimeStep);
|
||||
}
|
||||
this._displacement.setValue(0, 0, 0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
可以在 `move` 方法中指定角色位移,并且该方法返回一个枚举类型的复合值,通过该枚举类型 `ControllerCollisionFlag` 可以判断角色控制器是否碰到其他的碰撞器组件:
|
||||
|
||||
```typescript
|
||||
export enum ControllerCollisionFlag {
|
||||
/** Character is colliding to the sides. */
|
||||
Sides = 1,
|
||||
/** Character has collision above. */
|
||||
Up = 2,
|
||||
/** Character has collision below. */
|
||||
Down = 4
|
||||
}
|
||||
```
|
||||
|
||||
由此角色接下来的动画和运动要怎么进行。在下面的例子当中,可以通过键盘控制角色的运动,使其爬上或者跳过特定的障碍物。
|
||||
|
||||
<Playground href="/embed/physx-controller" />
|
||||
@@ -6,10 +6,8 @@ label: Physics
|
||||
---
|
||||
|
||||
物理碰撞器由基础物理外形复合而成,包括了球,盒,胶囊和无限大的平面。在实际应用中,这些碰撞器外形很少与渲染的物体刚好是完全重合的,这为可视化调试带来了很大的困难。
|
||||
有两种调试方法:
|
||||
1. 借助 PhysX Visual Debugger(PVD),是 Nvidia 官方开发的调试工具,但是用这一工具需要自行编译 debug 版本的PhysX,并且借助 WebSocket 串联浏览器和该调试工具。
|
||||
具体的使用方法,可以参考 [physx.js](https://github.com/galacean/physX.js) 的Readme种的介绍。
|
||||
2. 我们还提供了轻量级的[辅助线工具](https://github.com/galacean/engine-toolkit/tree/main/packages/auxiliary-lines),该工具根据物理组件的配置绘制对应的线框,辅助配置和调试物理组件。
|
||||
|
||||
我们提供了轻量级的[辅助线工具](https://github.com/galacean/engine-toolkit/tree/main/packages/auxiliary-lines),该工具根据物理组件的配置绘制对应的线框,辅助配置和调试物理组件。
|
||||
使用起来也非常容易,只需要在挂载 `WireframeManager` 脚本,然后设置其关联各种物理组件,或者直接关联节点即可:
|
||||
```typescript
|
||||
const wireframe = rootEntity.addComponent(WireframeManager);
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
---
|
||||
order: 5
|
||||
title: 基础物理约束组件
|
||||
type: 物理
|
||||
label: Physics
|
||||
---
|
||||
|
||||
物理约束组件是一种非常重要的物理组件,通过约束可以更好控制动态碰撞器组件的运动,为场景添加有趣的交互响应。本文主要介绍最基础的三种物理约束组件:
|
||||
|
||||
1. 固定约束组件
|
||||
|
||||
<Image src="https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/_images/fixedJoint.png" />
|
||||
2. 弹性约束组件
|
||||
|
||||
<Image src="https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/_images/distanceJoint.png" />
|
||||
3. 铰链约束组件
|
||||
|
||||
<Image src="https://gameworksdocs.nvidia.com/PhysX/4.1/documentation/physxguide/_images/revoluteJoint.png" />
|
||||
|
||||
所有的物理约束都有两个作用对象,其中代表受到物理约束作用的动态碰撞器(在该节点上挂载物理约束组件),另外一个是约束挂载的位置或者是另外一个动态碰撞器(通过组件配置来设置)。
|
||||
因此,这些组件的使用方法类似,以固定约束组件`FixedJoint`为例:
|
||||
|
||||
```typescript
|
||||
const fixedJoint = currentEntity.addComponent(FixedJoint);
|
||||
fixedJoint.connectedCollider = prevCollider;
|
||||
```
|
||||
|
||||
## 局部坐标与世界坐标
|
||||
|
||||
理解物理约束组件的使用,其中一个关键点就是理解**局部坐标**和**世界坐标**。所有的物理约束,都可以配置 `connectedCollider` 属性。
|
||||
此外,某些物理约束组件还可以通过配置 `connectedAnchor` 属性,设置物理约束挂载的位置。
|
||||
|
||||
**需要特别注意的是,当 `connectedCollider` 被设置后,`connectedAnchor` 代表的是相对于该碰撞器的局部坐标。`connectedCollider` 为 null 时,
|
||||
`connectedAnchor` 代表的是世界坐标。**
|
||||
|
||||
## 铰链约束
|
||||
|
||||
以上三种物理约束中,相对比较复杂的是铰链约束,因为除了需要配置 `connectedCollider` 和 `connectedAnchor` 之外,还需要指定铰链的旋转轴方向和旋转半径。
|
||||
可以通过配置 `axis` (默认方向是朝向 x 轴正方向)和 `swingOffset` 指定这两个属性。
|
||||
其中 `swingOffset` 也是一个向量,可以理解成从 `connectedAnchor` 和 `connectedCollider` 共同确定的旋转中心出发的偏移,动态碰撞就被挪到该点开始绕着旋转轴旋转。
|
||||
|
||||
上述物理约束组件的使用,可以参照:
|
||||
<Playground href="/embed/physx-joint-basic" />
|
||||
95
docs/zh/physics/joint/fixedJoint.mdx
Normal file
95
docs/zh/physics/joint/fixedJoint.mdx
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
order: 1
|
||||
title: 固定关节
|
||||
type: 物理
|
||||
label: Physics
|
||||
---
|
||||
|
||||
[固定关节](/apis/core/#FixedJoint)是一种刚性约束组件,它能够完全限制两个碰撞体之间的相对运动。当两个物体通过固定关节连接后,它们会保持相对位置和方向不变,就像被刚性胶水粘在一起一样。
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 选中目标实体,并在检查器中点击添加组件按钮,添加 FixedJoint 组件。
|
||||
<Callout type="info">
|
||||
添加关节组件时,需要确保目标实体上已经挂载了一个[动态碰撞器](/docs/physics/collider/dynamicCollider)组件,如果你未添加,编辑器会自动为你添加一个`动态碰撞器组件`。
|
||||
</Callout>
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*x3SBT4XKDUcAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
2. 通过组件属性 connectedCollider 设置连接的目标碰撞体(若不需要,可保持为 null,即连接到世界空间中的一点)。
|
||||
<Callout type="positive">
|
||||
如果连接的目标是碰撞体,则目标实体需要挂载碰撞体组件([动态碰撞器](/docs/physics/collider/dynamicCollider),[静态碰撞器](/docs/physics/collider/staticCollider),[角色控制器](/docs/physics/collider/characterController))。
|
||||
</Callout>
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*ARu_S7PhgiMAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
3. 根据需要设置关节的属性调整关节的表现,各属性的含义和作用请参考下文。
|
||||
|
||||
## 属性说明
|
||||
|
||||
### 碰撞体设置
|
||||
- [**connectedCollider**](/apis/core/#FixedJoint-connectedCollider)
|
||||
指定要连接的目标碰撞体。当设置为 null 时,关节会连接到世界空间中的一个固定点。这允许你将物体固定在空间中的特定位置。
|
||||
|
||||
### 锚点设置
|
||||
- [**anchor**](/apis/core/#FixedJoint-anchor)
|
||||
在自身碰撞体上定义的锚点位置,使用局部坐标。这个点定义了关节的连接位置。
|
||||
|
||||
- [**connectedAnchor**](/apis/core/#FixedJoint-connectedAnchor)
|
||||
定义连接点的位置。其含义取决于 connectedCollider 的设置:
|
||||
- 当 connectedCollider 为 null 时,表示世界空间中的绝对位置
|
||||
- 当 connectedCollider 不为 null 时,表示目标碰撞体局部空间中的相对位置
|
||||
|
||||
- [**automaticConnectedAnchor**](/apis/core/#FixedJoint-automaticConnectedAnchor)
|
||||
是否自动计算 connectedAnchor 的值。启用时,系统会自动设置连接点以确保物体间的初始位置关系。如需手动精确控制连接点,可将此属性设为 false。
|
||||
|
||||
### 断裂阈值
|
||||
- [**breakForce**](/apis/core/#FixedJoint-breakForce)
|
||||
关节能承受的最大力,超过此值时关节会断裂。设置为 Infinity 表示关节永远不会因受力而断裂。该属性可用于模拟物体间的可破坏连接。
|
||||
|
||||
- [**breakTorque**](/apis/core/#FixedJoint-breakTorque)
|
||||
关节能承受的最大扭矩,超过此值时关节会断裂。设置为 Infinity 表示关节永远不会因扭转而断裂。与 breakForce 配合使用可以更真实地模拟连接的破坏过程。
|
||||
|
||||
### 质量计算干预
|
||||
- [**connectedMassScale**](/apis/core/#FixedJoint-connectedMassScale) 和 [**massScale**](/apis/core/#FixedJoint-massScale)
|
||||
分别用于调整连接碰撞体和自身碰撞体的质量影响。这些缩放值会影响关节约束的计算,允许你微调关节的物理行为。默认值为 1.0,增大数值会增加对应碰撞体在约束求解中的"重要性"。
|
||||
|
||||
## 脚本使用
|
||||
|
||||
### 基础使用
|
||||
|
||||
```typescript
|
||||
// 添加固定关节组件
|
||||
const fixedJoint = entity.addComponent(FixedJoint);
|
||||
|
||||
// 设置连接的目标碰撞体
|
||||
fixedJoint.connectedCollider = targetEntity.getComponent(Collider);
|
||||
|
||||
// 设置锚点
|
||||
fixedJoint.anchor.setValue(0, 1, 0);
|
||||
|
||||
// 手动设置连接点
|
||||
fixedJoint.automaticConnectedAnchor = false;
|
||||
fixedJoint.connectedAnchor.setValue(0, 0, 0);
|
||||
```
|
||||
|
||||
### 断裂阈值设置
|
||||
|
||||
```typescript
|
||||
// 设置断裂条件
|
||||
fixedJoint.breakForce = 1000; // 断裂力
|
||||
fixedJoint.breakTorque = 1000; // 断裂扭矩
|
||||
|
||||
// 设置为不可断裂
|
||||
fixedJoint.breakForce = Infinity;
|
||||
fixedJoint.breakTorque = Infinity;
|
||||
```
|
||||
|
||||
### 质量计算影响
|
||||
|
||||
```typescript
|
||||
// 调整质量影响
|
||||
fixedJoint.massScale = 1.5; // 增加自身的质量影响
|
||||
fixedJoint.connectedMassScale = 0.5; // 减小连接对象的质量影响
|
||||
```
|
||||
|
||||
145
docs/zh/physics/joint/hingeJoint.mdx
Normal file
145
docs/zh/physics/joint/hingeJoint.mdx
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
order: 3
|
||||
title: 铰链关节
|
||||
type: 物理
|
||||
label: Physics
|
||||
---
|
||||
|
||||
铰链关节用于模拟两个物体通过一个轴连接的情况,允许物体绕着这个轴自由旋转。这种关节常用于实现门铰链、车轮、钟摆等需要绕固定轴旋转的物理效果。
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 选中目标实体,并在检查器中点击添加组件按钮,添加 HingeJoint 组件。
|
||||
<Callout type="info">
|
||||
添加关节组件时,需要确保目标实体上已经挂载了一个 `动态碰撞器组件`,如果你未添加,编辑器会自动为你添加一个`动态碰撞器组件`。
|
||||
</Callout>
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*SL8YQLNbsJwAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
2. 通过组件属性 connectedCollider 设置连接的目标碰撞体(若不需要,可保持为 null,即连接到世界空间中的一点)。
|
||||
<Callout type="positive">
|
||||
如果连接的目标是碰撞体,则目标实体需要挂载碰撞体组件(动态碰撞器,静态碰撞器,角色控制器)。
|
||||
</Callout>
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*SwsmS572tcAAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
3. 根据需要设置关节的属性调整关节的表现,各属性的含义和作用请参考下文。
|
||||
|
||||
## 属性说明
|
||||
|
||||
### 碰撞体设置
|
||||
- [**connectedCollider**](/apis/core/#HingeJoint-connectedCollider)
|
||||
指定要连接的目标碰撞体。当设置为 null 时,关节会连接到世界空间中的一个固定点。
|
||||
|
||||
### 锚点设置
|
||||
- [**anchor**](/apis/core/#HingeJoint-anchor)
|
||||
在自身碰撞体上定义的锚点位置(局部坐标)。这个点是铰链的旋转中心。
|
||||
|
||||
- [**connectedAnchor**](/apis/core/#HingeJoint-connectedAnchor)
|
||||
定义连接点的位置:
|
||||
- 当 connectedCollider 为 null 时,表示世界空间中的固定点
|
||||
- 当 connectedCollider 不为 null 时,表示目标碰撞体局部空间中的连接点
|
||||
|
||||
- [**automaticConnectedAnchor**](/apis/core/#HingeJoint-automaticConnectedAnchor)
|
||||
是否自动计算连接点位置。启用时会自动保持物体的初始相对位置。
|
||||
|
||||
### 旋转设置
|
||||
- [**axis**](/apis/core/#HingeJoint-axis)
|
||||
定义铰链的旋转轴方向。物体将沿着这个轴进行旋转。
|
||||
|
||||
### 运动限制
|
||||
- [**useLimits**](/apis/core/#HingeJoint-useLimits)
|
||||
是否启用角度限制。启用后可以限制铰链的转动范围。
|
||||
|
||||
- [**limits**](/apis/core/#HingeJoint-limits)
|
||||
设置铰链的转动范围:
|
||||
|
||||
| 属性 | 描述 |
|
||||
| --------------- | -------------------------------------------------------------------- |
|
||||
| **min** | 最小角度限制(度) |
|
||||
| **max** | 最大角度限制(度) |
|
||||
| **contactDistance** | 接触距离,定义限制器开始生效的距离 |
|
||||
| **stiffness** | 弹簧刚度(仅在 useSpring 为 true 时生效) |
|
||||
| **damping** | 阻尼系数(仅在 useSpring 为 true 时生效) |
|
||||
|
||||
### 马达驱动
|
||||
- [**useMotor**](/apis/core/#HingeJoint-useMotor)
|
||||
是否启用马达功能。启用后可以主动驱动铰链转动。
|
||||
|
||||
- [**motor**](/apis/core/#HingeJoint-motor)
|
||||
马达的参数设置:
|
||||
|
||||
| 属性 | 描述 |
|
||||
| --------------- | -------------------------------------------------------------------- |
|
||||
| **targetVelocity** | 目标角速度(度/秒) |
|
||||
| **forceLimit** | 最大扭矩限制 |
|
||||
| **freeSpin** | 是否允许自由旋转 |
|
||||
| **gearRatio** | 齿轮比,用于调整实际输出的角速度 |
|
||||
|
||||
### 弹性设置
|
||||
- [**useSpring**](/apis/core/#HingeJoint-useSpring)
|
||||
是否启用弹簧效果。启用后,限制器会表现出弹性特征。
|
||||
|
||||
### 断裂阈值
|
||||
- [**breakForce**](/apis/core/#HingeJoint-breakForce)
|
||||
关节能承受的最大力。超过此值时关节会断裂。
|
||||
|
||||
- [**breakTorque**](/apis/core/#HingeJoint-breakTorque)
|
||||
关节能承受的最大扭矩。超过此值时关节会断裂。
|
||||
|
||||
### 质量计算干预
|
||||
- [**connectedMassScale**](/apis/core/#HingeJoint-connectedMassScale) 和 [**massScale**](/apis/core/#HingeJoint-massScale)
|
||||
调整连接碰撞体和自身碰撞体的质量影响。默认值为 1.0。
|
||||
|
||||
## 脚本使用
|
||||
|
||||
### 基础使用
|
||||
|
||||
```typescript
|
||||
// 添加铰链关节组件
|
||||
const hingeJoint = entity.addComponent(HingeJoint);
|
||||
|
||||
// 设置连接对象
|
||||
hingeJoint.connectedCollider = targetEntity.getComponent(Collider);
|
||||
|
||||
// 设置旋转轴
|
||||
hingeJoint.axis.setValue(0, 1, 0); // Y轴旋转
|
||||
```
|
||||
|
||||
### 运动限制
|
||||
|
||||
```typescript
|
||||
// 启用角度限制
|
||||
hingeJoint.useLimits = true;
|
||||
hingeJoint.limits = new JointLimits();
|
||||
hingeJoint.limits.min = -45; // 最小角度
|
||||
hingeJoint.limits.max = 45; // 最大角度
|
||||
hingeJoint.limits.contactDistance = 5; // 接触距离
|
||||
|
||||
// 启用弹性限制
|
||||
hingeJoint.useSpring = true;
|
||||
hingeJoint.limits.stiffness = 100; // 弹簧刚度
|
||||
hingeJoint.limits.damping = 0.2; // 阻尼系数
|
||||
```
|
||||
|
||||
### 马达驱动
|
||||
|
||||
```typescript
|
||||
// 启用马达
|
||||
hingeJoint.useMotor = true;
|
||||
hingeJoint.motor = new JointMotor();
|
||||
hingeJoint.motor.targetVelocity = 180; // 目标角速度(度/秒)
|
||||
hingeJoint.motor.forceLimit = 500; // 最大扭矩
|
||||
hingeJoint.motor.freeSpin = false; // 自由旋转
|
||||
hingeJoint.motor.gearRatio = 1; // 齿轮比
|
||||
```
|
||||
|
||||
### 获取运动信息
|
||||
|
||||
```typescript
|
||||
// 获取当前角度
|
||||
const currentAngle = hingeJoint.angle;
|
||||
|
||||
// 获取当前角速度
|
||||
const angularVelocity = hingeJoint.velocity;
|
||||
```
|
||||
107
docs/zh/physics/joint/overview.mdx
Normal file
107
docs/zh/physics/joint/overview.mdx
Normal file
@@ -0,0 +1,107 @@
|
||||
---
|
||||
order: 0
|
||||
title: 总览
|
||||
type: 物理
|
||||
label: Physics
|
||||
---
|
||||
|
||||
关节组件用于模拟碰撞体之间或碰撞体与世界空间任意点之间的连接关系。通过施加力来限制运动自由度,从而实现特定的物理效果。目前,Galacean支持以下三种关节组件:
|
||||
|
||||
1. [固定关节](/docs/physics/joint/fixedJoint)([FixedJoint](/apis/core/#FixedJoint))
|
||||
固定关节完全限制了两个碰撞体之间的相对运动,使其保持固定的相对位置和方向。当需要实现能够分离的物体,或在无需层级父子结构的情况下实现同步移动时,这种关节十分适用。
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*h050RoMxg8sAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
2. [弹性关节](/docs/physics/joint/springJoint)([SpringJoint](/apis/core/#SpringJoint))
|
||||
弹性关节通过设定的弹簧力和阻尼来维持两个物体之间的距离。它允许物体在一定范围内自由运动,并在超出范围时施加弹性约束力,类似于两者间存在一根弹簧。
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*eWoxQ57nWeEAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
3. [铰链关节](/docs/physics/joint/hingeJoint)([HingeJoint](/apis/core/#HingeJoint))
|
||||
铰链关节允许物体绕着固定的轴自由旋转,常用于实现门铰链、车轮、钟摆等需要绕固定轴旋转的物理效果。
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*yZPrRohs-VcAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
## 属性说明
|
||||
|
||||
所有关节类型都具有以下共同特征:
|
||||
|
||||
1. **关节端点**:每个关节都包含两个作用对象:
|
||||
|
||||
- 主体碰撞体:挂载关节组件的碰撞体
|
||||
- 连接对象:通过 `connectedCollider` 属性指定的目标碰撞体或世界空间中的点
|
||||
|
||||
2. **锚点设置**:
|
||||
|
||||
- `anchor`:在主体碰撞体上的连接点
|
||||
- `connectedAnchor`:在目标对象上的连接点
|
||||
|
||||
3. **断裂阈值**:
|
||||
|
||||
- `breakForce`:关节能承受的最大力
|
||||
- `breakTorque`:关节能承受的最大扭矩
|
||||
|
||||
4. **质量计算干预**:
|
||||
- `connectedMassScale` 和 `massScale`:用于调整连接碰撞体和主体碰撞体的质量影响
|
||||
|
||||
## 脚本使用
|
||||
|
||||
### 创建关节
|
||||
|
||||
通过脚本可以动态创建和配置关节组件:
|
||||
|
||||
```typescript
|
||||
// 创建固定关节
|
||||
const fixedJoint = entity.addComponent(FixedJoint);
|
||||
|
||||
// 创建弹性关节
|
||||
const springJoint = entity.addComponent(SpringJoint);
|
||||
|
||||
// 创建铰链关节
|
||||
const hingeJoint = entity.addComponent(HingeJoint);
|
||||
```
|
||||
|
||||
### 设置连接对象
|
||||
|
||||
所有关节都可以设置连接目标和锚点:
|
||||
|
||||
```typescript
|
||||
// 设置连接的目标碰撞体
|
||||
joint.connectedCollider = targetEntity.getComponent(Collider);
|
||||
|
||||
// 设置关节在自身碰撞体上的锚点
|
||||
joint.anchor.setValue(0, 1, 0);
|
||||
|
||||
// 手动设置关节在目标碰撞体上的连接点
|
||||
fixedJoint.automaticConnectedAnchor = false;
|
||||
joint.connectedAnchor.setValue(0, 0, 0);
|
||||
```
|
||||
|
||||
### 通用属性配置
|
||||
|
||||
所有类型的关节都支持以下设置:
|
||||
|
||||
```typescript
|
||||
// 设置断裂力和扭矩
|
||||
joint.breakForce = 1000;
|
||||
joint.breakTorque = 1000;
|
||||
|
||||
// 设置质量影响
|
||||
joint.massScale = 1.0;
|
||||
joint.connectedMassScale = 1.0;
|
||||
|
||||
// 设置是否自动计算连接点
|
||||
joint.automaticConnectedAnchor = true;
|
||||
```
|
||||
|
||||
上述物理约束组件的使用,可以参照:
|
||||
|
||||
<Playground href="/embed/physx-joint-basic" />
|
||||
|
||||
## 更多信息
|
||||
|
||||
有关各类型关节的具体使用方法,请参考对应文档:
|
||||
|
||||
- [固定关节](/docs/physics/joint/fixedJoint)
|
||||
- [弹性关节](/docs/physics/joint/springJoint)
|
||||
- [铰链关节](/docs/physics/joint/hingeJoint)
|
||||
103
docs/zh/physics/joint/springJoint.mdx
Normal file
103
docs/zh/physics/joint/springJoint.mdx
Normal file
@@ -0,0 +1,103 @@
|
||||
---
|
||||
order: 2
|
||||
title: 弹性关节
|
||||
type: 物理
|
||||
label: Physics
|
||||
---
|
||||
|
||||
弹性关节用于维持两个物体之间的距离约束,通过弹簧力和阻尼来控制物体间的相对运动。它可以设置最小和最大距离范围,并在物体超出范围时施加弹性约束力。
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 选中目标实体,并在检查器中点击添加组件按钮,添加 SpringJoint 组件。
|
||||
<Callout type="info">
|
||||
添加关节组件时,需要确保目标实体上已经挂载了一个 `动态碰撞器组件`,如果你未添加,编辑器会自动为你添加一个`动态碰撞器组件`。
|
||||
</Callout>
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*Jy9kSaMDsJoAAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
2. 通过组件属性 connectedCollider 设置连接的目标碰撞体(若不需要,可保持为 null,即连接到世界空间中的一点)。
|
||||
<Callout type="positive">
|
||||
如果连接的目标是碰撞体,则目标实体需要挂载碰撞体组件(动态碰撞器,静态碰撞器,角色控制器)。
|
||||
</Callout>
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*JRtfRoM1vS4AAAAAAAAAAAAAesJ_AQ/original" />
|
||||
|
||||
3. 根据需要设置关节的属性调整关节的表现,各属性的含义和作用请参考下文。
|
||||
|
||||
## 属性说明
|
||||
|
||||
### 碰撞体设置
|
||||
- [**connectedCollider**](/apis/core/#SpringJoint-connectedCollider)
|
||||
指定要连接的目标碰撞体。当设置为 null 时,关节会连接到世界空间中的一个固定点。这允许你将物体固定在空间中的特定位置。
|
||||
|
||||
### 锚点设置
|
||||
- [**anchor**](/apis/core/#SpringJoint-anchor)
|
||||
在自身碰撞体上定义的锚点位置,使用局部坐标。这个点定义了关节的连接位置。
|
||||
|
||||
- [**connectedAnchor**](/apis/core/#SpringJoint-connectedAnchor)
|
||||
定义连接点的位置。其含义取决于 connectedCollider 的设置:
|
||||
- 当 connectedCollider 为 null 时,表示世界空间中的绝对位置
|
||||
- 当 connectedCollider 不为 null 时,表示目标碰撞体局部空间中的相对位置
|
||||
|
||||
- **automaticConnectedAnchor**
|
||||
是否自动计算 connectedAnchor 的值。启用时,系统会自动设置连接点以确保物体间的初始位置关系。如需手动精确控制连接点,可将此属性设为 false。
|
||||
|
||||
### 距离约束
|
||||
- [**minDistance**](/apis/core/#SpringJoint-minDistance)
|
||||
允许的最小距离。当物体间距离小于此值时,关节会施加推开力。
|
||||
|
||||
- [**maxDistance**](/apis/core/#SpringJoint-maxDistance)
|
||||
允许的最大距离。当物体间距离大于此值时,关节会施加拉近力。
|
||||
|
||||
- [**tolerance**](/apis/core/#SpringJoint-tolerance)
|
||||
容差范围值,默认为 0.25。这个值决定了关节开始产生约束力的时机:
|
||||
- 当物体实际距离超出允许范围的 tolerance 值时,关节才会开始施加约束力
|
||||
- 例如:如果 maxDistance = 10,tolerance = 0.25,则只有当距离超过 10.25 时才会开始施加拉近力
|
||||
- 较大的容差值可以让关节的约束表现更柔和,较小的值则会使约束更严格
|
||||
|
||||
### 弹簧特性
|
||||
- [**stiffness**](/apis/core/#SpringJoint-stiffness)
|
||||
弹簧刚度系数。较高的值会产生更强的回复力,使弹簧表现更"硬"。
|
||||
|
||||
- [**damping**](/apis/core/#SpringJoint-damping)
|
||||
阻尼系数。用于抑制弹簧振动,较高的值会使运动更快停止。
|
||||
|
||||
### 断裂阈值
|
||||
- [**breakForce**](/apis/core/#SpringJoint-breakForce)
|
||||
关节能承受的最大力,超过此值时关节会断裂。设置为 Infinity 表示关节永远不会因受力而断裂。该属性可用于模拟物体间的可破坏连接。
|
||||
|
||||
- [**breakTorque**](/apis/core/#SpringJoint-breakTorque)
|
||||
关节能承受的最大扭矩,超过此值时关节会断裂。设置为 Infinity 表示关节永远不会因扭转而断裂。与 breakForce 配合使用可以更真实地模拟连接的破坏过程。
|
||||
|
||||
### 质量计算干预
|
||||
- [**connectedMassScale**](/apis/core/#SpringJoint-connectedMassScale) 和 [**massScale**](/apis/core/#SpringJoint-massScale)
|
||||
分别用于调整连接碰撞体和自身碰撞体的质量影响。这些缩放值会影响关节约束的计算,允许你微调关节的物理行为。默认值为 1.0,增大数值会增加对应碰撞体在约束求解中的"重要性"。
|
||||
|
||||
## 脚本使用
|
||||
|
||||
### 基础使用
|
||||
|
||||
```typescript
|
||||
// 添加弹性关节组件
|
||||
const springJoint = entity.addComponent(SpringJoint);
|
||||
|
||||
// 设置连接对象
|
||||
springJoint.connectedCollider = targetEntity.getComponent(Collider);
|
||||
|
||||
// 设置距离范围
|
||||
springJoint.minDistance = 1;
|
||||
springJoint.maxDistance = 5;
|
||||
```
|
||||
|
||||
### 弹性设置
|
||||
|
||||
```typescript
|
||||
// 配置弹簧属性
|
||||
springJoint.stiffness = 50; // 弹簧刚度
|
||||
springJoint.damping = 0.2; // 阻尼系数
|
||||
|
||||
// 设置容差
|
||||
springJoint.tolerance = 0.25;
|
||||
```
|
||||
|
||||
@@ -1,85 +1,114 @@
|
||||
---
|
||||
order: 2
|
||||
title: 物理管理器
|
||||
title: 物理场景
|
||||
type: 物理
|
||||
label: Physics
|
||||
---
|
||||
|
||||
物理管理器(PhysicsManager)用于管理场景中所有的物理组件,并且负责与物理后端通信,实现有关物理场景的全局操作,例如更新和射线检测等等。在多场景项目中,每个Scene都有自己的PhysicsManager,Scene之间的物理系统是相互隔离互不影响的。
|
||||
物理场景([PhysicsScene](/apis/core/#PhysicsScene))用于管理场景中所有的物理组件,并且负责与物理后端通信,实现有关物理场景的全局操作,例如更新和射线检测等等。在多场景项目中,每个[Scene](/apis/core/Scene)都有自己的物理场景,Scene之间的物理系统是相互隔离互不影响的。
|
||||
|
||||
## 物理更新
|
||||
|
||||
物理场景和渲染场景相互独立,但在程序运行过程中不断同步各自的数据。因此,和脚本一样,同步的时序也非常重要。一般来说,物理场景的更新频率和渲染场景不同,在物理管理器中可以对其进行设置:
|
||||
|
||||
物理场景和渲染场景相互独立,但在程序运行过程中不断同步各自的数据。因此,和[脚本](/docs/script/script)一样,同步的时序也非常重要。物理场景的更新频率和渲染场景不同,通过以下参数进行控制:
|
||||
```typescript
|
||||
/** The fixed time step in seconds at which physics are performed. */
|
||||
/** 物理场景的固定更新时间步长(秒) */
|
||||
fixedTimeStep: number = 1 / 60;
|
||||
|
||||
/** The max sum of time step in seconds one frame. */
|
||||
maxSumTimeStep: number = 1 / 3;
|
||||
```
|
||||
|
||||
每一个渲染帧中,物理引擎都会按照固定时间步长进行更新 `fixedTimeStep`。
|
||||
### 更新机制
|
||||
|
||||
如果时时间间隔大于 `fixedTimeStep`,则单步模拟的最大时间步长由 `maxSumTimeStep` 确定。此时,如果按照上面列出的默认参数,有可能会发生追帧现象。
|
||||
这时候可以通过调节 `maxSumTimeStep` 降低每帧物理模拟的更新次数。
|
||||
- 每个渲染帧中,物理引擎会按照固定时间步长 [fixedTimeStep](/apis/core/#PhysicsScene-fixedTimeStep) 进行更新
|
||||
- 如果实际帧间隔大于 `fixedTimeStep`:
|
||||
- 会执行多次物理更新直到追赶上实际时间
|
||||
- 单帧最大更新时间由 `engine.time.maximumDeltaTime` 限制
|
||||
- 如果实际帧间隔小于 `fixedTimeStep`,则累积到下一帧处理
|
||||
|
||||
如果不满一个 `fixedTimeStep`,则顺延到下一帧再处理。因此,每一个渲染帧,物理场景可能会更新多次,也可能只更新一次,因此对于有关物理组件更新,都需要放在特定的更新函数,`Script`
|
||||
提供了这一接口:
|
||||
### 物理更新回调
|
||||
|
||||
针对物理组件的更新,需要使用[脚本](/docs/script/script)中专门的回调函数:
|
||||
```typescript
|
||||
export class Script extends Component {
|
||||
/**
|
||||
* Called before physics calculations, the number of times is related to the physical update frequency.
|
||||
* 在物理计算前调用,调用次数取决于物理更新频率
|
||||
*/
|
||||
onPhysicsUpdate(): void {
|
||||
}
|
||||
onPhysicsUpdate(): void {}
|
||||
}
|
||||
```
|
||||
|
||||
物理场景在更新时,除了调用该函数,还会同步 Collider 和其所挂载的 Entity 的姿态。物理更新的时序如下:
|
||||
物理更新在整个更新流程中的位置可以参考下图
|
||||
<Image src="https://gw.alipayobjects.com/mdn/rms_7c464e/afts/img/A*_8C-TJP2UIgAAAAAAAAAAAAAARQnAQ" />
|
||||
|
||||
1. 调用 `onPhysicsUpdate` 中的用户逻辑
|
||||
2. `callColliderOnUpdate` 将被修改的 Entity 新姿态同步给物理碰撞器
|
||||
### 物理系统内部更新流程
|
||||
|
||||
物理场景在更新时的执行顺序:
|
||||
|
||||
1. 执行 `onPhysicsUpdate` 中的用户逻辑
|
||||
2. `callColliderOnUpdate` 将被修改的 [实体](/apis/core/#Entity) `Transform` 数据同步给物理碰撞器
|
||||
3. 更新物理场景
|
||||
4. `callColliderOnLateUpdate` 将所有 DynamicCollider 更新后的位置同步给对应的 Entity
|
||||
4. `callColliderOnLateUpdate` 将所有 [碰撞器](/docs/physics/collider/overview) 更新后的位置同步给对应的 `实体`
|
||||
|
||||
## 设置场景重力
|
||||
|
||||
物理场景允许自定义重力方向和大小。重力会影响场景中所有启用重力的[动态碰撞器](/docs/physics/collider/dynamicCollider)。
|
||||
|
||||
```typescript
|
||||
// 获取物理场景的重力值
|
||||
const gravity = scene.physics.gravity;
|
||||
|
||||
// 修改重力 - 将重力改为 0
|
||||
scene.physics.gravity.set(0, 0, 0);
|
||||
|
||||
// 修改重力 - 设置为地球重力加速度 (默认值)
|
||||
scene.physics.gravity.set(0, -9.81, 0);
|
||||
```
|
||||
|
||||
## 使用射线检测
|
||||
|
||||
<Playground href="/embed/physx-raycast" />
|
||||
|
||||
射线可以理解成 3D 世界中一个点向一个方向发射的一条无终点的线。射线投射在 3D 应用中非常广泛。通过射线投射,可以在用户点击屏幕时,拾取 3D 场景中的物体;也可以在射击游戏中,判断子弹能否射中目标。
|
||||
射线可以理解成 3D 世界中一个点向一个方向发射的一条无终点的线。射线投射在 3D 应用中应用非常广泛:
|
||||
|
||||
<Image src="https://gw.alipayobjects.com/mdn/rms_7c464e/afts/img/A*SHM1RI49Bd4AAAAAAAAAAAAAARQnAQ" />
|
||||
- 在用户点击屏幕时,用于拾取3D场景中的物体
|
||||
- 在射击游戏中,用于判断子弹能否射中目标
|
||||
- 检测物体之间的可视性和遮挡关系
|
||||
|
||||
<Image src="https://gw.alipayobjects.com/mdn/rms_7c464e/afts/img/A*SHM1RI49Bd4AAAAAAAAAAAAAARQnAQ" />
|
||||
(_图片来源于网络_)
|
||||
|
||||
在使用射线投射,首先要在代码中引入 [Ray](/apis/math/#Ray) 模块;然后生成射线,射线可以自定义生成,也可以通过相机([camera](/apis/core/#Camera-viewportPointToRay)
|
||||
)将屏幕输入转化成射线;最后调用 [PhysicsManager.raycast](/apis/core/#PhysicsManager-raycast) 方法即可检测射线投射命中的碰撞体。代码如下:
|
||||
### 射线检测示例
|
||||
|
||||
使用射线检测需要以下步骤:
|
||||
|
||||
1. 引入 [Ray](/apis/math/Ray) 等必要的模块
|
||||
2. 创建射线(可以自定义或通过 [camera.screenPointToRay](/apis/core/#Camera-screenPointToRay) 生成)
|
||||
3. 调用 [`raycast`](/apis/core/#PhysicsScene-raycast) 方法检测碰撞
|
||||
|
||||
```typescript
|
||||
// 加载 Raycast 模块
|
||||
import {WebGLEngine, HitResult, Ray} from "@galacean/engine";
|
||||
import {LitePhysics} from "@galacean/engine-physics-lite";
|
||||
import { WebGLEngine, HitResult, Ray } from "@galacean/engine";
|
||||
import { LitePhysics } from "@galacean/engine-physics-lite";
|
||||
|
||||
const engine = await WebGLEngine.create({
|
||||
canvas: "canvas",
|
||||
physics: new LitePhysics(),
|
||||
physics: new LitePhysics()
|
||||
});
|
||||
engine.canvas.resizeByClientSize();
|
||||
|
||||
const scene = engine.scenes[0];
|
||||
// 将屏幕输入转换成Ray
|
||||
document.getElementById('canvas').addEventListener('click', (e) => {
|
||||
document.getElementById("canvas").addEventListener("click", (e) => {
|
||||
const ratio = window.devicePixelRatio;
|
||||
let ray = new Ray();
|
||||
camera.screenPointToRay(new Vector2(e.offsetX, e.offsetY).scale(ratio), ray);
|
||||
const hit = new HitResult();
|
||||
result = engine.physicsManager.raycast(ray, Number.MAX_VALUE, Layer.Everything, hit);
|
||||
result = scene.physics.raycast(ray, Number.MAX_VALUE, Layer.Everything, hit);
|
||||
if (result) {
|
||||
console.log(hit.entity.name);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
需要特别指出,如果想要对 Entity 启用射线投射,该 Entity 就必须拥有 **Collider** ,否则无法触发。若射线命中的Collider的Shape距离相同,则返回先被添加的Shape(举个例子:有两个Collider相同的Entity完全重合,则会返回先添加Collider,更准确的说是先添加Shape的Entity)。
|
||||
### 注意事项
|
||||
|
||||
同时,在 Galacean 当中,还提供了 InputManager,该管理器将输入源做了封装,提供了更加易用的逻辑,使用方式可以[参考](/docs/input/input/) .
|
||||
- Entity 必须添加 [碰撞器](/docs/physics/collider/overview) 组件才能被射线检测到
|
||||
- 当射线命中多个相同距离的 [碰撞形状](/docs/physics/collider/colliderShape) 时,会返回先添加的碰撞形状所在的 Entity
|
||||
- 建议使用 [InputManager](/docs/input/input/) 来处理输入,它提供了更便捷的输入处理方式
|
||||
|
||||
@@ -1,27 +1,81 @@
|
||||
---
|
||||
order: 1
|
||||
order: 0
|
||||
title: 物理总览
|
||||
type: 物理
|
||||
label: Physics
|
||||
---
|
||||
|
||||
物理引擎是游戏引擎中非常重要的组成部分。 业界普遍采用 PhysX 引入相关功能。 但是对于轻量级的场景,PhysX 使得最终的应用体积非常大,超出了这些项目的限制。 Galacean 基于多后端设计。 一方面,它通过 WebAssembly
|
||||
编译得到 [PhysX.js](https://github.com/galacean/physX.js) ; 另一方面,它也提供了轻量级的物理引擎。
|
||||
两者在 [API](https://github.com/galacean/engine/tree/main/packages/design/src/physics) 设计上是一致的。 用户只需要在初始化引擎时选择特定的物理后端。
|
||||
可以满足轻量级应用、重量级游戏等各种场景的需求。有关物理组件的总体设计,可以参考 [Wiki](https://github.com/galacean/engine/wiki/Physical-system-design).
|
||||
物理引擎是游戏引擎中非常重要的组成部分,主要负责以下功能:
|
||||
- 物理碰撞检测
|
||||
- 刚体动力学模拟
|
||||
- 关节约束系统
|
||||
- 物理事件响应
|
||||
|
||||
对于需要使用各种物理组件,以及 `InputManager` 等需要 Raycast 拾取的场景,都需要在使用之前初始化物理引擎。目前 Galacean 引擎提供两种内置的物理引擎后端实现:
|
||||
为了满足不同应用场景的需求,Galacean 采用了多后端设计:
|
||||
|
||||
- [physics-lite](https://github.com/galacean/engine/tree/main/packages/physics-lite)
|
||||
- [physics-physx](https://github.com/galacean/engine/tree/main/packages/physics-physx)
|
||||
- **Lite**: 针对简单交互场景优化,体积小巧
|
||||
- **PhysX**: 基于 physX物理引擎 提供完整物理特性
|
||||
|
||||
两个后端实现了统一的 [物理系统 API](https://github.com/galacean/engine/tree/main/packages/design/src/physics),开发者可以根据项目需求灵活选择。有关物理系统的详细设计说明,请参考 [设计文档](https://github.com/galacean/engine/wiki/Physical-system-design).
|
||||
|
||||
<Callout type="info">
|
||||
对于需要使用物理组件或 `InputManager` 等需要 Raycast 拾取的场景,都需要在使用之前初始化物理引擎。
|
||||
</Callout>
|
||||
|
||||
目前 Galacean 引擎提供两种内置的物理引擎后端实现:
|
||||
|
||||
1. [Lite](/apis/physics-lite)([physics-lite](https://github.com/galacean/engine/tree/main/packages/physics-lite))
|
||||
- 轻量级物理引擎实现
|
||||
- 仅支持基础的碰撞检测
|
||||
- 适用于简单交互场景
|
||||
|
||||
2. [PhysX](/apis/physics-physx)([physics-physx](https://github.com/galacean/engine/tree/main/packages/physics-physx))
|
||||
- 基于physX物理引擎,通过WebAssembly编译
|
||||
- 支持高级物理特性和精确模拟
|
||||
- 适用于复杂物理交互场景
|
||||
|
||||
## 选择物理后端
|
||||
|
||||
选择物理后端需要考虑功能,性能和包尺寸这三个因素:
|
||||
|
||||
### 1. 功能支持
|
||||
|
||||
| 功能 | physics-lite | physics-physx |
|
||||
|------|-------------|--------------|
|
||||
| 碰撞检测 | ✓ | ✓ |
|
||||
| 物理效果及反馈 | × | ✓ |
|
||||
| 连续碰撞检测 | × | ✓ |
|
||||
| 关节系统 | × | ✓ |
|
||||
|
||||
### 2. 性能表现
|
||||
- **PhysX**:
|
||||
- WebAssembly 平台下性能最佳
|
||||
- 自动降级为 JavaScript 实现
|
||||
- 内置场景加速结构
|
||||
|
||||
- **Lite**:
|
||||
- 轻量实现,性能开销小
|
||||
- 适合简单场景
|
||||
|
||||
### 3. 包体积
|
||||
- **PhysX**: 约 2.5MB(wasm/js)
|
||||
- **Lite**: 轻量级,几乎无额外开销
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 在编辑器中配置
|
||||
|
||||
开发者可以在 [主菜单](/docs/interface/sidebar) 界面打开的 **项目设置** 面板中设置物理后端。
|
||||
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*LO_FRIsaIzIAAAAAAAAAAAAADsJ_AQ/original" />
|
||||
<Image src="https://mdn.alipayobjects.com/huamei_3zduhr/afts/img/A*ZvWdQqEfIKoAAAAAAAAAAAAADsJ_AQ/original" />
|
||||
|
||||
### 脚本使用
|
||||
|
||||
若通过脚本初始化引擎,只需要将物理后端对象传入 `Engine` 中即可:
|
||||
|
||||
#### 使用 Lite 物理引擎
|
||||
|
||||
```typescript
|
||||
import {LitePhysics} from "@galacean/engine-physics-lite";
|
||||
|
||||
@@ -31,7 +85,7 @@ const engine = await WebGLEngine.create({
|
||||
});
|
||||
```
|
||||
|
||||
## PhysX 版物理引擎加载与初始化
|
||||
#### 使用 PhysX 物理引擎
|
||||
|
||||
```typescript
|
||||
import { PhysXPhysics } from "@galacean/engine-physics-physx";
|
||||
@@ -42,8 +96,26 @@ const engine = await WebGLEngine.create({
|
||||
});
|
||||
```
|
||||
|
||||
## 选择物理后端
|
||||
选择物理后端需要考虑到功能,性能和包尺寸这三个因素:
|
||||
1. 功能:追求完整物理引擎功能以及高性能的物理模拟,推荐选择 PhysX 后端,Lite 后端只支持碰撞检测。
|
||||
2. 性能:PhysX 会在不支持 WebAssembly 的平台自动降级为纯 JavaScript 的代码,因此性能也会随之降低。但由于内置了用于场景搜索的数据结构,性能比 Lite 后端还是要更加好。
|
||||
3. 包尺寸:选择 PhysX 后端会额外引入接近 2.5mb 的 wasm 文件(纯 JavaScript 版的大小接近),增加包的大小的同时降低应用初始化的速度。
|
||||
## 更多信息
|
||||
|
||||
有关物理系统的详细使用,请参考以下文档:
|
||||
|
||||
### 碰撞器
|
||||
- [碰撞器概述](/docs/physics/collider/overview)
|
||||
- [动态碰撞器](/docs/physics/collider/dynamicCollider) - 可自由运动的物理对象
|
||||
- [静态碰撞器](/docs/physics/collider/staticCollider) - 固定在场景中的物理对象
|
||||
- [角色控制器](/docs/physics/collider/characterController) - 专用于角色控制的碰撞器
|
||||
- [碰撞形状](/docs/physics/collider/colliderShape) - 碰撞器的形状定义
|
||||
|
||||
### 关节系统
|
||||
- [关节系统概述](/docs/physics/joint/overview)
|
||||
- [固定关节](/docs/physics/joint/fixedJoint) - 完全限制物体间相对运动
|
||||
- [弹性关节](/docs/physics/joint/springJoint) - 弹簧式距离约束
|
||||
- [铰链关节](/docs/physics/joint/hingeJoint) - 轴向旋转约束
|
||||
|
||||
### 物理场景
|
||||
- [物理场景](/docs/physics/manager) - 场景中的物理系统管理器
|
||||
|
||||
### 物理调试
|
||||
- [物理调试](/docs/physics/debug) - 物理系统调试工具
|
||||
```
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@galacean/engine-e2e",
|
||||
"private": true,
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"case": "vite serve .dev --config .dev/vite.config.js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@galacean/engine-root",
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"packageManager": "pnpm@9.3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@galacean/engine-core",
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
|
||||
@@ -251,7 +251,7 @@ export class Entity extends EngineObject {
|
||||
*/
|
||||
constructor(engine: Engine, name?: string, ...components: ComponentConstructor[]) {
|
||||
super(engine);
|
||||
this.name = name;
|
||||
this.name = name ?? "Entity";
|
||||
for (let i = 0, n = components.length; i < n; i++) {
|
||||
this.addComponent(components[i]);
|
||||
}
|
||||
|
||||
@@ -486,7 +486,7 @@ export class Renderer extends Component implements IComponentCustomClone {
|
||||
|
||||
private _createInstanceMaterial(material: Material, index: number): Material {
|
||||
const insMaterial: Material = material.clone();
|
||||
insMaterial.name = insMaterial.name + "(Instance)";
|
||||
insMaterial.name = material.name + "(Instance)";
|
||||
this._addResourceReferCount(material, -1);
|
||||
this._addResourceReferCount(insMaterial, 1);
|
||||
this._materialsInstanced[index] = true;
|
||||
|
||||
@@ -220,7 +220,7 @@ export class BaseMaterial extends Material {
|
||||
*/
|
||||
override clone(): BaseMaterial {
|
||||
const dest = new BaseMaterial(this._engine, this.shader);
|
||||
this.cloneTo(dest);
|
||||
this._cloneToAndModifyName(dest);
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ export class BlinnPhongMaterial extends BaseMaterial {
|
||||
|
||||
override clone(): BlinnPhongMaterial {
|
||||
var dest: BlinnPhongMaterial = new BlinnPhongMaterial(this._engine);
|
||||
this.cloneTo(dest);
|
||||
this._cloneToAndModifyName(dest);
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ export class Material extends ReferResource implements IClone {
|
||||
constructor(engine: Engine, shader: Shader) {
|
||||
super(engine);
|
||||
this.shader = shader;
|
||||
this.name = shader.name;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,7 +92,7 @@ export class Material extends ReferResource implements IClone {
|
||||
*/
|
||||
clone(): Material {
|
||||
const dest = new Material(this._engine, this.shader);
|
||||
this.cloneTo(dest);
|
||||
this._cloneToAndModifyName(dest);
|
||||
return dest;
|
||||
}
|
||||
|
||||
@@ -112,6 +113,11 @@ export class Material extends ReferResource implements IClone {
|
||||
this._shader._addReferCount(value);
|
||||
}
|
||||
|
||||
protected _cloneToAndModifyName(target: Material): void {
|
||||
this.cloneTo(target);
|
||||
target.name = this.name + "(Clone)";
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
|
||||
@@ -484,7 +484,7 @@ export class PBRMaterial extends PBRBaseMaterial {
|
||||
*/
|
||||
override clone(): PBRMaterial {
|
||||
const dest = new PBRMaterial(this._engine);
|
||||
this.cloneTo(dest);
|
||||
this._cloneToAndModifyName(dest);
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ export class PBRSpecularMaterial extends PBRBaseMaterial {
|
||||
*/
|
||||
override clone(): PBRSpecularMaterial {
|
||||
const dest = new PBRSpecularMaterial(this._engine);
|
||||
this.cloneTo(dest);
|
||||
this._cloneToAndModifyName(dest);
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ export class UnlitMaterial extends BaseMaterial {
|
||||
*/
|
||||
override clone(): UnlitMaterial {
|
||||
const dest = new UnlitMaterial(this._engine);
|
||||
this.cloneTo(dest);
|
||||
this._cloneToAndModifyName(dest);
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ export class ParticleGenerator {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_emit(time: number, count: number): void {
|
||||
_emit(playTime: number, count: number): void {
|
||||
if (this.emission.enabled) {
|
||||
// Wait the existing particles to be retired
|
||||
const notRetireParticleCount = this._getNotRetiredParticleCount();
|
||||
@@ -250,7 +250,7 @@ export class ParticleGenerator {
|
||||
const shape = this.emission.shape;
|
||||
for (let i = 0; i < count; i++) {
|
||||
if (shape?.enabled) {
|
||||
shape._generatePositionAndDirection(this.emission._shapeRand, time, position, direction);
|
||||
shape._generatePositionAndDirection(this.emission._shapeRand, playTime, position, direction);
|
||||
const positionScale = this.main._getPositionScale();
|
||||
position.multiply(positionScale);
|
||||
direction.normalize().multiply(positionScale);
|
||||
@@ -258,7 +258,7 @@ export class ParticleGenerator {
|
||||
position.set(0, 0, 0);
|
||||
direction.set(0, 0, -1);
|
||||
}
|
||||
this._addNewParticle(position, direction, transform, time);
|
||||
this._addNewParticle(position, direction, transform, playTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -669,7 +669,7 @@ export class ParticleGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private _addNewParticle(position: Vector3, direction: Vector3, transform: Transform, time: number): void {
|
||||
private _addNewParticle(position: Vector3, direction: Vector3, transform: Transform, playTime: number): void {
|
||||
const firstFreeElement = this._firstFreeElement;
|
||||
let nextFreeElement = firstFreeElement + 1;
|
||||
if (nextFreeElement >= this._currentParticleCount) {
|
||||
@@ -711,9 +711,7 @@ export class ParticleGenerator {
|
||||
const offset = firstFreeElement * ParticleBufferUtils.instanceVertexFloatStride;
|
||||
|
||||
// Position
|
||||
instanceVertices[offset] = position.x;
|
||||
instanceVertices[offset + 1] = position.y;
|
||||
instanceVertices[offset + 2] = position.z;
|
||||
position.copyToArray(instanceVertices, offset);
|
||||
|
||||
// Start life time
|
||||
instanceVertices[offset + ParticleBufferUtils.startLifeTimeOffset] = main.startLifetime.evaluate(
|
||||
@@ -722,12 +720,10 @@ export class ParticleGenerator {
|
||||
);
|
||||
|
||||
// Direction
|
||||
instanceVertices[offset + 4] = direction.x;
|
||||
instanceVertices[offset + 5] = direction.y;
|
||||
instanceVertices[offset + 6] = direction.z;
|
||||
direction.copyToArray(instanceVertices, offset + 4);
|
||||
|
||||
// Time
|
||||
instanceVertices[offset + ParticleBufferUtils.timeOffset] = time;
|
||||
instanceVertices[offset + ParticleBufferUtils.timeOffset] = playTime;
|
||||
|
||||
// Color
|
||||
const startColor = ParticleGenerator._tempColor0;
|
||||
@@ -736,19 +732,19 @@ export class ParticleGenerator {
|
||||
startColor.toLinear(startColor);
|
||||
}
|
||||
|
||||
instanceVertices[offset + 8] = startColor.r;
|
||||
instanceVertices[offset + 9] = startColor.g;
|
||||
instanceVertices[offset + 10] = startColor.b;
|
||||
instanceVertices[offset + 11] = startColor.a;
|
||||
startColor.copyToArray(instanceVertices, offset + 8);
|
||||
|
||||
const duration = this.main.duration;
|
||||
const normalizedEmitAge = (playTime % duration) / duration;
|
||||
|
||||
// Start size
|
||||
const startSizeRand = main._startSizeRand;
|
||||
if (main.startSize3D) {
|
||||
instanceVertices[offset + 12] = main.startSizeX.evaluate(undefined, startSizeRand.random());
|
||||
instanceVertices[offset + 13] = main.startSizeY.evaluate(undefined, startSizeRand.random());
|
||||
instanceVertices[offset + 14] = main.startSizeZ.evaluate(undefined, startSizeRand.random());
|
||||
instanceVertices[offset + 12] = main.startSizeX.evaluate(normalizedEmitAge, startSizeRand.random());
|
||||
instanceVertices[offset + 13] = main.startSizeY.evaluate(normalizedEmitAge, startSizeRand.random());
|
||||
instanceVertices[offset + 14] = main.startSizeZ.evaluate(normalizedEmitAge, startSizeRand.random());
|
||||
} else {
|
||||
const size = main.startSize.evaluate(undefined, startSizeRand.random());
|
||||
const size = main.startSize.evaluate(normalizedEmitAge, startSizeRand.random());
|
||||
instanceVertices[offset + 12] = size;
|
||||
instanceVertices[offset + 13] = size;
|
||||
instanceVertices[offset + 14] = size;
|
||||
@@ -815,15 +811,10 @@ export class ParticleGenerator {
|
||||
|
||||
if (this.main.simulationSpace === ParticleSimulationSpace.World) {
|
||||
// Simulation world position
|
||||
instanceVertices[offset + 27] = pos.x;
|
||||
instanceVertices[offset + 28] = pos.y;
|
||||
instanceVertices[offset + 29] = pos.z;
|
||||
pos.copyToArray(instanceVertices, offset + 27);
|
||||
|
||||
// Simulation world position
|
||||
instanceVertices[offset + 30] = rot.x;
|
||||
instanceVertices[offset + 31] = rot.y;
|
||||
instanceVertices[offset + 32] = rot.z;
|
||||
instanceVertices[offset + 33] = rot.w;
|
||||
rot.copyToArray(instanceVertices, offset + 30);
|
||||
}
|
||||
|
||||
// Simulation UV
|
||||
|
||||
@@ -56,7 +56,7 @@ export class ParticleMaterial extends BaseMaterial {
|
||||
*/
|
||||
override clone(): ParticleMaterial {
|
||||
const dest = new ParticleMaterial(this._engine);
|
||||
this.cloneTo(dest);
|
||||
this._cloneToAndModifyName(dest);
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Vector2 } from "@galacean/engine-math";
|
||||
import { deepClone, ignoreClone } from "../../clone/CloneManager";
|
||||
import { UpdateFlagManager } from "../../UpdateFlagManager";
|
||||
import { ParticleCurveMode } from "../enums/ParticleCurveMode";
|
||||
import { CurveKey, ParticleCurve } from "./ParticleCurve";
|
||||
import { UpdateFlagManager } from "../../UpdateFlagManager";
|
||||
|
||||
/**
|
||||
* Particle composite curve.
|
||||
@@ -175,6 +175,11 @@ export class ParticleCompositeCurve {
|
||||
return this.constant;
|
||||
case ParticleCurveMode.TwoConstants:
|
||||
return this.constantMin + (this.constantMax - this.constantMin) * lerpFactor;
|
||||
case ParticleCurveMode.Curve:
|
||||
return this.curve?._evaluate(time);
|
||||
case ParticleCurveMode.TwoCurves:
|
||||
const min = this.curveMin?._evaluate(time);
|
||||
return min + (this.curveMax?._evaluate(time) - min) * lerpFactor;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -67,9 +67,10 @@ export class ParticleCurve {
|
||||
* @param index - The remove key index
|
||||
*/
|
||||
removeKey(index: number): void {
|
||||
this._keys.splice(index, 1);
|
||||
const keys = this._keys;
|
||||
const removeKey = keys[index];
|
||||
keys.splice(index, 1);
|
||||
this._typeArrayDirty = true;
|
||||
const removeKey = this._keys[index];
|
||||
removeKey._unRegisterOnValueChanged(this._updateDispatch);
|
||||
this._updateDispatch();
|
||||
}
|
||||
@@ -86,6 +87,32 @@ export class ParticleCurve {
|
||||
this._typeArrayDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_evaluate(normalizedAge: number): number {
|
||||
const { keys } = this;
|
||||
const { length } = keys;
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const key = keys[i];
|
||||
const { time } = key;
|
||||
if (normalizedAge <= time) {
|
||||
if (i === 0) {
|
||||
// Small than first key
|
||||
return key.value;
|
||||
} else {
|
||||
// Between two keys
|
||||
const { time: lastTime, value: lastValue } = keys[i - 1];
|
||||
const age = (normalizedAge - lastTime) / (time - lastTime);
|
||||
return lastValue + (key.value - lastValue) * age;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Large than last key
|
||||
return keys[length - 1].value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
@@ -96,7 +96,7 @@ export class SkyBoxMaterial extends Material {
|
||||
|
||||
override clone(): SkyBoxMaterial {
|
||||
const dest = new SkyBoxMaterial(this._engine);
|
||||
this.cloneTo(dest);
|
||||
this._cloneToAndModifyName(dest);
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ export class SkyProceduralMaterial extends Material {
|
||||
*/
|
||||
override clone(): SkyProceduralMaterial {
|
||||
const dest = new SkyProceduralMaterial(this._engine);
|
||||
this.cloneTo(dest);
|
||||
this._cloneToAndModifyName(dest);
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@galacean/engine-design",
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@galacean/engine",
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@galacean/engine-loader",
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
|
||||
@@ -28,7 +28,7 @@ export class GLTFMaterialParser extends GLTFParser {
|
||||
*/
|
||||
static _checkOtherTextureTransform(texture: ITextureInfo, textureName: string): void {
|
||||
if (texture.extensions?.KHR_texture_transform) {
|
||||
Logger.warn(`${textureName} texture always use the KHR_texture_transform of the base texture.`);
|
||||
Logger.warn(`${textureName} texture ignore KHR_texture_transform extension.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@galacean/engine-math",
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@galacean/engine-physics-lite",
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@galacean/engine-physics-physx",
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@galacean/engine-rhi-webgl",
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"repository": {
|
||||
"url": "https://github.com/galacean/engine.git"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@galacean/engine-shaderlab",
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@galacean/engine-shader-shaderlab",
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@galacean/engine-ui",
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
DisorderedArray,
|
||||
Entity,
|
||||
EntityModifyFlags,
|
||||
Logger,
|
||||
MathUtil,
|
||||
Matrix,
|
||||
Ray,
|
||||
@@ -153,9 +154,22 @@ export class UICanvas extends Component implements IElement {
|
||||
set renderCamera(value: Camera) {
|
||||
const preCamera = this._renderCamera;
|
||||
if (preCamera !== value) {
|
||||
this._isSameOrChildEntity(value.entity) &&
|
||||
Logger.warn(
|
||||
"Camera entity matching or nested within the canvas entity disables canvas auto-adaptation in ScreenSpaceCamera mode."
|
||||
);
|
||||
this._renderCamera = value;
|
||||
this._updateCameraObserver();
|
||||
this._setRealRenderMode(this._getRealRenderMode());
|
||||
const preRenderMode = this._realRenderMode;
|
||||
const curRenderMode = this._getRealRenderMode();
|
||||
if (preRenderMode === curRenderMode) {
|
||||
if (curRenderMode === CanvasRenderMode.ScreenSpaceCamera) {
|
||||
this._adapterPoseInScreenSpace();
|
||||
this._adapterSizeInScreenSpace();
|
||||
}
|
||||
} else {
|
||||
this._setRealRenderMode(curRenderMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,8 +332,9 @@ export class UICanvas extends Component implements IElement {
|
||||
case CanvasRenderMode.WorldSpace:
|
||||
const boundsCenter = this._getCenter();
|
||||
if (isOrthographic) {
|
||||
Vector3.subtract(boundsCenter, cameraPosition, boundsCenter);
|
||||
this._sortDistance = Vector3.dot(boundsCenter, cameraForward);
|
||||
const distance = UICanvas._tempVec3;
|
||||
Vector3.subtract(boundsCenter, cameraPosition, distance);
|
||||
this._sortDistance = Vector3.dot(distance, cameraForward);
|
||||
} else {
|
||||
this._sortDistance = Vector3.distanceSquared(boundsCenter, cameraPosition);
|
||||
}
|
||||
@@ -382,15 +397,18 @@ export class UICanvas extends Component implements IElement {
|
||||
const transform = this.entity.transform;
|
||||
const realRenderMode = this._realRenderMode;
|
||||
if (realRenderMode === CanvasRenderMode.ScreenSpaceCamera) {
|
||||
const { transform: cameraTransform } = this._renderCamera.entity;
|
||||
const { worldPosition: cameraWorldPosition, worldForward: cameraWorldForward } = cameraTransform;
|
||||
const distance = this._distance;
|
||||
transform.setWorldPosition(
|
||||
cameraWorldPosition.x + cameraWorldForward.x * distance,
|
||||
cameraWorldPosition.y + cameraWorldForward.y * distance,
|
||||
cameraWorldPosition.z + cameraWorldForward.z * distance
|
||||
);
|
||||
transform.worldRotationQuaternion.copyFrom(cameraTransform.worldRotationQuaternion);
|
||||
const cameraEntity = this._renderCamera.entity;
|
||||
if (!this._isSameOrChildEntity(cameraEntity)) {
|
||||
const { transform: cameraTransform } = cameraEntity;
|
||||
const { worldPosition: cameraWorldPosition, worldForward: cameraWorldForward } = cameraTransform;
|
||||
const distance = this._distance;
|
||||
transform.setWorldPosition(
|
||||
cameraWorldPosition.x + cameraWorldForward.x * distance,
|
||||
cameraWorldPosition.y + cameraWorldForward.y * distance,
|
||||
cameraWorldPosition.z + cameraWorldForward.z * distance
|
||||
);
|
||||
transform.worldRotationQuaternion.copyFrom(cameraTransform.worldRotationQuaternion);
|
||||
}
|
||||
} else {
|
||||
const { canvas } = this.engine;
|
||||
transform.setWorldPosition(canvas.width * 0.5, canvas.height * 0.5, 0);
|
||||
@@ -646,6 +664,15 @@ export class UICanvas extends Component implements IElement {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _isSameOrChildEntity(cameraEntity: Entity): boolean {
|
||||
const canvasEntity = this.entity;
|
||||
while (cameraEntity) {
|
||||
if (cameraEntity === canvasEntity) return true;
|
||||
cameraEntity = cameraEntity.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -103,7 +103,6 @@ export class UIPointerEventEmitter extends PointerEventEmitter {
|
||||
const draggedPath = this._draggedPath;
|
||||
if (draggedPath.length > 0) {
|
||||
this._bubble(draggedPath, pointer, this._fireDrag);
|
||||
draggedPath.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@galacean/engine-xr-webxr",
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@galacean/engine-xr",
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"registry": "https://registry.npmjs.org"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@galacean/engine-tests",
|
||||
"private": true,
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"license": "MIT",
|
||||
"main": "dist/main.js",
|
||||
"module": "dist/module.js",
|
||||
|
||||
@@ -196,7 +196,7 @@ describe("MeshRenderer", async function () {
|
||||
// Test that getInstanceMaterial works correctly.
|
||||
const material = mr.getInstanceMaterial();
|
||||
expect(material).to.be.instanceOf(UnlitMaterial);
|
||||
expect(material.name).to.be.equal("undefined(Instance)");
|
||||
expect(material.name).to.be.equal("unlit(Instance)");
|
||||
|
||||
// Test that material0 is same as material.
|
||||
const material0 = mr.getInstanceMaterial(0);
|
||||
@@ -204,7 +204,7 @@ describe("MeshRenderer", async function () {
|
||||
|
||||
const material2 = mr.getInstanceMaterial(2);
|
||||
expect(material2).to.be.instanceOf(PBRMaterial);
|
||||
expect(material2.name).to.be.equal("undefined(Instance)");
|
||||
expect(material2.name).to.be.equal("pbr(Instance)");
|
||||
|
||||
expect(mr.getInstanceMaterial(1)).to.be.null;
|
||||
|
||||
@@ -220,9 +220,9 @@ describe("MeshRenderer", async function () {
|
||||
// Test that getInstanceMaterials works correctly.
|
||||
const materials = mr.getInstanceMaterials();
|
||||
expect(materials[0]).to.be.instanceOf(UnlitMaterial);
|
||||
expect(materials[0].name).to.be.equal("undefined(Instance)");
|
||||
expect(materials[0].name).to.be.equal("unlit(Instance)");
|
||||
expect(materials[1]).to.be.instanceOf(PBRMaterial);
|
||||
expect(materials[1].name).to.be.equal("undefined(Instance)");
|
||||
expect(materials[1].name).to.be.equal("pbr(Instance)");
|
||||
});
|
||||
|
||||
it("priority", () => {
|
||||
|
||||
@@ -91,4 +91,12 @@ describe("Material", () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("clone", () => {
|
||||
const material = new Material(engine, Shader.find("blinn-phong"));
|
||||
expect(material.name).to.equal("blinn-phong");
|
||||
|
||||
const clone = material.clone();
|
||||
expect(clone.name).to.equal("blinn-phong(Clone)");
|
||||
});
|
||||
});
|
||||
|
||||
74
tests/src/core/particle/ParticleCurve.test.ts
Normal file
74
tests/src/core/particle/ParticleCurve.test.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { CurveKey, ParticleCompositeCurve, ParticleCurve, ParticleCurveMode } from "@galacean/engine-core";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("ParticleCurve tests", () => {
|
||||
it("Constructor with const params", () => {
|
||||
const gradient = new ParticleCompositeCurve(0.5);
|
||||
expect(gradient.mode).to.equal(ParticleCurveMode.Constant);
|
||||
expect(gradient.evaluate(undefined, undefined)).to.equal(0.5);
|
||||
});
|
||||
|
||||
it("Constructor with two const params", () => {
|
||||
const gradient = new ParticleCompositeCurve(0.5, 0.2);
|
||||
expect(gradient.mode).to.equal(ParticleCurveMode.TwoConstants);
|
||||
expect(gradient.evaluate(undefined, 0.5)).to.equal(0.35);
|
||||
expect(gradient.evaluate(undefined, 0.0)).to.equal(0.5);
|
||||
expect(gradient.evaluate(undefined, 1.0)).to.equal(0.2);
|
||||
});
|
||||
|
||||
it("Constructor with curve params", () => {
|
||||
const gradient0 = new ParticleCompositeCurve(new ParticleCurve(new CurveKey(0, 0.333)));
|
||||
expect(gradient0.mode).to.equal(ParticleCurveMode.Curve);
|
||||
expect(gradient0.evaluate(0.2, undefined)).to.equal(0.333);
|
||||
|
||||
const gradient1 = new ParticleCompositeCurve(new ParticleCurve(new CurveKey(0, 0.3), new CurveKey(0.6, 0.7)));
|
||||
expect(gradient1.evaluate(0.0, undefined)).to.equal(0.3);
|
||||
expect(gradient1.evaluate(0.5, undefined)).to.equal(0.6333333333333333);
|
||||
expect(gradient1.evaluate(0.6, undefined)).to.equal(0.7);
|
||||
expect(gradient1.evaluate(0.9, undefined)).to.equal(0.7);
|
||||
expect(gradient1.evaluate(1.0, undefined)).to.equal(0.7);
|
||||
});
|
||||
|
||||
it("Constructor with two curve params", () => {
|
||||
const curveMin = new ParticleCurve(new CurveKey(0, 0.3), new CurveKey(0.6, 0.7));
|
||||
const curveMax = new ParticleCurve(new CurveKey(0.4, 0.5), new CurveKey(1.0, 0.8));
|
||||
|
||||
const compositeCurve = new ParticleCompositeCurve(curveMin, curveMax);
|
||||
|
||||
expect(compositeCurve.evaluate(0.0, 0.0)).to.equal(0.3);
|
||||
expect(compositeCurve.evaluate(0.5, 0.0)).to.equal(0.6333333333333333);
|
||||
expect(compositeCurve.evaluate(0.6, 0.0)).to.equal(0.7);
|
||||
expect(compositeCurve.evaluate(0.9, 0.0)).to.equal(0.7);
|
||||
expect(compositeCurve.evaluate(1.0, 0.0)).to.equal(0.7);
|
||||
|
||||
expect(compositeCurve.evaluate(0.0, 1.0)).to.equal(0.5);
|
||||
expect(compositeCurve.evaluate(0.5, 1.0)).to.equal(0.55);
|
||||
expect(compositeCurve.evaluate(0.6, 1.0)).to.equal(0.6);
|
||||
expect(compositeCurve.evaluate(0.9, 1.0)).to.equal(0.75);
|
||||
expect(compositeCurve.evaluate(1.0, 1.0)).to.equal(0.8);
|
||||
|
||||
expect(compositeCurve.evaluate(0.6, 0.5)).to.equal(0.6499999999999999);
|
||||
});
|
||||
|
||||
it("Add and remove", () => {
|
||||
const curve = new ParticleCurve(new CurveKey(0, 0.3), new CurveKey(0.6, 0.7));
|
||||
+ expect(curve.keys.length).to.equal(2);
|
||||
+
|
||||
|
||||
curve.addKey(new CurveKey(0, 0.4));
|
||||
+ expect(curve.keys.length).to.equal(3);
|
||||
+ expect(curve.keys[0].value).to.equal(0.3);
|
||||
+
|
||||
curve.removeKey(2);
|
||||
+ expect(curve.keys.length).to.equal(2);
|
||||
+
|
||||
curve.removeKey(0);
|
||||
|
||||
+ expect(curve.keys.length).to.equal(1);
|
||||
+ expect(curve.keys[0].time).to.equal(0.0);
|
||||
+ expect(curve.keys[0].value).to.equal(0.4);
|
||||
+
|
||||
curve.removeKey(0);
|
||||
+ expect(curve.keys.length).to.equal(0);
|
||||
});
|
||||
});
|
||||
@@ -95,6 +95,49 @@ describe("UICanvas", async () => {
|
||||
expect(rootCanvas._isRootCanvas).to.eq(true);
|
||||
});
|
||||
|
||||
// Pose
|
||||
it("Pose Fit", () => {
|
||||
const canvasTransform = <UITransform>canvasEntity.transform;
|
||||
const canvasPosition = canvasTransform.position;
|
||||
rootCanvas.referenceResolution = new Vector2(800, 600);
|
||||
rootCanvas.renderMode = CanvasRenderMode.ScreenSpaceCamera;
|
||||
rootCanvas.distance = 10;
|
||||
canvasPosition.set(0, 0, 0);
|
||||
expect(canvasPosition.x).to.eq(0);
|
||||
expect(canvasPosition.y).to.eq(0);
|
||||
expect(canvasPosition.z).to.eq(0);
|
||||
|
||||
// Same entity
|
||||
const cameraSame = canvasEntity.addComponent(Camera);
|
||||
rootCanvas.renderCamera = cameraSame;
|
||||
expect(canvasPosition.x).to.eq(0);
|
||||
expect(canvasPosition.y).to.eq(0);
|
||||
expect(canvasPosition.z).to.eq(0);
|
||||
rootCanvas.distance = 100;
|
||||
expect(canvasPosition.x).to.eq(0);
|
||||
expect(canvasPosition.y).to.eq(0);
|
||||
expect(canvasPosition.z).to.eq(0);
|
||||
|
||||
// Not same entity or child entity
|
||||
rootCanvas.renderCamera = camera;
|
||||
expect(canvasPosition.x).to.eq(0);
|
||||
expect(canvasPosition.y).to.eq(0);
|
||||
expect(canvasPosition.z).to.eq(-100);
|
||||
rootCanvas.distance = 10;
|
||||
expect(canvasPosition.x).to.eq(0);
|
||||
expect(canvasPosition.y).to.eq(0);
|
||||
expect(canvasPosition.z).to.eq(-10);
|
||||
|
||||
// Child entity
|
||||
const cameraEntityChild = canvasEntity.createChild("cameraChild");
|
||||
const cameraChild = cameraEntityChild.addComponent(Camera);
|
||||
cameraEntityChild.transform.setPosition(2, 2, 2);
|
||||
rootCanvas.renderCamera = cameraChild;
|
||||
expect(canvasPosition.x).to.eq(0);
|
||||
expect(canvasPosition.y).to.eq(0);
|
||||
expect(canvasPosition.z).to.eq(-10);
|
||||
});
|
||||
|
||||
// Size
|
||||
it("Size Fit", () => {
|
||||
rootCanvas.referenceResolution = new Vector2(800, 600);
|
||||
|
||||
@@ -126,6 +126,75 @@ describe("UIEvent", async () => {
|
||||
expect(script3.upCount).toBe(0);
|
||||
expect(script3.dropCount).toBe(0);
|
||||
|
||||
target.dispatchEvent(generatePointerEvent("pointermove", 2, left + 5, top + 5));
|
||||
engine.update();
|
||||
|
||||
|
||||
expect(script1.enterCount).toBe(1);
|
||||
expect(script1.exitCount).toBe(0);
|
||||
expect(script1.downCount).toBe(1);
|
||||
expect(script1.clickCount).toBe(0);
|
||||
expect(script1.beginDragCount).toBe(1);
|
||||
expect(script1.dragCount).toBe(1);
|
||||
expect(script1.endDragCount).toBe(0);
|
||||
expect(script1.upCount).toBe(0);
|
||||
expect(script1.dropCount).toBe(0);
|
||||
|
||||
expect(script2.enterCount).toBe(1);
|
||||
expect(script2.exitCount).toBe(0);
|
||||
expect(script2.downCount).toBe(0);
|
||||
expect(script2.clickCount).toBe(0);
|
||||
expect(script2.beginDragCount).toBe(0);
|
||||
expect(script2.dragCount).toBe(0);
|
||||
expect(script2.endDragCount).toBe(0);
|
||||
expect(script2.upCount).toBe(0);
|
||||
expect(script2.dropCount).toBe(0);
|
||||
|
||||
expect(script3.enterCount).toBe(0);
|
||||
expect(script3.exitCount).toBe(0);
|
||||
expect(script3.downCount).toBe(0);
|
||||
expect(script3.clickCount).toBe(0);
|
||||
expect(script3.beginDragCount).toBe(0);
|
||||
expect(script3.dragCount).toBe(0);
|
||||
expect(script3.endDragCount).toBe(0);
|
||||
expect(script3.upCount).toBe(0);
|
||||
expect(script3.dropCount).toBe(0);
|
||||
|
||||
|
||||
target.dispatchEvent(generatePointerEvent("pointermove", 2, left + 5, top + 5));
|
||||
engine.update();
|
||||
|
||||
|
||||
expect(script1.enterCount).toBe(1);
|
||||
expect(script1.exitCount).toBe(0);
|
||||
expect(script1.downCount).toBe(1);
|
||||
expect(script1.clickCount).toBe(0);
|
||||
expect(script1.beginDragCount).toBe(1);
|
||||
expect(script1.dragCount).toBe(2);
|
||||
expect(script1.endDragCount).toBe(0);
|
||||
expect(script1.upCount).toBe(0);
|
||||
expect(script1.dropCount).toBe(0);
|
||||
|
||||
expect(script2.enterCount).toBe(1);
|
||||
expect(script2.exitCount).toBe(0);
|
||||
expect(script2.downCount).toBe(0);
|
||||
expect(script2.clickCount).toBe(0);
|
||||
expect(script2.beginDragCount).toBe(0);
|
||||
expect(script2.dragCount).toBe(0);
|
||||
expect(script2.endDragCount).toBe(0);
|
||||
expect(script2.upCount).toBe(0);
|
||||
expect(script2.dropCount).toBe(0);
|
||||
|
||||
expect(script3.enterCount).toBe(0);
|
||||
expect(script3.exitCount).toBe(0);
|
||||
expect(script3.downCount).toBe(0);
|
||||
expect(script3.clickCount).toBe(0);
|
||||
expect(script3.beginDragCount).toBe(0);
|
||||
expect(script3.dragCount).toBe(0);
|
||||
expect(script3.endDragCount).toBe(0);
|
||||
expect(script3.upCount).toBe(0);
|
||||
expect(script3.dropCount).toBe(0);
|
||||
|
||||
target.dispatchEvent(generatePointerEvent("pointerup", 2, left + 5, top + 5));
|
||||
engine.update();
|
||||
|
||||
@@ -134,7 +203,7 @@ describe("UIEvent", async () => {
|
||||
expect(script1.downCount).toBe(1);
|
||||
expect(script1.clickCount).toBe(1);
|
||||
expect(script1.beginDragCount).toBe(1);
|
||||
expect(script1.dragCount).toBe(0);
|
||||
expect(script1.dragCount).toBe(2);
|
||||
expect(script1.endDragCount).toBe(1);
|
||||
expect(script1.upCount).toBe(1);
|
||||
expect(script1.dropCount).toBe(1);
|
||||
@@ -167,7 +236,7 @@ describe("UIEvent", async () => {
|
||||
expect(script1.downCount).toBe(1);
|
||||
expect(script1.clickCount).toBe(1);
|
||||
expect(script1.beginDragCount).toBe(1);
|
||||
expect(script1.dragCount).toBe(0);
|
||||
expect(script1.dragCount).toBe(2);
|
||||
expect(script1.endDragCount).toBe(1);
|
||||
expect(script1.upCount).toBe(1);
|
||||
expect(script1.dropCount).toBe(1);
|
||||
@@ -199,7 +268,7 @@ describe("UIEvent", async () => {
|
||||
expect(script1.downCount).toBe(1);
|
||||
expect(script1.clickCount).toBe(1);
|
||||
expect(script1.beginDragCount).toBe(1);
|
||||
expect(script1.dragCount).toBe(0);
|
||||
expect(script1.dragCount).toBe(2);
|
||||
expect(script1.endDragCount).toBe(1);
|
||||
expect(script1.upCount).toBe(1);
|
||||
expect(script1.dropCount).toBe(1);
|
||||
|
||||
Reference in New Issue
Block a user