[task] | single_turn [pass] | ⏳ 미판정 [title] | 카드 섞기, 선택하기 [question] | `_UtilLogic:RandomIntegerRange`를 이용해 카드 테이블을 섞는 로직에서 `temp` 변수를 사용하여 값을 교환하는 과정의 원리를 설명해 주시겠어요? [answer] | `_UtilLogic:RandomIntegerRange`를 이용한 카드 섞기(셔플) 로직에서 `temp` 변수를 사용하는 이유는 **두 값이 서로의 자리에 놓이기 전에 값이 덮어쓰기(overwrite)되어 사라지는 것을 방지**하기 위해서입니다. 이 과정은 일반적인 **스왑(Swap, 교환)** 알고리즘의 원리이며, 카드 테이블을 섞는 구체적인 단계는 다음과 같습니다. 1. **무작위 위치 선정**: 현재 인덱스 `i`와 교환할 무작위 위치 `randomIndex`를 `_UtilLogic:RandomIntegerRange(1, [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 카드 섞기 카드 보여주기에 성공했으니 이번에는 한 쌍의 카드가 나오게 만들어 봅시다. 1. **CardBoard** 컴포넌트의 `SetCards()` 함수에서 세 번째 `for i = 1, deckCount do`를 찾습니다. 처음에는 deckCount의 수만큼 실행했지만, 이번에는 deckCount의 절반만큼만 실행하기 위해 (deckCount / 2)로 변경합니다. 그 절반을 동일하게 두 번 불러와 쌍을 이루는 판을 만듭니다. `table.insert`는 [table](/apiReference/Lua/table) 레
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 짝 맞추기 [question] | 모든 카드를 제거하는 `ClearCardEntities` 함수의 로직을 `for` 반복문 사용 여부와 대상 프로퍼티 이름으로 3문장 이내로 설명하세요. [answer] | `ClearCardEntities` 함수는 `for` 반복문을 사용합니다. 반복 대상은 `self.CardSpawnRoot.Children` 프로퍼티입니다. 이 자식 엔티티들에 대해 `Destroy()`를 호출하여 모두 제거합니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 짝 맞추기 카드의 색상을 바꿔보았으니 이번에는 두 개의 카드를 선택하고, 맞으면 사라질 수 있도록 만들어봅시다. 1. **Card 컴포넌트**에서 `SetDestroyed` 함수에 아래와 같이 작성합니다. 카드 엔티티의 Visible 프로퍼티를 false로 바꾸어 비활성화 시킵니다. 이는 파괴된 상태로 사용합니다. ```lua [client only] void SetDestroyed () { self.Entity.Visible = false } ``` 2. 짝이
[task] | single_turn [pass] | ⏳ 미판정 [title] | 카드 섞기, 선택하기 [question] | cardboard:SetCards()와 cardboard:SelectCard(newCard)를 호출할 때 카드 엔티티를 직접 참조하는 것과의 차이점과 각각의 용도를 구분해서 알려주세요. [answer] | 제공된 문서에는 `cardboard:SetCards()`와 `cardboard:SelectCard(newCard)`의 내부 구현이나, 카드 엔티티를 직접 참조하는 방식과의 기술적 차이점에 대한 설명이 포함되어 있지 않습니다. 문서에서 확인할 수 있는 정보는 다음과 같습니다. 1. **`cardboard:SetCards()`**: * `CardPairGameLogic` 스크립트의 `StartGame()` 메서드에서 호출됩니다. * 게임 시작 시(`OnBeginPlay` -> `StartGame`) 카드 보드 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ```lua Property: [None] string SpriteRUID = "" Method: void CardOn () { self.Entity.SpriteGUIRendererComponent.Color = Color(81/255,81/255,81/255,1) } void CardOff () { self.Entity.SpriteGUIRendererComponent.Color = Color(255/255,255/255,255/255,1) } Event Handler: [self] HandleButtonClic
[task] | code [pass] | ⏳ 미판정 [title] | 카드 섞기, 선택하기 [question] | CardPairGameLogic 컴포넌트에 `OnCardClicked` 메서드를 구현하여 현재 선택된 카드(`SelectedCard`)가 없으면 새 카드를 선택하고, 이미 선택된 카드가 있으면 해제하는 로직을 처리하는 스크립트를 작성해줘 [answer] | 요청하신 로직(선택된 카드가 없으면 선택, 있으면 해제)을 구현한 `CardPairGameLogic` 컴포넌트 스크립트는 다음과 같습니다. 참고 문서에 따르면, `CardBoard` 컴포넌트에는 `SelectedCard` 프로퍼티와 `SelectCard`, `DeselectCard` 메서드가 존재하며, `Card` 컴포넌트는 `CardOn`, `CardOff` 메서드를 가집니다. `CardPairGameLogic`은 UI 경로(`/ui/CardPairGameUI`)를 통해 `CardBoard`에 접근해야 합니다. ```lua P [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 1. **CardBoard** 컴포넌트에서 새로운 프로퍼티를 추가하고 프로퍼티 타입을 **Component**로 지정합니다. ```lua Property: [None] Card SelectedCard = nil ``` 2. CardBoard 컴포넌트에서 `SelctCard` 함수를 찾습니다. 매개 변수를 추가하고 타입을 Component로 변경합니다. Component를 **Card**로 지정합니다. ```lua [client only] void SelectCard (C
[task] | code [pass] | ⏳ 미판정 [title] | 카드 섞기, 선택하기 [question] | Card 컴포넌트에 CardOn()과 CardOff() 메서드를 구현하여 self.Entity.SpriteGUIRendererComponent.Color를 각각 회색(Color(81/255,81/255,81/255,1))과 흰색(Color(255/255,255/255,255/255,1))으로 변경하도록 작성해줘. [answer] | Card 컴포넌트에 요청하신 두 메서드를 구현했습니다. `CardOn()`은 스프라이트 색을 회색으로, `CardOff()`는 흰색으로 변경하도록 `SpriteGUIRendererComponent.Color`를 설정합니다. ```lua Method: void CardOn() { self.Entity.SpriteGUIRendererComponent.Color = Color(81/255,81/255,81/255,1) } void CardOff() { self.Entity.SpriteGUIRendererComponent [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 카드 섞기 카드 보여주기에 성공했으니 이번에는 한 쌍의 카드가 나오게 만들어 봅시다. 1. **CardBoard** 컴포넌트의 `SetCards()` 함수에서 세 번째 `for i = 1, deckCount do`를 찾습니다. 처음에는 deckCount의 수만큼 실행했지만, 이번에는 deckCount의 절반만큼만 실행하기 위해 (deckCount / 2)로 변경합니다. 그 절반을 동일하게 두 번 불러와 쌍을 이루는 판을 만듭니다. `table.insert`는 [table](/apiReference/Lua/table) 레
[task] | code [pass] | ⏳ 미판정 [title] | 카드 섞기, 선택하기 [question] | CardBoard 컴포넌트의 SetCards() 함수에서 deckCount의 절반만큼 루프를 실행하며 table.insert를 통해 동일 카드를 두 번 삽입하고, _UtilLogic:RandomIntegerRange를 이용해 Fisher-Yates 방식의 카드를 섞는 로직을 작성해줘. [answer] | CardBoard 컴포넌트의 `SetCards()` 함수 내에서 deckCount의 절반만큼 루프를 실행하여 각 카드를 `table.insert`로 두 번 삽입하고, 그 후 `_UtilLogic:RandomIntegerRange`를 사용하여 카드를 섞는 로직은 다음과 같습니다. ```lua for i = 1, (deckCount / 2) do table.insert(cardTable, deckTable[i]) table.insert(cardTable, deckTable[i]) end for i = 1, #cardT [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 카드 섞기 카드 보여주기에 성공했으니 이번에는 한 쌍의 카드가 나오게 만들어 봅시다. 1. **CardBoard** 컴포넌트의 `SetCards()` 함수에서 세 번째 `for i = 1, deckCount do`를 찾습니다. 처음에는 deckCount의 수만큼 실행했지만, 이번에는 deckCount의 절반만큼만 실행하기 위해 (deckCount / 2)로 변경합니다. 그 절반을 동일하게 두 번 불러와 쌍을 이루는 판을 만듭니다. `table.insert`는 [table](/apiReference/Lua/table) 레
[task] | code [pass] | ⏳ 미판정 [title] | 카드 섞기, 선택하기 [question] | CardBoard 컴포넌트에 `SelectedCard` 프로퍼티를 추가하고 `SelectCard`, `DeselectCard` 메서드를 구현하여 카드 선택 및 해제 로직을 처리하는 스크립트를 작성해줘 [answer] | CardBoard 컴포넌트에서 카드 선택 및 해제 로직을 구현하기 위해 `SelectedCard` 프로퍼티를 추가하고, `SelectCard` 및 `DeselectCard` 메서드를 작성합니다. 1. **`SelectedCard` 프로퍼티**: 현재 선택된 카드 컴포넌트를 저장하기 위해 `Component` 타입(구체적으로 **Card**로 지정)의 프로퍼티를 추가합니다. 2. **`SelectCard` 메서드**: 매개변수로 전달받은 `Card`를 `SelectedCard`에 저장하고, 해당 카드의 `CardOn()` 메서드 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 1. **CardBoard** 컴포넌트에서 새로운 프로퍼티를 추가하고 프로퍼티 타입을 **Component**로 지정합니다. ```lua Property: [None] Card SelectedCard = nil ``` 2. CardBoard 컴포넌트에서 `SelctCard` 함수를 찾습니다. 매개 변수를 추가하고 타입을 Component로 변경합니다. Component를 **Card**로 지정합니다. ```lua [client only] void SelectCard (C
[task] | single_turn [pass] | ⏳ 미판정 [title] | 여러 개의 카드 보여주기 [question] | CreateCardEntities 함수에 매개변수를 추가했을 때 기존 호출부에서 에러가 나는 이유는 무엇인가요? [answer] | `CreateCardEntities` 함수에 매개변수를 추가했을 때 기존 호출부에서 에러가 나는 이유는 Lua 언어의 함수 호출 규칙 때문입니다. Lua에서 함수에 매개변수를 선언하면, 해당 함수를 호출할 때 반드시 해당 수만큼의 인수를 전달해야 합니다. 만약 함수 정의에서 매개변수를 추가했는데, 기존에 있던 호출부에서는 여전히 인수를 전달하지 않고 `self:CreateCardEntities()` 형태로 호출한다면, 함수 본문에서 해당 매개변수를 참조하는 순간 `nil` 값으로 인해 오류(예: `attempt to index a [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ```lua local cardCount = #cardTable for i = 1, cardCount do local spriteRUID = cardTable[i] ``` 6. `CreateCardEntities()` 함수에서 `local card = _SpawnService:SpawnByModelId(cardModelId, "Card", Vector3(0,0,0), self.CardSpawnRoot)`를 찾습니다. `"Card"` 부분을 `"Card " .. tostring(i)`로 변경하며 아
[task] | single_turn [pass] | ⏳ 미판정 [title] | 카드 섞기, 선택하기 [question] | HandleButtonClickEvent에서 self.Entity.Visible == false 조건을 먼저 확인하는 코드가 없다면 어떤 버그가 발생할 수 있는지에 대한 예시를 들어주세요. [answer] | `HandleButtonClickEvent` 핸들러에서 `self.Entity.Visible == false` 조건을 먼저 확인하지 않으면, **이미 비활성화(숨김)된 카드에 대한 클릭 이벤트가 여전히 처리되는 버그**가 발생할 수 있습니다. ### 발생할 수 있는 버그 시나리오 1. **상황**: 카드가 이미 정답으로 짝이 맞춰져서 `SetDestroyed()` 메서드를 통해 `self.Entity.Visible = false`로 설정되었습니다. 2. **문제**: 사용자가 그 영역을 클릭하거나, 이전 클릭 이벤트가 지연되어 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ```lua Property: [None] string SpriteRUID = "" Method: void CardOn () { self.Entity.SpriteGUIRendererComponent.Color = Color(81/255,81/255,81/255,1) } void CardOff () { self.Entity.SpriteGUIRendererComponent.Color = Color(255/255,255/255,255/255,1) } Event Handler: [self] HandleButtonClic
[task] | code [pass] | ⏳ 미판정 [title] | 카드 섞기, 선택하기 [question] | 생성된 카드 엔티티의 `Card` 컴포넌트와 `SpriteGUIRendererComponent`를 접근하여 `SpriteRUID`와 `ImageRUID`를 동일한 값으로 설정하는 `CreateCardEntities` 메서드를 작성해줘. [answer] | 제공된 문서의 `CreateCardEntities` 메서드 구현을 기반으로, 생성된 카드 엔티티의 `Card` 컴포넌트와 `SpriteGUIRendererComponent`에 동일한 `SpriteRUID` 값을 설정하는 코드를 작성했습니다. ```lua Property: [None] Entity CardSpawnRoot = /ui/CardParirGameUi/UI_Board/UI_Blocks/UI_Block_Group Method: [client only] void CreateCardEntities(table cardTable) [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ```lua Property: [None] Entity CardSpawnRoot = /ui/CardParirGameUi/UI_Board/UI_Blocks/UI_Block_Group [None] Card SelectedCard = nil Method: [client only] Void SetCards() { local deckTable = {} local cardTable = {} local deckCount = _DataService:GetRowCount("CardSet")
[task] | code [pass] | ⏳ 미판정 [title] | 카드 섞기, 선택하기 [question] | Card 컴포넌트에 [self] 컨텍스트의 HandleButtonClickEvent(BtnClickEvent event) 핸들러를 추가하고, self.Entity.Visible이 false인 경우 즉시 return 하며 그렇지 않으면 _CardPairGameLogic:OnCardClicked()를 호출하도록 작성해줘. [answer] | 참고 문서에 따르면 `HandleButtonClickEvent` 핸들러는 `ButtonClickEvent` 타입을 인수로 받으며, `BtnClickEvent`라는 이벤트 타입은 문서에 존재하지 않습니다. 또한, `_CardPairGameLogic:OnCardClicked()`를 호출할 때 문서의 예제 코드에서는 `self`를 인수로 전달하는 것으로 나타나므로, 정확한 동작을 위해 `self`를 함께 전달하도록 작성해야 합니다. 요구하신 로직(Visible 체크 후 즉시 return, 아니면 로직 호출)을 적용한 **Card** 컴 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 카드 섞기 카드 보여주기에 성공했으니 이번에는 한 쌍의 카드가 나오게 만들어 봅시다. 1. **CardBoard** 컴포넌트의 `SetCards()` 함수에서 세 번째 `for i = 1, deckCount do`를 찾습니다. 처음에는 deckCount의 수만큼 실행했지만, 이번에는 deckCount의 절반만큼만 실행하기 위해 (deckCount / 2)로 변경합니다. 그 절반을 동일하게 두 번 불러와 쌍을 이루는 판을 만듭니다. `table.insert`는 [table](/apiReference/Lua/table) 레
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 짝 맞추기 [question] | `IsPairOf` 함수가 반환하는 조건을 2개 이상의 카드를 비교할 때 성립하는 논리식 두 가지로 표로 정리하세요. [answer] | `IsPairOf` 함수는 두 카드가 짝이 되는지 확인하기 위해 다음 두 가지 조건이 모두 참(true)이어야만 `true`를 반환합니다. | 구분 | 조건식 | 설명 | | :--- | :--- | :--- | | 1 | `self.SpriteRUID == other.SpriteRUID` | 비교하는 두 카드의 `SpriteRUID` 값이 동일해야 합니다. (같은 모양/이미지의 카드) | | 2 | `self.Entity ~= other.Entity` | 비교하는 두 카드가 서로 다른 엔티티여야 합니다. (자기 자신과 비교하는 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 짝 맞추기 카드의 색상을 바꿔보았으니 이번에는 두 개의 카드를 선택하고, 맞으면 사라질 수 있도록 만들어봅시다. 1. **Card 컴포넌트**에서 `SetDestroyed` 함수에 아래와 같이 작성합니다. 카드 엔티티의 Visible 프로퍼티를 false로 바꾸어 비활성화 시킵니다. 이는 파괴된 상태로 사용합니다. ```lua [client only] void SetDestroyed () { self.Entity.Visible = false } ``` 2. 짝이
[task] | single_turn [pass] | ⏳ 미판정 [title] | 여러 개의 카드 보여주기 [question] | 카드 생성 루프 안에서 SpriteRUID를 카드와 SpriteGUIRendererComponent에 동시에 할당하는 코드의 역할을 설명해 주세요. [answer] | 카드 생성 루프 안에서 `SpriteRUID`를 두 곳에 동시에 할당하는 코드의 역할은 다음과 같습니다. 1. **`card.Card.SpriteRUID = spriteRUID`**: * 생성된 카드 엔티티에 붙어 있는 `Card` 컴포넌트의 `SpriteRUID` 프로퍼티에 값을 저장합니다. * 이 값은 카드의 **논리적 상태**를 나타냅니다. 예를 들어, `IsPairOf` 메서드에서 두 카드가 짝인지 비교할 때(`self.SpriteRUID == other.SpriteRUID`) 이 값이 사용되어 게임 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ```lua local cardCount = #cardTable for i = 1, cardCount do local spriteRUID = cardTable[i] ``` 6. `CreateCardEntities()` 함수에서 `local card = _SpawnService:SpawnByModelId(cardModelId, "Card", Vector3(0,0,0), self.CardSpawnRoot)`를 찾습니다. `"Card"` 부분을 `"Card " .. tostring(i)`로 변경하며 아
[task] | single_turn [pass] | ⏳ 미판정 [title] | 스크립트 라이프 사이클 [question] | 출시된 월드에서는 서버가 모든 맵을 불러오는데, 이 때문에 제작 중에는 잘 동작하던 로직이 출시 후 메모리나 성능 이슈로 오류가 나는 원인이 될 수 있는지 궁금합니다. [answer] | 네, 그 추정은 정확합니다. 메이플스토리 월드의 플레이 라이프 사이클 구조상, **제작 중인 월드**와 **출시된 월드**의 맵 로딩 방식 차이가 성능 및 메모리 이슈의 원인이 될 수 있습니다. 구체적인 원인과 동작 방식을 설명하면 다음과 같습니다. ### 1. 맵 로딩 방식의 차이 * **제작 중인 월드 (테스트 플레이 시):** * 서버는 기본적으로 **편집 중인 맵(플레이어가 있는 맵)만** 불러옵니다. * 하지만 `_EntityService`를 사용하여 다른 맵의 엔티티를 조회하거나, 플레이어가 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 서버와 클라이언트가 호출되는 전체 흐름을 알아봅시다. 플레이 라이프 사이클은 **제작 중인 월드**와 **출시된 월드**에서 약간의 차이가 있습니다. 가장 큰 차이점은 맵을 불러오는 방식입니다. * **제작 중인 월드**에서 플레이할 때는 10개의 맵을 만들었더라도 플레이어가 있는 맵만 불러옵니다. * **출시된 월드**에서는 플레이어 유무와 상관없이 모든 맵을 불러옵니다. 이와 같은 차이를 알고 있다면 **제작 중인 월드** 또는 **출시된 월드**에서 크리에이터 의도대로 동작하지 않는 무언가가 있을 때, 그 원인이 잘못된
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 여러 개의 카드 보여주기 [question] | CardBoard 컴포넌트의 SetCards와 CreateCardEntities 메서드만 출력하고 설명은 주석으로 작성하세요. [answer] | ```lua Property: [None] Entity CardSpawnRoot = /ui/CardParirGameUi/UI_Board/UI_Blocks/UI_Block_Group Method: [client only] void SetCards() { -- 데이터 서비스에서 카드 세트(CardSet)의 행 수를 조회하여 덱 크기를 결정합니다. local deckCount = _DataService:GetRowCount("CardSet") -- 1행부터 deckCount까지 반복하며, 각 행의 1열 값 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 전체 스크립트 #### CardBoard 컴포넌트 ```lua Property: [None] Entity CardSpawnRoot = /ui/CardParirGameUi/UI_Board/UI_Blocks/UI_Block_Group Method: [client only] Void SetCards() { local deckTable = {} local cardTable = {} local deckCount = _DataService:GetRowCount("CardSet") for
[task] | single_turn [pass] | ⏳ 미판정 [title] | 여러 개의 카드 보여주기 [question] | CreateCardEntities 함수의 매개변수를 table 타입으로 변경할 때 기존에 사용하던 단일 문자열 변수는 어떻게 처리해야 하나요? [answer] | `CreateCardEntities` 함수의 매개변수를 `table` 타입으로 변경하면, 이전에 함수 내부에서 사용하던 단일 문자열 변수(예: `local cardModelId = "..."` 또는 개별 카드의 RUID 문자열)는 더 이상 해당 매개변수를 통해 전달받지 않으므로, **함수 내부에서 직접 제거하거나, 테이블에서 필요한 값을 꺼내어 사용하는 방식으로 변경**해야 합니다. 참고 문서에 따르면 `CreateCardEntities(table cardTable)` 함수는 `cardTable`이라는 테이블을 매개변수로 받습니다 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 하나의 카드를 보여주는 것에 성공했으니, 이제는 모든 카드를 켜줄 차례입니다. 아래 그림과 같이 모든 카드가 나타나게 만들어 봅시다. # 여러 개의 카드 보여주기 하나의 카드가 잘 나타나는 걸 확인했으니 이제는 모든 카드를 모든 카드를 뒤섞어 보여줄 차례입니다. 이 과정에서 다양한 함수와 이전에 완성한 **CardSet** 데이터 셋을 활용합니다. [DataService](/apiReference/Services/DataService)를 참고하세요. 1. **Card** 컴포넌트를 활용해 여러 개의 RUID 값을 가지고 올 수
[task] | single_turn [pass] | ⏳ 미판정 [title] | 짝 맞추기 [question] | SetCards 함수 실행 시 기존에 남아있는 카드 엔티티를 정리하기 위해 ClearCardEntities 함수를 언제 호출해야 하는지 알려주세요. [answer] | `SetCards` 함수 내에서 새로운 카드 엔티티를 생성하기 직전에 `ClearCardEntities` 함수를 호출해야 합니다. 구체적으로 `SetCards` 함수의 본문에서 `self:CreateCardEntities(cardTable)` 호출 윗 줄에 `self:ClearCardEntities()`를 배치하면, 기존에 남아있는 카드 엔티티가 모두 파괴된 후 새로운 카드판이 생성됩니다. ```lua [client only] void SetCards() { self:ClearCardEntities() self:C [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 짝 맞추기 카드의 색상을 바꿔보았으니 이번에는 두 개의 카드를 선택하고, 맞으면 사라질 수 있도록 만들어봅시다. 1. **Card 컴포넌트**에서 `SetDestroyed` 함수에 아래와 같이 작성합니다. 카드 엔티티의 Visible 프로퍼티를 false로 바꾸어 비활성화 시킵니다. 이는 파괴된 상태로 사용합니다. ```lua [client only] void SetDestroyed () { self.Entity.Visible = false } ``` 2. 짝이
[task] | code [pass] | ⏳ 미판정 [title] | 짝 맞추기 [question] | CardBoard 컴포넌트의 CardSpawnRoot 자식들을 모두 Destroy()하는 ClearCardEntities() 함수를 작성해줘 [answer] | CardBoard 컴포넌트에 `CardSpawnRoot`의 모든 자식 엔티티를 파괴하는 `ClearCardEntities()` 함수는 다음과 같이 작성합니다. ```lua Method: [client only] void ClearCardEntities() { local cardEntities = self.CardSpawnRoot.Children for i = 1, #cardEntities do cardEntities[i]:Destroy() end } ``` [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 짝 맞추기 카드의 색상을 바꿔보았으니 이번에는 두 개의 카드를 선택하고, 맞으면 사라질 수 있도록 만들어봅시다. 1. **Card 컴포넌트**에서 `SetDestroyed` 함수에 아래와 같이 작성합니다. 카드 엔티티의 Visible 프로퍼티를 false로 바꾸어 비활성화 시킵니다. 이는 파괴된 상태로 사용합니다. ```lua [client only] void SetDestroyed () { self.Entity.Visible = false } ``` 2. 짝이
[task] | code [pass] | ⏳ 미판정 [title] | 카드 섞기, 선택하기 [question] | Model_Card 엔티티에 ButtonComponent를 적용하고, 카드 선택 시 색상 변동을 위해 Card 컴포넌트의 CardOn/CardOff 함수가 SpriteGUIRendererComponent의 Color 속성을 제어하도록 연결하는 스크립트를 작성해줘. [answer] | Model_Card 엔티티에 ButtonComponent를 추가하면 카드가 눌렸을 때 ButtonClickEvent가 발생합니다. 이 이벤트를 처리하기 위해 Card 컴포넌트에 HandleButtonClickEvent 이벤트 핸들러를 정의하고, 카드가 눌렸을 때(ColorOn)와 해제되었을 때(ColorOff) SpriteGUIRendererComponent의 Color 속성을 변경하는 CardOn과 CardOff 메서드를 구현해야 합니다. 참고 문서에 제시된 코드 예제와 API를 기반으로 작성한 Card 컴포넌트 스크립트는 다음 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 카드 섞기 카드 보여주기에 성공했으니 이번에는 한 쌍의 카드가 나오게 만들어 봅시다. 1. **CardBoard** 컴포넌트의 `SetCards()` 함수에서 세 번째 `for i = 1, deckCount do`를 찾습니다. 처음에는 deckCount의 수만큼 실행했지만, 이번에는 deckCount의 절반만큼만 실행하기 위해 (deckCount / 2)로 변경합니다. 그 절반을 동일하게 두 번 불러와 쌍을 이루는 판을 만듭니다. `table.insert`는 [table](/apiReference/Lua/table) 레
[task] | single_turn [pass] | ⏳ 미판정 [title] | 짝 맞추기 [question] | IsPairOf 함수에서 두 카드가 같은 모양일 때 self.Entity ~= other.Entity 조건까지 함께 확인해야 하는 이유는 무엇인가요? [answer] | `IsPairOf` 함수에서 `self.SpriteRUID == other.SpriteRUID` 조건만으로 판단하면, 같은 모양의 카드 두 장이 아닌 **같은 카드 엔티티 한 장**을 두 번 클릭한 경우에도 "짝이 맞다"고 오인할 수 있습니다. 메이플스토리 월드의 카드 짝 맞추기 게임 로직에서 한 장의 카드를 선택한 상태에서 다시 그 카드를 클릭하게 되면, `oldCard`와 `newCard`가 실제 동일한 엔티티를 가리키게 됩니다. 만약 `self.Entity ~= other.Entity` 조건이 없다면, 같은 카드 한 장이 짝 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 짝 맞추기 카드의 색상을 바꿔보았으니 이번에는 두 개의 카드를 선택하고, 맞으면 사라질 수 있도록 만들어봅시다. 1. **Card 컴포넌트**에서 `SetDestroyed` 함수에 아래와 같이 작성합니다. 카드 엔티티의 Visible 프로퍼티를 false로 바꾸어 비활성화 시킵니다. 이는 파괴된 상태로 사용합니다. ```lua [client only] void SetDestroyed () { self.Entity.Visible = false } ``` 2. 짝이
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 짝 맞추기 [question] | Card 스크립트에서 IsPairOf 메서드가 두 카드가 짝으로 판정되려면 만족해야 하는 조건을 2문장 이내로 설명하세요. [answer] | 두 카드의 `SpriteRUID` 프로퍼티 값이 동일해야 합니다. 또한 두 카드는 서로 다른 엔티티(`self.Entity ~= other.Entity`)여야 합니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ```lua Property: [None] string SpriteRUID = "" Method: void CardOn() { self.Entity.SpriteGUIRendererComponent.Color = Color(81/255,81/255,81/255,1) } void CardOff() { self.Entity.SpriteGUIRendererComponent.Color = Color(255/255,255/255,255/255,1) } void SetDestroyed() { self.Entity.Vi
[task] | single_turn [pass] | ⏳ 미판정 [title] | 짝 맞추기 [question] | SetDestroyed에서 Visible을 false로 설정하면 이후 클릭 이벤트가 처리되지 않는 원리 [answer] | `SetDestroyed` 메서드에서 `self.Entity.Visible = false`를 설정하면 해당 엔티티가 비활성화 상태가 되어 이후 클릭 이벤트가 처리되지 않습니다. 이 원리는 카드 스크립트의 이벤트 핸들러 첫 줄에서 확인할 수 있습니다. 이벤트가 발생하면 다음 조건문을 먼저 실행합니다. ```lua if self.Entity.Visible == false then return end ``` 즉, `Visible` 프로퍼티가 `false`로 변경된 상태(즉, `SetDestroyed`가 호출된 상태)의 카드를 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ```lua Property: [None] string SpriteRUID = "" Method: void CardOn() { self.Entity.SpriteGUIRendererComponent.Color = Color(81/255,81/255,81/255,1) } void CardOff() { self.Entity.SpriteGUIRendererComponent.Color = Color(255/255,255/255,255/255,1) } void SetDestroyed() { self.Entity.Vi
[task] | single_turn [pass] | ⏳ 미판정 [title] | 카드 섞기, 선택하기 [question] | `HandleButtonClickEvent` 핸들러 내부에서 `self.Entity.Visible`이 `false`인 경우 바로 `return`하는 처리가 왜 필수적인지, 그리고 `_CardPairGameLogic:OnCardClicked()` 호출 전후의 차이점을 알려주세요. [answer] | 카드 짝 맞추기 게임 로직에서 `HandleButtonClickEvent` 핸들러의 `self.Entity.Visible` 체크와 `_CardPairGameLogic:OnCardClicked()` 호출은 다음과 같은 이유로 필수적입니다. ### 1. `self.Entity.Visible == false`일 때 `return`하는 이유 카드가 이미 선택되어서 뒤집히거나, 게임 로직에 의해 비활성화(보이지 않는 상태로 전환)된 상태라면, 해당 카드를 다시 클릭하는 것은 무의미하며 오히려 게임 로직을 깨뜨릴 수 있습니다. * * [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 카드 섞기 카드 보여주기에 성공했으니 이번에는 한 쌍의 카드가 나오게 만들어 봅시다. 1. **CardBoard** 컴포넌트의 `SetCards()` 함수에서 세 번째 `for i = 1, deckCount do`를 찾습니다. 처음에는 deckCount의 수만큼 실행했지만, 이번에는 deckCount의 절반만큼만 실행하기 위해 (deckCount / 2)로 변경합니다. 그 절반을 동일하게 두 번 불러와 쌍을 이루는 판을 만듭니다. `table.insert`는 [table](/apiReference/Lua/table) 레
[task] | code [pass] | ⏳ 미판정 [title] | 짝 맞추기 [question] | Card 컴포넌트의 IsPairOf() 함수에서 SpriteRUID가 동일하고 Entity가 서로 다른지 확인하여 boolean을 반환하도록 작성해줘 [answer] | Card 컴포넌트에서 `IsPairOf()` 함수는 다른 Card 인스턴스(`other`)를 매개변수로 받아, 두 카드의 `SpriteRUID`가 같고 실제 엔티티(`Entity`)가 서로 다른지 확인합니다. 이 조건이 모두 참이면 `true`를 반환하여 짝이 맞는 것으로 판단합니다. ```lua [client only] boolean IsPairOf (Card other) { return (self.SpriteRUID == other.SpriteRUID) and (self.Entity ~= other.Entity) } ` [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 짝 맞추기 카드의 색상을 바꿔보았으니 이번에는 두 개의 카드를 선택하고, 맞으면 사라질 수 있도록 만들어봅시다. 1. **Card 컴포넌트**에서 `SetDestroyed` 함수에 아래와 같이 작성합니다. 카드 엔티티의 Visible 프로퍼티를 false로 바꾸어 비활성화 시킵니다. 이는 파괴된 상태로 사용합니다. ```lua [client only] void SetDestroyed () { self.Entity.Visible = false } ``` 2. 짝이
[task] | single_turn [pass] | ⏳ 미판정 [title] | 여러 개의 카드 보여주기 [question] | SpawnByModelId 호출 시 네이밍 규칙을 어떻게 바꿔야 카드 개수만큼 고유한 엔티티가 생성되는지 알려주세요. [answer] | `SpawnByModelId` 호출 시 카드 개수만큼 고유한 엔티티가 생성되려면, 두 번째 인자인 엔티티 이름에 카드의 순번을 포함시켜야 합니다. 기존의 `"Card"`와 같은 고정된 문자열 대신, `"Card " .. tostring(i)`와 같이 순번 변수 `i`를 문자열로 변환하여 연결하는 방식을 사용합니다. 이를 통해 각 카드가 고유한 이름(예: "Card 1", "Card 2", ...)을 가지게 되어 식별이 가능합니다. 아래는 `CreateCardEntities()` 함수에 `cardTable` 매개변수를 추가하고, [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ```lua local cardCount = #cardTable for i = 1, cardCount do local spriteRUID = cardTable[i] ``` 6. `CreateCardEntities()` 함수에서 `local card = _SpawnService:SpawnByModelId(cardModelId, "Card", Vector3(0,0,0), self.CardSpawnRoot)`를 찾습니다. `"Card"` 부분을 `"Card " .. tostring(i)`로 변경하며 아
[task] | code [pass] | ⏳ 미판정 [title] | 짝 맞추기 [question] | CardSpawnRoot 자식 엔티티들을 순회하며 Destroy 로 삭제하는 ClearCardEntities 메서드를 작성해줘 [answer] | 참고 문서에 포함된 `ClearCardEntities` 메서드 구현 방식을 기준으로 작성했습니다. `self.CardSpawnRoot`의 `Children` 배열을 순회하며 각 엔티티에 `Destroy()`를 호출하는 로직입니다. ```lua Method: [client only] void ClearCardEntities () { local cardEntities = self.CardSpawnRoot.Children for i = 1, #cardEntities do cardEntit [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 Method: void SetCards () { local deckTable = {} local cardTable = {} local deckCount = _DataService:GetRowCount("CardSet") for i = 1, deckCoun
[task] | single_turn [pass] | ⏳ 미판정 [title] | 짝 맞추기 [question] | SpriteGUIRendererComponent의 Color를 변경할 때 왜 255로 나누어 소수값을 주어야 하는지 [answer] | 참고 문서에 제시된 코드 예제(`CardOn`, `CardOff`)를 보면 `SpriteGUIRendererComponent.Color`를 설정할 때 `Color(81/255, 81/255, 81/255, 1)`과 `Color(255/255, 255/255, 255/255, 1)`처럼 255로 나누어 소수값을 전달하는 것을 확인할 수 있습니다. 이는 `Color` 생성자가 0.0에서 1.0 사이의 부동소수점(float) 값을 색상 채널(R, G, B, A)로 기대하기 때문입니다. 일반적인 그래픽 시스템에서 색상 값은 0부터 255 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ```lua Property: [None] string SpriteRUID = "" Method: void CardOn() { self.Entity.SpriteGUIRendererComponent.Color = Color(81/255,81/255,81/255,1) } void CardOff() { self.Entity.SpriteGUIRendererComponent.Color = Color(255/255,255/255,255/255,1) } void SetDestroyed() { self.Entity.Vi
[task] | code [pass] | ⏳ 미판정 [title] | 짝 맞추기 [question] | CardSpawnRoot 자식들의 Visible 상태를 확인하여 모든 카드가 비활성화되었는지 반환하는 IsEmpty 메서드를 작성해줘 [answer] | CardSpawnRoot의 자식 Entity 목록을 순회하며 `Visible` 속성이 `false`인 경우가 있는지 확인하는 로직으로, 하나라도 `true`가 있으면 `false`를 반환하고 모두 `false`이면 `true`를 반환합니다. ```lua [client only] boolean IsEmpty() { local cards = self.CardSpawnRoot.Children for i = 1, #cards do if cards[i].Visible then return [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 Method: void SetCards () { local deckTable = {} local cardTable = {} local deckCount = _DataService:GetRowCount("CardSet") for i = 1, deckCoun
[task] | single_turn [pass] | ⏳ 미판정 [title] | 짝 맞추기 [question] | IsPairOf 메서드에서 self.Entity ~= other.Entity 조건이 반드시 필요한 이유 [answer] | `IsPairOf` 메서드에서 `self.Entity ~= other.Entity` 조건이 필요한 이유는 **같은 카드 객체 자신을 서로 다른 두 카드처럼 오인하는 것을 방지하기** 위해서입니다. 짝 맞추기 게임에서는 한 장의 카드를 클릭한 뒤, 그 카드와 **다른** 카드 중 같은 이미지(`SpriteRUID`)를 가진 카드를 찾아야 합니다. 만약 `self.Entity ~= other.Entity` 조건이 없다면, 이미 선택된 카드(`oldCard`)를 다시 클릭했을 때 `oldCard:IsPairOf(oldCard)`가 호출 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | ```lua Property: [None] string SpriteRUID = "" Method: void CardOn() { self.Entity.SpriteGUIRendererComponent.Color = Color(81/255,81/255,81/255,1) } void CardOff() { self.Entity.SpriteGUIRendererComponent.Color = Color(255/255,255/255,255/255,1) } void SetDestroyed() { self.Entity.Vi
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 뒤통수를 덮는 모자 [question] | 빨간색 레이어인 data:vslot:CpH1H4가 모자 아이템에서 어떤 역할을 하는지 3문장 이내로 설명해 주세요. [answer] | 빨간색 레이어인 data:vslot:CpH1H4는 모자와 헤어의 겹침 우선순위를 결정하는 역할을 합니다. 이 레이어를 통해 머리카락과 모자가 겹칠 때 어느 쪽이 위에 보이는지 지정할 수 있습니다. 모자 아이템에만 존재하는 개념으로, 올바른 렌더링 순서를 보장하기 위해 필요합니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 뒤통수를 덮는 모자의 제작 시트를 소개합니다. [아바타 아이템 제작의 기본 개념](/docs?postId=588%7B%22target%22:%22_self%22%7D)을 참고해 아바타의 기본 개념을 먼저 숙지하길 바랍니다. # 사전 준비 아이템 제작을 위해선 Adobe Photoshop이 필요합니다. 해당 소프트웨어를 설치한 후 아래 파일을 받아 엽니다. $$attachFile {"name":"Avatar_Cap_B.psd","url":"https://mod-file.dn.nexoncdn.co.kr/sto