조명 태그 퍼즐 튜토리얼의 이번 단계에서는 플레이어가 상호작용하는 버튼에 따라 특정 조합의 조명을 켜고 끄는 방법을 알아볼 것입니다.
조명 켜고 끄기
플레이어가 버튼과 상호작용할 때 켜거나 끌 특정 조합의 조명과 해당 버튼을 매핑해야 합니다. 이를 위해 Lights 배열 내 조명의 인덱스에 각 버튼을 매핑할 수 있습니다.
이 예시에서는 버튼과 조명 간에 다음 매핑을 사용합니다.
- 버튼 1은 인덱스 0, 3의 조명에 매핑됩니다.
- 버튼 2는 인덱스 0, 1, 2의 조명에 매핑됩니다.
- 버튼 3은 인덱스 0, 1의 조명에 매핑됩니다.
- 버튼 4는 인덱스 1의 조명에 매핑됩니다.
ButtonsToLights 배열로 이 매핑을 나타낼 수 있습니다. ButtonsToLights 의 각 엘리먼트는 조명들의 인덱스를 담은 배열입니다. ButtonsToLights 는 integer 배열의 배열이므로 ButtonsToLights 의 타입은 [][]int 입니다.
| 인덱스 | 0 | 1 | 2 | 3 |
| 엘리먼트 | array{0, 3} | array{0, 1, 2} | array{0, 1} | array{1} |
다음 단계를 따라 조명을 토글해 켜거나 끕니다.
- integer 배열의 배열인
ButtonsToLights를 생성하고 위 표에 설명된 대로 버튼과 조명 인덱스 간 매핑으로 초기화합니다.ButtonsToLights : [][]int = array{array{0, 3}, array{0, 1, 2}, array{0, 1}, array{1}}배열의 인덱스는 0에서 시작하며 엘리먼트 수에서 1을 뺀 값으로 늘어납니다.
ButtonsToLights의 첫 번째 엘리먼트의 인덱스는 0이고 마지막 엘리먼트의 인덱스는 3입니다. tagged_lights_puzzle클래스에 새 메서드ToggleLights()를 추가합니다. 이 메서드에ButtonsToLights배열의 엘리먼트와 같이 인덱스를 담은 integer 배열을 전달하면 해당 인덱스에 따라Lights배열의 조명을 켜거나 끄고LightsState에서 같은 인덱스에 있는 엘리먼트를 업데이트합니다.LightIndices : []int파라미터를ToggleLights()메서드에 추가하고for표현식을 사용해 각 인덱스를 출력 로그에 출력합니다.ToggleLights(LightIndices : []int) : void = for: LightIndex : LightIndices do: Logger.Print("Toggling light at {LightIndex}")OnBegin()에서ToggleLights()를 호출해 생성한 메서드를 테스트합니다. 테스트를 위해ButtonsToLights의 첫 엘리먼트를 사용합니다. 배열 인덱싱은 실패 가능 표현식이므로 실패 컨텍스트에서 배열에 액세스해야 합니다. 이 예시에서는 실패 컨텍스트로if표현식을 사용합니다.
OnBegin<override>()<suspends> : void = SetupPuzzleLights() # ToggleLights 메서드를 테스트하기 위해 ButtonsToLights의 첫 번째 엘리먼트를 사용합니다. if (LightIndices : []int = ButtonsToLights[0]): ToggleLights(LightIndices)LightIndex를 가져왔으니 해당 인덱스를 사용해Lights와LightsState배열에 액세스하고 설정 가능한 조명 장치의 레퍼런스와 현재 상태를 가져옵니다. ToggleLights는 조명이 켜져 있으면 끄고 조명이 꺼져 있으면 켭니다. 출력문을 수정해 토글 후 조명의 상태를 출력합니다.ToggleLights(LightIndices : []int) : void = for: LightIndex : LightIndices Light := Lights[LightIndex] IsLightOn := LightsState[LightIndex] do: Logger.Print("Turning light at {LightIndex} {if (IsLightOn?) then "Off" else "On"}")- 이제 설정 가능한 조명 장치의 스테이트를 인게임과
LightsState배열에서 모두 업데이트합니다.IsLightOn이false면 조명에TurnOn()을 호출하고IsLightOn이true면 조명에TurnOff()를 호출합니다. 코드 블록의 마지막 표현식이 코드 블록의 결과이므로 마지막 표현식이false또는true가 되도록 만들면 해당 값이NewLightState에 저장됩니다.ToggleLights(LightIndices : []int) : void = for: LightIndex : LightIndices Light := Lights[LightIndex] IsLightOn := LightsState[LightIndex] do: Logger.Print("Turning light at {LightIndex} {if (IsLightOn?) then "Off" else "On"}") NewLightState := if (IsLightOn?): Light.TurnOff() False else: Light.TurnOn() TrueLightIndex에 있는LightsState엘리먼트를NewLightState값으로 업데이트합니다. 배열 인덱싱은 실패 가능 표현식이므로LightsState를 설정하는 코드를 실패 컨텍스트로 감싸야 합니다. 이 예시에서 실패 컨텍스트는if표현식입니다. 상태가 업데이트되었음을 출력 로그에 출력합니다.ToggleLights(LightIndices : []int) : void = for: LightIndex : LightIndices IsLightOn := LightsState[LightIndex] Light := Lights[LightIndex] do: Logger.Print("Turning light at {LightIndex} {if (IsLightOn?) then "Off" else "On"}") NewLightState := if (IsLightOn?): Light.TurnOff() False else: Light.TurnOn() True if (set LightsState[LightIndex] = NewLightState): Logger.Print("Updated the state for light at {LightIndex}")
실패 컨텍스트를 사용할 때는 출력 로그에 로그를 출력하는 것이 좋습니다. 실패 컨텍스트 내에서 실패하는 표현식이 있는 경우, 실패 컨텍스트에서 변경된 모든 사항은 발생하지 않았던 것처럼 롤백됩니다. 출력 로그에 출력하면 인게임 스테이트에만 의존할 필요 없이 출력 로그의 텍스트를 통해 변경 여부를 재확인할 수 있습니다.
버튼 상호작용과 조명 토글 연결하기
이제 조명을 토글하는 방식을 정의했으니, 다음 단계는 버튼 누르기를 조명 켜기 또는 끄기와 연결하는 것입니다.
이를 위해 버튼의 InteractedWithEvent 에 등록합니다. 이벤트 등록에 대한 자세한 내용은 장치 상호작용 코딩하기을 참고하세요.
InteractedWithEvent 는 1개의 파라미터 InPlayer : agent 를 가지고 void 타입을 반환하는 이벤트 핸들러를 받습니다. 또한 사용할 이벤트 핸들러는 이벤트를 전달한 버튼이 어떤 조명들에 연결되어 있는지에 대한 정보를 가져야 하고, 조명 태그 퍼즐 장치 tagged_lights_puzzle 의 메서드 ToggleLights() 를 호출하기 위해 해당 장치의 레퍼런스도 가져야 합니다.
필요한 모든 정보를 하나의 커스텀 오브젝트에 묶으려면 조명의 인덱스와 등록할 함수를 담고 있는 새로운 클래스를 생성합니다. 이렇게 하면 각 버튼이 새로운 클래스로 표현되는 각자의 상태와 이벤트 핸들러를 가집니다.
다음 단계를 따라 이벤트 핸들링을 위한 커스텀 오브젝트를 생성하세요.
- 새 클래스
button_event_handler를 생성합니다. 클래스 정의에는 다음을 추가합니다.- integer 배열
Indices. tagged_lights_puzzle필드PuzzleDevice.ToggleLights()메서드를 호출하기 위한 Verse 장치의 레퍼런스입니다.- 파라미터
InPlayer : agent를 가지고void타입을 반환하는 메서드OnButtonPressed().PuzzleDevice의ToggleLights()를 호출하는 메서드입니다.button_event_handler := class(): # 이 버튼이 제어하는 조명들에 액세스하기 위해 사용하는 위치. Indices : []int # button_event_handler를 생성한 tagged_lights_puzzle. 해당 퍼즐의 함수를 호출하기 위해 필요합니다. PuzzleDevice : tagged_lights_puzzle OnButtonPressed(InPlayer : agent) : void = # 이 버튼이 제어하는 위치에 있는 조명들을 PuzzleDevice가 토글하도록 만듭니다. PuzzleDevice.ToggleLights(Indices)
- integer 배열
- 플레이어가 상호작용할 수 있는 버튼의 레퍼런스를 저장하기 위해
tagged_lights_puzzle클래스에 편집 가능한 배열 필드button_device를 생성합니다.@editable Buttons : []button_device = array{} - 이제
tagged_lights_puzzle클래스의OnBegin()을 업데이트해 각 버튼의button_event_handler인스턴스를 생성합니다. button_event_handler에는 다음 정보를 전달해야 합니다.- 버튼과 연결된 조명들의 인덱스.
for표현식이 제공하는ButtonIndex를 사용해ButtonsToLights배열에서 가져올 수 있습니다. 모든Buttons를 반복작업하는for표현식의 조건문에서 가져올 수 있습니다. 또한 이렇게 코드를 작성하면 유효하지 않은 데이터를 인덱싱하는 오류를 방지할 수 있습니다. 인덱싱이 실패하더라도 순회하는 과정에서 실패하는 반복작업은 건너뛰므로 프로그램이 여전히 유효하게 실행됩니다.ButtonsToLights의 엘리먼트 숫자와Buttons의 엘리먼트 숫자가 일치하지 않는 경우 이처럼 실패할 수 있습니다. - Verse 장치의 레퍼런스.
tagged_lights_puzzle의 인스턴스입니다. 클래스 정의 안에서 현재 오브젝트의 레퍼런스를 가져오려면Self를 사용합니다. - 이 데이터를 가지는
button_event_handler오브젝트를 생성하는 코드는 다음과 같습니다.OnBegin<override>()<suspends> : void = SetupPuzzleLights() for: ButtonIndex -> Button : 버튼 LightIndices := ButtonsToLights[ButtonIndex] do: button_event_handler{Indices := LightIndices, PuzzleDevice := Self}
- 버튼과 연결된 조명들의 인덱스.
- 이제 새로 생성한 핸들러의
OnButtonPressed()함수를 사용해 버튼 장치의InteractedWithEvent에 등록할 수 있습니다.OnButtonPressed()함수가 호출되면 조명들의 인덱스와, 플레이어가 상호작용한 버튼과 연관된tagged_lights_puzzle장치의 레퍼런스에 액세스할 수 있습니다.OnBegin<override>()<suspends> : void = SetupPuzzleLights() for: ButtonIndex -> Button : 버튼 LightIndices := ButtonsToLights[ButtonIndex] do: Button.InteractedWithEvent.Subscribe(button_event_handler{Indices := LightIndices, PuzzleDevice := Self}.OnButtonPressed) - Visual Studio Code에서 스크립트를 저장합니다.
- UEFN 툴바에서 Verse 스크립트 빌드(Build Verse Scripts) 를 클릭하여 새로 작성한 코드로 레벨의 Verse 장치를 업데이트합니다.
- 아웃라이너(Outliner) 에서 tagged_lights_puzzle 장치를 선택하고 디테일(Details) 패널을 엽니다.
- 디테일 패널에서
Buttons배열에 4개의 엘리먼트를 추가하고 각 엘리먼트에 버튼을 하나씩 할당합니다. 스크립트가 다른 프로퍼티를 채울 것이므로 따로 수정하지 않아도 됩니다. - UEFN 툴바에서 플레이(Play) 를 클릭하여 레벨을 플레이테스트합니다.
이제 레벨을 플레이테스트하면 버튼과 상호작용할 수 있으며, ButtonsLightsIndices 에 설정된 대로 각 버튼이 서로 다른 조합의 조명들을 켜거나 끕니다. GetCreativeObjectsWithTag() 는 특정한 순서를 보장하지 않으므로 스크립트 내 순서를 기준으로 토글되는 조명이 레벨에 표시되는 순서와 일치하지 않을 수 있습니다.

다음 단계
이 튜토리얼의 다음 단계에서는 플레이어가 퍼즐을 푸는 시점을 탐지해 아이템을 생성하고, 퍼즐과 더는 상호작용할 수 없도록 만드는 방법을 알아보겠습니다.