[task] | single_turn [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | 서버의 OnBeginPlay에서 클라이언트로 정보를 보내려고 했는데 안 들어오는데, 왜 그런 건가요? [answer] | 서버의 `OnBeginPlay()`에서 클라이언트로 정보를 보내지 않는 이유는 서버와 클라이언트의 시작 시점 차이 때문입니다. 메이플스토리 월드에서는 기본적으로 서버가 먼저 생성되고, 이후에 월드가 생성됩니다. 각 클라이언트는 서버에 접속하는 시점에 서버로부터 정보를 받아 클라이언트를 구성합니다. 서버가 생성된 시점에 `OnBeginPlay()`가 호출되지만, 그 시점에는 아직 클라이언트가 접속되어 있지 않아서 `OnBeginPlay()`에서 클라이언트로 전달하는 정보를 받을 수 없습니다. 클라이언트가 접속할 때 서버로부터 정보 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 함수의 실행 공간을 설정하면 별도의 과정 없이 쉽게 클라이언트와 서버가 통신할 수 있습니다. 자동으로 처리하기 때문에 편리하지만, 몇몇 경우는 제약이 있어 크리에이터가 원하는 대로 동작하지 않을 수 있습니다. 일부는 로그가 나오지만, 어떤 것은 동작하지 않는 이유를 확인할 수 없을 수도 있습니다. 이번 가이드에서는 실행 제어를 사용할 수 없는 경우를 알아봅시다. # 매개 변수가 지원하지 않는 종류 함수에는 여러 종류의 매개 변수(parameter)가 있습니다. 이 매개 변수는 다른 공간에서 실행 제어를 사용
[task] | two_turn [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | 멀티 플레이 환경에서 내 클라이언트에만 존재하는 Local 개념이 필요한 주요 항목은 무엇이며, 왜 별도로 처리해야 하는가? [answer] | 멀티 플레이 환경에서 내 클라이언트에만 존재하는 Local 개념이 필요한 주요 항목은 다음과 같습니다. 1. **Localized Entity** 2. **입력 처리** 3. **내 캐릭터 처리** 이 항목들이 별도로 처리되어야 하는 이유는 **UIEntity, 입력, 내 캐릭터**가 모두 **나의 클라이언트에만 존재하는 개념**이기 때문입니다. 특히 '내 캐릭터'는 클라이언트에서 처리하기 위해 `_UserService.LocalPlayer`를 지원합니다. 만약 이 Local 개념을 무시하고 코드를 작성하면, 멀티 플레이 환 [followup] | `SkillComponent`에서 키보드 입력을 처리할 때, 다른 플레이어의 입력까지 반영되지 않도록 `_UserService.LocalPlayer`를 사용하여 내 Entity인지 확인하는 로직을 어떻게 수정해야 하는가? [followup_answer] | `SkillComponent`에서 키보드 입력을 처리할 때, 다른 플레이어의 입력까지 반영되지 않도록 하려면 이벤트 핸들러 내에서 `self.Entity`가 `_UserService.LocalPlayer`와 동일한지 확인하여 내 클라이언트의 로컬 플레이어가 아닐 경우 함수를 즉시 종료하도록 로직을 추가해야 합니다. 기존에 단순히 키 입력만 확인하던 로직은 다음과 같이 수정합니다. ```lua Event Handler: [service : InputService] HandleKeyDownEvent (KeyDownEvent even [scores] | [judge_reason] | [source_doc] | 메이플스토리 월드에서는 기본적으로 멀티 플레이를 손쉽게 구현할 수 있습니다. 대부분의 기능을 멀티 플레이 기반으로 처리하는 것을 권장합니다. 하지만 일부 항목은 별도의 Local 개념이 필요합니다. 주요 항목은 다음과 같습니다. * Localized Entity * 입력 처리 * 내 캐릭터 처리 **UIEntity**와 **입력** 그리고 **내 캐릭터** 모두 **나의 클라이언트에만 존재하는 개념**이라 별도로 처리해야 합니다. 특히 **내 캐릭터**는 클라이언트에서 처리하기 위해 `_UserService.LocalPlay
[task] | single_turn [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | 매개변수에 any 타입을 쓰면 'Failed to convert argument' 오류가 나는 이유가 뭔가요? [answer] | mLua에서 다른 실행 공간(예: 서버에서 클라이언트로, 또는 그 반대로)으로 실행 제어 함수를 호출할 때, 매개 변수는 전송 가능한 **약속된 형태**로 변환된 후 전달되어야 합니다. `number`나 `string`과 같은 기본 타입은 이러한 약속된 전송 형태가 정의되어 있어 정상적으로 전송됩니다. 반면, `any` 타입은 어떤 타입과도 호환되도록 설계된 것이지만, 구체적으로 어떤 데이터 구조나 형태를 가지고 있는지 명확하지 않아 전송 가능한 약속된 형태로 변환할 수 없습니다. 따라서 `any` 타입의 매개 변수를 사용하여 다 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 함수의 실행 공간을 설정하면 별도의 과정 없이 쉽게 클라이언트와 서버가 통신할 수 있습니다. 자동으로 처리하기 때문에 편리하지만, 몇몇 경우는 제약이 있어 크리에이터가 원하는 대로 동작하지 않을 수 있습니다. 일부는 로그가 나오지만, 어떤 것은 동작하지 않는 이유를 확인할 수 없을 수도 있습니다. 이번 가이드에서는 실행 제어를 사용할 수 없는 경우를 알아봅시다. # 매개 변수가 지원하지 않는 종류 함수에는 여러 종류의 매개 변수(parameter)가 있습니다. 이 매개 변수는 다른 공간에서 실행 제어를 사용
[task] | single_turn [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | OnUpdate()에서 wait()를 쓰면 기대했던 지연 후 실행이 아닌, 매 프레임마다 처음부터 다시 실행되는 현상이 발생하는 원리가 무엇인가요? [answer] | `OnUpdate()`에서 `wait()`를 사용했을 때 기대했던 지연 후 실행이 아니라 매 프레임마다 처음부터 다시 실행되는 현상은 `OnUpdate()`의 생명주기 특성과 `wait()`의 동작 방식이 충돌하기 때문에 발생합니다. **1. OnUpdate()의 매 프레임 재실행 특성** `OnUpdate()`는 논리적으로 수행이 끝날 당시 해당 부분을 종료시키고, 다음 프레임에서는 새로운 `OnUpdate()` 인스턴스가 생성되어 처음부터 다시 실행됩니다. 즉, `OnUpdate()`는 매 프레임마다 완전히 새로운 루프를 돌기 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 그럼 `OnUpdate()`에서 사용할 경우를 살펴보겠습니다. `OnUpdate()` 문에서 아래 코드는 어떻게 동작할까요? ```lua log("TEST") -- No.1 wait(1) log("TEST2") -- No.2 ``` * 기대 동작: TEST - 1초 대기 - TEST2 - TEST1 - 1초 대기 - TEST2 * 실제 동작: **TEST - TEST - TEST - .... (첫 번째 TEST가 찍힌 시점부터 1초가 지났을 때 ) - TEST2** `OnUpdate()`는 논리적인 측면에서 수행이 끝날 당시
[task] | code [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | wait()로 인한 순차적 대기 문제를 해결하기 위해 _UIToast:ShowMessage 호출을 _TimerService로 분리하여 비동기적으로 실행하는 스크립트를 작성해줘 (_TimerService, _UIToast) [answer] | `wait()`는 실행 흐름을 일시정지(블로킹)시키기 때문에, 여러 작업을 순차적으로 호출하면 전체 처리 시간이 길어지고 그 동안 다른 로직이 진행되지 않는 문제가 발생합니다. 이를 해결하기 위해 `_TimerService:SetTimerOnce()`를 사용하면 특정 시간 후에 함수가 실행되도록 예약할 수 있어, 대기 없이 즉시 다음 코드로 넘어갈 수 있습니다. 아래는 `_UIToast:ShowMessage`를 `wait()` 대신 `_TimerService`를 사용하여 비동기적으로 실행하는 예시입니다. ```lua Proper [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | `wait()`는 보통 아래 예시 코드처럼 간단하게 사용합니다. ```lua _UIToast:ShowMessage("Show Message") wait(1) _UIToast:ShowMessage("Show Delay Message") ``` 하지만 `wait()`를 사용할 때는 주의해야 합니다. `wait()`는 말 그대로 행동을 '기다리고' 있기 때문에 여기저기에 마구 사용한다면 전체적인 흐름을 파악하기 어려워집니다. 또한, '대기'를 하므로 이후 작업이 진행되지 않는 문제도 있습니다. 아래 예시를 통해 '대기' 때문에
[task] | instruction_following [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | Localized Entity인 UI Entity에서 서버의 실행 제어 함수를 직접 호출할 수 없는 이유와, 서버와 통신해야 할 때 반드시 거쳐야 하는 중개 객체를 표로 정리해서 제시하세요. [answer] | UI Entity에서 서버의 실행 제어 함수를 직접 호출할 수 없는 이유는 **UI Entity가 Localized Entity**이기 때문입니다. Localized Entity는 각 클라이언트에 별도로 생성되어 서버에는 존재하지 않습니다. 실행 제어 함수는 서버와 클라이언트 사이에 매칭되는 Entity 쌍이 존재해야만 전송이 이루어지는데, UI Entity는 서버에 해당 엔티티가 없으므로 통신 대상이 없어 함수가 전송되지 않습니다. 이때 별도의 오류 메시지가 출력되지 않을 수 있으므로 주의가 필요합니다. 서버와 통신해야 할 때 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 함수의 실행 공간을 설정하면 별도의 과정 없이 쉽게 클라이언트와 서버가 통신할 수 있습니다. 자동으로 처리하기 때문에 편리하지만, 몇몇 경우는 제약이 있어 크리에이터가 원하는 대로 동작하지 않을 수 있습니다. 일부는 로그가 나오지만, 어떤 것은 동작하지 않는 이유를 확인할 수 없을 수도 있습니다. 이번 가이드에서는 실행 제어를 사용할 수 없는 경우를 알아봅시다. # 매개 변수가 지원하지 않는 종류 함수에는 여러 종류의 매개 변수(parameter)가 있습니다. 이 매개 변수는 다른 공간에서 실행 제어를 사용
[task] | single_turn [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | UI Entity에서 서버 실행 제어 함수를 직접 호출하면 안 되는 이유와 해결 방법은 무엇인가요? [answer] | UI Entity에서 서버 실행 제어 함수를 직접 호출하면 안 되는 이유는, UI Entity가 클라이언트에만 존재하는 **Localized Entity**이기 때문입니다. 서버에는 해당 UI Entity가 존재하지 않으므로, 서버와 클라이언트 간에 매칭되는 Entity 쌍이 없습니다. 실행 제어 함수는 동일한 Entity의 서버와 클라이언트 사이에서만 전송되므로, 쌍이 없으면 함수가 전송되지 않습니다. 또한 이 경우 오류 메시지가 출력되지 않기 때문에 문제가 발생했는지 판단하기 어렵습니다. 해결 방법은 **World Entity [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 함수의 실행 공간을 설정하면 별도의 과정 없이 쉽게 클라이언트와 서버가 통신할 수 있습니다. 자동으로 처리하기 때문에 편리하지만, 몇몇 경우는 제약이 있어 크리에이터가 원하는 대로 동작하지 않을 수 있습니다. 일부는 로그가 나오지만, 어떤 것은 동작하지 않는 이유를 확인할 수 없을 수도 있습니다. 이번 가이드에서는 실행 제어를 사용할 수 없는 경우를 알아봅시다. # 매개 변수가 지원하지 않는 종류 함수에는 여러 종류의 매개 변수(parameter)가 있습니다. 이 매개 변수는 다른 공간에서 실행 제어를 사용
[task] | single_turn [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | Client 함수의 마지막 매개변수로 UserId를 생략하고 호출하면 어떤 유저의 클라이언트에 함수가 실행되나요? [answer] | Client 실행 공간의 함수를 호출할 때 마지막 매개변수인 `UserId`를 생략하면, **해당 함수를 호출한 서버 로직의 컨텍스트에서 관련이 있는 유저**, 즉 해당 엔티티에 연결된 유저의 클라이언트에 함수가 실행됩니다. 제공된 문서의 예시(`RequestBuyItem`)를 보면 서버에서 `self:ResponseBuyItem(itemID)`처럼 `UserId`를 명시적으로 전달하지 않고 호출합니다. 이때 서버는 자동으로 해당 스크립트가 붙어 있는 엔티티의 유저 ID(`self.Entity.PlayerComponent.User [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 어떤 함수의 실행 제어 옵션이 **Client**로 설정되어 있다면 해당 함수는 모든 클라이언트에서 실행됩니다. 하지만 서버에서는 모든 클라이언트가 아니라 특정 클라이언트에만 함수를 실행하고 싶은 상황이 생길 수 있습니다. 특정 클라이언트가 서버에 어떤 요청(Request)을 보내고 서버가 로직을 처리한 이후, 해당 클라이언트에 응답(Response) 하는 상황이 그것입니다. 예를 들어 봅시다. 1. A 클라이언트에서 아이템 구매 버튼을 누른다면 : (Request) 2. 서버에서는 A가 가진 돈을 차감하고 아이템 개수를 늘린 다
[task] | single_turn [pass] | ⏳ 미판정 [title] | 카드 짝 맞추기 [question] | 정해진 시간이 0초가 되었을 때 게임 UI가 종료되지 않고 계속 남아 있는 경우, 시간 체크 로직과 UI 종료 처리 간의 연결 방식에 어떤 문제가 있을 수 있나요? [answer] | 제공된 참고 문서에는 카드 짝 맞추기 게임의 기본 규칙(0초가 되면 게임 UI가 종료됨)와 mLua의 기본 문법 예제(OnUpdate, TimerService, EntityService 등)가 포함되어 있지만, **UI를 제어하는 구체적인 API, 이벤트, 또는 프로퍼티가 문서에 존재하지 않습니다.** 따라서 "시간 체크 로직과 UI 종료 처리 간의 연결 방식"에서 발생할 수 있는 구체적인 문제(예: 특정 UI 컴포넌트의 가시성 프로퍼티 오류, 클라이언트-서버 동기화 문제 등)에 대해 **문서에 근거한 정확한 기술적 진단을 내려줄 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 카드 짝 맞추기 응용 가이드를 따라 해보며 UI 에디터를 활용 방법을 익혀봅시다. 간단한 규칙을 정해 게임을 만들어봅시다. 이번에 만들어 볼 게임의 기본 규칙은 다음과 같습니다. * 정해진 시간동안 게임 진행합니다. * 한 판에 30개의 카드가 펼쳐집니다. * 짝을 맞출 때마다 10점씩 증가합니다. * 0초가 되면 게임 UI가 종료됩니다. * 게임 시작하기 버튼을 누르면 다시 게임을 할 수 있습니다. 가이드는 총 8단계로 진행됩니다. 월드를 제작하는 동안 크리에이터의 취향에 맞게 UI를 수정해 색다르게 꾸밀 수도 있습니다.
[task] | single_turn [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | 클라이언트가 접속할 때 서버로부터 초기 정보를 받아 처리하기 위한 올바른 코드 작성 방법은 무엇인가요? [answer] | 클라이언트가 서버에 접속할 때 초기 정보를 받아 처리하려면, **클라이언트의 `OnBeginPlay()`**에서 서버로 신호를 보내고, 서버가 그 신호를 받아 필요한 정보를 클라이언트로 전송하는 방식으로 작성해야 합니다. 서버의 `OnBeginPlay()`에서 직접 클라이언트로 정보를 보내면 안 됩니다. 서버는 클라이언트보다 먼저 생성되므로, 서버의 `OnBeginPlay()`가 실행되는 시점에는 아직 클라이언트가 접속하지 않아 정보를 받을 수 없기 때문입니다. 올바른 코드 작성 방법은 다음과 같습니다. ```lua Metho [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 함수의 실행 공간을 설정하면 별도의 과정 없이 쉽게 클라이언트와 서버가 통신할 수 있습니다. 자동으로 처리하기 때문에 편리하지만, 몇몇 경우는 제약이 있어 크리에이터가 원하는 대로 동작하지 않을 수 있습니다. 일부는 로그가 나오지만, 어떤 것은 동작하지 않는 이유를 확인할 수 없을 수도 있습니다. 이번 가이드에서는 실행 제어를 사용할 수 없는 경우를 알아봅시다. # 매개 변수가 지원하지 않는 종류 함수에는 여러 종류의 매개 변수(parameter)가 있습니다. 이 매개 변수는 다른 공간에서 실행 제어를 사용
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | CardPairGameUI를 활성화하기 위한 StartGame 메서드의 실행 공간은 무엇이며, Enable 프로퍼티를 어떻게 설정해야 하는지 2문장 이내로 답변하세요. [answer] | StartGame 메서드의 실행 공간은 [client only]입니다. Enable 프로퍼티를 활성화하려면 self.CardPairGameUI.Enable = true 로 설정해야 합니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 리메이크한 월드에는 제작에 필요한 UI, 리소스, 그리고 비어 있는 컴포넌트 등이 있습니다. 단계 별로 하나씩 채워나가며 함께 게임을 완성해봅시다. # CardPairGameUI 활성화 월드에 유저가 입장하면 **CardPairGameUI**가 **Enable**로 변경되도록 만들어 봅시다. 월드 전체에 적용되는 규칙이나 로직을 만들 때는 Logic을 사용합니다. **CardPairGameLogic** 로직에는 짝 맞추기에 기본적으로 필요한 UI를 켜거나 끄기, 카드 입력 등 전반적인 규칙을 작성할 것입니다. 우선 월드에 입장
[task] | single_turn [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | 서버에서 특정 클라이언트에만 Client 함수를 호출할 때 마지막 매개변수로 UserId를 명시해야 하나요? [answer] | 아니요, 반드시 명시적으로 작성할 필요는 없습니다. 실행 공간이 **Client**로 설정된 함수를 특정 클라이언트에게만 호출하려면 마지막 매개 변수에 보내려는 `UserId`를 지정하는 것이 원칙이지만, 이 `UserId`는 시스템에 의해 자동으로 처리되므로 코드 작성 시 생략할 수 있습니다. 생략한 경우에도 서버는 해당 함수를 호출한 컨텍스트나 내부 로직을 통해 대상 클라이언트를 식별하여 해당 유저의 클라이언트에만 함수를 전송합니다. 따라서 `ResponseBuyItem`과 같은 Client 함수를 선언할 때 `targetUs [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 어떤 함수의 실행 제어 옵션이 **Client**로 설정되어 있다면 해당 함수는 모든 클라이언트에서 실행됩니다. 하지만 서버에서는 모든 클라이언트가 아니라 특정 클라이언트에만 함수를 실행하고 싶은 상황이 생길 수 있습니다. 특정 클라이언트가 서버에 어떤 요청(Request)을 보내고 서버가 로직을 처리한 이후, 해당 클라이언트에 응답(Response) 하는 상황이 그것입니다. 예를 들어 봅시다. 1. A 클라이언트에서 아이템 구매 버튼을 누른다면 : (Request) 2. 서버에서는 A가 가진 돈을 차감하고 아이템 개수를 늘린 다
[task] | instruction_following [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | `_UserService.LocalPlayer`를 사용하여 내 캐릭터만 특정 동작을 수행하도록 처리하는 이유를 3문장 이내로 설명하세요. [answer] | 메이플스토리 월드의 Entity는 멀티 플레이 환경에서 모든 클라이언트에 존재하므로, 입력 이벤트가 발생하면 모든 플레이어 캐릭터에 동시에 동작이 적용되는 문제가 생깁니다. 이를 방지하기 위해 `_UserService.LocalPlayer`와 현재 Entity를 비교하여 해당 Entity가 내 클라이언트의 캐릭터인지 확인해야 합니다. 이를 통해 키 입력 등 로컬 이벤트가 발생한 플레이어 본인만 해당 동작을 수행하도록 제한할 수 있습니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 메이플스토리 월드에서는 기본적으로 멀티 플레이를 손쉽게 구현할 수 있습니다. 대부분의 기능을 멀티 플레이 기반으로 처리하는 것을 권장합니다. 하지만 일부 항목은 별도의 Local 개념이 필요합니다. 주요 항목은 다음과 같습니다. * Localized Entity * 입력 처리 * 내 캐릭터 처리 **UIEntity**와 **입력** 그리고 **내 캐릭터** 모두 **나의 클라이언트에만 존재하는 개념**이라 별도로 처리해야 합니다. 특히 **내 캐릭터**는 클라이언트에서 처리하기 위해 `_UserService.LocalPlay
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | 모델을 사용하여 카드를 생성할 때, World 실행 시점에 UI에 소환되도록 만드는 방식의 장점을 1문장으로 설명하세요. [answer] | 월드 실행 시점에 특정 로직에 따라 UI에 소환되도록 만드는 방식은 필요한 만큼의 카드 엔티티를 미리 만드는 것보다 효율적이며, 동일한 프로퍼티를 가진 엔티티를 여러 번 사용할 때 모델이라는 판본을 활용하여 관리하기 좋기 때문입니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 리메이크한 월드에는 제작에 필요한 UI, 리소스, 그리고 비어 있는 컴포넌트 등이 있습니다. 단계 별로 하나씩 채워나가며 함께 게임을 완성해봅시다. # CardPairGameUI 활성화 월드에 유저가 입장하면 **CardPairGameUI**가 **Enable**로 변경되도록 만들어 봅시다. 월드 전체에 적용되는 규칙이나 로직을 만들 때는 Logic을 사용합니다. **CardPairGameLogic** 로직에는 짝 맞추기에 기본적으로 필요한 UI를 켜거나 끄기, 카드 입력 등 전반적인 규칙을 작성할 것입니다. 우선 월드에 입장
[task] | instruction_following [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | 서버의 OnBeginPlay에서 클라이언트로 정보를 전달하지 못하는 구조적 이유와, 클라이언트 접속 시점에 데이터를 수신하기 위한 올바른 구현 방식을 단계별 번호 목록으로 정리하세요. [answer] | 서버의 `OnBeginPlay()`에서 클라이언트로 정보를 전달하지 못하는 구조적 이유와 올바른 구현 방식은 다음과 같습니다. 1. **서버와 클라이언트의 생성/접속 시점 차이**: 메이플스토리 월드에서는 서버가 먼저 생성되고, 이후 클라이언트가 서버에 접속하여 정보를 받아 월드를 구성합니다. 2. **`OnBeginPlay()` 호출 시점의 클라이언트 부재**: 서버가 생성되는 시점에 `OnBeginPlay()`가 호출되지만, 이 시점에는 클라이언트가 아직 서버에 접속하지 않은 상태입니다. 3. **정보 수신 불가**: 클라이언 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 함수의 실행 공간을 설정하면 별도의 과정 없이 쉽게 클라이언트와 서버가 통신할 수 있습니다. 자동으로 처리하기 때문에 편리하지만, 몇몇 경우는 제약이 있어 크리에이터가 원하는 대로 동작하지 않을 수 있습니다. 일부는 로그가 나오지만, 어떤 것은 동작하지 않는 이유를 확인할 수 없을 수도 있습니다. 이번 가이드에서는 실행 제어를 사용할 수 없는 경우를 알아봅시다. # 매개 변수가 지원하지 않는 종류 함수에는 여러 종류의 매개 변수(parameter)가 있습니다. 이 매개 변수는 다른 공간에서 실행 제어를 사용
[task] | instruction_following [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | 코드만 출력하고 설명은 주석으로 [answer] | ```lua Event Handler: [service: InputService] HandleKeyDownEvent(KeyDownEvent event) { -- Parameters local key = event.key -------------------------------------------------------- -- 현재 Entity가 '나'(LocalPlayer)인지 확인 if self.Entity ~= _UserService.LocalPlayer then ret [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 메이플스토리 월드에서는 기본적으로 멀티 플레이를 손쉽게 구현할 수 있습니다. 대부분의 기능을 멀티 플레이 기반으로 처리하는 것을 권장합니다. 하지만 일부 항목은 별도의 Local 개념이 필요합니다. 주요 항목은 다음과 같습니다. * Localized Entity * 입력 처리 * 내 캐릭터 처리 **UIEntity**와 **입력** 그리고 **내 캐릭터** 모두 **나의 클라이언트에만 존재하는 개념**이라 별도로 처리해야 합니다. 특히 **내 캐릭터**는 클라이언트에서 처리하기 위해 `_UserService.LocalPlay
[task] | code [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | _EntityService 로 특정 엔티티를 조회한 후 isvalid() 함수를 통해 유효성을 확인하고, 유효할 경우에만 해당 엔티티의 TransformComponent.Position 을 수정하는 스크립트를 작성해줘 [answer] | `_EntityService`를 통해 엔티티를 조회한 후 `isvalid()`로 유효성을 확인하고, 유효한 경우에만 `TransformComponent.Position`을 수정하는 스크립트입니다. ```lua Method: [server only] void UpdateEntityPosition() { local targetEntity = _EntityService:GetEntity("a22396e1-6365-42d4-9cef-5924c223f7fa") if isvalid(targetEntity) then [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 메이플스토리 월드는 쉬운 코딩을 위해 다양한 편의 기능을 지원하고 있습니다. 대표적으로 간편한 실행 제어 설정 기능이 있습니다. 그러나 간단한 설정이 얽히며 복잡해질 수 있고, 잘못 설정하면 원하는 기능이 제대로 실행되지 않을 수도 있습니다. 때로는 월드가 매우 느려질 수도 있습니다. 이런 문제는 월드의 규모가 작거나 월드 제작 초창기에는 발견하기 어렵습니다. 하지만 월드의 규모가 커지고 완성도가 높아질수록 문제가 될 수 있습니다. 그러므로 제작 초기부터 모든 동작이 원활하고 효율적으로 진행되도록 구조를 고민
[task] | instruction_following [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | 멀티 플레이 환경에서 S 키 입력 시 궁극기 스킬이 실행되도록 하는 Event Handler 코드를 `self.Entity`와 `_UserService.LocalPlayer` 비교 로직 포함하여 출력하세요. [answer] | 멀티 플레이 환경에서 모든 클라이언트의 캐릭터가 S 키 입력에 반응하는 문제를 방지하기 위해, 해당 Entity가 현재 플레이어가 소유한 `LocalPlayer`인지 확인하는 로직이 필요합니다. 아래 코드는 `self.Entity`와 `_UserService.LocalPlayer`를 비교하여 나만 S 키를 눌렀을 때 궁극기 슂�을 실행하도록 처리합니다. ```lua Event Handler: [service : InputService] HandleKeyDownEvent (KeyDownEvent event) { -- Para [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 메이플스토리 월드에서는 기본적으로 멀티 플레이를 손쉽게 구현할 수 있습니다. 대부분의 기능을 멀티 플레이 기반으로 처리하는 것을 권장합니다. 하지만 일부 항목은 별도의 Local 개념이 필요합니다. 주요 항목은 다음과 같습니다. * Localized Entity * 입력 처리 * 내 캐릭터 처리 **UIEntity**와 **입력** 그리고 **내 캐릭터** 모두 **나의 클라이언트에만 존재하는 개념**이라 별도로 처리해야 합니다. 특히 **내 캐릭터**는 클라이언트에서 처리하기 위해 `_UserService.LocalPlay
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | UI_Block_Group의 프로퍼티 중 가로에 보일 자식 UI Entity 개수를 고정하는 설정 이름과 해당 값 2가지만 코드 블록으로 출력하세요. [answer] | ```lua Constraint = FixedColumnCount ConstraintCount = 6 ``` [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 이제 카드를 화면에 불러낼 차례입니다. 카드를 불러내기 위해선 **/ui/CardPairGameUI/UI_Board/UI_Blocks/UI_Block_Group**를 활용합니다. **UI_Block_Group**은 [ScrollLayoutGroupComponent](/apiReference?postId=382%7B%22target%22:%22_self%22%7D)를 활용했습니다. **ScollLayoutGroupComponent**는 여러 개의 엔티티를 펼쳐야할 때 사용합니다. 주로 상점이나, 인벤토리처럼 한 눈에 봐야하는 이미지
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | CardSet 데이터셋에 추가해야 할 마지막 SpriteRUID를 찾기 위한 Resource Storage 내 탐색 경로를 단계별 번호 목록으로 정리하세요. [answer] | CardSet 데이터셋에 추가할 마지막 SpriteRUID를 찾기 위한 Resource Storage 내 탐색 경로는 다음과 같습니다. 1. Resource Storage를 엽니다. 2. **MSW 리소스**를 선택합니다. 3. **Sprite - Item** 카테고리를 선택합니다. 4. 사용할 네모 모양 스프라이트를 찾습니다. 5. 해당 스프라이트에서 **Copy RUID**를 선택합니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 리메이크한 월드에는 제작에 필요한 UI, 리소스, 그리고 비어 있는 컴포넌트 등이 있습니다. 단계 별로 하나씩 채워나가며 함께 게임을 완성해봅시다. # CardPairGameUI 활성화 월드에 유저가 입장하면 **CardPairGameUI**가 **Enable**로 변경되도록 만들어 봅시다. 월드 전체에 적용되는 규칙이나 로직을 만들 때는 Logic을 사용합니다. **CardPairGameLogic** 로직에는 짝 맞추기에 기본적으로 필요한 UI를 켜거나 끄기, 카드 입력 등 전반적인 규칙을 작성할 것입니다. 우선 월드에 입장
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | ScrollLayoutGroupComponent가 주로 사용される UI 유형과 자식 Entity에 적용할 수 있는 기능을 2문장 이내로 요약하세요. [answer] | ScrollLayoutGroupComponent는 상점이나 인벤토리처럼 한눈에 봐야 하는 이미지가 많은 UI를 제작할 때 주로 사용됩니다. 자식 UI Entity들의 크기와 배치 간격 등을 일괄적으로 적용할 수 있습니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 이제 카드를 화면에 불러낼 차례입니다. 카드를 불러내기 위해선 **/ui/CardPairGameUI/UI_Board/UI_Blocks/UI_Block_Group**를 활용합니다. **UI_Block_Group**은 [ScrollLayoutGroupComponent](/apiReference?postId=382%7B%22target%22:%22_self%22%7D)를 활용했습니다. **ScollLayoutGroupComponent**는 여러 개의 엔티티를 펼쳐야할 때 사용합니다. 주로 상점이나, 인벤토리처럼 한 눈에 봐야하는 이미지
[task] | code [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | `[Sync]` 프로퍼티인 `eventActiveTime`을 매 프레임 갱신하지 않고, 이벤트 시작 시 `_UtilLogic.ServerElapsedSeconds`를 한 번만 저장하고 Client에서 현재 서버 시간과 차이를 계산해 경과 시간을 얻는 로직을 포함하는 스크립트를 작성해줘 [answer] | `[Sync]` 프로퍼티인 `eventActiveTime`을 매 프레임 갱신하면 네트워크 동기화 비용이 매우 높아지므로, 이벤트가 시작되는 시점에 `_UtilLogic.ServerElapsedSeconds` 값을 한 번만 저장합니다. 이후 Client 측에서 현재 서버 시간과 저장된 시작 시간의 차이를 계산하여 경과 시간을 구하는 방식으로 작성합니다. ```lua Property: [Sync] number eventActiveTime = 0 Method: [server] void StartEvent() { self.eve [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | `wait`는 대기한 뒤에 직접 어떤 행동을 수행하는 것이고, `TimerService`는 해당 행동을 수행하는 별도의 일을 만드는 것입니다. `wait`는 직렬, `TimerService`는 병렬 형태로 동작합니다. 따라서 `wait`의 사용 코드는 다음과 같이 표현할 수 있습니다 ```lua local targetEntity = _EntityService:GetEntity("a22396e1-6365-42d4-9cef-5924c223f7fa") targetEntity.TestComponent:ShowMessage() func
[task] | two_turn [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | Client 실행 공간의 함수가 모든 클라이언트에 실행되는 원리와 특정 클라이언트에만 응답하는 시나리오에 대해 설명해 주세요 [answer] | 메이플스토리 월드(mLua)에서 **Client** 실행 공간의 함수가 기본적으로 모든 클라이언트에 실행되는 원리와, 이를 특정 클라이언트에만 전송하는 방법(응답 시나리오)을 설명합니다. ### 1. 기본 원리: Client 실행 공간의 함수는 모든 클라이언트에 실행됨 실행 공간이 **Client**로 설정된 함수는 서버에서 호출될 경우, 서버가 아닌 **모든 연결된 클라이언트**에서 동시에 실행됩니다. * **문제점:** 서버가 특정 유저(A)의 요청(Request)을 처리한 후, 처리 결과를 A에게만 알려야 할 때 `[C [followup] | 특정 클라이언트에만 함수를 전송하기 위해 마지막 매개 변수로 UserId를 추가하는 메커니즘과 생략 가능한 이유, 그리고 이 방식이 네트워크 비용 절감에 도움이 되는 구체적인 코드를 보여주세요 [followup_answer] | 특정 클라이언트에만 `[Client]` 함수를 전송하는 메커니즘, `UserId` 매개변수 생략 이유, 그리고 네트워크 비용 절감 효과를 보여주는 구체적인 코드는 다음과 같습니다. ### 1. 메커니즘: 마지막 매개 변수로 UserId 지정 실행 공간이 **Client**인 함수를 특정 클라이언트에게만 전송하려면, 서버에서 해당 함수를 호출할 때 **마지막 매개 변수**에 보내고 싶은 유저의 `UserId`를 전달하면 됩니다. * **일반적 `[Client]` 호출:** 서버가 `[Client]` 함수를 호출하면, 기본적으로 [scores] | [judge_reason] | [source_doc] | 어떤 함수의 실행 제어 옵션이 **Client**로 설정되어 있다면 해당 함수는 모든 클라이언트에서 실행됩니다. 하지만 서버에서는 모든 클라이언트가 아니라 특정 클라이언트에만 함수를 실행하고 싶은 상황이 생길 수 있습니다. 특정 클라이언트가 서버에 어떤 요청(Request)을 보내고 서버가 로직을 처리한 이후, 해당 클라이언트에 응답(Response) 하는 상황이 그것입니다. 예를 들어 봅시다. 1. A 클라이언트에서 아이템 구매 버튼을 누른다면 : (Request) 2. 서버에서는 A가 가진 돈을 차감하고 아이템 개수를 늘린 다
[task] | single_turn [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | 서버 로직 처리 후 요청을 보낸 유저에게만 응답을 보내야 하는 상황에서 기존 방식과 비교해 개선된 코드의 장점은 무엇인가요? [answer] | 서버 로직 처리 후 요청을 보낸 유저에게만 응답을 보내야 하는 상황에서, 기존 방식과 비교해 개선된 코드의 장점은 다음과 같습니다. **1. 네트워크 비용 절감 및 월드 최적화** * **기존 방식:** 서버가 `[Client]` 실행 공간의 함수를 호출할 때, 모든 클라이언트에게 해당 함수 호출 메시지를 전송합니다. 이후 각 클라이언트에서 `UserId`를 비교하여 요청한 유저만 실제 로직을 실행합니다. 이는 불필요한 네트워크 트래픽을 발생시켜 전체 월드의 성능을 저하시킵니다. * **개선된 방식:** 실행 공간이 `Cl [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 어떤 함수의 실행 제어 옵션이 **Client**로 설정되어 있다면 해당 함수는 모든 클라이언트에서 실행됩니다. 하지만 서버에서는 모든 클라이언트가 아니라 특정 클라이언트에만 함수를 실행하고 싶은 상황이 생길 수 있습니다. 특정 클라이언트가 서버에 어떤 요청(Request)을 보내고 서버가 로직을 처리한 이후, 해당 클라이언트에 응답(Response) 하는 상황이 그것입니다. 예를 들어 봅시다. 1. A 클라이언트에서 아이템 구매 버튼을 누른다면 : (Request) 2. 서버에서는 A가 가진 돈을 차감하고 아이템 개수를 늘린 다
[task] | code [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | `_EntityService:GetEntity`로 특정 엔티티를 가져와 `TestComponent:ShowMessage()`를 즉시 호출하고, `_TimerService:SetTimerOnce`를 사용하여 3초 후 동일한 메시지를 다시 출력하는 병렬 처리 스크립트를 작성해줘 [answer] | 요청하신 동작은 `wait`의 직렬 처리가 아니라 `_TimerService`를 활용하는 병렬 처리 패턴입니다. `_EntityService:GetEntity`로 엔티티를 가져와 즉시 `TestComponent:ShowMessage()`를 호출하고, `_TimerService:SetTimerOnce`를 통해 3초 뒤 실행될 콜백 함수를 등록하여 동일한 메시지를 다시 출력하도록 작성한 스크립트는 다음과 같습니다. ```lua local targetEntity = _EntityService:GetEntity("a22396e1-636 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | `wait`는 대기한 뒤에 직접 어떤 행동을 수행하는 것이고, `TimerService`는 해당 행동을 수행하는 별도의 일을 만드는 것입니다. `wait`는 직렬, `TimerService`는 병렬 형태로 동작합니다. 따라서 `wait`의 사용 코드는 다음과 같이 표현할 수 있습니다 ```lua local targetEntity = _EntityService:GetEntity("a22396e1-6365-42d4-9cef-5924c223f7fa") targetEntity.TestComponent:ShowMessage() func
[task] | two_turn [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | CardPairGameUI 활성화 스크립트에서 Entity 프로퍼티 경로를 설정하는 방법 [answer] | CardPairGameUI 활성화 스크립트에서 `Entity` 프로퍼티의 경로는 **UI 엔티티의 절대 경로**를 문자열로 지정하여 설정합니다. 제공된 문서의 예제에 따르면, `CardPairGameUI`의 경로는 `/ui/CardPairGameUI`입니다. 따라서 `Property:` 블록에서 `[None]` 속성으로 `Entity` 타입을 선언하고, 기본값으로 해당 경로를 직접 입력합니다. ```lua Property: [None] Entity CardPairGameUI = /ui/CardPairGameUI Method: [followup] | OnBeginPlay에서 StartGame을 호출할 때 self:StartGame() 콜론 콜을 사용하는 이유와 일반 함수 호출과의 차이 [followup_answer] | mLua에서 `self:StartGame()`과 같이 콜론(`:`)을 사용하여 메서드를 호출하는 이유는 **`self`를 함수의 첫 번째 인자로 자동 전달**하기 위해서입니다. 일반 함수 호출(`self.StartGame()`)과 콜론 호출(`self:StartGame()`)의 차이는 다음과 같습니다: 1. **콜론 호출 (`self:StartGame()`)** * 내부적으로 `StartGame(self)`와 동일하게 동작합니다. * 함수 정의 시 첫 번째 파라미터로 `self`를 명시적으로 받지 않아도, [scores] | [judge_reason] | [source_doc] | 리메이크한 월드에는 제작에 필요한 UI, 리소스, 그리고 비어 있는 컴포넌트 등이 있습니다. 단계 별로 하나씩 채워나가며 함께 게임을 완성해봅시다. # CardPairGameUI 활성화 월드에 유저가 입장하면 **CardPairGameUI**가 **Enable**로 변경되도록 만들어 봅시다. 월드 전체에 적용되는 규칙이나 로직을 만들 때는 Logic을 사용합니다. **CardPairGameLogic** 로직에는 짝 맞추기에 기본적으로 필요한 UI를 켜거나 끄기, 카드 입력 등 전반적인 규칙을 작성할 것입니다. 우선 월드에 입장
[task] | code [pass] | ⏳ 미판정 [title] | Effective MSW 1 [question] | _DataStorageService:GetAndWait 대신 GetAsync를 사용하여 여러 능력치 값을 병렬로 조회하고 응답이 도착했을 때 처리하는 스크립트를 작성해줘 (_DataStorageService) [answer] | 제공된 참고 문서에는 `_DataStorageService:GetAsync`의 구체적인 시그니처(인자 목록)나 콜백 함수의 정확한 형태에 대한 정보가 포함되어 있지 않습니다. 문서에서는 `wait()`의 문제점을 지적하며 `GetAsync`를 병렬 처리의 대안으로 언급하고, "상세한 사용 방법은 [DataStorageService](/apiReference?postId=309)를 참고하세요"라고만 지시하고 있을 뿐, 실제 코드 예시나 API 문서를 제공하지 않았습니다. 따라서 문서에 근거하여 `GetAsync`를 사용한 정확한 코 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | `wait()`는 보통 아래 예시 코드처럼 간단하게 사용합니다. ```lua _UIToast:ShowMessage("Show Message") wait(1) _UIToast:ShowMessage("Show Delay Message") ``` 하지만 `wait()`를 사용할 때는 주의해야 합니다. `wait()`는 말 그대로 행동을 '기다리고' 있기 때문에 여기저기에 마구 사용한다면 전체적인 흐름을 파악하기 어려워집니다. 또한, '대기'를 하므로 이후 작업이 진행되지 않는 문제도 있습니다. 아래 예시를 통해 '대기' 때문에
[task] | single_turn [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | 다른 플레이어의 키보드 입력까지 제 캐릭터에 반영되는 문제를 해결하려면 어떤 조건문을 추가해야 하나요? [answer] | 다른 플레이어의 입력까지 제 캐릭터에 반영되는 문제를 해결하려면, 이벤트 핸들러의 시작 부분에 `self.Entity`가 `_UserService.LocalPlayer`와 동일한지 확인하는 조건문을 추가해야 합니다. `self.Entity`가 `_UserService.LocalPlayer`가 아니라면(즉, 다른 플레이어의 캐릭터에 부착된 컴포넌트라면) 즉시 함수를 종료(`return`)하도록 처리하면 됩니다. 이를 통해 해당 로직이 '나'의 클라이언트에서만 실행되도록 보장할 수 있습니다. 다음은 키보드 'S' 키 입력 시 궁극기 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 메이플스토리 월드에서는 기본적으로 멀티 플레이를 손쉽게 구현할 수 있습니다. 대부분의 기능을 멀티 플레이 기반으로 처리하는 것을 권장합니다. 하지만 일부 항목은 별도의 Local 개념이 필요합니다. 주요 항목은 다음과 같습니다. * Localized Entity * 입력 처리 * 내 캐릭터 처리 **UIEntity**와 **입력** 그리고 **내 캐릭터** 모두 **나의 클라이언트에만 존재하는 개념**이라 별도로 처리해야 합니다. 특히 **내 캐릭터**는 클라이언트에서 처리하기 위해 `_UserService.LocalPlay
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 카드와 UI 만들기 [question] | UI_Block_Group에 적용된 프로퍼티 값과 설명을 5행 표로 정리해 주세요. [answer] | UI_Block_Group에 적용된 ScrollLayoutGroupComponent의 프로퍼티 값과 설명은 다음과 같습니다. | 프로퍼티 | 값 | 설명 | | ---- | --- | --- | | Padding | top:15, right, left, bottom:0 | 상단의 여유 공간을 15로 설정합니다. | | CellSize | 90, 90 | 펼쳐질 카드의 크기를 90, 90 크기로 설정합니다. | | Constraint | FixedColumnCount | ScrollLayoutGroupComponent의 설정입니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 이제 카드를 화면에 불러낼 차례입니다. 카드를 불러내기 위해선 **/ui/CardPairGameUI/UI_Board/UI_Blocks/UI_Block_Group**를 활용합니다. **UI_Block_Group**은 [ScrollLayoutGroupComponent](/apiReference?postId=382%7B%22target%22:%22_self%22%7D)를 활용했습니다. **ScollLayoutGroupComponent**는 여러 개의 엔티티를 펼쳐야할 때 사용합니다. 주로 상점이나, 인벤토리처럼 한 눈에 봐야하는 이미지
[task] | code [pass] | ⏳ 미판정 [title] | Effective MSW 2 [question] | InputService의 HandleKeyDownEvent에서 _UserService.LocalPlayer 확인 후 KeyboardKey.Space 입력 시 self:Jump()를 호출하는 스크립트를 작성해줘 [answer] | InputService의 `HandleKeyDownEvent`에서 `_UserService.LocalPlayer`를 확인하여, 해당 Entity가 내 클라이언트의 캐릭터인지 검증한 후 `KeyboardKey.Space`가 입력되면 `self:Jump()`를 호출하는 스크립트는 다음과 같습니다. ```lua Event Handler: [service : InputService] HandleKeyDownEvent (KeyDownEvent event) { -- Parameters local key = event.key [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 메이플스토리 월드에서는 기본적으로 멀티 플레이를 손쉽게 구현할 수 있습니다. 대부분의 기능을 멀티 플레이 기반으로 처리하는 것을 권장합니다. 하지만 일부 항목은 별도의 Local 개념이 필요합니다. 주요 항목은 다음과 같습니다. * Localized Entity * 입력 처리 * 내 캐릭터 처리 **UIEntity**와 **입력** 그리고 **내 캐릭터** 모두 **나의 클라이언트에만 존재하는 개념**이라 별도로 처리해야 합니다. 특히 **내 캐릭터**는 클라이언트에서 처리하기 위해 `_UserService.LocalPlay