베리언트 매니저 구성 스크립팅

에디터 스크립팅을 사용하여 모든 씬 베리언트와 함께 '베리언트 매니저'를 구성하는 방법을 살펴봅니다.

Choose your operating system:

Windows

macOS

Linux

베리언트 매니저 개요를 읽어봤다면 '베리언트 매니저(Variant Manager)'에 포함된 베리언트 및 베리언트 세트에 대한 정보에 액세스하고 베리언트를 동적으로 전환하기 위해 '베리언트 매니저'가 런타임에서 사용할 수 있는 블루프린트 API를 제공한다는 것을 알고 있을 것입니다. 하지만 '베리언트 매니저'는 에디터에서 실행되는 스크립트에 대해서만 사용 가능한 별도의 API도 노출합니다. 이 에디터 전용 API를 사용하여 새로운 레벨 베리언트 세트(Level Variant Sets) 에셋 및 액터를 생성하고 씬 베리언트를 통해 이를 프로그래밍 방식으로 구성할 수 있습니다.

이는 다른 애플리케이션에서 이미 작성한 베리언트 또는 세팅을 반영하기 위해 언리얼 엔진 프로젝트에서 씬 베리언트를 자동으로 구성해야 하는 경우 특히 유용합니다. 이러한 씬 베리언트에 대한 데이터를 언리얼 에디터 스크립팅 시스템이 읽을 수 있는 포맷으로 익스포트할 수 있는 경우, 이 페이지에 설명된 방식을 사용하여 동일한 씬 베리언트를 언리얼 엔진에서 구성할 수 있습니다. 이렇게 하면 '베리언트 매니저' UI에서 동일한 베리언트를 수동으로 리빌딩하는 데 드는 시간과 노력을 절약할 수 있습니다. 또한 오류가 줄어들고 반복 사용할 수 있게 됩니다.

'베리언트 매니저'의 구성을 스크립트로 작성하는 데는 블루프린트 또는 Python을 사용할 수 있습니다.

구현 방법 선택

블루프린트

Python

씬 베리언트를 구성하는 데 있어 가장 중요한 노드는 블루프린트 팔레트의 베리언트 매니저 카테고리에 있습니다. 이 카테고리에 있는 노드를 사용하면 새로운 레벨 베리언트 세트 에셋을 생성하고, 해당 에셋에 베리언트 세트와 베리언트를 추가하고, 액터를 베리언트에 바인딩하고, 해당 액터를 위한 프로퍼티 값을 캡처할 수 있습니다.

현재는 베리언트 활성화 시 함수 호출하기에서 설명되어 있는 것과 같이 함수 호출자(Function Caller) 를 사용하여 바인딩된 액터를 구성하는 데 에디터 스크립팅 API를 사용할 수 없습니다. 스크립팅 API는 바인딩된 액터에 프로퍼티 값을 캡처하고 설정하는 데만 사용할 수 있습니다.

아래 표에는 가장 많이 사용되는 노드가 설명되어 있습니다.

Create Level Variant Sets Asset

Create Level Variant Sets Asset

'콘텐츠 브라우저'에서 이름과 경로가 지정된 새로운 빈 레벨 베리언트 세트/Game/

Add Variant Set

Add Variant Set

지정된 베리언트를 지정된 베리언트 세트의 자손으로 추가합니다. 이는 '베리언트 매니저' UI에서 + 베리언트 세트(+ Variant Set) 버튼을 클릭하는 것과 같습니다. 새로운 베리언트 세트를 처음부터 새로 생성하는 방법에 대한 지침은 아래 팁을 참고하세요.

Add Actor Binding

Add Actor Binding

지정된 베리언트에 지정된 액터를 추가합니다. 이는 월드 아웃라이너(World Outliner) 에서 액터를 드래그하여 '베리언트 매니저' UI에서 베리언트에 드롭하는 것과 같습니다.

Get Capturable Properties

Get Capturable Properties

'베리언트 매니저'가 지정된 액터에 대해 제어할 수 있는 모든 프로퍼티의 이름을 반환합니다. Capture Property 노드를 호출할 때 이러한 값 중에서 어떤 값이든 사용할 수 있습니다.

Capture Property

Capture Property

지정된 베리언트에서 액터에 지정한 프로퍼티 값을 저장합니다.

Add Variant

Add Variant

지정된 베리언트 세트의 자손으로 지정된 베리언트를 추가합니다.

레벨 베리언트 세트 에셋에 추가하기 위해 새 베리언트 세트를 생성해야 하거나, 베리언트 세트에 추가하기 위해 새 베리언트를 생성해야 하는 경우 Construct Object from Class 노드를 사용할 수 있습니다. 첫 번째 입력에서, 생성해야 하는 클래스에 따라 VariantSet 또는 Variant 클래스를 지정합니다. 두 번째 Outer 입력에서, 구성하려는 레벨 베리언트 세트 에셋을 항상 사용합니다. 마지막으로, 출력 값을 Add 노드에 전달합니다. 예를 들어 이 스니펫은 새 베리언트 세트를 생성하여 이를 에셋에 추가한 다음 새 베리언트를 생성하고 이를 해당 베리언트 세트에 추가합니다.

블루프린트 예시

에디터 유틸리티 위젯의 다음 그래프는 '베리언트 매니저'를 프로그래밍 방식으로 구성하는 방법을 보여줍니다. 먼저, 작업할 새 액터를 생성합니다. 그런 다음 새 레벨 베리언트 세트 에셋을 생성하고 새 베리언트 세트로 이를 구성합니다. 마지막으로, 해당 베리언트 세트에 베리언트 두 개를 추가합니다. 하나는 액터가 표시되는 곳이고, 다른 하나는 액터가 숨겨지는 곳입니다.

이 예시를 실행한 후에는 레벨의 원점에 새 큐브가 생성된 것을 확인할 수 있습니다. 또한 베리언트를 전환하여 큐브를 표시하거나 숨길 수 있는 새 레벨 베리언트 세트 에셋이 생성됩니다.

씬 베리언트를 구성하는 데 있어 가장 중요한 클래스는 `unreal.VariantManagerLibrary`입니다. 이 클래스를 사용하면 새로운 **레벨 베리언트 세트** 에셋을 생성하고, 해당 에셋에 베리언트 세트와 베리언트를 추가하고, 액터를 베리언트에 바인딩하고, 해당 액터에 대한 프로퍼티 값을 캡처할 수 있습니다.

현재는 베리언트 활성화 시 함수 호출하기에서 설명되어 있는 것과 같이 함수 호출자(Function Caller) 를 사용하여 바인딩된 액터를 구성하는 데 에디터 스크립팅 API를 사용할 수 없습니다. 스크립팅 API는 바인딩된 액터에 프로퍼티 값을 캡처하고 설정하는 데만 사용할 수 있습니다.

아래 표에는 가장 많이 사용되는 함수가 설명되어 있습니다. 이러한 함수와 함께 `VariantManagerLibrary`에서 제공하는 기타 함수에 대한 자세한 내용은 Python API 레퍼런스를 참고하세요.

unreal.VariantManagerLibrary.create_level_variant_sets_asset(asset_name, asset_path) '콘텐츠 브라우저'에서 이름과 경로가 지정된 새로운 빈 레벨 베리언트 세트 에셋을 생성합니다.

unreal.VariantManagerLibrary.add_variant_set(level_variant_sets, variant_set) 레벨 베리언트 세트 에셋에 지정한 베리언트 세트를 추가합니다. 이는 '베리언트 매니저' UI에서 + 베리언트 세트(+ Variant Set) 버튼을 클릭하는 것과 같습니다.

unreal.VariantManagerLibrary.add_actor_binding(variant, actor) 지정된 베리언트에 지정된 액터를 추가합니다. 이는 월드 아웃라이너(World Outliner) 에서 액터를 드래그하여 '베리언트 매니저' UI의 베리언트에 드롭하는 것과 같습니다.

unreal.VariantManagerLibrary.get_capturable_properties(actor_or_class) '베리언트 매니저'가 지정된 액터에 대해 제어할 수 있는 모든 프로퍼티의 이름을 반환합니다. capture_property() 함수를 호출할 때 이러한 값 중에서 어떤 값이든 사용할 수 있습니다.

unreal.VariantManagerLibrary.capture_property(variant, actor, property_path) 지정된 베리언트에서 액터에 지정한 프로퍼티 값을 저장합니다.

unreal.VariantManagerLibrary.add_variant(variant_set, variant) 지정된 베리언트 세트의 자손으로 지정된 베리언트를 추가합니다.

Python 예시

다음 예시는 새로운 레벨 베리언트 세트 에셋을 처음부터 새로 구성하는 방법을 보여줍니다.

이 예시에서는 먼저 큐브와 구체로 이루어진 씬을 구성한 다음 '베리언트 매니저'를 구성하여 이러한 액터의 위치와 비저빌리티를 변경합니다.

import math
import unreal

# 스크립트의 이 섹션은 10개의 부모 액터가 있는 기본적인 시작 씬을 구성합니다.
# 각 부모 액터는 씬 계층구조에서 구체와 큐브가 각각 1개인 총 2개의 자손 액터를 가지고 있습니다.
sphere_mesh = unreal.EditorAssetLibrary.load_asset('/Engine/BasicShapes/Sphere')
cube_mesh = unreal.EditorAssetLibrary.load_asset('/Engine/BasicShapes/Cube')

parent_actors = []
sphere_actors = []
cube_actors = []
for i in range(0,10):
    parent_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(unreal.StaticMeshActor, unreal.Vector(0, 0, 0), unreal.Rotator(0, 0, 0))
    parent_actor.root_component.set_editor_property("mobility", unreal.ComponentMobility.MOVABLE)
    parent_actor.set_actor_label("actor_" + str(i).zfill(3))
    sphere_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(unreal.StaticMeshActor, unreal.Vector(0, 0, 0), unreal.Rotator(0, 0, 0))
    sphere_actor.static_mesh_component.set_static_mesh(sphere_mesh)
    sphere_actor.root_component.set_editor_property("mobility", unreal.ComponentMobility.MOVABLE)
    sphere_actor.attach_to_actor(parent_actor, unreal.Name(), unreal.AttachmentRule.KEEP_WORLD, unreal.AttachmentRule.KEEP_WORLD, unreal.AttachmentRule.KEEP_WORLD, False)
    sphere_actor.set_actor_label("sphere_" + str(i).zfill(3))
    cube_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(unreal.StaticMeshActor, unreal.Vector(0, 0, 0), unreal.Rotator(0, 0, 0))
    cube_actor.static_mesh_component.set_static_mesh(cube_mesh)
    cube_actor.root_component.set_editor_property("mobility", unreal.ComponentMobility.MOVABLE)
    cube_actor.attach_to_component(parent_actor.root_component, unreal.Name(), unreal.AttachmentRule.KEEP_WORLD, unreal.AttachmentRule.KEEP_WORLD, unreal.AttachmentRule.KEEP_WORLD, False)
    cube_actor.set_actor_label("cube_" + str(i).zfill(3))
    parent_actors.append(parent_actor)
    sphere_actors.append(sphere_actor)
    cube_actors.append(cube_actor)

# '콘텐츠 브라우저(Content Browser)'에서 이름과 경로가 지정된 레벨 베리언트 세트 에셋을 생성합니다.
# 경로는 항상 /Game/으로 시작해야 합니다.
lvs = unreal.VariantManagerLibrary.create_level_variant_sets_asset('ProceduralVariants', '/Game')

# 새로운 베리언트 세트를 생성하여 위치 베리언트를 관리합니다.
position_variant_set = unreal.VariantSet()
position_variant_set.set_display_text('Position')
# 레벨 베리언트 세트 에셋에 빈 베리언트 세트를 추가합니다.
unreal.VariantManagerLibrary.add_variant_set(lvs, position_variant_set)

# 이 섹션은 부모 액터를 일렬로 위치시키는 새로운 베리언트를 구성합니다.
# 먼저, 베리언트를 생성합니다.
position_variant_0 = unreal.Variant()
position_variant_0.set_display_text('Line')
# 각 부모 액터의 경우...
for i, actor in enumerate(parent_actors):
    # 액터를 X 축을 따라 일렬로 위치시킵니다.
    actor.set_actor_location_and_rotation(unreal.Vector(i*200, 0, 0), unreal.Rotator(0, 0, 0), False, True)
    # 액터를 베리언트에 바인딩합니다.
    unreal.VariantManagerLibrary.add_actor_binding(position_variant_0, actor)
    # 액터의 스태틱 메시 컴포넌트의 상대적 위치 및 회전 프로퍼티를 캡처하여 현재 값에 저장합니다.
    unreal.VariantManagerLibrary.capture_property(position_variant_0, actor, 'Static Mesh Component / Relative Location')
    unreal.VariantManagerLibrary.capture_property(position_variant_0, actor, 'Static Mesh Component / Relative Rotation')
# 이제 베리언트가 구성되었으므로 이를 베리언트 세트에 추가합니다.
unreal.VariantManagerLibrary.add_variant(position_variant_set, position_variant_0)

# 이 섹션에서는 동일한 프로세스를 따라 두 번째 베리언트를 구성하지만,
# 이번에는 부모 액터를 일렬이 아닌 원형으로 배치합니다.
position_variant_1 = unreal.Variant()
position_variant_1.set_display_text('Circle')
for i, actor in enumerate(parent_actors):
    actor.set_actor_location_and_rotation(unreal.Vector(1000*math.cos(i*360.0/(len(parent_actors)-1)), 1000*math.sin(i*360.0/(len(parent_actors)-1)), 0), unreal.Rotator(0, 0, 0), False, True)
    unreal.VariantManagerLibrary.add_actor_binding(position_variant_1, actor)
    unreal.VariantManagerLibrary.capture_property(position_variant_1, actor, 'Static Mesh Component / Relative Location')
    unreal.VariantManagerLibrary.capture_property(position_variant_1, actor, 'Static Mesh Component / Relative Rotation')
unreal.VariantManagerLibrary.add_variant(position_variant_set, position_variant_1)

# 다양한 모양을 가진 여러 액터를 표시하고 숨기는 것을 관리하기 위해 새로운 베리언트 세트를 생성합니다.
shape_variant_set = unreal.VariantSet()
shape_variant_set.set_display_text('Shape')
unreal.VariantManagerLibrary.add_variant_set(lvs, shape_variant_set)

# 두 개의 베리언트를 생성합니다. 하나는 구체를 표시하고 큐브를 숨기며,
#다른 하나는 큐브를 표시하고 구체를 숨깁니다.
shape_variant_0 = unreal.Variant()
shape_variant_0.set_display_text('Sphere')
shape_variant_1 = unreal.Variant()
shape_variant_1.set_display_text('Cube')
# 각 구체 액터를 두 개의 베리언트에 바인딩하여 액터의 스태틱 메시 컴포넌트의 시각적 프로퍼티를 캡처합니다.
for i, actor in enumerate(sphere_actors):
    actor.root_component.set_visibility(True)
    unreal.VariantManagerLibrary.add_actor_binding(shape_variant_0, actor)
    unreal.VariantManagerLibrary.capture_property(shape_variant_0, actor, 'Static Mesh Component / Visible')
    actor.root_component.set_visibility(False)
    unreal.VariantManagerLibrary.add_actor_binding(shape_variant_1, actor)
    unreal.VariantManagerLibrary.capture_property(shape_variant_1, actor, 'Static Mesh Component / Visible')
# 각 구체 액터를 두 개의 베리언트에 바인딩하여 액터의 스태틱 메시 컴포넌트의 시각적 프로퍼티를 캡처합니다.
# 하지만 위 구체 액터에서 반대 비저빌리티 값을 저장합니다.
for i, actor in enumerate(cube_actors):
    actor.root_component.set_visibility(False)
    unreal.VariantManagerLibrary.add_actor_binding(shape_variant_0, actor)
    unreal.VariantManagerLibrary.capture_property(shape_variant_0, actor, 'Static Mesh Component / Visible')
    actor.root_component.set_visibility(True)
    unreal.VariantManagerLibrary.add_actor_binding(shape_variant_1, actor)
    unreal.VariantManagerLibrary.capture_property(shape_variant_1, actor, 'Static Mesh Component / Visible')
# 마지막으로, 베리언트 세트에 두 개의 베리언트를 추가합니다.
unreal.VariantManagerLibrary.add_variant(shape_variant_set, shape_variant_0)
unreal.VariantManagerLibrary.add_variant(shape_variant_set, shape_variant_1)

# 런타임에 블루프린트에서 사용할 수 있도록 현재 레벨에 레벨 베리언트 세트 에셋을 추가합니다.
lvs_actor = unreal.VariantManagerLibrary.create_level_variant_sets_actor(lvs)

결과는 다음과 같습니다. 각각 두 개의 베리언트가 있는 두 개의 베리언트 세트입니다.

  • 첫 번째 베리언트 세트는 3D 공간에 있는 액터의 위치를 전환하여 액터를 일렬 또는 원형으로 정렬합니다.

  • 두 번째 베리언트 세트는 큐브 메시와 구체 메시 간에 액터를 교체합니다.

언리얼 엔진의 이전 버전을 위해 작성된 페이지입니다. 현재 언리얼 엔진 5 버전을 위해 업데이트되지 않았습니다.