> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/creator/content-creator-ko/sdk7/architecture/systems.md).

# 시스템

시스템이 씬 상태를 업데이트하는 데 어떻게 사용되는지 알아봅니다

Decentraland 씬은 *시스템* 시간이 지남에 따라 모든 데이터를 업데이트하기 위해, 각 엔티티의 [컴포넌트](/creator/content-creator-ko/sdk7/architecture/entities-components.md).

![](https://3980763956-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-de62a18d51b28bebd55c86e8ef5a8a783467ae62%2Fecs-big-picture.png?alt=media\&token=0d7a02a2-955a-44ed-ae77-5df99c49e530)

*시스템* 씬을 동적으로 만드는 요소입니다. 이들은 씬의 게임 루프의 매 틱마다 주기적으로 실행되어 렌더링될 내용을 변경하는 함수입니다.

다음 예제는 기본적인 시스템 선언을 보여줍니다:

```ts
// 시스템을 정의합니다
function mySystem() {
  console.log("매 틱마다 수행됨. 내 시스템이 실행 중입니다")
}
// 시스템을 엔진에 추가합니다
engine.addSystem(mySystem)
```

시스템 안의 함수는 원하는 것은 무엇이든 수행할 수 있습니다. 일반적으로는 특정 [쿼리](/creator/content-creator-ko/sdk7/architecture/querying-components.md)를 만족하는 모든 엔티티에 대해, 엔티티의 컴포넌트에 저장된 값을 변경하는 특정 로직을 따릅니다.

```ts
function moveSystem(dt: number) {
  // Transform이 있는 모든 엔티티를 반복합니다
  for (const [entity] of engine.getEntitiesWith(Transform)) {

  // 변경 가능한 Transform 컴포넌트를 가져옵니다
  const transform = Transform.getMutable(entity)

  // 위치 값을 업데이트합니다
    transform.position.z += 0.01
  }
}

engine.addSystem(moveSystem)
```

위 예제에서 시스템 `MoveSystem` 은 게임 루프의 각 틱마다 실행되어, 씬에서 Transform을 가진 모든 엔티티의 위치를 변경하는 함수입니다.

![](https://3980763956-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-826c0dcfcfc5a6d5719de5a0939214a534078567%2Fecs-system-new.png?alt=media\&token=59a83c9e-0d01-454b-bd5e-4ca61779a3f3)

씬에 여러 시스템을 두어 서로 다른 동작을 분리할 수 있으며, 이렇게 하면 코드가 더 깔끔해지고 확장 및 재사용이 쉬워집니다. 예를 들어, 한 시스템은 물리를 처리하고, 다른 시스템은 장애물 엔티티가 계속해서 앞뒤로 움직이게 하며, 또 다른 시스템은 캐릭터의 AI를 처리할 수 있습니다.

여러 시스템이 하나의 엔티티에 작용할 수 있습니다. 예를 들어, 플레이어가 아닌 캐릭터는 AI에 따라 스스로 움직일 수 있지만, 절벽 밖으로 실수로 걸어 나갔을 때 중력의 영향을 받을 수도 있습니다. 그런 경우 물리 시스템과 AI 시스템은 서로를 알 필요조차 없습니다. 각 시스템은 게임 루프의 각 틱마다 현재 상태를 독립적으로 다시 평가하고, 자신만의 별도 로직을 구현합니다.

## 시스템 함수

시스템의 함수는 게임 루프의 매 틱마다 한 번씩 주기적으로 실행됩니다. 이는 자동으로 이루어지며, 코드 어딘가에서 이 함수를 명시적으로 호출할 필요가 없습니다.

Decentraland 씬에서는 게임 루프를 씬에 있는 모든 시스템 함수의 집합으로 생각할 수 있습니다.

{% hint style="warning" %}
**📔 참고**: 같은 시스템 함수를 엔진에 두 번 이상 추가할 수는 없습니다. 이미 추가된 시스템을 다시 추가하려고 하면 엔진은 오류를 발생시킵니다: `시스템 "<name>"이 이미 엔진에 추가되었습니다`.
{% endhint %}

## 참조로 엔티티 처리하기

일부 컴포넌트와 시스템은 씬에서 하나의 엔티티에만 사용하도록 만들어졌습니다. 예를 들어, 게임 점수를 저장하는 엔티티나 씬에 하나뿐인 메인 게이트 같은 경우입니다. 시스템 안에서 그런 엔티티에 접근하려면, 시스템 함수에서 엔티티 또는 그 컴포넌트를 이름으로 간단히 참조하면 됩니다.

```ts
// 시스템이 접근할 수 있도록 모듈 범위에 엔티티 참조를 선언합니다
let game: Entity

export function main(){
	// 새 엔티티를 만듭니다
	game = engine.addEntity()

	// 해당 엔티티에 컴포넌트를 추가합니다
	ScoreComponent.create(game)
}

// 시스템을 정의합니다
export function UpdateScore() {

  // 개별 엔티티에 대한 참조를 호출합니다
  const points = ScoreComponent.get(game).points
  console.log(points)
}

// 시스템을 엔진에 추가합니다
engine.addSystem(UpdateScore)
```

더 큰 프로젝트에서는 시스템 정의를 엔티티와 컴포넌트의 인스턴스화와는 별도의 파일에 두는 것을 권장합니다.

## 컴포넌트 쿼리 반복하기

대부분의 경우 씬에는 같은 유형의 엔티티가 여러 개 있고, 이들은 비슷한 동작을 합니다. 예를 들어 열 수 있는 문이 여러 개 있거나, 플레이어를 공격할 수 있는 적이 여러 명일 수 있습니다. 이런 비슷한 엔티티들을 하나의 시스템에서 모두 처리하고, 목록을 반복하면서 각 항목에 같은 검사를 수행하는 것이 합리적입니다.

시스템 함수가 *전체* 씬의 엔티티 집합을 반복하도록 하고 싶지는 않을 것입니다. 그렇게 하면 처리 성능 면에서 매우 비용이 클 수 있습니다. 이를 피하려면 [컴포넌트 쿼리](/creator/content-creator-ko/sdk7/architecture/querying-components.md)를 사용하여 관련된 엔티티만 반복할 수 있습니다.

예를 들어, 씬에는 `PhysicsSystem` 가 있을 수 있으며, 이는 씬의 엔티티들에 대한 중력의 효과를 계산합니다. 씬의 일부 엔티티, 예를 들어 나무 같은 것은 절대 움직이도록 의도된 것이 아니므로, 이들에 대해 중력 효과를 계산하지 않는 것이 좋습니다. 다음과 같은 `HasPhysics` 컴포넌트를 정의하여 중력의 영향을 받을 수 있는 엔티티를 표시한 다음, `PhysicsSystem` 가 이 쿼리에서 반환된 엔티티만 처리하도록 할 수 있습니다.

```ts
// 시스템을 정의합니다
export function PhysicsSystem() {
  // HasPhysics가 있는 모든 엔티티를 반복합니다
  for (const [entity] of engine.getEntitiesWith(HasPhysics)) {

  // 변경 가능한 Transform 컴포넌트를 가져옵니다
  const transform = Transform.getMutable(entity)

  // 물리 효과를 계산합니다
  }
}

// 시스템을 엔진에 추가합니다
engine.addSystem(PhysicsSystem)
```

## 프레임 간 델타 시간

시스템 안의 함수에는 선택적으로 다음과 같은 인수 하나를 포함할 수 있습니다. `dt`, 타입은 `number` (를 나타내는 *델타 시간*).

```ts
function MySystem(dt: number) {

  // 씬을 업데이트합니다
  console.log("마지막 틱 이후 경과 시간: ", dt)
}

engine.addSystem(MySystem)
```

*델타 시간* 는 게임 루프의 마지막 틱 이후 경과한 시간을 초 단위로 나타냅니다.

이 `dt` 인수는 게임 루프의 마지막 틱 이후 경과한 실제 시간이며, 초 단위로 측정됩니다. SDK는 고정된 틱 속도를 강제하지 않습니다. 호스트 런타임이 이 값을 전달하며, 이는 씬의 프레임레이트에 따라 달라집니다.

씬이 더 부드럽게 실행될수록 틱은 더 자주 발생하고, 각 `dt` 값은 더 작아집니다. 틱을 처리하는 데 더 오래 걸리면 다음 틱 전까지 더 많은 시간이 지나며, `dt` 도 그에 따라 커집니다.

이 씬 측 프레임레이트는 초당 30틱으로 제한되며, 이 경우 `dt` 가 *1/30* (0.0333...).

![](https://3980763956-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-9ebcec63913c57ddf96107a817b14797211c1de2%2Fecs-framerate.png?alt=media\&token=3493a280-5778-492f-afc5-e98b45d6beb6)

프레임 처리에 더 많은 시간이 걸리면 해당 프레임의 그리기가 지연되고, `dt` 는 다음 틱에서 더 긴 간격을 반영합니다. 엔진은 지연을 소급해서 보정하지 않으며; `dt` 항상 실제 경과 시간을 보고하므로 시스템이 이에 맞춰 조정할 수 있습니다.

![](https://3980763956-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-951edc3e7611eb138145de377ec07dfc8ec4836b%2Fecs-framerate-heavy.png?alt=media\&token=e3656977-e7aa-4f14-854b-e9071ed18b02)

이상적으로는 씬에서 프레임이 떨어지는 일이 없도록 해야 합니다. 이는 플레이어 경험의 품질에 영향을 주기 때문입니다. 이는 플레이어의 기기 처리 성능에 달려 있으므로, 씬이 항상 이를 우아하게 처리할 준비가 되어 있어야 한다는 가능성이 있습니다.

이 `dt` 변수는 프레임 처리 시간이 기본 시간을 초과할 때 유용합니다. 현재 프레임이 이전 프레임만큼의 시간이 걸릴 것이라고 가정하면, 이 정보는 점진적인 변화를 얼마나 조정해야 하는지 계산하는 데 사용될 수 있으며, 그 결과 변화율이 프레임 간 지연에 비례해 일정하게 보이도록 할 수 있습니다.

다음 [엔티티 위치 지정](/creator/content-creator-ko/sdk7/3d/entity-positioning.md) 사용 방법의 예시를 보려면 `dt` 을 사용해 움직임을 더 부드럽게 만드세요.

## 시간 간격으로 반복하기

시스템이 일정한 시간 간격으로 어떤 작업을 수행하게 하려면, `dt` 인수와 타이머를 결합하면 됩니다.

```ts
let timer: number = 10

function LoopSystem(dt: number) {
  timer -= dt
  if (timer <= 0) {
      timer = 10
      // 무엇인가를 수행합니다
    }
}

engine.addSystem(LoopSystem)
```

바로 사용할 수 있는 단축 함수도 있습니다 `setInterval` 과 `clearInterval` 이며, 백그라운드에서 시스템을 사용합니다. 특정 함수를 일정한 X 시간마다 실행하려는 의도라면 더 쉽고 짧은 버전을 사용할 수 있습니다.

```ts
import { timers } from '@dcl/sdk/ecs'

const intervalId = timers.setInterval(() => {
    console.log('이것을 10초마다 출력합니다')
}, 10000)
```

여기서 첫 번째 인수인 `callback`은 실행될 함수이며(이 경우, `console.log()`), 두 번째 인수인 `ms`(이 경우 10000)은 각 함수 실행 사이에 기다릴 밀리초입니다.

중지하려면 `setInterval` 함수, `clearInterval` 를 사용합니다.

```
timers.clearInterval(intervalId)
```

여기서 `intervalId` 는 `setInterval` 를 앞서 정의한 값에 대한 참조입니다.

더 복잡한 사용 사례에서는 여러 지연과 루프가 동적으로 생성될 수 있으므로, 각 엔티티에 대해 개별 타이머 값을 저장하는 사용자 정의 컴포넌트를 정의하는 것이 좋을 수 있습니다. 다음을 참조하세요 [사용자 정의 컴포넌트](/creator/content-creator-ko/sdk7/architecture/custom-components.md).

## 시스템 실행 순서

일부 경우에는 여러 시스템이 실행 중일 때, 씬이 어떤 시스템을 먼저 실행하는지가 중요할 수 있습니다.

예를 들어, *물리* 시스템이 씬의 엔티티 위치를 업데이트하고, 또 다른 *경계* 시스템이 어떤 엔티티도 씬 경계 밖에 위치하지 않도록 보장한다고 합시다. 이 경우 *경계* 시스템이 마지막에 실행되도록 해야 합니다. 그렇지 않으면 *물리* 시스템이 엔티티를 씬 경계 밖으로 이동시킬 수 있지만 *경계* 시스템은 다음 프레임에서 다시 실행될 때까지 이를 알지 못합니다.

엔진에 시스템을 추가할 때, 선택적인 `priority` 필드를 설정하여 시스템이 다른 시스템들과 비교해 언제 실행될지 결정합니다.

```ts
engine.addSystem(PhysicsSystem, 5)
engine.addSystem(BoundariesSystem, 1)
```

우선순위 숫자가 더 높은 시스템이 먼저 실행되므로, 우선순위가 *5* 인 시스템은 우선순위가 *1*.

인 시스템보다 먼저 실행됩니다. 명시적인 우선순위가 주어지지 않은 시스템의 기본 우선순위는 *100000*입니다. 숫자가 클수록 먼저 실행되므로, 이러한 시스템은 더 낮은 명시적 우선순위를 가진 시스템보다 먼저 실행됩니다.

두 시스템의 우선순위 숫자가 같다면, 둘 중 어느 것이 먼저 실행될지 확실하게 알 방법이 없습니다.

## 시스템 제거하기

시스템의 인스턴스는 엔진에 추가하거나 제거하여 켜거나 끌 수 있습니다.

시스템이 정의되어 있지만 엔진에 추가되지 않았다면, 그 함수는 엔진에 의해 호출되지 않습니다.

시스템을 제거하려면, 나중에 시스템을 참조할 수 있도록 먼저 엔진에 추가할 때 이름을 지정해야 합니다.

```ts
// 시스템 선언
function mySystem(dt: number){
  console.log("마지막 틱 이후 지연 시간: ", dt)
}

// 시스템 추가(우선순위와 이름 부여)
engine.addSystem(mySystem, 1, "DelaySystem")

// 시스템 제거
engine.removeSystem("DelaySystem")
```

다음에 전달하는 문자열 `engine.removeSystem()` 는 시스템을 추가할 때 지정한 이름과 일치해야 합니다.

시스템을 제거하는 또 다른 방법은 시스템의 함수 자체를 `engine.removeSystem()` 메서드에 전달하는 것입니다. 이렇게 하면 추가할 때 시스템에 이름을 지정할 필요가 없습니다.

```ts
// 시스템 선언
function mySystem(dt: number){
  console.log("마지막 틱 이후 지연 시간: ", dt)
}

// 시스템 추가
engine.addSystem(mySystem)

// 함수 자체를 전달하여 시스템 제거
engine.removeSystem(mySystem)
```

`engine.removeSystem()` 는 시스템을 추가할 때 지정한 이름 문자열 또는 시스템 함수 자체에 대한 참조 중 어느 것이든 허용합니다.

아래 메서드를 사용하면 시스템의 목적이 완료되었을 때 스스로 종료되도록 만들 수 있습니다.

```ts
   const mySystem = function(dt: number){
        time += dt
        if(time > 3){
		engine.removeSystem(mySystem)
        }
    }
    engine.addSystem(mySystem)
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.decentraland.org/creator/content-creator-ko/sdk7/architecture/systems.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
