From 25b0f6821583292b03a6206cfa09cc484277fd19 Mon Sep 17 00:00:00 2001 From: Archbee BOT Date: Thu, 20 Feb 2025 01:29:52 +0000 Subject: [PATCH] Archbee draft docs --- .../02_hello_world/00_initial_setup.md | 2 +- .../00_project_code_overview.md | 4 +- .../typescript/02_hello_world/01_ecs.md | 24 +- .../typescript/02_hello_world/02_engine.md | 14 +- .../typescript/02_hello_world/03_system.md | 2 +- .../typescript/02_hello_world/04_component.md | 4 +- .../typescript/02_hello_world/05_query.md | 16 +- .../03_basics_tutorial/01_styling_code.md | 222 +++++++++--------- .../03_basics_tutorial/02_custom_component.md | 167 ++++++++----- 9 files changed, 255 insertions(+), 200 deletions(-) diff --git a/docs/developer/typescript/02_hello_world/00_initial_setup.md b/docs/developer/typescript/02_hello_world/00_initial_setup.md index e19190ae83c3..3ac57cc4e7ac 100644 --- a/docs/developer/typescript/02_hello_world/00_initial_setup.md +++ b/docs/developer/typescript/02_hello_world/00_initial_setup.md @@ -1,6 +1,6 @@ # Initial setup -In this tutorial, you will use the **Hello World project** to learn how to work with iR Engine. This project provides a minimal working example to help you explore the engine’s core concepts. +In this tutorial, you will use the Hello World project to learn how to work with iR Engine. This project provides a minimal working example to help you explore the engine’s core concepts. ## Prerequisites diff --git a/docs/developer/typescript/02_hello_world/00_project_code_overview.md b/docs/developer/typescript/02_hello_world/00_project_code_overview.md index 7e87f58fdd60..d6c147b1da17 100644 --- a/docs/developer/typescript/02_hello_world/00_project_code_overview.md +++ b/docs/developer/typescript/02_hello_world/00_project_code_overview.md @@ -71,6 +71,6 @@ This ensures the engine loads and executes `Hello.ts` when the project starts. ## ➡️ Next steps -Your project is minimal but introduces **critical engine concepts**. Next, you will explore **how iR Engine structures data and logic using the ECS pattern**. +Your project is minimal but introduces critical engine concepts. Next, you will explore **how iR Engine structures data and logic using the ECS pattern**. -Continue to [The ECS pattern](./01_ecs.md) to understand the engine’s core architecture. +📌 Continue to [The ECS pattern](./01_ecs.md) to understand the engine’s core architecture. diff --git a/docs/developer/typescript/02_hello_world/01_ecs.md b/docs/developer/typescript/02_hello_world/01_ecs.md index 169e2dce1ce0..dba551aa82ab 100644 --- a/docs/developer/typescript/02_hello_world/01_ecs.md +++ b/docs/developer/typescript/02_hello_world/01_ecs.md @@ -1,8 +1,8 @@ # The ECS pattern -iR Engine is built on the **Entity-Component-System (ECS)** pattern, a **modular and scalable** architecture used for game engines and simulations. +Now that your Hello World project is set up, it is time to understand the most crucial concept of the engine: **The ECS pattern.** -Understanding ECS is essential to working with iR Engine. This guide provides an overview of how ECS organizes **entities, components, and systems** to define behavior. +This guide provides an overview of how ECS **organizes** **entities, components, and systems** to define behavior. *** @@ -26,9 +26,11 @@ The **Entity-Component-System (ECS) pattern** is a **data-driven architecture** This separation allows for **efficient, modular, and reusable** code. +:::hint{type="info"} 💡 **Why ECS?** Unlike traditional object-oriented programming, ECS avoids deep inheritance hierarchies. Instead, it **stores data separately** and **applies logic only when needed**, improving performance. +::: *** @@ -63,13 +65,14 @@ ECS.setComponent(entity, PrimitiveGeometryComponent, { geometryType: 1 }) To display an entity in a scene, iR Engine requires specific components: -| **Component** | **Purpose** | -| :-------------------------------------------------------- | :----------------------------------------------------------- | -| `NameComponent` | Assigns a human-readable name to the entity. | -| `VisibleComponent` | Ensures the entity is **visible** in the scene. | -| `TransformComponent` | Defines the **position, rotation, and scale** of the entity. | -| - `PrimitiveGeometryComponent` or: -- `MeshComponent` | Assigns a **basic 3D shape** or a **mesh **to the entity. | +| **Component** | **Purpose** | +| :------------------------------------------------- | :----------------------------------------------------------- | +| `NameComponent` | Assigns a human-readable name to the entity. | +| `VisibleComponent` | Ensures the entity is **visible** in the scene. | +| `TransformComponent` | Defines the **position, rotation, and scale** of the entity. | +| `PrimitiveGeometryComponent` or a `MeshComponent` | Assigns a **basic 3D shape** or a **mesh **to the entity. | + +See more details about these components in the following section. *** @@ -127,6 +130,8 @@ ECS.setComponent(entity, PrimitiveGeometryComponent, { geometryType: 1 }) The number `1` represents a [`SphereGeometry`](https://github.com/ir-engine/ir-engine/blob/dev/packages/engine/src/scene/constants/GeometryTypeEnum.ts#L28). +*** + ## Understand system logic A **system** is responsible for processing logic on entities **that contain specific components**. @@ -156,4 +161,3 @@ You will define a **system** in the following guides. Now that you understand **ECS**, it’s time to start your first task, **modify an entity in iR Engine**. Continue to [Work in the engine](./02_engine.md) to modify your project’s source code**,** and start interacting with the application. - diff --git a/docs/developer/typescript/02_hello_world/02_engine.md b/docs/developer/typescript/02_hello_world/02_engine.md index 66816f2f7583..a2750674bc8e 100644 --- a/docs/developer/typescript/02_hello_world/02_engine.md +++ b/docs/developer/typescript/02_hello_world/02_engine.md @@ -34,7 +34,7 @@ iR Engine supports **projects**, which function similarly to projects in other g Each project’s **source code runs globally**, which will become an important concept later. -### **Projects directory structure** +### Projects directory structure By default, iR Engine scans for projects inside: @@ -61,7 +61,7 @@ To integrate a project’s source code with iR Engine, you must: 1. **Import iR Engine modules**. 2. **Export the code so the engine can execute it**. -### **Project configuration file** +### Project configuration file Each project has an `xrengine.config.ts` file, which registers it with the engine: @@ -88,7 +88,7 @@ For now, just remember: **this file tells iR Engine how to load your project**. In this tutorial, we will modify a **sphere primitive** in the scene. -### **Importing spatial components** +### Importing spatial components Since the sphere is a **spatial** object, we must import components from the **spatial engine module**: @@ -100,7 +100,7 @@ import { PrimitiveGeometryComponent } from '@ir-engine/packages/engine/src/scene import { Vector3 } from 'three' ``` -### **Importing ECS utilities** +### Importing ECS utilities To manage entities, we also import **ECS functions**: @@ -112,17 +112,17 @@ import { ECS } from '@ir-engine/packages/ecs' Now that we understand the project structure, let's modify the **sphere’s geometry**. -### **Updating the geometry type** +### Updating the geometry type Instead of hardcoding the value `1` for a sphere, we will use a readable **enum**. -### **Steps** +### Steps 1. Open `ir-tutorial-hello/src/Hello.ts`. 2. Import `GeometryTypeEnum` from the `scene/constants/` module. 3. Replace `1` with `GeometryTypeEnum.SphereGeometry`. -### **Where is the enum?** +### Where is the enum? If you’re unsure where the enum is located, use these hints: diff --git a/docs/developer/typescript/02_hello_world/03_system.md b/docs/developer/typescript/02_hello_world/03_system.md index 39d477abd77c..b5ede1598ae4 100644 --- a/docs/developer/typescript/02_hello_world/03_system.md +++ b/docs/developer/typescript/02_hello_world/03_system.md @@ -22,7 +22,7 @@ A **system** executes logic on entities that contain specific components. Withou Currently, your **entity creation logic** runs immediately when the project loads. -📌 To follow **best practices**, move it into a function. +📌 To follow best practices, move it into a function. ```typescript let initialized = false // Track execution state diff --git a/docs/developer/typescript/02_hello_world/04_component.md b/docs/developer/typescript/02_hello_world/04_component.md index 81220043bc68..0d44559c0f75 100644 --- a/docs/developer/typescript/02_hello_world/04_component.md +++ b/docs/developer/typescript/02_hello_world/04_component.md @@ -36,7 +36,7 @@ To restrict the sphere’s execution, you need to: 2. **Use a query** to execute logic only when an entity has this component. 3. **Ensure the system only runs under the right conditions**. -By following this approach, your system will **only create the sphere when needed** rather than running globally. +By following this approach, your system will only create the sphere when needed rather than running globally. *** @@ -54,7 +54,7 @@ export const HelloComponent = ECS.defineComponent({ }) ``` -Right now, this component **does nothing on its own**. You will connect it to your system in the next step. +Right now, this component does nothing on its own. You will connect it to your system in the next step. ### What does this do? diff --git a/docs/developer/typescript/02_hello_world/05_query.md b/docs/developer/typescript/02_hello_world/05_query.md index a28fb4757937..429cd5d7c2f1 100644 --- a/docs/developer/typescript/02_hello_world/05_query.md +++ b/docs/developer/typescript/02_hello_world/05_query.md @@ -44,11 +44,11 @@ This query **returns all entities** that have the `HelloComponent`. Here's a table to help you understand the query: -| **Function** | **Description** | -| :------------------ | :---------------------------------------------------------------- | -| defineQuery(\[...]) | Creates a query to filter entities based on components. | -| \[HelloComponent] | The query will match **only** entities containing this component. | -| helloQuery() | When called, it returns **all matching entities**. | +| **Function** | **Description** | +| :------------------- | :---------------------------------------------------------------- | +| `defineQuery([...])` | Creates a query to filter entities based on components. | +| `[HelloComponent]` | The query will match **only** entities containing this component. | +| `helloQuery()` | When called, it returns **all matching entities**. | At this stage, the query **does not run automatically**. You need to integrate it into your system. @@ -101,10 +101,10 @@ export const HelloSystem = ECS.defineSystem({ }) ``` -### **How does this improve the system?** +### How does this improve the system? | **Problem** | **Before** | **Now** | -| -------------------------------- | ------------------------------ | --------------------------------------------- | +| :------------------------------- | :----------------------------- | :-------------------------------------------- | | Entities were processed manually | Had to store entity references | Query retrieves matching entities dynamically | | Inefficient execution | System executed every frame | Now only executes for relevant entities | | No filtering mechanism | Processed all entities | Now limited to entities with `HelloComponent` | @@ -199,6 +199,6 @@ By using `defineQuery()`, your system now **retrieves entities dynamically inste ## ➡️ Next steps -Now that you have structured **entities, components, systems, and queries**, it's time to **recap what you've learned and move forward**. +Now that you have structured **entities, components, systems, and queries**, it's time to recap what you've learned and move forward. 📌 Continue to [Recap and next steps](./90_recap.md) . diff --git a/docs/developer/typescript/03_basics_tutorial/01_styling_code.md b/docs/developer/typescript/03_basics_tutorial/01_styling_code.md index 96ed4303524e..85a95cacef3e 100644 --- a/docs/developer/typescript/03_basics_tutorial/01_styling_code.md +++ b/docs/developer/typescript/03_basics_tutorial/01_styling_code.md @@ -1,87 +1,101 @@ - - +# Coding style and best practices -# Styling your code +Structure your code effectively to make it easier to read, maintain, and collaborate on. -In the last step of the [Hello World Tutorial](/developer/typescript/gettingStarted/hello/component#create) we created a custom Scene Component. -But we never really explained how we did it. +## Code formatting and linting -We also skimmed over multiple concepts that are very important for working with the Engine. So lets start on the right foot and explain them now. -Also, now that we are into it, we are going to style our code in a way that matches iR Engine's code a bit more. +Use automated formatting and linting tools to keep your code clean and consistent: -Lets start with Styling. +- **Prettier**: Automatically formats your code. +- **ESLint**: Analyzes your code and enforces best practices. -We took a lot of shortcuts in previous sections of the tutorial. -This made learning much simpler to get started with, but we also left out a few concepts that will make our codebase much easier to navigate as soon as our project starts growing. +Ensure both tools are installed and enabled in your development environment. -## ID Naming Convention +*** -Lets start with the simplest style change. -You may have noticed that we changed the `uuid` and `NameComponent` in the HelloWorld's final solution. -The engine doesn't have a standard for these names yet, but this is a good naming convention that you can follow: +## Naming conventions -- Separate words with `.` -- Start with the namespace/organization/author of your project -- Follow by the project name of the thing that you are naming -- Follow by the name of the thing -- Separate multi-word names with `-` -```md +Using clear and structured names helps you understand, navigate, and maintain your code. Follow these guidelines to name different elements correctly. -# Example -Namespace : ee -Project : tutorial -Thing : HelloSystem +### Capitalization styles -Result : ee.tutorial.HelloSystem -Multi-word : ee.multi-word-example.HelloSystem -``` +Use these capitalization rules to keep your code consistent: -:::note[assignment zero] -This is not really an assignment, as we already did this before. -But see if you have any names leftover in your code that are not using this standard, and change them so that they do. -::: +| Type | Convention | Example | +| :-------------------- | :------------- | :---------------- | +| Classes & definitions | `CapitalCase` | `CapsuleGeometry` | +| Variables & functions | `camelCase` | `createEntity()` | +| Enums & namespaces | `CAPITAL_CASE` | `ENTITY_TYPE` | + +### Component and system naming -## `jsonID` Naming Requirements +Follow a **dot-separated format** when naming components and systems. This keeps them organized and easy to identify. -You may have also noticed that the `jsonID` field does not respect the naming convention we just explained above. -Internally, the field `jsonID` will be used to define the name of a [glTF](https://www.khronos.org/gltf) extension. -As such, the engine has a different standard for them: -- Separate words and multi-words with `_` -- Start with the namespace/organization/author of your project in UPPERCASE -- Follow by the project name of the thing that you are naming -- Follow by the name of the thing -```md +Each name should include: -# Example -Namespace : EE -Project : tutorial -Thing : hello +- **Namespace**: The name of your organization or module. +- **Project**: The project to which the system belongs. +- **System name**: A meaningful identifier for the system. -Result : EE_tutorial_hello -Multi-word : EE_multi_word_example_hello +📌 **Example:** + +```plaintext +ir-engine.tutorial.HelloSystem ``` -## Arrow Functions +For multi-word names, avoid underscores and use camelCase within dot-separated segments: -We talked about Javascript [`Arrow Functions`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) during one of the earlier sections of the HelloWorld tutorial. They are used a lot throughout the engine's codebase. +```plaintext +ir-engine.multiwordexample.HelloSystem +``` -They are specially helpful when defining Systems and Components. -This is how a `defineSystem` call would look like using each type of function: -```ts title="Regular Function : Simpler, but less common in iR Engine" -// Our function -function sayhello() { console.log("Hello World") } +### Component JSON identifiers (`jsonID`) -// Our System -const HelloSystem = ECS.defineSystem({ - uuid: 'ee.tutorial.HelloSystem', - execute: sayhello, - insert: { after: PhysicsSystem } -}) +The `jsonID` field follows a different convention because iR Engine uses it for **glTF extensions**. Follow these rules: + +- Use underscores (`_`) instead of dots (`.`). +- Write the namespace in uppercase (`IR_ENGINE`). +- Use lowercase with underscores for the project and component names. + +📌 **Example:** + +```plaintext +IR_ENGINE_TUTORIAL_HELLO +``` + +For multi-word names: + +```plaintext +IR_ENGINE_MULTIWORD_EXAMPLE_HELLO ``` -```ts title="Arrow Function : How defining a multi-field object usually looks like" -// Our function can be declared inside our system. Doesn't need a name + +### Avoid unnecessary abbreviations or underscores + +Keep names clear and easy to read: + +✅ Use full words unless it's a common acronym (e.g., `HTTPRequest`). +✅ Avoid shortening words unnecessarily (`PlayerController`, not `PlrCtrl`). +✅ Use camelCase instead of underscores for variable names (`playerPosition`, not `player_position`). + +Good naming makes your code easier to understand at a glance. + +*** + +## TypeScript typing + +Always use **explicit TypeScript types** for functions, variables, and objects. This prevents errors and makes your code easier to maintain. + +*** + +## Use arrow functions + +Arrow functions make your code more concise and readable. They are the preferred way to define functions inside objects or systems. + +### Arrow function (recommended) + +```typescript const HelloSystem = ECS.defineSystem({ - uuid: 'ee.tutorial.HelloSystem', + uuid: 'ir-engine.tutorial.HelloSystem', execute: () => { console.log("Hello World") }, @@ -89,92 +103,70 @@ const HelloSystem = ECS.defineSystem({ }) ``` -As you can see, this gives us a very small gain for this tiny example. -But, when the codebase grows, this style can make a big difference in the readability of our code. +### Regular function (less common in iR Engine) + +```typescript +function sayHello() { console.log("Hello World") } -Arrow functions are also used extensively all throughout the engine's codebase. -So, even if you don't prefer them, at least you need to know about how they work so that you are not confused the first time you find code using this style. +const HelloSystem = ECS.defineSystem({ + uuid: 'ir-engine.tutorial.HelloSystem', + execute: sayHello, + insert: { after: PhysicsSystem } +}) +``` -:::important[first assignment] -Change the style of the BasicsTutorial code: -. Remove any named functions that are assigned to an object -. Use arrow functions directly as object fields +:::hint{type="info"} +ℹ️ **Why use arrow functions?** -There are not that many to change. We only had one named function! :) +- Makes your code **shorter** and **easier to read**. +- Matches the **style used across iR Engine’s codebase**. +- Works well for **defining system logic**. ::: - +Use arrow functions whenever you define functions inside objects or systems. -```ts -// Define our system -export const HelloSystem = ECS.defineSystem({ - uuid: 'ee.tutorial.HelloSystem', - //highlight-start - execute: () => { - //highlight-end - 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 }) - } - //highlight-start - }, - //highlight-end - insert: { after: PhysicsSystem } -}) -``` - +## Apply best practices -```ts title="ir-tutorial-basic/src/step1.ts" showLineNumbers +Here’s an example of a properly structured system following all best practices. + +```typescript import { ECS } from '@ir-engine/packages/ecs' +import { PhysicsSystem } from '@ir-engine/packages/spatial/src/physics/PhysicsModule' 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' -// Define our component +// Define a structured component name export const HelloComponent = ECS.defineComponent({ - name: 'ee.tutorial.HelloComponent', - jsonID: 'EE_tutorial_hello', - onInit: () => { return { initialized: false } } + name: 'ir-engine.tutorial.HelloComponent', + jsonID: 'IR_ENGINE_TUTORIAL_HELLO', + onInit: () => ({ initialized: false }) }) -// Define the query that will find our Scene's Entity +// Define the query to find entities with HelloComponent const helloQuery = ECS.defineQuery([HelloComponent]) -// Define our system +// Define the system using arrow functions export const HelloSystem = ECS.defineSystem({ - uuid: 'ee.tutorial.HelloSystem', - //highlight-start + uuid: 'ir-engine.tutorial.HelloSystem', execute: () => { - //highlight-end for (const entity of helloQuery()) { - // Check if we have already initialized our Sphere let { initialized } = ECS.getMutableComponent(entity, HelloComponent) if (initialized.value) continue - initialized.set(true) // Set our initialized state to true + initialized.set(true) - ECS.setComponent(entity, NameComponent, 'ee.tutorial.hello-entity') + ECS.setComponent(entity, NameComponent, 'ir-engine.hello-entity') ECS.setComponent(entity, VisibleComponent) ECS.setComponent(entity, TransformComponent, { position: new Vector3(0, 1, 0) }) ECS.setComponent(entity, PrimitiveGeometryComponent, { geometryType: GeometryTypeEnum.SphereGeometry }) } - //highlight-start }, - //highlight-end insert: { after: PhysicsSystem } }) ``` - - - - diff --git a/docs/developer/typescript/03_basics_tutorial/02_custom_component.md b/docs/developer/typescript/03_basics_tutorial/02_custom_component.md index 982ab4f56c60..0c8de31e7ba7 100644 --- a/docs/developer/typescript/03_basics_tutorial/02_custom_component.md +++ b/docs/developer/typescript/03_basics_tutorial/02_custom_component.md @@ -1,93 +1,152 @@ - - - # Custom Components -The `defineComponent` function accepts a `ComponentPartial` that has multiple fields available. -```ts -// Define our component +Learn how to define custom components in iR Engine using `defineComponent()`, structure component data with `ComponentPartial`, and configure essential properties. + +## Introduction + +A **component** is a fundamental part of the **Entity Component System (ECS)** pattern in iR Engine. Components store data that describes **an entity’s properties and behaviors**, allowing systems to process and update them efficiently. + +*** + +## Creating a component + +To define a new component, use `defineComponent()`, which accepts a `ComponentPartial` object containing key configuration fields. + +### Example: Defining a simple component + +The following code creates a **HelloComponent** that tracks whether an entity has been initialized: + +```tsx const HelloComponent = ECS.defineComponent({ - name: 'ee.tutorial.HelloComponent', - jsonID: 'EE_tutorial_hello', - onInit: () => { return { initialized: false } } + name: 'ir-engine.tutorial.HelloComponent', + jsonID: 'IR_ENGINE_TUTORIAL_HELLO', + onInit: () => ({ initialized: false }) // Default state }) ``` -:::note -See [Typescript.Partial](https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype) for a reference of what Partials are. +This component assigns an `initialized` property to entities, allowing a system to determine whether an entity has already processed specific logic. + +*** + +## Understanding `ComponentPartial` + +The `defineComponent()` function accepts a **ComponentPartial** object, which provides flexible definitions for components. + +:::hint{type="info"} +ℹ️ **What are TypeScript Partials?** + +A **partial** in TypeScript means that some properties are optional during definition but are required when used. + +📌 **Reference:** See [TypeScript.Partial](https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype) for more details. ::: -Lets review what each of these fields do and how to use them. -## `name` -`name` is a `string` that defines the human readable label for the component that we are creating. -It will be displayed in the editor and debugging tools. -```ts -name: 'ee.tutorial.HelloComponent', +The `ComponentPartial` object includes several key properties that define how a component behaves. + +*** + +## Core component properties + +Below is a breakdown of the **most important properties** in `ComponentPartial` and how they affect component behavior. + +### 1. `name` (Required) + +The `name` property defines a **human-readable label** for the component. This name appears in **Studio, debugging tools, and logs**, making it easier to identify. + +```tsx +name: 'ir-engine.tutorial.HelloComponent' ``` -As we saw before, the engine does not define strict naming requirements for this field, but it does define a [naming convention](./styling#id-naming-convention) that is good practice to follow in your projects. -## `jsonID` -`jsonID` is an optional `string` that defines the internal ID used to reference this component in JSON data. -```ts -jsonID: 'EE_tutorial_hello', +📌 **Follow the naming conventions** outlined in [Coding Style and Best Practices](https://www.notion.so/styling#id-naming-convention). + +*** + +### 2. `jsonID` (Optional, but recommended) + +The `jsonID` property assigns an **internal identifier** used when **serializing components into JSON**. This is required for **glTF-based exports and scene persistence**. + +```tsx +jsonID: 'IR_ENGINE_TUTORIAL_HELLO' ``` -As we saw before, this field will be used by the engine to define the name of a [glTF](https://www.khronos.org/gltf) extension. -Because of this, the `jsonID` field has very specific [naming requirements](./styling#jsonid-naming-requirements) that must be followed. -## `onInit` -`onInit` is a function that will be called once when the Component is added to an entity _(ie: initialized)_. -It returns the shape of the Component's runtime data, which has the type `Schema`. -```ts +📌 **Follow the required JSON naming format** outlined in [Coding Style and Best Practices](https://www.notion.so/styling#jsonid-naming-requirements). + +*** + +### 3. `onInit` (Optional, but commonly used) + +The `onInit` property defines a function that executes **once** when the component is added to an entity. It sets the **initial state** of the component. + +```tsx +onInit: () => ({ initialized: false }) +``` + +This function **returns an object** that describes the component’s **runtime data structure**. + +📌 **How does **`onInit`** work?** + +The `onInit` function returns the shape of the component’s runtime data, which has the type `Schema`. + +```tsx onInit?: (this: SoAComponentType, entity: Entity) => ComponentType & OnInitValidateNotState // this : `@internal` The component partial itself. -// entity : The Entity to which this Component is being assigned. +// entity : The entity to which this Component is being assigned. // returns : The `Schema` (aka shape) of the component's runtime data. ``` -A Component's Schema can contain any type of data that you want. -In our example from before, we saw how to use this data to store our `initialized` state variable inside the component, instead of storing it in our module. -## Other fields -The `ComponentPartial` type accepts multiple other fields that we haven't needed for our simple HelloComponent example. These fields are: -`schema`, `toJSON`, `onSet`, `onRemove`, `reactor`, `errors`. +This means that `onInit` is responsible for defining **what kind of data the component holds**. You can use this to store **state variables, metadata, or any relevant information** about an entity. + +*** + +## Additional component properties + +While the properties above define the **basic functionality** of a component, `defineComponent()` supports several **advanced features**. + +| **Property** | **Purpose** | +| :----------- | :------------------------------------------------------- | +| `schema` | Defines the **expected structure** of component data. | +| `toJSON` | **Serializes** the component’s data when saving a scene. | +| `onSet` | Triggers when the component’s data updates. | +| `onRemove` | Triggers when the component is removed from an entity. | +| `reactor` | Defines **React-based side effects** for the component. | +| `errors` | Stores error-handling information. | -We will explore them further in later sections of the tutorial. +You don’t need to use these properties in every component. However, understanding them helps when creating more complex systems. - -The data used to create a Component with `defineComponent` is declared by the `ComponentPartial` interface. -This type exists so that some of the properties of Components are optional when defining them, but required during normal use. +*** -This is the shape of the `ComponentPartial` interface, defined in the [`ComponentFunctions.ts`](https://github.com/ir-engine/ir-engine/blob/dev/packages/ecs/src/ComponentFunctions.ts) file: -```ts +## Full `ComponentPartial` interface + +The following is the full interface for `ComponentPartial`, as defined in ComponentFunctions.ts. + +```tsx { name: string jsonID?: string onInit?: (this: SoAComponentType, entity: Entity) => ComponentType & OnInitValidateNotState - // A Component's Schema is the shape of its runtime data. + // Defines the expected structure of the component’s runtime data schema?: Schema - // Serializer function called when the component is saved to a snapshot or scene file. - // Its logic must convert the component's runtime data into a JSON object. - // entity : The Entity to which this Component is assigned. - // component : The Component's global data (aka State). + // Converts component data to JSON format when saving a scene toJSON?: (entity: Entity, component: State) => JSON - // Called when the component's data is updated via the setComponent function. - // This is where deserialization logic should happen. - // entity : The Entity to which this Component is assigned. - // component : The Component's global data (aka State). - // json : The JSON object that contains this component's serialized data. + // Handles updates to the component’s data onSet?: (entity: Entity, component: State, json?: SetJSON) => void - // Called when the Component is removed from an Entity + // Handles cleanup logic when the component is removed from an entity onRemove?: (entity: Entity, component: State) => void | Promise - // Defines the React.FC (Function Component) async logic of the resulting Component type. - // Any side-effects that depend on the component's data should be defined here. + // Defines React-based logic related to this component reactor?: React.FC errors?: ErrorTypes[] } ``` - +*** + +## Next steps + +Now that you know how to define a custom component, the next step is **using queries** to find and process entities dynamically. + +📌 **Continue to** Defining Queries.