Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

현재 씬 변경하기

문제 X에 Y 전략 또는 Z 전략으로 접근해야 하는지 궁금한 적이 있습니까? 이 기사에서는 이러한 딜레마와 관련된 다양한 주제를 다룹니다.

노드 추가 및 속성 변경: 어느 것이 먼저입니까?

런타임에 스크립트에서 노드를 초기화하는 경우 노드의 이름이나 위치와 같은 속성을 변경해야 할 수도 있습니다. 일반적인 딜레마는 언제 해당 값을 변경해야 하는가입니다.

씬 트리에 추가하기 전에 노드의 값을 변경하는 것이 가장 좋습니다. 일부 속성의 설정자에는 다른 해당 값을 업데이트하는 코드가 있으며 해당 코드는 느릴 수 있습니다! 대부분의 경우 이 코드는 게임 성능에 영향을 미치지 않지만, 절차적 생성과 같은 사용량이 많은 경우에는 게임이 크롤링될 수 있습니다.

이러한 이유로 일반적으로 씬 트리에 추가하기 전에 노드의 초기 값을 설정하는 것이 가장 좋습니다. 전역 위치 설정과 같이 씬 트리에 추가되기 전에 값을 설정할 수 없는 몇 가지 예외가 있습니다.

불러오기 대 미리 불러오기

GDScript에는 전역 preload 메소드가 있습니다. "로딩" 작업을 전면적으로 로드하고 성능에 민감한 코드 도중에 리소스를 로드하지 않도록 가능한 한 빨리 리소스를 로드합니다.

그에 상응하는 load 메서드는 load 문에 도달할 때만 리소스를 로드합니다. 즉, 민감한 프로세스 도중에 발생하면 속도 저하를 일으킬 수 있는 리소스를 제자리에 로드합니다. load() 함수는 모든 스크립트 언어에 액세스할 수 있는 :ref:`ResourceLoader.load(path) <class_ResourceLoader_method_load>`의 별칭이기도 합니다.

그렇다면 로드와 비교하여 정확히 언제 사전 로드가 발생하며 언제 둘 중 하나를 사용해야 합니까? 예를 살펴보겠습니다:

# my_buildings.gd
extends Node

# Note how constant scripts/scenes have a different naming scheme than
# their property variants.

# This value is a constant, so it spawns when the Script object loads.
# The script is preloading the value. The advantage here is that the editor
# can offer autocompletion since it must be a static path.
const BuildingScn = preload("res://building.tscn")

# 1. The script preloads the value, so it will load as a dependency
#    of the 'my_buildings.gd' script file. But, because this is a
#    property rather than a constant, the object won't copy the preloaded
#    PackedScene resource into the property until the script instantiates
#    with .new().
#
# 2. The preloaded value is inaccessible from the Script object alone. As
#    such, preloading the value here actually does not provide any benefit.
#
# 3. Because the user exports the value, if this script stored on
#    a node in a scene file, the scene instantiation code will overwrite the
#    preloaded initial value anyway (wasting it). It's usually better to
#    provide `null`, empty, or otherwise invalid default values for exports.
#
# 4. Instantiating the script on its own with .new() triggers
#    `load("office.tscn")`, ignoring any value set through the export.
@export var a_building : PackedScene = preload("office.tscn")

# Uh oh! This results in an error!
# One must assign constant values to constants. Because `load` performs a
# runtime lookup by its very nature, one cannot use it to initialize a
# constant.
const OfficeScn = load("res://office.tscn")

# Successfully loads and only when one instantiates the script! Yay!
var office_scn = load("res://office.tscn")

사전 로드를 사용하면 스크립트가 스크립트를 로드하는 순간 모든 로드를 처리할 수 있습니다. 미리 로드하는 것은 유용하지만 사용하고 싶지 않은 경우도 있습니다. 사용할 항목을 결정할 때 고려해야 할 몇 가지 사항은 다음과 같습니다.

  1. 스크립트가 로드될 시기를 결정할 수 없는 경우 리소스(특히 씬 또는 스크립트)를 미리 로드하면 예상하지 못한 추가 로드가 발생할 수 있습니다. 이로 인해 원래 스크립트의 로드 작업 외에 의도하지 않은 가변 길이 로드 시간이 발생할 수 있습니다.

  2. 다른 값이 값을 대체할 수 있는 경우(예: 씬의 내보낸 초기화) 값을 미리 로드하는 것은 의미가 없습니다. 항상 스크립트를 자체적으로 생성하려는 경우 이 점은 중요한 요소가 아닙니다.

  3. 다른 클래스 리소스(스크립트 또는 씬)만 '가져오기'하려는 경우 미리 로드된 상수를 사용하는 것이 종종 최선의 조치입니다. 그러나 예외적인 경우에는 다음을 수행하지 않을 수도 있습니다.

    1. '가져온' 클래스가 변경되기 쉬운 경우에는 대신 @export 또는 ``load()``를 사용하여 초기화된 속성이어야 합니다(아마도 나중에 초기화되지도 않았을 것입니다).

    2. 스크립트에 매우 많은 종속성이 필요하고 너무 많은 메모리를 소비하고 싶지 않은 경우 상황 변화에 따라 런타임에 다양한 종속성을 로드 및 언로드할 수 있습니다. 리소스를 상수로 미리 로드하는 경우 이러한 리소스를 언로드하는 유일한 방법은 전체 스크립트를 언로드하는 것입니다. 대신 속성으로 로드되는 경우 이러한 속성을 ``null``로 설정하고 리소스에 대한 모든 참조를 제거할 수 있습니다(RefCounted 확장 유형으로 인해 리소스가 메모리에서 자체적으로 삭제됩니다).

대규모 레벨: 정적 vs. 동적

대규모 레벨을 생성하는 경우 어떤 상황이 가장 적합합니까? 레벨을 하나의 정적 공간으로 만드는 것이 더 낫습니까? 아니면 레벨을 조각으로 로드하고 필요에 따라 세계의 콘텐츠를 이동하는 것이 더 낫습니까?

글쎄요, 간단한 대답은 "성능이 필요할 때"입니다. 두 가지 옵션과 관련된 딜레마는 오래된 프로그래밍 선택 중 하나입니다. 속도보다 메모리를 최적화합니까, 아니면 그 반대입니까?

순진한 대답은 모든 것을 한 번에 로드하는 정적 레벨을 사용하는 것입니다. 그러나 프로젝트에 따라 이는 많은 양의 메모리를 소비할 수 있습니다. 사용자의 RAM을 낭비하면 프로그램이 느리게 실행되거나 컴퓨터가 동시에 수행하려고 하는 다른 모든 작업이 완전히 중단됩니다.

무슨 일이 있어도 큰 장면을 작은 장면으로 나누어야 합니다(자산 재사용을 돕기 위해). 그런 다음 개발자는 리소스 생성/로드 및 삭제/언로드를 관리하는 노드와 노드를 실시간으로 설계할 수 있습니다. 크고 다양한 환경이나 절차적으로 생성된 요소가 있는 게임에서는 메모리 낭비를 피하기 위해 이러한 전략을 구현하는 경우가 많습니다.

반면에 동적 시스템을 코딩하는 것은 더 복잡합니다. 더 많이 프로그래밍된 논리를 사용하므로 오류와 버그가 발생할 가능성이 있습니다. 주의하지 않으면 애플리케이션의 기술적 부채를 부풀리는 시스템을 개발할 수 있습니다.

따라서 최선의 선택은 다음과 같습니다.

  1. 소규모 게임에는 정적 레벨을 사용하십시오.

  2. 중형/대형 게임에 대한 시간/리소스가 있는 경우 코드로 노드 및 리소스를 관리할 수 있는 라이브러리 또는 플러그인을 만듭니다. 유용성과 안정성을 향상시키기 위해 시간이 지남에 따라 개선되면 프로젝트 전반에 걸쳐 신뢰할 수 있는 도구로 발전할 수 있습니다.

  3. 코딩 기술은 있지만 코드를 다듬는 데 필요한 시간이나 리소스가 없기 때문에 중대형 게임에는 동적 논리를 사용합니다(게임은 완료되어야 함). 나중에 코드를 플러그인으로 아웃소싱하기 위해 잠재적으로 리팩터링할 수 있습니다.

런타임에 장면을 바꿀 수 있는 다양한 방법의 예는 "수동으로 장면 변경" 문서를 참조하세요.