지정자와 어트리뷰트에 대해 알아보고 Verse 코드에 추가 시맨틱과 행동을 적용하는 방법을 학습합니다.
Verse에서 지정자는 시맨틱과 관련된 행동을 설명하고, 식별자 및 특정 키워드에 추가할 수 있습니다. 지정자 구문은 홑화살괄호(< 및 >) 사이에 키워드를 넣어 사용합니다. 예를 들어 IsPuzzleSolved()<decides><transacts>:void에서 'decides'와 'transacts'가 지정자입니다.
Verse에서 어트리뷰트는 Verse 시맨틱을 설명하는 지정자와 달리 Verse 언어 외부에서 사용되는 행동을 설명합니다. 어트리뷰트 구문은 키워드 앞에 at 기호(@)를 사용합니다(예: @editable).
다음 섹션에서는 Verse에서 제공되는 모든 지정자와 어트리뷰트를 설명하고 각각 언제 사용할 수 있는지를 살펴봅니다.
이펙트 지정자
Verse에서 이펙트란 함수가 표시할 수 있는 행동 카테고리를 가리킵니다. 이펙트 지정자는 다음과 같이 추가할 수 있습니다.
함수 정의에서 () 뒤: name()<specifier>:type = codeblock.
class 키워드: name := class<specifier>(){}.
지정자
설명
예시
no_rollback
익스클루시브 이펙트가 지정되지 않은 경우의 디폴트 이펙트입니다. no_rollback 이펙트는 함수가 수행한 액션을 실행 취소할 수 없기 때문에 실패 컨텍스트에서 함수를 사용할 수 없음을 나타냅니다. 이 이펙트는 수동으로 지정할 수 없습니다.
name():type = codeblock
transacts
<transacts> 이펙트는 <allocates>, <reads>, <writes>가 합쳐진 결과물입니다. <transacts> 이펙트는 이러한 지정자와 결합할 수 있습니다.
name()<transacts> : type = codeblock
# transacts가 reads를 암시하므로 허용되지 않음
name()<transacts><reads>: type = codeblock
# transacts가 writes를 암시하므로 허용되지 않음
name()<transacts><writes>: type = codeblock
allocates
이 이펙트는 함수가 메모리의 오브젝트를 인스턴스화할 수 있음을 나타냅니다. 메서드가 실패 컨텍스트에서 호출되어 실패하는 경우 이펙트가 실행 취소됩니다.
name()<allocates> : type = codeblock
reads
이 이펙트가 포함된 메서드는 변경 가능 상태에서 읽을 수 있습니다.
name()<reads> : type = codeblock
writes
이 이펙트가 포함된 메서드는 변경 가능 상태에 쓸 수 있습니다. 메서드가 실패 컨텍스트에서 호출되어 실패하는 경우 이펙트가 실행 취소됩니다.
name()<writes> : type = codeblock
computes
<computes> 메서드는 지정된 모든 입력에 대해 동일한 출력을 영원히 반환하도록 보장합니다. <computes> 메서드는 <transacts><reads>, <writes> 또는 <allocates>일 수 없습니다.
name()<computes>
converges
이 이펙트는 관련 함수의 실행으로 인해 부작용이 발생하지 않을 뿐만 아니라 해당 함수가 완료된다는 것을 보장합니다. 즉, 무한히 재귀하지 않습니다. 이 이펙트는 native 지정자를 갖춘 함수에서만 나타날 수 있지만 컴파일러에서는 확인되지 않습니다. 글로벌 변수 값 또는 클래스 필드의 기본값을 제공하는 코드는 이 이펙트를 가져야 합니다.
name()<converges> : type = codeblock
decides
이 이펙트는 함수가 실패할 수 있으며 해당 함수 호출은 실패 가능 표현식임을 나타냅니다. <decides> 함수가 실패할 수 있으므로 <suspends> 이펙트와 상호 배타적입니다. <decides> 이펙트와 <transacts> 또는 <computes> 이펙트의 결합에 유용할 수 있습니다. 이를 통해 함수에 실패가 있는 경우 (액션을 수행하지 않은 것처럼) 이 함수에서 수행하는 액션의 롤백이 가능합니다.
# 허용됨
name()<decides><transacts> : type = codeblock
# 허용됨
name()<decides><computes> : type = codeblock
# decides와 suspends가 상호 배타적이므로 허용되지 않음
name()<decides><suspends> : type = codeblock
suspends
이 이펙트는 함수가 비동기임을 나타냅니다. 함수 바디에 대한 비동기화 컨텍스트를 생성합니다. <decides> 이펙트와 상호 배타적입니다.
name()<suspends> : type = codeblock
# decides와 suspends가 상호 배타적이므로 허용되지 않음
name()<decides><suspends> : type = codeblock
모든 경우, 특정 이펙트가 있는 함수를 호출하려면 호출자 역시 해당 이펙트가 있어야 합니다.
액세스 지정자
액세스 지정자는 멤버와 상호작용할 수 있는 요소와 그 방식을 정의합니다. 다음에 액세스 지정자를 적용할 수 있습니다.
멤버의 식별자: name<specifier> : type = value
멤버의 키워드 var: var<specifier> name : type = value
변수에 대한 식별자와 var 키워드 모두에 액세스 지정자를 두어 변수에 대한 읽기 및 쓰기 액세스 권한을 누가 가지고 있는지 구분할 수 있습니다. 예를 들어 다음 MyInteger 변수의 식별자에 public 지정자가 있어 누구나 값을 읽을 수 있지만 var 키워드에 protected 지정자가 있어 현재 클래스와 서브타입만 변수에 쓸 수 있습니다.
Verse
var<protected> MyInteger<public>:int = 2
지정자
설명
사용
예시
public
식별자를 어디서든 액세스할 수 있습니다.
이 지정자는 다음에서 사용할 수 있습니다.
모듈
클래스
Interface
struct
열거형
메서드
data
Verse
name<public> : type = value
protected
식별자를 현재 클래스 및 모든 서브타입에서만 액세스할 수 있습니다.
이 지정자는 다음에서 사용할 수 있습니다.
클래스
Interface
struct
클래스 내 함수
열거형
모듈이 아닌 메서드
data
Verse
name<protected> : type = value
private
식별자를 현재 직접 감싸는 스코프(모듈, 클래스, 구조체 등)에서만 액세스할 수 있습니다.
이 지정자는 다음에서 사용할 수 있습니다.
클래스
Interface
struct
클래스 내 함수
열거형
모듈이 아닌 메서드
data
Verse
name<private> : type = value
internal
식별자를 현재 직접 감싸는 모듈에서만 액세스할 수 있습니다. 이것이 디폴트 액세스 레벨입니다.
이 지정자는 다음에서 사용할 수 있습니다.
모듈
클래스
Interface
struct
열거형
메서드
data
Verse
name<internal> : type = value
scoped
식별자는 현재 스코프 및 감싸는 스코프에서만 액세스할 수 있습니다. Assets.digest.Verse 파일에 표시되는 Verse에 노출시키는 모든 에셋에는 <scoped> 지정자가 있습니다.
이 지정자는 다음에서 사용할 수 있습니다.
모듈
클래스
Interface
함수
struct
열거형
모듈이 아닌 메서드
data
Verse
# Enclosing scope for ModuleB and ModuleC.
ModuleA<public> := module:
ModuleB<public> := module:
# Internal to ModuleB.
class_b1 := class{}
# Allows access from anywhere inside ModuleA.
class_b2<scoped{ModuleA}> := class {}
클래스 지정자
클래스 지정자는 클래스의 서브클래스 생성 가능 여부와 같은 클래스의 특정 특성 또는 해당 멤버를 정의합니다.
지정자
설명
예시
abstract
클래스 또는 클래스 메서드에 abstract 지정자가 있는 경우 클래스의 인스턴스를 생성할 수 없습니다. 추상 클래스는 일부 구현을 갖춘 수퍼클래스 또는 일반적인 인터페이스로 사용됩니다. 수퍼클래스의 인스턴스를 갖추는 것이 적절하지 않지만 유사한 클래스 간에 프로퍼티 및 행동을 복제하고 싶지 않은 경우에 유용합니다.
이 타입은 동적으로 형변환이 가능함을 나타냅니다. <castable> 지정자에는 사용에 관한 역호환성 제한이 있습니다. 클래스 또는 인터페이스가 퍼블리싱되면 <castable> 어트리뷰트는 추가 또는 제거를 할 수 없습니다. 추가 또는 제거하면 안전하지 않은 형변환 행동이 발생할 수 있기에 허용되지 않습니다.
castable_subtype 타입은 subtype과 매우 유사하지만 사용하는 타입에 <castable> 표시가 있어야 합니다. 이를 통해 동적 형변환이 사용되는 위치의 코드 안전성이 증가합니다.
클래스에 concrete 지정자가 있는 경우 빈 아키타입을 사용하여 클래스의 인스턴스를 생성할 수 있습니다. 즉, 해당 클래스의 모든 필드가 디폴트값을 가져야 합니다. concrete 클래스의 모든 서브클래스는 묵시적으로 concrete입니다. concrete 클래스가 추상 클래스를 상속할 수 있으려면 두 클래스 모두 동일한 모듈에서 정의되어 있어야 합니다.
Verse
cat := class<concrete>():
# field must be initialized because the class is concrete
Name : string = "Cat"
unique
Verse의 고유 클래스에는 각 인스턴스의 고유 ID가 할당됩니다. 따라서 동일한 고유 클래스의 두 인스턴스에 동일한 필드 값이 있어도 서로 다른 인스턴스이기에 같지 않습니다. 이렇게 하면 ID를 비교함으로써 고유 클래스의 인스턴스가 동등한지 비교할 수 있습니다. unique 지정자가 없는 클래스에는 이러한 ID가 없으므로 필드 값에 기반해서만 동등한지 비교할 수 있습니다. 즉, 고유 클래스는 `=` 및 <> 연산으로 비교되며, comparable 타입의 서브타입입니다.
Verse
unique_class := class<unique>:
Field : int
Main()<decides> : void =
X := unique_class{Field := 1}
X = X # X is equal to itself
Y := unique_class{Field := 1}
X <> Y # X and Y are unique and therefore not equal
final
final 지정자는 클래스 및 클래스 멤버에만 사용할 수 있으며, 다음과 같은 제한 사항이 있습니다.
클래스에 final 지정자가 있는 경우 클래스의 서브클래스를 생성할 수 없습니다.
필드에 final 지정자가 있는 경우 서브클래스에서 필드를 오버라이드 할 수 없습니다.
메서드에 final 지정자가 있는 경우 서브클래스의 메서드를 오버라이드 할 수 없습니다.
Verse
cat := class<final>():
final_super
final_super 지정자는 클래스 정의에만 적용할 수 있으며 클래스 정의가 부모 클래스 또는 인터페이스에서 파생되어야 합니다. 해당 클래스는 항상 부모로부터 직접 파생되므로 이 지정자는 이 클래스 정의의 이 버전과 향후 퍼블리싱되는 모든 버전에 대해 향후 호환성 제약을 야기합니다.
씬 그래프 엔티티당 인스턴스 수를 정확히 0 또는 1개로 제한하기 위해 씬 그래프에서 component의 직계 서브타입에 사용해야 합니다. 이 제한은 이러한 타입의 서브타입에도 확대됩니다.
Verse
component := class {}
my_final_class := class<final_super>(component) {}
# Not allowed since my_final_class has the final_super specifier.
my_subclass_type := class(my_final_class) {}
퍼시스턴스 지정자
커스텀 타입(예: 클래스)에 <persistable> 지정자가 있다면, 모듈 스코프 weak_map 변수에서 사용할 수 있고 값이 게임 세션 간에 유지된다는 의미입니다. Verse의 퍼시스턴스에 대한 자세한 내용은 Verse에서 퍼시스턴스 데이터 사용하기를 확인하세요.
퍼시스턴스 지정자는 다음 타입에 사용할 수 있습니다. 자세한 내용은 각 링크에서 확인하세요.
열린 열거형에 값을 추가하거나, 순서를 변경하거나, <closed> 열거형으로 변경할 수 있습니다.
열린 열거형은 열거형 값의 수가 향후 증가할 것으로 예상되는 경우에 사용하는 것이 가장 좋습니다. 예를 들어 무기 유형의 열거형이 여기에 해당합니다.
Verse
# Enums are <closed> by default so you must explicitly define the enum as an open enum with the <open> specifier
my_enum := enum<open>{Value1, Value2, Value3}
닫힘(Closed)
현재 열거형에만 적용되는 지정자입니다.
열거형은 기본적으로 닫혀 있습니다.
닫힌 열거형은 요일과 같이 동일하게 유지될 것으로 예상되는 값에 사용하는 것이 가장 좋습니다.
Verse
# Enums are <closed> by default so the specifier is not required.
my_enum := enum{Value1, Value2, Value3}
# You can also explicitly define the enum as closed by adding the <closed> specifier
my_enum := enum<closed>{Value1, Value2, Value3}
구현 지정자
코드를 작성할 때는 구현 지정자를 사용할 수 없지만, UEFN API에서는 확인할 수 있습니다.
지정자
설명
예시
native
엘리먼트의 정의 세부 사항이 C++로 구현되었다는 것을 나타냅니다. native 지정자가 있는 Verse 정의는 C++ 정의를 자동으로 생성합니다. Verse 개발자가 사용하여 구현을 채울 수 있는 C++ 정의를 자동으로 생성합니다. 이 지정자는 다음에서 사용할 수 있습니다.
인스턴스 메서드가 C++로 구현된 native이며, 다른 C++ 코드에서 호출할 수 있다는 것을 나타냅니다. 이 지정자는 인스턴스 메서드에서 사용됩니다. 이 지정자는 서브클래스로 전파되지 않기 때문에 해당 지정자가 있는 메서드를 오버라이드할 때 정의에 추가하지 않아도 됩니다.
에디터에서 텍스트 박스로 표시되는 editable 스트링. editable 텍스트 박스는 현재 툴팁 또는 카테고리를 지원하지 않습니다.
자세한 내용은 장치 프로퍼티 커스터마이징을 참고하세요.
Verse
# An editable string that displays as a text box in the editor.
# Editable text boxes currently do not support tooltips or categories.
@editable_text_box:
# Whether this text can span multiple lines.
MultiLine := true
# The maximum amount of characters this text block can display.
MaxLength := 32
MessageBox:string = "This is a short message!"
editable_slider
float 타입을 사용하는 editable 슬라이더. 에디터에서 슬라이더를 드래그하여 값을 늘리거나 줄일 수 있습니다.
자세한 내용은 장치 프로퍼티 커스터마이징을 참고하세요.
Verse
# An editable slider that uses the float type. You can drag the slider in the editor to increase
# or decrease the value.
@editable_slider(float):
# The categories this editable belongs to.
Categories := array{FloatsCategory}
# The tool tip for this editable.
ToolTip := SliderTip
# The minimum value of each component. You cannot set an editable value for this number lower
editable_number
최소 및 최대가 포함된 editable 숫자.
자세한 내용은 장치 프로퍼티 커스터마이징을 참고하세요.
Verse
# An editable number with minimum and maximum
@editable_number(int):
# The tool tip for this editable.
ToolTip := EditableIntTip
# The category this editable belongs to.
Categories := array{IntsCategory}
# The minimum value of each component. You cannot set an editable value for this number lower
editable_vector_slider
editable 벡터 슬라이더. 드래그하여 각 벡터 컴포넌트의 값을 변경할 수 있습니다.
자세한 내용은 장치 프로퍼티 커스터마이징을 참고하세요.
Verse
# An editable vector slider. You can drag to change the values of each of the vector components.
@editable_vector_slider(float):
# The tool tip for this editable.
ToolTip := VectorSliderTip
# The categories this editable belongs to.
Categories := array{FloatsCategory}
# Shows the option to preserve the ratio between vector values in the editor.
ShowPreserveRatio := true
editable_vector_number
vector2, vector2i 또는 vector3과 같은 editable 벡터 번호입니다.
자세한 내용은 장치 프로퍼티 커스터마이징을 참고하세요.
Verse
# An editable vector number, which can be a vector2, vector2i, or vector3.
@editable_vector_number(float):
# The categories this editable belongs to.
Categories := array{FloatsCategory}
# The tool tip for this editable.
ToolTip := VectorFloatTip
# Shows the option to preserve the ratio between vector values in the editor.
editable_container
editable 값 컨테이너입니다. 현재는 배열만 지원합니다.
자세한 내용은 장치 프로퍼티 커스터마이징을 참고하세요.
Verse
An editable container of values. Currently, this only supports arrays.
@editable_container:
# The category this editable belongs to.
Categories := array{IntsCategory}
# The tool tip for this editable.
ToolTip := IntArrayTip
# Whether dragging elements to reorder this container is allowed.