[task] | two_turn [pass] | ⏳ 미판정 [title] | 아바타 애니메이션 제어하기 [question] | ActionSheet에서 특정 상태의 애니메이션을 수동으로 변경하거나 새로운 사용자 정의 상태를 추가하는 방법은 무엇인가요? [answer] | ActionSheet에서 특정 상태의 애니메이션을 변경하거나 새로운 사용자 정의 상태를 추가하는 방법은 다음과 같습니다. **1. 특정 상태의 애니메이션 변경** ActionSheet 테이블에서 해당 상태(Key)에 대응하는 값(Value)을 변경하면 됩니다. Value는 `MapleAvatarBodyActionState`를 나타내는 문자열(예: `stand`, `walk`, `attack`, `blink`, `fly`, `alert` 등)을 사용합니다. * **예시:** `IDLE` 상태일 때 기본값인 `stand` 대신 [followup] | IDLE 상태에 walk 애니메이션을 재생하도록 ActionSheet 값을 수정하고, 이를 적용하기 위해 StateComponent의 상태를 변경하는 구체적인 설정 및 코드 절차를 알려주세요. [followup_answer] | IDLE 상태에 walk 애니메이션을 재생하고 이를 적용하는 절차는 다음과 같습니다. ### 1. ActionSheet 값 수정 `AvatarStateAnimationComponent`의 **ActionSheet**에서 `IDLE` 상태(Key)에 해당하는 Value를 `walk`로 변경합니다. * **Key**: `IDLE` * **Value**: `walk` 이 수정으로 아바타의 상태가 `IDLE`로 변경되면 ActionSheet는 `walk` 값을 참조하여 `Walk` 애니메이션을 재생합니다. ### 2. Stat [scores] | [judge_reason] | [source_doc] | 이미 정의된, 혹은 새로 정의할 아바타의 상태가 바뀔 때마다 매번 이벤트를 보내서 애니메이션을 변경하는 것도 번거로울 수 있습니다. 이벤트를 보내지 않고도 아바타 상태가 바뀔 때마다 애니메이션도 함께 변경되면 매우 편리할 것입니다. [AvatarStateAnimationComponent](/apiReference/Components/AvatarStateAnimationComponent)의 **ActionSheet**에 아바타 상태와 연결될 애니메이션을 설정하고 [StateComponent](/apiReference/Component
[task] | two_turn [pass] | ⏳ 미판정 [title] | 아바타 애니메이션 제어하기 [question] | 메이플스토리 월드에서 아바타 애니메이션을 제어하는 3가지 방법의 장단점을 비교하여 어떤 상황에서 각각을 선택해야 하는지 알려줘 [answer] | 메이플스토리 월드에서 아바타 애니메이션을 제어하는 3가지 방법과 각각의 장단점, 그리고 적합한 상황은 다음과 같습니다. ### 1. ActionStateChangedEvent * **장점** * 몸통(Body)과 파츠(Face 등) 애니메이션을 각각 다르게 조절할 수 있습니다. * 재생 속도, 재생 타입, 애니메이션 시작/종료 프레임을 세밀하게 설정할 수 있습니다. * **단점** * 자주 사용하는 액션을 매번 이러한 방식으로 설정해야 하므로 번거롭습니다. * 무기에 따라 달라지는 [followup] | ActionSheet와 StateComponent를 사용할 때, 다른 상태 변경 조건으로 인해 애니메이션이 의도대로 출력되지 않는 예외 상황을 방지하는 코드는 어떻게 작성해야 하나? [followup_answer] | 제공된 참고 문서에는 `ActionSheet`와 `StateComponent`를 사용할 때 다른 상태 변경 조건으로 인해 애니메이션이 의도대로 출력되지 않는 예외 상황을 방지하는 구체적인 코드 작성 방법이나 관련 API에 대한 정보가 포함되어 있지 않습니다. 문서에서는 해당 방법의 단점으로 "다른 상태 변경 조건에 의해 상태가 자동으로 변경되면 원하는 애니메이션이 출력되지 않을 수 있습니다"라는 사실만 언급하고 있으며, 이를 방지하기 위한 코드 로직이나 대안은 설명하지 않습니다. [scores] | [judge_reason] | [source_doc] | 애니메이션 변경 과정을 정리하면 다음과 같습니다. 위에서 알아본 3가지 아바타 애니메이션 제어 방법의 장단점을 정리하면 아래 표와 같습니다. 각 방법은 장단점이 명확하므로 크리에이터의 의도에 맞는 방법을 적절히 선택하면 됩니다. | 아바타 애니메이션 제어 방법 | 장점 | 단점 | | :---: | --- | --- | | ActionStateChangedEvent | <ul> <li>몸통과 파츠 애니메이션을 각각 다르게 조절할 수 있습니다.</li> <li>재생 속도, 재생 타입, 애니메이션 시작/종료 프레임을 세밀하게 설
[task] | code [pass] | ⏳ 미판정 [title] | 아바타 애니메이션 제어하기 [question] | 아바타의 Face에 연결된 SpriteAnimPlayerStartFrameEvent를 처리하여 이벤트의 ReversePlaying 값이 true일 경우 log로 역재생 시작을 출력하는 스크립트를 작성해줘 [answer] | 아바타의 Face에 `SpriteAnimPlayerStartFrameEvent`를 연결하여, 이벤트의 `ReversePlaying` 값이 `true`일 때 로그를 출력하는 스크립트는 다음과 같습니다. 이벤트는 `ConnectEvent`를 통해 Face에 연결해야 하며, 핸들러에서 `event.ReversePlaying`을 확인합니다. ```lua Property: [None] Entity TargetFaceEntity = nil Method: [client only] void OnBeginPlay() { -- Face [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 애니메이션 변경 과정을 정리하면 다음과 같습니다. 위에서 알아본 3가지 아바타 애니메이션 제어 방법의 장단점을 정리하면 아래 표와 같습니다. 각 방법은 장단점이 명확하므로 크리에이터의 의도에 맞는 방법을 적절히 선택하면 됩니다. | 아바타 애니메이션 제어 방법 | 장점 | 단점 | | :---: | --- | --- | | ActionStateChangedEvent | <ul> <li>몸통과 파츠 애니메이션을 각각 다르게 조절할 수 있습니다.</li> <li>재생 속도, 재생 타입, 애니메이션 시작/종료 프레임을 세밀하게 설
[task] | single_turn [pass] | ⏳ 미판정 [title] | 아바타 애니메이션 제어하기 [question] | SpriteAnimPlayerStartFrameEvent와 SpriteAnimPlayerEndFrameEvent의 사용법이 ChangeFrameEvent와 동일한지 확인하고 싶어요. [answer] | 네, `SpriteAnimPlayerStartFrameEvent`와 `SpriteAnimPlayerEndFrameEvent`의 사용법은 `SpriteAnimPlayerChangeFrameEvent`와 동일합니다. 각 이벤트는 `event` 객체를 인수로 받아 처리하며, 이벤트 핸들러 내부에서 `self.Entity.AvatarRendererComponent:GetBodyEntity()`를 통해 아바타 본체 엔티티를 획득한 뒤 `body:SendEvent()`를 호출하여 다른 애니메이션으로 전환하거나 로직을 실행하는 방식으로 동작합 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 3. 아까 저장한 **handler**를 통해 **SpriteAnimPlayerChangeFrameEvent** 구독을 취소합니다. `ChangeFrameEvent` 함수를 수정해 봅시다. ```lua [client] void ChangeFrameEvent(any event) { if event.FrameIndex == 2 then local body = self.Entity.AvatarRendererComponent:GetBodyEntity()
[task] | single_turn [pass] | ⏳ 미판정 [title] | 아바타 애니메이션 제어하기 [question] | SpriteAnimPlayerChangeFrameEvent 구독을 취소할 때 handler 변수를 어떻게 관리해야 하는지 설명해 주세요. [answer] | `SpriteAnimPlayerChangeFrameEvent` 구독을 취소할 때 `handler` 변수는 이벤트 핸들러를 등록할 때 반환된 값을 저장해 두어야 하며, 구독 취소 시에는 그 저장된 값을 `DisconnectEvent` 메서드의 인자로 전달해야 합니다. 참고 문서의 예시에서는 `self.handler`라는 인스턴스 변수(또는 객체 멤버)에 핸들러를 저장하고, 이벤트가 발생하면 해당 변수를 참조하여 구독을 해제하는 방식을 사용하고 있습니다. 즉, **핸들러를 등록한 시점에 반환된 참조값을 잃지 않도록 변수에 보관**하는 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 3. 아까 저장한 **handler**를 통해 **SpriteAnimPlayerChangeFrameEvent** 구독을 취소합니다. `ChangeFrameEvent` 함수를 수정해 봅시다. ```lua [client] void ChangeFrameEvent(any event) { if event.FrameIndex == 2 then local body = self.Entity.AvatarRendererComponent:GetBodyEntity()
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 타이머 만들기 [question] | 카드가 모두 사라졌을 때 IsEmpty 메서드가 반환하는 값을 단계별 번호 목록으로 정리해 주세요. [answer] | 카드가 모두 사라졌을 때 `IsEmpty` 메서드가 반환하는 값은 **`true`**입니다. `IsEmpty` 메서드의 실행 단계는 다음과 같습니다. 1. `self.CardSpawnRoot.Children` 배열을 `cards` 변수에 저장합니다. 2. `cards` 배열의 크기(`#cards`)만큼 반복문을 돌립니다. 3. 반복문 내에서 각 카드(`cards[i]`)의 `Visible` 속성을 확인합니다. 4. 만약 `cards[i].Visible`이 `true`인 카드가 하나라도 있으면, 즉시 `false`를 반환하 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | [client only] void DeselectCard() { self.SelectedCard:CardOff() self.SelectedCard = nil } [client only] boolean IsEmpty () { local cards = self.CardSpawnRoot.Children for i = 1, #cards do if cards[i].Visible then return false end end
[task] | single_turn [pass] | ⏳ 미판정 [title] | 아바타 애니메이션 제어하기 [question] | GetBodyEntity() 메서드를 호출한 뒤 SendEvent로 ActionStateChangedEvent를 전송할 때의 주의사항은 무엇인가요? [answer] | `GetBodyEntity()` 메서드로 바디 엔티티를 가져온 후 `SendEvent`로 `ActionStateChangedEvent`를 전송할 때 주의해야 할 사항은 다음과 같습니다. **1. 프레임 인덱스 범위의 보정** `ActionStateChangedEvent` 생성 시 전달하는 시작/끝 프레임 인덱스(`startFrameIndex`, `endFrameIndex`)는 실제 클립의 프레임 범위 내에서만 유효하게 처리됩니다. 범위를 벗어나는 값은 자동으로 보정됩니다. * **음수 시작 프레임:** `startFrameIn [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 3. 아까 저장한 **handler**를 통해 **SpriteAnimPlayerChangeFrameEvent** 구독을 취소합니다. `ChangeFrameEvent` 함수를 수정해 봅시다. ```lua [client] void ChangeFrameEvent(any event) { if event.FrameIndex == 2 then local body = self.Entity.AvatarRendererComponent:GetBodyEntity()
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 타이머 만들기 [question] | 3문장 이내로, CreateCardEntities 메서드에서 카드 엔티티를 생성할 때 _SpawnService:SpawnByModelId 호출 인자와 생성 후 설정되는 컴포넌트 프로퍼티를 설명하세요. [answer] | `_SpawnService:SpawnByModelId`에는 카드 모델 ID, 인덱스 기반 이름, `Vector3(0,0,0)` 위치, 그리고 `self.CardSpawnRoot`를 부모로 전달합니다. 생성된 엔티티에는 `Card` 컴포넌트의 `SpriteRUID`와 `SpriteGUIRendererComponent`의 `ImageRUID`에 스프라이트 RUID가 설정됩니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ```lua Property: [None] Entity CardSpawnRoot = /ui/CardPairGameUI/UI_Board/UI_Blocks/UI_Block_Group [None] Card SelectedCard = nil [None] Entity TimeText = /ui/CardPairGameUI/UI_Board/Background/Title/Timer_Background/UI_Time/UI_Time_Text Method: [client only] void OnUpdate (number delta) { sel
[task] | code [pass] | ⏳ 미판정 [title] | 아바타 애니메이션 제어하기 [question] | 아바타의 Body에 연결된 SpriteAnimPlayerEndFrameEvent를 처리하여 이벤트의 FrameIndex가 10일 때 ActionSheet와 StateComponent를 활용해 아바타 상태를 'idle'로 변경하는 스크립트를 작성해줘 [answer] | 사용자가 요청한 기능은 `SpriteAnimPlayerEndFrameEvent`를 Body에 연결하여 처리하고, `FrameIndex`가 10일 때 `StateComponent`를 통해 아바타 상태를 `idle`로 변경하는 것입니다. 참고 문서에 따르면: 1. `SpriteAnimPlayerEndFrameEvent`는 스프라이트 애니메이션이 마지막 프레임을 재생할 때 발생합니다. 2. 이 이벤트는 `ConnectEvent`를 사용해 아바타의 `Body` 또는 `Face`에 연결해야 합니다. 3. `StateComponent`는 ` [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 애니메이션 변경 과정을 정리하면 다음과 같습니다. 위에서 알아본 3가지 아바타 애니메이션 제어 방법의 장단점을 정리하면 아래 표와 같습니다. 각 방법은 장단점이 명확하므로 크리에이터의 의도에 맞는 방법을 적절히 선택하면 됩니다. | 아바타 애니메이션 제어 방법 | 장점 | 단점 | | :---: | --- | --- | | ActionStateChangedEvent | <ul> <li>몸통과 파츠 애니메이션을 각각 다르게 조절할 수 있습니다.</li> <li>재생 속도, 재생 타입, 애니메이션 시작/종료 프레임을 세밀하게 설
[task] | two_turn [pass] | ⏳ 미판정 [title] | 타이머 만들기 [question] | 카드 짝 맞추기 게임에서 타이머 텍스트를 실시간으로 갱신하는 로직은 어떻게 구현되었나요? [answer] | 카드 짝 맞추기 게임에서 타이머 텍스트를 실시간으로 갱신하는 로직은 `OnUpdate` 생명주기 메서드를 활용하여 클라이언트 사이드에서 매 프레임마다 실행되는 방식으로 구현됩니다. 구체적인 구현 흐름은 다음과 같습니다. 1. **클라이언트 전용 실행**: `OnUpdate` 메서드에 `[client only]` 실행 공간 속성이 지정되어 있어, 이 로직은 서버가 아닌 클라이언트에서 처리됩니다. 이는 UI 갱신은 클라이언트에서 수행하는 것이 일반적이기 때문입니다. 2. **매 프레임 호출**: `OnUpdate(number d [followup] | `UpdateText` 메서드에서 `_CardPairGameLogic.RemainingTime` 값을 `math.floor`로 변환하여 `TimeText.TextComponent.Text`에 할당하는 구체적인 코드 구조와 실행 공간([client only])의 의미를 설명해 주세요. [followup_answer] | `UpdateText` 메서드의 코드 구조와 `[client only]` 실행 공간의 의미는 다음과 같습니다. ### 1. `UpdateText` 메서드의 코드 구조 `UpdateText` 메서드는 `_CardPairGameLogic` 서비스의 `RemainingTime` 값을 읽어와서 UI 텍스트에 표시하도록 하는 로직으로 구성됩니다. ```lua [client only] void UpdateText() { local remainingTime = _CardPairGameLogic.RemainingTime sel [scores] | [judge_reason] | [source_doc] | ```lua Property: [None] Entity CardSpawnRoot = /ui/CardPairGameUI/UI_Board/UI_Blocks/UI_Block_Group [None] Card SelectedCard = nil [None] Entity TimeText = /ui/CardPairGameUI/UI_Board/Background/Title/Timer_Background/UI_Time/UI_Time_Text Method: [client only] void OnUpdate (number delta) { sel
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 타이머 만들기 [question] | 코드만 출력하고 설명은 주석으로, UpdateText 메서드에서 _CardPairGameLogic.RemainingTime 값을 TimeText.TextComponent.Text 에 설정하는 로직을 구현하세요. [answer] | ```lua Property: [None] Entity TimeText = /ui/CardPairGameUI/UI_Board/Background/Title/Timer_Background/UI_Time/UI_Time_Text Method: [client only] void UpdateText() { -- _CardPairGameLogic 서비스의 RemainingTime 값을 가져옵니다. local remainingTime = _CardPairGameLogic.RemainingTime -- 가져온 시간 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ```lua Property: [None] Entity CardSpawnRoot = /ui/CardPairGameUI/UI_Board/UI_Blocks/UI_Block_Group [None] Card SelectedCard = nil [None] Entity TimeText = /ui/CardPairGameUI/UI_Board/Background/Title/Timer_Background/UI_Time/UI_Time_Text Method: [client only] void OnUpdate (number delta) { sel
[task] | single_turn [pass] | ⏳ 미판정 [title] | 타이머 만들기 [question] | OnUpdate 안에서 RemainingTime 값이 0 이하가 될 때 EndGame을 호출하려면 어떤 조건문을 사용해야 하나요? [answer] | `OnUpdate` 안에서 `RemainingTime`이 0초 이하가 되는지 확인하려면 `<=` (작거나 같다) 연산자를 사용합니다. 구체적인 조건문은 다음과 같습니다. ```lua if self.RemainingTime <= 0 then self:EndGame() end ``` 이 조건을 만족하면 `EndGame()` 메서드가 호출되어 게임이 종료되고 UI가 꺼지도록 처리할 수 있습니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 타이머를 완성했지만 이번에도 한 가지 추가하면 좋겠습니다 타이머가 0초가 된 이후에 수가 음수로 계속해서 감소하고 있습니다. 일반적으로 수가 줄어드는 모습을 보면, 0초가 되었을 때 게임이 종료될 거라 기대합니다. 그러나 지금은 0초 이후 수가 음수로 줄어들고 있습니다. 0초가 되면 ui가 꺼지게 만들어 봅시다. 1. **CardPairGameLogic** 로직의 `EndGame` 함수에 아래와 같이 작성합니다. ```lua [client only] void EndGame () {
[task] | code [pass] | ⏳ 미판정 [title] | 아바타 애니메이션 제어하기 [question] | `AvatarStateAnimationComponent`의 `ActionSheet`에서 `IDLE` 상태에 연결된 애니메이션을 `stand` 대신 `walk`로 수정하여, 아바타가 대기 상태일 때도 걷기 애니메이션이 재생되도록 하는 스크립트를 작성해줘. [answer] | `AvatarStateAnimationComponent`의 `ActionSheet`는 아바타의 상태(State)와 재생할 애니메이션(MapleAvatarBodyActionState)을 매핑하는 테이블입니다. `IDLE` 상태에 연결된 값을 `walk`로 변경하면, 아바타가 `IDLE` 상태가 되었을 때 자동으로 걷기 애니메이션이 재생됩니다. 아바타의 상태 변경은 `StateComponent`의 `ChangeState` 메서드를 통해 수행하며, `ActionSheet`는 컴포넌트 속성으로 직접 수정할 수 있습니다. ```lua P [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 이미 정의된, 혹은 새로 정의할 아바타의 상태가 바뀔 때마다 매번 이벤트를 보내서 애니메이션을 변경하는 것도 번거로울 수 있습니다. 이벤트를 보내지 않고도 아바타 상태가 바뀔 때마다 애니메이션도 함께 변경되면 매우 편리할 것입니다. [AvatarStateAnimationComponent](/apiReference/Components/AvatarStateAnimationComponent)의 **ActionSheet**에 아바타 상태와 연결될 애니메이션을 설정하고 [StateComponent](/apiReference/Component
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 타이머 만들기 [question] | OnUpdate 메서드에서 시간이 0 이하로 떨어질 때 호출되는 함수명을 1단어 이내로 답하세요. [answer] | EndGame [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | #### CardPairGameLogic 로직 ```lua Property: [None] Entity CardPairGameUI = /ui/CardPairGameUI [None] number PlayTime = 60 [None] number RemainingTime = 0 Method: [client only] void OnBeginPlay() { self:StartGame() } [client only] void StartGame() { self.CardPairGameUI.Enable = true
[task] | code [pass] | ⏳ 미판정 [title] | 아바타 애니메이션 제어하기 [question] | `StateComponent`로 아바타 상태를 `DEAD`로 변경하여 `AvatarStateAnimationComponent`의 `ActionSheet`를 통해 `dead` 애니메이션이 재생되도록 하는 스크립트를 작성해줘. [answer] | `StateComponent`의 `ChangeState` 메서드를 호출하여 아바타 상태를 `DEAD`로 변경하면, `AvatarStateAnimationComponent`의 기본 `ActionSheet` 설정에 따라 `dead` 애니메이션이 자동으로 재생됩니다. 아래는 키 입력 시 아바타를 `DEAD` 상태로 변경하는 스크립트 예시입니다. ```lua Property: [None] number KeyDownTime = 0 Event Handler: [service: InputService] HandleKeyDownEvent(K [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 이미 정의된, 혹은 새로 정의할 아바타의 상태가 바뀔 때마다 매번 이벤트를 보내서 애니메이션을 변경하는 것도 번거로울 수 있습니다. 이벤트를 보내지 않고도 아바타 상태가 바뀔 때마다 애니메이션도 함께 변경되면 매우 편리할 것입니다. [AvatarStateAnimationComponent](/apiReference/Components/AvatarStateAnimationComponent)의 **ActionSheet**에 아바타 상태와 연결될 애니메이션을 설정하고 [StateComponent](/apiReference/Component
[task] | single_turn [pass] | ⏳ 미판정 [title] | 타이머 만들기 [question] | CardPairGameLogic의 EndGame 함수에서 self.CardPairGameUI.Enable 값을 false로 설정하면 어떤 결과가 발생하나요? [answer] | `self.CardPairGameUI.Enable` 값을 `false`로 설정하면 해당 UI 객체가 비활성화됩니다. 즉, 카드 짝 맞추기 게임의 사용자 인터페이스가 화면에서 꺼지거나 비활성 상태가 되어 더 이상 보이거나 상호작용할 수 없게 됩니다. 이는 타이머가 0초 이하가 되었을 때 게임을 종료하고 UI를 닫기 위해 `EndGame` 함수에서 수행하는 동작입니다. ```lua [client only] void EndGame () { self.CardPairGameUI.Enable = false } ``` [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 타이머를 완성했지만 이번에도 한 가지 추가하면 좋겠습니다 타이머가 0초가 된 이후에 수가 음수로 계속해서 감소하고 있습니다. 일반적으로 수가 줄어드는 모습을 보면, 0초가 되었을 때 게임이 종료될 거라 기대합니다. 그러나 지금은 0초 이후 수가 음수로 줄어들고 있습니다. 0초가 되면 ui가 꺼지게 만들어 봅시다. 1. **CardPairGameLogic** 로직의 `EndGame` 함수에 아래와 같이 작성합니다. ```lua [client only] void EndGame () {
[task] | code [pass] | ⏳ 미판정 [title] | 아바타 애니메이션 제어하기 [question] | 아바타의 Body에 연결된 SpriteAnimPlayerEndFrameEvent를 처리하여 ActionStateChangedEvent를 활용해 재생 속도를 1.5배로 설정하고 재생 타입을 loop로 변경하는 스크립트를 작성해줘 [answer] | 아바타의 `Body`에 `SpriteAnimPlayerEndFrameEvent`를 연결하고, 해당 이벤트가 발생하면 `ActionStateChangedEvent`를 보내어 재생 속도를 1.5배로, 재생 타입을 Loop로 설정하는 스크립트는 다음과 같습니다. 참고로 `ActionStateChangedEvent`를 통해 재생 속도(`playSpeed`)와 재생 타입(`playType`)을 제어할 수 있습니다. 재생 타입은 `MapleAvatarActionPlayType` enum을 사용하며, Loop는 `MapleAvatarActio [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 애니메이션 변경 과정을 정리하면 다음과 같습니다. 위에서 알아본 3가지 아바타 애니메이션 제어 방법의 장단점을 정리하면 아래 표와 같습니다. 각 방법은 장단점이 명확하므로 크리에이터의 의도에 맞는 방법을 적절히 선택하면 됩니다. | 아바타 애니메이션 제어 방법 | 장점 | 단점 | | :---: | --- | --- | | ActionStateChangedEvent | <ul> <li>몸통과 파츠 애니메이션을 각각 다르게 조절할 수 있습니다.</li> <li>재생 속도, 재생 타입, 애니메이션 시작/종료 프레임을 세밀하게 설
[task] | single_turn [pass] | ⏳ 미판정 [title] | 타이머 만들기 [question] | CardPairGameLogic 스크립트에서 EndGame 함수를 client only로 선언해야 하는 이유가 무엇인가요? [answer] | `EndGame` 함수가 `[client only]`로 선언되어야 하는 이유는, 이 함수가 수행하는 작업이 **클라이언트(사용자의 화면)에만 적용**되어야 하는 UI 조작이기 때문입니다. 참고 문서에 제시된 `EndGame` 함수의 구현을 보면 다음과 같습니다. ```lua [client only] void EndGame () { self.CardPairGameUI.Enable = false } ``` 이 함수는 `self.CardPairGameUI`라는 UI 엔티티의 `Enable` 속성을 `false`로 변경하여 화면 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 타이머를 완성했지만 이번에도 한 가지 추가하면 좋겠습니다 타이머가 0초가 된 이후에 수가 음수로 계속해서 감소하고 있습니다. 일반적으로 수가 줄어드는 모습을 보면, 0초가 되었을 때 게임이 종료될 거라 기대합니다. 그러나 지금은 0초 이후 수가 음수로 줄어들고 있습니다. 0초가 되면 ui가 꺼지게 만들어 봅시다. 1. **CardPairGameLogic** 로직의 `EndGame` 함수에 아래와 같이 작성합니다. ```lua [client only] void EndGame () {
[task] | single_turn [pass] | ⏳ 미판정 [title] | 타이머 만들기 [question] | 타이머가 0이 되었을 때 게임이 종료되도록 하려면 CardPairGameUI의 어떤 프로퍼티를 조작해야 하나요? [answer] | 타이머가 0이 되었을 때 게임을 종료하려면 `CardPairGameUI`의 **`Enable`** 프로퍼티를 `false`로 설정해야 합니다. 이 프로퍼티를 조작하는 `EndGame` 메서드와 `OnUpdate`에서 호출하는 로직은 다음과 같습니다. ```lua Property: [None] number RemainingTime = 60 Entity CardPairGameUI = /ui/CardPairGameUI Method: [client only] void EndGame() { self.CardPairGameUI.E [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 타이머를 완성했지만 이번에도 한 가지 추가하면 좋겠습니다 타이머가 0초가 된 이후에 수가 음수로 계속해서 감소하고 있습니다. 일반적으로 수가 줄어드는 모습을 보면, 0초가 되었을 때 게임이 종료될 거라 기대합니다. 그러나 지금은 0초 이후 수가 음수로 줄어들고 있습니다. 0초가 되면 ui가 꺼지게 만들어 봅시다. 1. **CardPairGameLogic** 로직의 `EndGame` 함수에 아래와 같이 작성합니다. ```lua [client only] void EndGame () {
[task] | single_turn [pass] | ⏳ 미판정 [title] | 타이머 만들기 [question] | OnUpdate 함수에서 delta를 빼주는데 왜 매 프레임마다 1초가 정확히 줄어드는 것처럼 보이는지 원리가 궁금합니다. [answer] | `OnUpdate` 함수의 `delta` 파라미터는 **이전 프레임에서 현재 프레임까지 지난 시간(초 단위)** 을 의미합니다. 따라서 매 프레임마다 `delta`를 `RemainingTime`에서 빼는 것은 "매 프레임마다 1초를 뺀다"는 의미가 아니라, **경과 시간을 누적하여 차감**하는 것입니다. ### 원리 설명 1. **`delta`의 역할**: * 게임 엔진은 매 프레임마다 `OnUpdate`를 호출할 때, 이전 프레임과의 시간 차이인 `delta` 값을 전달합니다. * 예를 들어, 60FPS [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 타이머 만들기 게임을 시작하면 1초씩 감소하는 타이머를 만들어봅시다. 1. **CardPairGameLogic** 로직에 게임 제한 시간으로 사용할 **PlayTime** 프로퍼티와 시간을 줄이기 위해 사용할 **RemainingTime** 프로퍼티를 추가합니다. PlayTime 초깃값은 **60**으로 설정합니다. ```lua Property: [None] number PlayTime = 60 [None] number RemainingTime = 0 ``` > **
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 타이머 만들기 [question] | isvalid 함수의 인자 타입이 false일 때 실행되는 코드 블록의 내용을 5문장 이내로 요약하세요. [answer] | `isvalid(card) == false` 조건이 참일 때 실행되는 코드 블록은 단순히 함수를 종료시키는 `return` 문만 포함하고 있습니다. 따라서 이후의 카드 판(`cardboard`) 검증이나 카드 선택 로직은 실행되지 않습니다. 이는 유효하지 않은 카드 객체가 전달되었을 때 추가적인 오류를 방지하기 위한 안전장치 역할을 합니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | #### CardPairGameLogic 로직 ```lua Property: [None] Entity CardPairGameUI = /ui/CardPairGameUI [None] number PlayTime = 60 [None] number RemainingTime = 0 Method: [client only] void OnBeginPlay() { self:StartGame() } [client only] void StartGame() { self.CardPairGameUI.Enable = true
[task] | code [pass] | ⏳ 미판정 [title] | 아바타 애니메이션 제어하기 [question] | `StateComponent`와 `AvatarStateAnimationComponent`를 사용하여, `StateComponent`의 상태를 `IDLE`에서 `ATTACK`으로 변경했을 때 아바타가 `attack` 애니메이션을 재생하도록 하는 스크립트를 작성해줘. [answer] | `StateComponent`의 상태를 `IDLE`에서 `ATTACK`으로 변경하면, `AvatarStateAnimationComponent`의 기본 `ActionSheet` 설정에 따라 `ATTACK` 상태에 매칭된 `attack` 애니메이션이 자동으로 재생됩니다. 아바타의 상태를 변경하는 데는 `self.Entity.StateComponent:ChangeState("ATTACK")` 메서드를 사용합니다. 아래 예제는 특정 입력(예: Z키)이 감지되었을 때 아바타 상태를 `ATTACK`으로 변경하여 공격 애니메이션을 재생하는 스 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 이미 정의된, 혹은 새로 정의할 아바타의 상태가 바뀔 때마다 매번 이벤트를 보내서 애니메이션을 변경하는 것도 번거로울 수 있습니다. 이벤트를 보내지 않고도 아바타 상태가 바뀔 때마다 애니메이션도 함께 변경되면 매우 편리할 것입니다. [AvatarStateAnimationComponent](/apiReference/Components/AvatarStateAnimationComponent)의 **ActionSheet**에 아바타 상태와 연결될 애니메이션을 설정하고 [StateComponent](/apiReference/Component
[task] | two_turn [pass] | ⏳ 미판정 [title] | 아바타 애니메이션 제어하기 [question] | AvatarStateAnimationComponent의 ActionSheet가 아바타 상태 변경 시 애니메이션을 자동으로 제어하는 원리는 무엇이며, 기본 제공되는 상태와 애니메이션 매핑은 어떻게 되나요? [answer] | `AvatarStateAnimationComponent`의 `ActionSheet`는 아바타의 상태(`State`)와 재생할 몸통 애니메이션(`MapleAvatarBodyActionState`)을 연결해 주는 매핑 테이블 역할을 합니다. `StateComponent`를 통해 아바타의 상태를 변경하면, `ActionSheet`에서 해당 상태의 `Key`를 찾아 `Value`에 지정된 애니메이션을 자동으로 적용합니다. 이로 인해 개발자는 매번 애니메이션 변경 이벤트를 수동으로 보내지 않고, 상태만 변경하면 애니메이션이 따라오도록 구현할 [followup] | StateComponent를 이용해 IDLE 상태를 ATTACK으로 변경하는 mLua 스크립트 코드 예시를 보여주세요. [followup_answer] | `StateComponent`의 `ChangeState` 메서드를 사용하여 아바타의 상태를 `IDLE`에서 `ATTACK`으로 변경하는 mLua 스크립트 예시는 다음과 같습니다. ```lua Method: [client only] void OnBeginPlay() { -- 스크립트가 실행되면 즉시 IDLE 상태에서 ATTACK 상태로 변경 local stateComponent = self.Entity.StateComponent if stateComponent ~= nil then stateCom [scores] | [judge_reason] | [source_doc] | 이미 정의된, 혹은 새로 정의할 아바타의 상태가 바뀔 때마다 매번 이벤트를 보내서 애니메이션을 변경하는 것도 번거로울 수 있습니다. 이벤트를 보내지 않고도 아바타 상태가 바뀔 때마다 애니메이션도 함께 변경되면 매우 편리할 것입니다. [AvatarStateAnimationComponent](/apiReference/Components/AvatarStateAnimationComponent)의 **ActionSheet**에 아바타 상태와 연결될 애니메이션을 설정하고 [StateComponent](/apiReference/Component
[task] | single_turn [pass] | ⏳ 미판정 [title] | 타이머 만들기 [question] | CardBoard 컴포넌트에서 CardPairGameLogic의 RemainingTime 값을 가져오지 못해 에러가 발생했는데 어떤 참조 방식으로 접근해야 하나요? [answer] | CardBoard 컴포넌트에서 CardPairGameLogic의 RemainingTime 값을 가져오려면, 해당 로직 컴포넌트를 참조하는 Entity 타입의 프로퍼티를 CardBoard에 먼저 선언해야 합니다. 참고 문서의 예제에서는 `_CardPairGameLogic`이라는 식별자가 사용되었으나, 이는 특정 전역 서비스나 주입된 참조가 아닌, CardBoard 컴포넌트 내부에 CardPairGameLogic을 가리키는 Entity 프로퍼티가 있어야 정상적으로 동작하는 방식입니다. 따라서 CardBoard 스크립트에서 다음과 같이 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 타이머 만들기 게임을 시작하면 1초씩 감소하는 타이머를 만들어봅시다. 1. **CardPairGameLogic** 로직에 게임 제한 시간으로 사용할 **PlayTime** 프로퍼티와 시간을 줄이기 위해 사용할 **RemainingTime** 프로퍼티를 추가합니다. PlayTime 초깃값은 **60**으로 설정합니다. ```lua Property: [None] number PlayTime = 60 [None] number RemainingTime = 0 ``` > **
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 타이머 만들기 [question] | OnCardClicked 이벤트에서 카드가 쌍을 이루는 경우 실행되는 동작 순서를 1부터 3까지 번호 붙여 나열하세요. [answer] | 카드가 쌍을 이루는 경우(`oldCard:IsPairOf(newCard)`가 참인 경우) `OnCardClicked`에서 실행되는 동작 순서는 다음과 같습니다. 1. `cardboard:DeselectCard()`를 호출하여 현재 선택된 카드의 선택 상태를 해제합니다. 2. `oldCard:SetDestroyed()`와 `newCard:SetDestroyed()`를 호출하여 쌍을 이룬 두 카드를 파괴 상태로 변경합니다. 3. `cardboard:IsEmpty()`를 확인하여 카드 보드가 비어 있는지 체크하고, 비어있다면 `card [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | #### CardPairGameLogic 로직 ```lua Property: [None] Entity CardPairGameUI = /ui/CardPairGameUI [None] number PlayTime = 60 [None] number RemainingTime = 0 Method: [client only] void OnBeginPlay() { self:StartGame() } [client only] void StartGame() { self.CardPairGameUI.Enable = true
[task] | code [pass] | ⏳ 미판정 [title] | 아바타 애니메이션 제어하기 [question] | 아바타의 SpriteAnimPlayerChangeFrameEvent 이벤트를 구독하고, 특정 프레임이 재생될 때 다른 애니메이션으로 전환하는 클라이언트 스크립트를 작성해줘. [answer] | 아바타의 `SpriteAnimPlayerChangeFrameEvent` 이벤트를 구독하고, 특정 프레임(예: 2번째 프레임)에 도달했을 때 다른 애니메이션으로 전환하는 클라이언트 스크립트는 다음과 같습니다. ```lua Property: [None] any handler = nil Method: [client only] void OnBeginPlay() { local body = self.Entity.AvatarRendererComponent:GetBodyEntity() -- SpriteAnimPlayer [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 3. 아까 저장한 **handler**를 통해 **SpriteAnimPlayerChangeFrameEvent** 구독을 취소합니다. `ChangeFrameEvent` 함수를 수정해 봅시다. ```lua [client] void ChangeFrameEvent(any event) { if event.FrameIndex == 2 then local body = self.Entity.AvatarRendererComponent:GetBodyEntity()
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 타이머 만들기 [question] | 표로 정리해서, SetCards 메서드에서 deckTable 을 생성하고 셔플하며 cardTable 을 만드는 과정의 순서와 사용된 API 를 나열하세요. [answer] | `SetCards` 메서드에서 `deckTable` 생성, 셔플, `cardTable` 생성 과정은 아래 표와 같이 순서대로 진행됩니다. | 순서 | 처리 내용 | 사용된 API / 함수 | | :--- | :--- | :--- | | 1 | `CardSet` 데이터셋의 행 개수를 조회하여 덱의 카드 수(`deckCount`)를 구함 | `_DataService:GetRowCount("CardSet")` | | 2 | 1부터 `deckCount`까지 반복하며 각 행의 1열(`spriteRUID`)을 조회하여 `deckTable`에 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ```lua Property: [None] Entity CardSpawnRoot = /ui/CardPairGameUI/UI_Board/UI_Blocks/UI_Block_Group [None] Card SelectedCard = nil [None] Entity TimeText = /ui/CardPairGameUI/UI_Board/Background/Title/Timer_Background/UI_Time/UI_Time_Text Method: [client only] void OnUpdate (number delta) { sel
[task] | code [pass] | ⏳ 미판정 [title] | 아바타 애니메이션 제어하기 [question] | `StateComponent`를 통해 아바타의 상태를 `SIT`로 변경하고, `AvatarStateAnimationComponent`의 `ActionSheet`가 `sit` 값에 매칭되는 `MapleAvatarBodyActionState`의 `Sit` 애니메이션을 자동으로 재생하도록 하는 스크립트를 작성해줘. [answer] | `StateComponent`의 `ChangeState` 메서드를 사용하여 아바타의 상태를 `SIT`로 변경하면, `AvatarStateAnimationComponent`의 기본 `ActionSheet` 설정에 따라 자동으로 `Sit` 애니메이션이 재생됩니다. 별도의 애니메이션 트리거 코드는 필요하지 않으며, 상태 변경만 수행하면 됩니다. 아래는 아바타가 생성되거나 특정 조건(예: 키 입력)이 충족될 때 `SIT` 상태로 변경하는 예제 스크립트입니다. ```lua Property: [None] boolean IsSitting = [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 이미 정의된, 혹은 새로 정의할 아바타의 상태가 바뀔 때마다 매번 이벤트를 보내서 애니메이션을 변경하는 것도 번거로울 수 있습니다. 이벤트를 보내지 않고도 아바타 상태가 바뀔 때마다 애니메이션도 함께 변경되면 매우 편리할 것입니다. [AvatarStateAnimationComponent](/apiReference/Components/AvatarStateAnimationComponent)의 **ActionSheet**에 아바타 상태와 연결될 애니메이션을 설정하고 [StateComponent](/apiReference/Component
[task] | single_turn [pass] | ⏳ 미판정 [title] | 타이머 만들기 [question] | IsEmpty 함수에서 self.CardSpawnRoot.Children의 길이가 0일 때 for 루프가 실행되지 않고 바로 true를 반환하는 것이 의도된 동작인가요? [answer] | 네, 의도된 동작입니다. `IsEmpty` 함수는 `for i = 1, #cards` 구문을 사용하며, 여기서 `#cards`는 테이블의 길이를 반환합니다. 만약 `self.CardSpawnRoot.Children`의 길이가 0이라면, 조건 `i = 1`이 `i <= 0`을 만족하지 못하기 때문에 for 루프 본문이 한 번도 실행되지 않습니다. 루프를 거치지 않고 바로 다음 줄인 `return true`로 이동하므로, 자식이 하나도 없는 상태(즉, 카드가 모두 소거되었거나 스폰되지 않은 상태)를 "비어 있음"으로 판단하고 `tr [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | [client only] void DeselectCard() { self.SelectedCard:CardOff() self.SelectedCard = nil } [client only] boolean IsEmpty () { local cards = self.CardSpawnRoot.Children for i = 1, #cards do if cards[i].Visible then return false end end
[task] | two_turn [pass] | ⏳ 미판정 [title] | 메이플스토리 월드 루아 기초 [question] | 메이플스토리 월드에서 전역 변수 대신 프로퍼티를 권장하는 이유는 무엇이며, 지역 변수의 유효 범위는 어떻게 되나요? [answer] | 메이플스토리 월드에서는 전역 변수 사용 대신 프로퍼티를 권장합니다. 전역 변수는 어디에서나 접근 가능하지만, 관리가 복잡해질 수 있기 때문입니다. 전역 변수처럼 스크립트 전체에서 공유하고 싶은 값이 있다면 프로퍼티를 사용하는 것이 좋습니다. 지역 변수는 해당 변수가 정의된 범위(함수 또는 구문) 내에서만 유효합니다. 즉, 지역 변수는 정의된 함수 내부나 조건문 블록 안쪽에서만 사용할 수 있으며, 해당 범위의 실행이 끝나면 변수는 파괴됩니다. 예를 들어, 함수 내에서 정의한 지역 변수는 그 함수가 끝나면 더 이상 접근할 수 없으며, [followup] | OnBeginPlay() 함수 내에서 정의한 지역 변수 b가 조건문 종료 후 nil로 출력되는 예시를 통해, 함수 밖에서 참조했을 때 발생하는 구체적인 오류나 동작 방식을 설명해 주세요. [followup_answer] | 참고 문서에 따르면, 조건문 내에서 정의한 지역 변수 `b`는 해당 조건문 블록의 실행이 끝나면 파괴됩니다. 따라서 조건문 종료 후 같은 함수(`OnBeginPlay()`) 범위에서 `b`를 참조하면 `nil` 값이 반환됩니다. 문서 예제에서는 `log(b) -- nil`로 이를 확인합니다. 또한, 다른 함수(`testFunction()`)에서 `OnBeginPlay()` 내에서 정의했던 지역 변수 `a`를 참조한 경우에도 마찬가지로 `nil`이 반환됩니다. 문서에서는 `nil`이 반환된다고만 명시하고 있으며, `nil` 참조 [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 메이플스토리 월드에서는 **루아**라는 언어를 사용하여 스크립트를 작성합니다. 이번 시간에는 **루아**의 기본 사용법과 메이플스토리 월드에서 사용하는 예제를 함께 살펴보겠습니다. # 변수 변수란 데이터를 저장하는 메모리 공간, 값을 담아두는 그릇입니다. ## 변수의 범위 루아에서 사용하는 변수는 아래의 두 가지로 나뉩니다. | 변수 | 설명 | | --- | --- | | 전역(global) 변수 | 어느 곳이든 접근 가능한 변수로 아무 선언 없이 사용하면 전역 변수입니다. **단, 메이플스토리