diff --git a/docs/developer/typescript/03_basics_tutorial/04_assigning_physics.md b/docs/developer/typescript/03_basics_tutorial/04_assigning_physics.md index a50d36cc2d52..ab61230e3f9a 100644 --- a/docs/developer/typescript/03_basics_tutorial/04_assigning_physics.md +++ b/docs/developer/typescript/03_basics_tutorial/04_assigning_physics.md @@ -1,82 +1,176 @@ - - +# Add physics to an entity -# Assigning Physics +This guide walks you through adding physics components to your entity, allowing it to behave like a real-world object. -So far we have learned how to create an `Entity`, and how to tell the engine what we want our entity to be. In simple terms, we told the engine how to **create** our sphere. +:::hint{type="info"} +ℹ️ **Complete** [Hello World tutorial](./../02_hello_world/index.md) **first** -## Our problem +This guide uses the Hello.ts file from the [Hello World tutorial](./../02_hello_world/index.md) project to teach you how to add physics to an entity. If you haven't completed such tutorial, you use the code snippet from the introduction as a reference to follow along with this guide. +::: -We added some components to our sphere, so that the engine can draw the sphere into the screen and we can see it. -But right now it is only an "empty shell" that sits there doing nothing. -We cannot even move it or push it around! What a boring ball. -Lets fix that. +## Introduction -## Our solution +In iR Engine, entities do not have physics properties by default. If you want an entity to interact with forces like gravity, collisions, and movement, you need to explicitly define its physics behavior. -We are going to add a Collider and a RigidBody to our sphere object. +For this excersice we'll work with the last implementation of the Hello.ts file from [Hello World tutorial](./../02_hello_world/index.md): -Physics properties are tricky to test, as they may not be readily visible. -Lets get a point of reference of how our project currently behaves, so we can be certain that the changes we make to our code are working as we expect them. -In order to do that, we are going to run our project from the studio and walk around the scene with an Avatar. +::::codedrawer{title="Hello.ts file"} +:::codeblocktabs-examples +```javascript +import { ECS } from '@ir-engine/packages/ecs' +import { NameComponent } from '@ir-engine/packages/spatial/src/common/NameComponent' +import { VisibleComponent } from '@ir-engine/packages/spatial/src/renderer/components/VisibleComponent' +import { TransformComponent } from '@ir-engine/packages/spatial/src/transform/components/TransformComponent' +import { PrimitiveGeometryComponent } from '@ir-engine/packages/engine/src/scene/components/PrimitiveGeometryComponent' +import { Vector3 } from 'three' +import { GeometryTypeEnum } from '@ir-engine/packages/engine/src/scene/constants/GeometryTypeEnum' +import { PhysicsSystem } from '@ir-engine/packages/spatial' -These are the steps needed to accomplish that: +// Define the custom component +export const HelloComponent = ECS.defineComponent({ + name: 'ee.tutorial.HelloComponent', + jsonID: 'EE_tutorial_hello', + onInit: () => ({ initialized: false }) +}) + +// Define the query to find entities with HelloComponent +const helloQuery = ECS.defineQuery([HelloComponent]) + +// Define the function to execute +const hello = () => { + for (const entity of helloQuery()) { + let { initialized } = ECS.getMutableComponent(entity, HelloComponent) + if (initialized.value) continue + initialized.set(true) + + ECS.setComponent(entity, NameComponent, 'ee.tutorial.hello-entity') + ECS.setComponent(entity, VisibleComponent) + ECS.setComponent(entity, TransformComponent, { position: new Vector3(0, 1, 0) }) + ECS.setComponent(entity, PrimitiveGeometryComponent, { geometryType: GeometryTypeEnum.SphereGeometry }) + } +} + +// Define the system +export const HelloSystem = ECS.defineSystem({ + uuid: 'ee.tutorial.HelloSystem', + execute: hello, + insert: { after: PhysicsSystem } +}) +``` +::: + +:::codeblocktabs-responses +```javascript +``` +::: +:::: -- Open the scene you created before, or click on `Create Scene` if you don't have it -- Press the `Play` button in the studio -- Move your Avatar around the scene by either: - - Pressing `WASD` in your keyboard - - Clicking anywhere on the ground with your mouse +*** -You may notice that, if you try to hit the sphere with your avatar... you will instead walk right through it! -This happens because our Sphere doesn't have any Physics properties yet, so it can be "seen" but not "collided against". +## The problem: A static object -## Physics Properties +So far, we have **created an entity** and assigned it a **shape**. However, the entity does not interact with the world—it simply exists without movement or collision. -In order to correct our problem, we are now going to: +- **Issue**: The entity is **visible** but **does not respond to gravity or collisions**. +- **Expected behavior**: The entity should **fall, collide, and respond to forces** in the environment. -- Add a `RigidBodyComponent` of type `dynamic` to our entity -- Add a `ColliderComponent` with the shape of a `sphere` +You can confirm this by running the scene and **walking into the sphere with your avatar**. Right now, the avatar **walks through the object** because it has no physics properties. -Lets also change the position of ball so that it spawns some distance above the ground. -Here are your hints for this tutorial: +*** -```ts -// Both the RigidBody and Collider components are part of the `Spatial/physics` engine module -'@ir-engine/packages/spatial/src/physics/components/.....' -// We can specify the dynamic type with: -{ type: 'dynamic' } -// We can specify the shape with: -{ shape: 'sphere' } -// Make the ball spawn 3units above the ground -Vector3(/* X */, /* Y */, /* Z */) -``` +## Solution: Adding physics components -You will know that your code is correct if: +To fix this issue, you need to **assign physics properties** to the sphere by adding two key components: -- The ball has gravity and falls to the ground -- You try to go through the ball with the Avatar, but the engine stops you and you push the ball instead. +1. `RigidBodyComponent` – Enables **physics simulation** (gravity, forces, movement). +2. `ColliderComponent` – Defines a **collision shape**, allowing objects to interact physically. - +Additionally, you will adjust the **spawn height** of the sphere so it falls naturally due to gravity. -```ts -// Import both components from the Spatial/physics module +*** + +## Step 1: Import physics components + +Physics components belong to the **spatial physics module** in iR Engine. Import them into your script: + +```typescript import { RigidBodyComponent } from '@ir-engine/packages/spatial/src/physics/components/RigidBodyComponent' import { ColliderComponent } from '@ir-engine/packages/spatial/src/physics/components/ColliderComponent' ``` -```ts -// Set both components to our entity + +*** + +## Step 2: Assign physics properties + +### 1. Enable rigid body physics + +The **RigidBodyComponent** allows the sphere to move dynamically in response to forces and collisions. Set its **type** to `'dynamic'` so it responds to gravity and forces. + +```typescript ECS.setComponent(entity, RigidBodyComponent, { type: 'dynamic' }) +``` + +**What this does:** + +✅ The sphere **falls** to the ground. + +✅ It can be **pushed by other objects**. + +*** + +### 2. Define a collider shape + +The **ColliderComponent** assigns a **collision shape** to the sphere, preventing other objects from passing through it. Use a **sphere collider** to match the shape of the entity. + +```typescript ECS.setComponent(entity, ColliderComponent, { shape: 'sphere' }) ``` -```ts -// Make the ball spawn 3 units along the Y axis (aka 3u above the ground) + +**What this does:** + +- The sphere **collides** with other objects. +- The avatar **can no longer walk through it**. + +*** + +### 3. Adjust the spawn position + +Currently, the sphere spawns **at ground level**, making it difficult to observe physics effects. Move it **3 units above the ground** so you can see it fall. + +```tsx ECS.setComponent(entity, TransformComponent, { position: new Vector3(0, 3, 0) }) ``` - +**What this does:** + +- The sphere starts **above the ground**. +- Gravity pulls it down **when the scene starts**. + +*** + +## Step 3: Confirm physics interactions + +Once you have added the physics components, **test the behavior** to ensure everything is working correctly. + +### Expected behaviors: + +✔ The sphere **falls** to the ground due to gravity. + +✔ When the avatar walks into the sphere, **it moves instead of passing through**. + +### If something is wrong: + +⚠ If the sphere **doesn’t fall**, ensure the `RigidBodyComponent` is set to `type: 'dynamic'`. + +⚠ If the avatar **still walks through the sphere**, double-check the `ColliderComponent`. -```ts title="ir-tutorial-basic/Step2.ts" showLineNumbers +*** + +## Final implementation + +After making these updates, your system definition should look like this: + +```typescript import { ECS } from '@ir-engine/packages/ecs' import { NameComponent } from '@ir-engine/packages/spatial/src/common/NameComponent' import { VisibleComponent } from '@ir-engine/packages/spatial/src/renderer/components/VisibleComponent' @@ -85,48 +179,65 @@ import { PrimitiveGeometryComponent } from '@ir-engine/packages/engine/src/scene import { Vector3 } from 'three' import { GeometryTypeEnum } from '@ir-engine/packages/engine/src/scene/constants/GeometryTypeEnum' import { PhysicsSystem } from '@ir-engine/packages/spatial' -// Import both components from the Spatial/physics module -//highlight-start + +// Import physics components import { RigidBodyComponent } from '@ir-engine/packages/spatial/src/physics/components/RigidBodyComponent' import { ColliderComponent } from '@ir-engine/packages/spatial/src/physics/components/ColliderComponent' -//highlight-end -export const BasicsComponent = ECS.defineComponent({ - name: 'ee.tutorial.BasicsComponent', - jsonID: 'EE_tutorial_basics', - onInit: () => { return { initialized: false } } +// Define the custom component +export const PhysicsComponent = ECS.defineComponent({ + name: 'ir-engine.tutorial.PhysicsComponent', + jsonID: 'IR_ENGINE_TUTORIAL_PHYSICS', + onInit: () => ({ initialized: false }) }) -const basicsQuery = ECS.defineQuery([BasicsComponent]) +// Define the query to find entities with PhysicsComponent +const physicsQuery = ECS.defineQuery([PhysicsComponent]) -export const BasicsSystem = ECS.defineSystem({ - uuid: 'ee.tutorial.BasicsSystem', +// Define the system to apply physics +export const PhysicsSystem = ECS.defineSystem({ + uuid: 'ir-engine.tutorial.PhysicsSystem', execute: () => { - for (const entity of basicsQuery()) { - let { initialized } = ECS.getMutableComponent(entity, BasicsComponent) + for (const entity of physicsQuery()) { + let { initialized } = ECS.getMutableComponent(entity, PhysicsComponent) if (initialized.value) continue initialized.set(true) - ECS.setComponent(entity, NameComponent, 'ee.tutorial.basics-entity') + ECS.setComponent(entity, NameComponent, 'ir-engine.physics-entity') ECS.setComponent(entity, VisibleComponent) - // Make the ball spawn 3 units along the Y axis (aka 3u above the ground) - //highlight-start + + // Set initial position (3 units above ground) ECS.setComponent(entity, TransformComponent, { position: new Vector3(0, 3, 0) }) - //highlight-end + + // Assign primitive geometry ECS.setComponent(entity, PrimitiveGeometryComponent, { geometryType: GeometryTypeEnum.SphereGeometry }) - // Set both components to our entity - //highlight-start + + // Enable physics ECS.setComponent(entity, RigidBodyComponent, { type: 'dynamic' }) ECS.setComponent(entity, ColliderComponent, { shape: 'sphere' }) - //highlight-end } }, insert: { after: PhysicsSystem } }) ``` - - - - +*** + +## Summary + +✅ **Entities need physics components** to interact with forces like gravity. + +✅ Use `RigidBodyComponent` to define movement and behavior. + +✅ Use `ColliderComponent` to specify how an entity collides. + +✅ Adjust the **spawn position** to test gravity and movement. + +✅ The entity should now **fall** and **collide with the avatar** in the scene. + +*** + +## ➡️ Next steps + +[State Management](./05_state_management.md)