> 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)

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

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

```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)

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

프레임 처리에 더 오래 걸리면 해당 프레임의 그리기가 지연되고, `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)

이상적으로는 씬이 프레임을 떨어뜨리지 않도록 해야 합니다. 이는 플레이어 경험의 품질에 영향을 주기 때문입니다. 이는 플레이어 기기의 처리 성능에 의존하므로, 씬이 언제든 부드럽게 처리하도록 준비되어 있어야 할 가능성이 항상 있습니다.

그 `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` 는 백그라운드에서 시스템을 사용합니다. 따라서 일정한 시간마다 특정 함수를 실행하려는 목적이라면 더 쉽고 짧은 버전을 사용할 수 있습니다.

```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).

## 시스템 실행 순서

어떤 경우에는 여러 시스템이 실행 중일 때, 씬에서 어떤 시스템이 먼저 실행되는지 신경 써야 할 수 있습니다.

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

엔진에 시스템을 추가할 때, 선택적인 `우선순위` 필드를 설정해 다른 시스템과의 실행 순서를 결정하세요.

```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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
