[task] | code [pass] | ⏳ 미판정 [title] | 몬스터를 스폰해 보자 [question] | MonsterSpawn 컴포넌트의 SpawnMonster 메서드를 number 타입 spawnCount 매개변수를 받아 _SpawnService:SpawnByModelId로 몬스터를 생성하고 MonsterArray에 등록하는 스크립트를 작성해줘 [answer] | 요청하신 `SpawnMonster` 메서드와, 이를 지원하기 위해 `spawnCount` 개수만큼의 무작위 좌표를 생성하는 `GetRandomPosition` 메서드를 포함하여 작성했습니다. ```lua Method: [server only] table GetRandomPosition(number spawnCount) { local leftX = -3.571 local rightX = 2.688 local y = 0.170 local areaWidth = rightX - leftX local p [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 이번에는 5초마다 한 마리씩 스폰하는 것이 아니라, 5초마다 현재 몬스터 수량을 체크하여 최대 몬스터 수량보다 부족한 만큼 생성하도록 수정해 봅시다. 1. **MonsterSpawn**의 `GetRandomPosition()` 함수에 매개 변수를 하나 추가합니다. 2. 매개 변수 타입은 **number**로, 이름은 **spawnCount**로 설정하고, 리턴 타입을 **table**로 변경합니다. 함수는 다음과 같이 수정합니다. ```lua table GetRandomPosition(nu
[task] | single_turn [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | Vector3 변수를 다른 변수에 대입한 후 원본의 x 값을 수정했는데, 왜 새 변수의 값도 함께 변하는 건가요? [answer] | Vector3는 참조 타입이기 때문입니다. `Vector3` 변수를 다른 변수에 대입할 때 값 자체를 복사하는 것이 아니라, 같은 메모리 주소를 가리키는 참조를 복사합니다. 따라서 원본 변수와 새 변수는 실제로는 동일한 `Vector3` 객체를 공유하고 있습니다. 이 상태에서 원본 변수의 `x` 값을 수정하면, 새 변수가 가리키고 있는 동일한 객체의 `x` 값도 함께 변경됩니다. 아래 코드를 통해 이 동작을 확인할 수 있습니다. ```lua Method: [Server Only] void OnBeginPlay() { [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 3차원 벡터와 위치를 표현합니다. **Vector2**와 마찬가지로 프로퍼티 또는 변수에 할당 시 참조됩니다. **Vector2**에서 멤버 변수 z가 추가됩니다. **TransformComponent**의 **Position**에 할당할 때를 제외하곤 주로 **Vector2**를 사용합니다. 값의 생성과 참조, 그리고 멤버 변수 x, y, z에 접근하는 방법은 아래를 참고합니다. ```lua Method: [Server Only] void OnBeginPlay() { local tmpVector3 = Vector3(10,
[task] | single_turn [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | SyncTable<key, value> 를 사용할 때 key 와 value 의 타입을 자유롭게 지정할 수 없나요? [answer] | SyncTable<key, value>에서 key와 value의 타입은 완전히 자유롭게 지정할 수 없습니다. 각 자리에 정해진 타입 목록 중 하나를 선택하여 설정해야 합니다. 참고로 SyncTable<key, value>는 일반 table 프로퍼티와 달리 동기화(Sync)를 지원하지만, 일반 table보다는 성능이 다소 느리다는 점을 유의해야 합니다. 또한 `next` 함수를 사용할 수 없습니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | #### table 루아 테이블을 참조합니다. 동기화(Sync)는 되지 않습니다. ```lua Property: [None] table TableProperty = {1,2,3,4,5} Method: [Server Only] void OnBeginPlay() { local tmpTable = self.TableProperty self.TableProperty[1] = 10 log("tmpTable : "..tmpTable[1]) --콘솔 창에 "tmpTable : 10" 출력 log("TablePr
[task] | single_turn [pass] | ⏳ 미판정 [title] | 몬스터를 스폰해 보자 [question] | 몬스터가 항상 화면 왼쪽에서만 스폰되는 경우 GetRandomPosition 함수에서 leftX 와 rightX 값의 설정에 어떤 문제가 있을 수 있을까요? [answer] | 몬스터가 항상 화면 왼쪽에서만 스폰되는 현상은 `GetRandomPosition()` 함수 내부의 좌표 계산 로직, 특히 `leftX`와 `rightX` 값의 관계나 실제 맵 좌표계와 일치하지 않는 경우에서 발생할 수 있습니다. 제공된 문서의 코드 예시를 기준으로 분석해 보면 다음과 같은 문제점이 있을 수 있습니다. ```lua Vector3 GetRandomPosition() { local leftX = -3.571 -- 스폰 영역 왼쪽의 x좌표 local rightX = 2.688 -- 스폰 영역 오른쪽의 x좌 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 지정한 영역 안에서 랜덤한 위치에 몬스터를 스폰하는 기능을 추가하겠습니다. 위에 확인했던 좌표를 기준으로 스폰 영역을 정의하고, 영역 내 랜덤한 위치에 몬스터를 스폰해 봅시다. 1. **MonsterSpawn**을 스크립트 에디터에서 열고 **Vector3** 타입의 새 함수 `GetRandomPosition()`을 추가합니다. 2. `GetRandomPosition()` 함수를 다음과 같이 작성합니다. 위에서 기록해두었던 좌표를 함수에 활용합니다. ```lua Vector3 GetRandomPositio
[task] | two_turn [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | 자기 컴포넌트 프로퍼티에 self.로 접근하는 기본 문법은? [answer] | 자기 컴포넌트의 프로퍼티에 접근하려면 `self.프로퍼티명`을 사용합니다. 아래 예시에서 `self.StringProperty`를 통해 같은 컴포넌트에 선언된 `StringProperty`의 값을 가져와 `local` 변수에 대입하고 있습니다. ```lua Property: [None] string StringProperty = "Hello!!" Method: [Server Only] void OnBeginPlay() { local stringProperty = self.StringProperty log(str [followup] | 다른 엔티티의 컴포넌트 프로퍼티를 _EntityService로 가져와 대입하는 코드 예시를 보여줘 [followup_answer] | 다른 엔티티의 컴포넌트 프로퍼티를 가져와 대입하려면 `_EntityService:GetEntityByPath`로 엔티티를 찾고, 해당 엔티티에서 컴포넌트를 참조한 뒤 프로퍼티에 접근합니다. 아래 예시는 엔티티 "A"의 "ComponentA"에 있는 `PropertyA` 값을 읽어와서 현재 컴포넌트의 `PropertyB`에 대입하는 코드입니다. ```lua Property: [None] string PropertyB = "" Method: [Server Only] void OnBeginPlay() { local entit [scores] | [judge_reason] | [source_doc] | 함수에서 프로퍼티에 접근하는 방법을 알아봅시다. 먼저 같은 컴포넌트에서는 다음과 같이 접근할 수 있습니다. ```lua Property: [None] string StringProperty = "Hello!!" Method: [Server Only] void OnBeginPlay() { local stringProperty = self.StringProperty --"self.프로퍼티명"으로 해당 프로퍼티를 가져옴 } ``` 다른 컴포넌트의 프로퍼티는 다음과 같이 접근할 수 있습니다. 아래 상황에서 접근하는 방법
[task] | single_turn [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | SyncTable 프로퍼티를 사용하려고 했는데 next 함수를 호출할 수 없다는 오류가 발생했는데, 일반 table 프로퍼티와의 차이점이 무엇 때문인가요? [answer] | SyncTable 프로퍼티는 일반 table 프로퍼티와 달리 `next` 함수를 사용할 수 없도록 제한된 타입입니다. 일반 table 프로퍼티는 표준 루아 테이블을 참조하며, 내부적으로 테이블을 순회하거나 키를 확인하는 데 사용되는 `next` 함수를 자유롭게 호출할 수 있습니다. 하지만 SyncTable< v > 또는 SyncTable< k, v > 프로퍼티는 서버와 클라이언트 간에 동기화(Sync)되도록 설계되었기 때문에, 이 동기화 메커니즘을 위해 테이블의 구조와 접근 방식에 제약이 걸려 있습니다. 이 제약으로 인해: 1. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | #### table 루아 테이블을 참조합니다. 동기화(Sync)는 되지 않습니다. ```lua Property: [None] table TableProperty = {1,2,3,4,5} Method: [Server Only] void OnBeginPlay() { local tmpTable = self.TableProperty self.TableProperty[1] = 10 log("tmpTable : "..tmpTable[1]) --콘솔 창에 "tmpTable : 10" 출력 log("TablePr
[task] | two_turn [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | 다른 컴포넌트 프로퍼티 접근 시 엔티티 경로가 정확하지 않으면 어떻게 처리해야 하나? [answer] | 참고 문서에는 다른 컴포넌트 프로퍼티 접근 시 엔티티 경로가 정확하지 않을 경우(경로 오류)를 처리하는 방법이나 예외 처리에 대한 내용이 포함되어 있지 않습니다. 문서에는 `_EntityService:GetEntityByPath("/maps/map01/A")`와 같이 경로를 지정하여 엔티티를 가져오는 방법과, 가져온 엔티티를 통해 `entityA.ComponentA` 형태로 컴포넌트에 접근하여 프로퍼티 값을 읽어오는 정상적인 흐름만 설명되어 있습니다. 따라서 경로가 잘못된 경우의 처리 방안은 이 문서의 범위를 벗어납니다. [followup] | 경로가 존재하지 않을 때 에러를 방지하는 안전 코드 작성법을 알려줘 [followup_answer] | 제공된 참고 문서에는 경로가 존재하지 않을 때의 에러 방지 방법이나 안전 코드 작성법에 대한 내용이 포함되어 있지 않습니다. 문서에는 `_EntityService:GetEntityByPath`를 사용하여 엔티티를 가져오고, 이를 통해 다른 컴포넌트의 프로퍼티에 접근하는 정상적인 코드 구조만 예시되어 있으며, 경로 오류 발생 시의 처리나 null 체크 등 방어적 프로그래밍 기법은 설명되어 있지 않습니다. [scores] | [judge_reason] | [source_doc] | 함수에서 프로퍼티에 접근하는 방법을 알아봅시다. 먼저 같은 컴포넌트에서는 다음과 같이 접근할 수 있습니다. ```lua Property: [None] string StringProperty = "Hello!!" Method: [Server Only] void OnBeginPlay() { local stringProperty = self.StringProperty --"self.프로퍼티명"으로 해당 프로퍼티를 가져옴 } ``` 다른 컴포넌트의 프로퍼티는 다음과 같이 접근할 수 있습니다. 아래 상황에서 접근하는 방법
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | 3문장 이내로 _T 프로퍼티가 일반 프로퍼티와 다른 생성 시점과 동기화 가능 여부를 설명하세요. [answer] | 일반 프로퍼티는 컴포넌트 선언 시점에 미리 정의되는 반면, _T 프로퍼티는 함수 내에서 처음 사용된 시점에 자동으로 생성됩니다. 또한 일반 프로퍼티는 동기화(Sync) 설정이 가능한 반면, _T 프로퍼티는 동기화를 할 수 없으므로 동일한 실행 공간 내에서만 사용해야 합니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 프로퍼티는 컴포넌트 내 멤버 변수로, 함수에서 쓰고 읽어도 종료되지 않고 함수에서 설정한 값이 보존됩니다. 따라서 함수를 통해 어떤 값을 담아놓거나 함수를 반복 호출하여 값을 누적할 때 활용될 수 있습니다. 다음은 3초 간격으로 콘솔 창에 "HelloMSW"를 출력하는 예제입니다. ```lua Property: [Sync] number AccTime = 0 Method: [Server Only] void OnUpdate(delta) { self.AccTime = self.AccTime + delta --OnUpd
[task] | single_turn [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | integer 타입 프로퍼티에 3.28 같은 실숫값을 대입하면 왜 소수부가 버려져 3이 되는지, number 타입과의 차이점을 설명해 주세요. [answer] | `integer` 타입 프로퍼티에 `3.28` 같은 실숫값을 대입하면 소수부가 버려져 `3`이 되는 이유는, `integer` 타입 자체가 **정숫값만** 저장하도록 설계되어 있기 때문입니다. 이 타입에 실숫값을 넣을 경우 시스템은 소수점을 기준으로 소수부를 잘라내고 정수 부분만 유지합니다. 반면 `number` 타입은 정수, 실수를 가리지 않고 **모든 숫자 값**을 그대로 저장합니다. 따라서 `number` 타입에 `3.28`을 대입하면 소수점 이하 자리까지 포함해 `3.28`이 정확히 유지됩니다. 두 타입의 차이를 코드로 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 이번 과정에서는 메이플스토리 월드의 프로퍼티가 무엇인지 알아봅니다. 또한 프로퍼티 선언 및 초깃값 설정, 프로퍼티 동기화 방법 등 프로퍼티 관련 정보를 살펴보겠습니다. # MSW의 프로퍼티 프로퍼티는 컴포넌트의 멤버 변수로써, 엔티티 별로 다른 값을 가질 수 있습니다. 프로퍼티는 세 가지 특징이 있습니다. * 루아 변수와 달리 메이플스토리 월드의 프로퍼티는 선언할 때 타입을 설정하고, 값을 대입하거나 참조할 수 있습니다. * 컴포넌트 내 외부에서 프로퍼티에 접근할 수 있고, 각 함수에서 값을 읽거나 쓸 수
[task] | single_turn [pass] | ⏳ 미판정 [title] | 몬스터를 스폰해 보자 [question] | _spawnService:SpawnByModelId 호출 시 parent 인자로 _EntityService:GetEntityByPath("/maps/map01")을 사용하는 것이 아니라 특정 오브젝트 엔티티를 부모로 지정하고 싶은 경우 어떻게 해야 하나요? [answer] | `_SpawnService:SpawnByModelId`의 마지막 인자인 `parent`에는 스폰할 엔티티의 부모가 될 `Entity` 객체를 전달하면 됩니다. 특정 오브젝트 엔티티를 부모로 지정하려면 해당 오브젝트의 경로를 `_EntityService:GetEntityByPath`로 조회하여 `Entity` 변수에 저장한 뒤, 그 변수를 `parent` 인자로 넘기면 됩니다. 다음은 특정 오브젝트(경로 `/maps/map01/obj/target_parent`)를 부모로 지정하여 몬스터를 스폰하는 예시입니다. ```lua Meth [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 이번에는 5초마다 한 마리씩 스폰하는 것이 아니라, 5초마다 현재 몬스터 수량을 체크하여 최대 몬스터 수량보다 부족한 만큼 생성하도록 수정해 봅시다. 1. **MonsterSpawn**의 `GetRandomPosition()` 함수에 매개 변수를 하나 추가합니다. 2. 매개 변수 타입은 **number**로, 이름은 **spawnCount**로 설정하고, 리턴 타입을 **table**로 변경합니다. 함수는 다음과 같이 수정합니다. ```lua table GetRandomPosition(nu
[task] | single_turn [pass] | ⏳ 미판정 [title] | 몬스터를 스폰해 보자 [question] | GetRandomPosition 함수가 table 타입으로 리턴값을 변경하면서 기존에 단일 좌표를 기대하던 다른 코드들이 깨진다면 어떻게 수정해야 하나요? [answer] | `GetRandomPosition` 함수의 리턴 타입이 `table`로 변경되면, 기존에 단일 좌표(`Vector3`)를 기대하고 있던 호출부 코드들은 반드시 수정해야 합니다. `table`은 배열처럼 인덱스 접근이 가능하므로, 단일 좌표가 필요한 경우 테이블의 첫 번째 요소(`positions[1]`)를 꺼내서 사용해야 합니다. 아래는 단일 좌표를 기대하던 기존 코드를 수정하는 예시입니다. ```lua Method: [server only] void SpawnSingleMonster() { local parent = _ [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 이번에는 5초마다 한 마리씩 스폰하는 것이 아니라, 5초마다 현재 몬스터 수량을 체크하여 최대 몬스터 수량보다 부족한 만큼 생성하도록 수정해 봅시다. 1. **MonsterSpawn**의 `GetRandomPosition()` 함수에 매개 변수를 하나 추가합니다. 2. 매개 변수 타입은 **number**로, 이름은 **spawnCount**로 설정하고, 리턴 타입을 **table**로 변경합니다. 함수는 다음과 같이 수정합니다. ```lua table GetRandomPosition(nu
[task] | two_turn [pass] | ⏳ 미판정 [title] | 몬스터를 스폰해 보자 [question] | `SpawnMonster()` 함수에서 `_EntityService:GetEntityByPath("/maps/map01")`으로 부모 엔티티를 조회하는데, 이 경로가 존재하지 않을 경우 어떻게 동작하는지 궁금합니다. [answer] | 제공된 참고 문서에는 `_EntityService:GetEntityByPath()` 메서드가 반환값을 확인하거나(예: `nil` 체크), 경로가 존재하지 않을 때 발생하는 예외(에러) 처리, 또는 대체 동작 방식에 대한 설명이 포함되어 있지 않습니다. 따라서 해당 경로가 존재하지 않을 경우의 정확한 동작 방식은 알 수 없습니다. [followup] | 경로가 존재하지 않을 때 `_SpawnService:SpawnByModelId` 호출 시 발생하는 에러 유형과, 스크립트 실행이 중단되지 않도록 안전하게 처리하는 예외 처리 코드를 작성해 주세요. [followup_answer] | 제공된 참고 문서에는 `_EntityService:GetEntityByPath()` 또는 `_SpawnService:SpawnByModelId()` 호출 시 경로나 엔티티가 존재하지 않을 경우 발생하는 구체적인 에러 유형(예외 타입, 에러 메시지 등)에 대한 정보가 없습니다. 또한, 이러한 오류 상황을 감지하고 스크립트 실행을 중단되지 않도록 방지하기 위한 예외 처리 코드(try-catch 블록, nil 체크 로직 등)에 대한 가이드라인이나 예제도 포함되어 있지 않습니다. 따라서 해당 상황에서 발생하는 에러 유형과 안전한 예외 처 [scores] | [judge_reason] | [source_doc] | 이번에는 5초마다 몬스터를 한 마리씩 추가로 스폰하되, 최대 10개까지만 생성하는 기능을 만들어보겠습니다. 1. **MonsterSpawn**을 열고 프로퍼티를 추가합니다. ```lua Property: [Sync] number MaxSpawnCount = 10 [Sync] number Time = 0 [None] table MonsterArray = {} ``` 2. `OnBeginPlay()` 함수를 다음과 같이 수정합니다. ```lua
[task] | two_turn [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | 일반 프로퍼티와 _T 프로퍼티의 생성 시점 및 동기화 가능 여부 차이는 무엇인가요? [answer] | 일반 프로퍼티와 _T 프로퍼티는 생성 시점과 동기화 가능 여부에서 다음과 같은 차이가 있습니다. **생성 시점** * **일반 프로퍼티**: `Property:` 블록에서 명시적으로 선언하고 초기값을 지정해야 생성됩니다. * **_T 프로퍼티**: `Property:` 블록에서 선언할 필요 없이, 코드의 함수 내에서 **처음 사용(쓰는) 시점에 즉시 생성**됩니다. 따라서 초기화 로직(예: `if self._T.accTime == nil then self._T.accTime = 0 end`)을 코드 내부에 직접 작성해야 합 [followup] | _T 프로퍼티를 사용할 때 nil 체크가 필요한 이유와, 외부 컴포넌트에서 _T 프로퍼티에 접근할 때 self를 붙이지 않아도 되는지 코드 예시로 설명해 주세요. [followup_answer] | ### 1. `_T` 프로퍼티에 `nil` 체크가 필요한 이유 `_T` 프로퍼티는 일반 프로퍼티와 달리 `Property:` 블록에서 미리 선언하고 초기값을 설정하는 과정이 없습니다. 대신 **코드의 함수 내에서 처음 사용(쓰는) 시점에 생성**됩니다. 첫 호출 시점에는 해당 프로퍼티가 아직 존재하지 않으므로 값이 `nil` 상태입니다. 만약 `nil` 상태인 `_T` 프로퍼티에 숫자 연산(예: `+ delta`)을 수행하면 오류가 발생할 수 있으므로, 사용 전 `nil`인지 확인하여 초기값을 설정하는 로직이 필요합니다. 참고 [scores] | [judge_reason] | [source_doc] | 프로퍼티는 컴포넌트 내 멤버 변수로, 함수에서 쓰고 읽어도 종료되지 않고 함수에서 설정한 값이 보존됩니다. 따라서 함수를 통해 어떤 값을 담아놓거나 함수를 반복 호출하여 값을 누적할 때 활용될 수 있습니다. 다음은 3초 간격으로 콘솔 창에 "HelloMSW"를 출력하는 예제입니다. ```lua Property: [Sync] number AccTime = 0 Method: [Server Only] void OnUpdate(delta) { self.AccTime = self.AccTime + delta --OnUpd
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | 표로 정리해서 일반 프로퍼티와 _T 프로퍼티의 접근 범위 및 동기화 여부를 비교하세요. [answer] | 일반 프로퍼티와 _T 프로퍼티의 접근 범위 및 동기화 여부는 다음과 같습니다. | 구분 | 일반 프로퍼티 | _T 프로퍼티 | | :--- | :--- | :--- | | **생성 시점** | 컴포넌트 선언 시점에 생성 | 함수 내에서 처음 사용된 시점에 생성 | | **접근 범위** | 문서에 명시된 접근 범위 정보 없음 | 내부 컴포넌트 및 외부 컴포넌트 모두에서 접근 가능 | | **동기화 여부** | `[Sync]` 속성을 통해 동기화 가능 | 동기화 불가 (동일한 공간에서만 사용 가능) | [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 프로퍼티는 컴포넌트 내 멤버 변수로, 함수에서 쓰고 읽어도 종료되지 않고 함수에서 설정한 값이 보존됩니다. 따라서 함수를 통해 어떤 값을 담아놓거나 함수를 반복 호출하여 값을 누적할 때 활용될 수 있습니다. 다음은 3초 간격으로 콘솔 창에 "HelloMSW"를 출력하는 예제입니다. ```lua Property: [Sync] number AccTime = 0 Method: [Server Only] void OnUpdate(delta) { self.AccTime = self.AccTime + delta --OnUpd
[task] | two_turn [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | Vector3, Vector4, Entity, EntityRef의 생성과 참조 방식이 어떻게 다른지 알려주세요. [answer] | Vector3, Vector4, Entity, EntityRef의 생성과 참조 방식은 다음과 같이 다릅니다. **Vector3** 3차원 벡터를 표현하는 **참조 타입**입니다. `Vector3(x, y, z)` 형태로 생성하며, 프로퍼티나 변수에 할당하면 참조됩니다. `Vector2`보다 `z` 멤버 변수가 추가됩니다. 하나의 변수에 할당된 Vector3의 멤버(`x`, `y`, `z`)를 수정하면, 같은 참조를 가진 다른 변수들의 값도 함께 변경됩니다. ```lua Method: [Server Only] void OnBegi [followup] | Vector3을 지역변수에 할당했을 때 원본의 x, y, z 값을 수정하면 참조한 변수도 함께 바뀌는 이유와, Entity와 EntityRef의 차이점을 실제 맵 이동 상황과 함께 비교해 주세요. [followup_answer] | ### Vector3의 참조 방식과 값 변경 Vector3은 **참조 타입**이기 때문에, 지역 변수에 할당할 때 값의 사본이 생성되는 것이 아니라 같은 메모리 주소를 가리키는 참조가 전달됩니다. 예를 들어 `tmpVector3`이라는 Vector3 객체를 생성하고 `Vt1`, `Vt2`라는 두 개의 지역 변수에 각각 할당하면, 세 변수는 모두 동일한 Vector3 객체를 가리키게 됩니다. 이때 `tmpVector3.x = 20`과 같이 원본으로 여겨지는 변수의 멤버 값을 수정하면, 같은 객체를 참조하고 있는 `Vt1`과 `Vt [scores] | [judge_reason] | [source_doc] | 3차원 벡터와 위치를 표현합니다. **Vector2**와 마찬가지로 프로퍼티 또는 변수에 할당 시 참조됩니다. **Vector2**에서 멤버 변수 z가 추가됩니다. **TransformComponent**의 **Position**에 할당할 때를 제외하곤 주로 **Vector2**를 사용합니다. 값의 생성과 참조, 그리고 멤버 변수 x, y, z에 접근하는 방법은 아래를 참고합니다. ```lua Method: [Server Only] void OnBeginPlay() { local tmpVector3 = Vector3(10,
[task] | code [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | EntityRef 타입의 프로퍼티를 정의하고 OnBeginPlay()에서 해당 엔티티의 Name 속성을 log()로 출력하는 스크립트를 작성해줘 [answer] | ```lua Property: [None] EntityRef EntityRefProperty = EntityPath Method: [Client Only] void OnBeginPlay() { log(self.EntityRefProperty.Name) } ``` [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 3차원 벡터와 위치를 표현합니다. **Vector2**와 마찬가지로 프로퍼티 또는 변수에 할당 시 참조됩니다. **Vector2**에서 멤버 변수 z가 추가됩니다. **TransformComponent**의 **Position**에 할당할 때를 제외하곤 주로 **Vector2**를 사용합니다. 값의 생성과 참조, 그리고 멤버 변수 x, y, z에 접근하는 방법은 아래를 참고합니다. ```lua Method: [Server Only] void OnBeginPlay() { local tmpVector3 = Vector3(10,
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | 같은 컴포넌트에서 프로퍼티를 참조하는 문법과 다른 컴포넌트의 프로퍼티를 읽어와 대입하는 전체 코드를 주석 포함 없이 코드 블록으로만 출력하세요. [answer] | ```lua Property: [None] string StringProperty = "Hello!!" Method: [Server Only] void OnBeginPlay() { local stringProperty = self.StringProperty } ``` ```lua Property: [None] string PropertyB = "" Method: [Server Only] void OnBeginPlay() { local entityA = _EntityService:GetEntityByPath("/m [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 함수에서 프로퍼티에 접근하는 방법을 알아봅시다. 먼저 같은 컴포넌트에서는 다음과 같이 접근할 수 있습니다. ```lua Property: [None] string StringProperty = "Hello!!" Method: [Server Only] void OnBeginPlay() { local stringProperty = self.StringProperty --"self.프로퍼티명"으로 해당 프로퍼티를 가져옴 } ``` 다른 컴포넌트의 프로퍼티는 다음과 같이 접근할 수 있습니다. 아래 상황에서 접근하는 방법
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | 코드만 출력하고 설명은 주석으로 _T 프로퍼티를 활용해 3초 간격으로 "HelloMSW"를 출력하는 OnUpdate 메서드를 작성하세요. [answer] | ```lua Property: [None] Method: [Server Only] void OnUpdate(delta) { -- _T 프로퍼티는 함수 내에서 처음 접근할 때 생성됩니다. -- 따라서 nil인지 확인하여 초기값을 설정해 줍니다. if self._T.accTime == nil then self._T.accTime = 0 end -- 매 프레임마다 delta를 누적합니다. self._T.accTime = self._T.accTime + delta -- 누적된 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 프로퍼티는 컴포넌트 내 멤버 변수로, 함수에서 쓰고 읽어도 종료되지 않고 함수에서 설정한 값이 보존됩니다. 따라서 함수를 통해 어떤 값을 담아놓거나 함수를 반복 호출하여 값을 누적할 때 활용될 수 있습니다. 다음은 3초 간격으로 콘솔 창에 "HelloMSW"를 출력하는 예제입니다. ```lua Property: [Sync] number AccTime = 0 Method: [Server Only] void OnUpdate(delta) { self.AccTime = self.AccTime + delta --OnUpd
[task] | code [pass] | ⏳ 미판정 [title] | 몬스터를 스폰해 보자 [question] | MonsterSpawn 컴포넌트와 _SpawnService, _EntityService를 사용하여 5초 간격으로 MaxSpawnCount 속성보다 적은 몬스터만 추가로 스폰하는 스크립트를 작성해줘 [answer] | 5초마다 현재 활성 몬스터 수를 확인하여 `MaxSpawnCount`보다 부족한 만큼만 스폰하는 `MonsterSpawn` 스크립트는 다음과 같습니다. ```lua Property: [None] number MaxSpawnCount = 5 [None] number Time = 0 [None] table MonsterArray = {} Method: [server only] void OnBeginPlay() { self.MonsterArray = {} self.Time = 0 } Method: [server onl [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 이번에는 5초마다 한 마리씩 스폰하는 것이 아니라, 5초마다 현재 몬스터 수량을 체크하여 최대 몬스터 수량보다 부족한 만큼 생성하도록 수정해 봅시다. 1. **MonsterSpawn**의 `GetRandomPosition()` 함수에 매개 변수를 하나 추가합니다. 2. 매개 변수 타입은 **number**로, 이름은 **spawnCount**로 설정하고, 리턴 타입을 **table**로 변경합니다. 함수는 다음과 같이 수정합니다. ```lua table GetRandomPosition(nu
[task] | single_turn [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | 일반 table 프로퍼티는 동기화가 안 된다는데, 서버와 클라이언트 간에 테이블 데이터를 실시간으로 공유하려면 어떤 타입을 선택해야 하나요? [answer] | 일반 `table` 프로퍼티는 동기화(Sync)가 되지 않으므로, 서버와 클라이언트 간에 테이블 데이터를 실시간으로 공유하려면 **`SyncTable`** 타입을 선택해야 합니다. `SyncTable`은 루아의 table 형태이지만 다음과 같은 특징을 가집니다: 1. **동기화 지원**: `[Sync]` 속성을 지정하면 서버와 클라이언트 간 데이터가 동기화됩니다. 2. **타입 제약**: 일반 `table`과 달리 값(value) 또는 키(key)와 값(value)에 정해진 타입만 설정할 수 있습니다. * `Sync [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | #### table 루아 테이블을 참조합니다. 동기화(Sync)는 되지 않습니다. ```lua Property: [None] table TableProperty = {1,2,3,4,5} Method: [Server Only] void OnBeginPlay() { local tmpTable = self.TableProperty self.TableProperty[1] = 10 log("tmpTable : "..tmpTable[1]) --콘솔 창에 "tmpTable : 10" 출력 log("TablePr
[task] | two_turn [pass] | ⏳ 미판정 [title] | 몬스터를 스폰해 보자 [question] | `OnUpdate()`에서 `self.Time >= 5` 조건이 참이 되었을 때 `self.Time = 0`으로 초기화되는 로직이 프레임 드랍 시 스폰 간격에 어떤 영향을 미치는지 알고 싶습니다. [answer] | 제시된 참고 문서에는 프레임 드랍이나 프레임 시간(디엘타, delta)과 관련된 기술적 설명, 그리고 `OnUpdate`의 `delta` 파라미터가 프레임 주기와 어떻게 상호작용하는지에 대한 정보가 포함되어 있지 않습니다. 문서에는 `OnUpdate(number delta)` 함수 내에서 `self.Time = self.Time + delta`를 수행하고, `self.Time >= 5`가 되면 `self.Time = 0`으로 초기화하는 코드 구조만 제시되어 있을 뿐, 프레임 드랍 발생 시 이 로직이 실제 스폰 간격에 미치는 구체적 [followup] | `delta` 값이 0.5초 이상인 극단적인 프레임 로스 상황에서 `self.Time`이 5를 초과했을 때 발생하는 오차와 이를 방지하기 위한 `delta` 처리 로직을 코드 블록으로 제시해 주세요. [followup_answer] | 제시된 참고 문서에는 프레임 로스(FPS 저하)나 `delta` 값이 특정 임계값을 초과하는 극단적인 상황에 대한 설명이 없으며, 이를 방지하기 위한 `delta` 클램핑(최대값 제한) 로직이나 오차 보정 코드가 포함되어 있지 않습니다. 문서에는 단순히 `self.Time = self.Time + delta`를 수행하고 `self.Time >= 5` 조건이 만족되면 초기화하는 기본적인 타이머 로직만 제시되어 있을 뿐, `delta` 값 자체를 처리하거나 제한하는 방법은 설명되어 있지 않습니다. 따라서 문서 근거만으로 `delta` [scores] | [judge_reason] | [source_doc] | 이번에는 5초마다 몬스터를 한 마리씩 추가로 스폰하되, 최대 10개까지만 생성하는 기능을 만들어보겠습니다. 1. **MonsterSpawn**을 열고 프로퍼티를 추가합니다. ```lua Property: [Sync] number MaxSpawnCount = 10 [Sync] number Time = 0 [None] table MonsterArray = {} ``` 2. `OnBeginPlay()` 함수를 다음과 같이 수정합니다. ```lua
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | 엔티티 A의 ComponentA 프로퍼티 값을 엔티티 B의 ComponentB 프로퍼티에 대입하는 과정의 핵심 API 호출 순서를 3단계 번호 목록으로 정리하세요. [answer] | 엔티티 A의 `ComponentA` 프로퍼티 값을 엔티티 B의 `ComponentB` 프로퍼티에 대입하는 과정의 핵심 API 호출 순서는 다음과 같습니다. 1. `_EntityService:GetEntityByPath` 를 사용하여 엔티티 A의 경로(예: `/maps/map01/A`)를 인자로 전달하여 엔티티 A의 참조(`entityA`)를 가져옵니다. 2. 가져온 `entityA`에서 `entityA.ComponentA` 형태로 접근하여 `ComponentA` 컴포넌트의 참조(`componentA`)를 가져옵니다. 3. ` [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 함수에서 프로퍼티에 접근하는 방법을 알아봅시다. 먼저 같은 컴포넌트에서는 다음과 같이 접근할 수 있습니다. ```lua Property: [None] string StringProperty = "Hello!!" Method: [Server Only] void OnBeginPlay() { local stringProperty = self.StringProperty --"self.프로퍼티명"으로 해당 프로퍼티를 가져옴 } ``` 다른 컴포넌트의 프로퍼티는 다음과 같이 접근할 수 있습니다. 아래 상황에서 접근하는 방법
[task] | single_turn [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | 스크립트 에디터에서 [+ 버튼]으로 프로퍼티를 추가할 때 이름, 타입, 초깃값, 동기화 여부를 설정하는 순서와 각 항목의 역할은 무엇인가요? [answer] | 스크립트 에디터에서 **[+]** 버튼을 클릭해 프로퍼티를 추가할 때 설정하는 항목과 각 항목의 역할은 다음과 같습니다. ### 설정 항목과 역할 1. **이름 (Name)** * **역할:** 프로퍼티를 식별하는 이름입니다. * **설명:** 스크립트 코드 내에서 `self.이름` 형식으로 접근하여 값을 읽거나 쓰기 위해 사용됩니다. 2. **타입 (Type)** * **역할:** 프로퍼티가 저장할 데이터의 종류를 정의합니다. * **설명:** 루아 변수와 달리 메이플스토리 월드 [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 이번 과정에서는 메이플스토리 월드의 프로퍼티가 무엇인지 알아봅니다. 또한 프로퍼티 선언 및 초깃값 설정, 프로퍼티 동기화 방법 등 프로퍼티 관련 정보를 살펴보겠습니다. # MSW의 프로퍼티 프로퍼티는 컴포넌트의 멤버 변수로써, 엔티티 별로 다른 값을 가질 수 있습니다. 프로퍼티는 세 가지 특징이 있습니다. * 루아 변수와 달리 메이플스토리 월드의 프로퍼티는 선언할 때 타입을 설정하고, 값을 대입하거나 참조할 수 있습니다. * 컴포넌트 내 외부에서 프로퍼티에 접근할 수 있고, 각 함수에서 값을 읽거나 쓸 수
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 공격과 피격 [question] | 피격 대상이 되는 엔티티의 필수 조건과 HitComponent가 수행하는 역할을 3문장 이내로 요약해 주세요. [answer] | 엔티티가 피격 대상이 되기 위해서는 `HitComponent`가 반드시 장착되어 있어야 합니다. `AttackComponent`의 공격 범위 안에 `HitComponent`를 가진 엔티티가 존재해야만 피격 처리 대상이 됩니다. `HitComponent`는 엔티티가 피격 대상인지 판단하고, 엔티티가 받을 최종 대미지를 결정하는 역할을 수행합니다. [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 메이플스토리에서 플레이어는 모험가가 되어 메이플 세계를 탐험하고 몬스터를 만나 전투를 합니다. 전투 과정에서 다른 몬스터를 공격하거나 피격당합니다. 메이플스토리뿐만 아니라 다양한 게임에서 공격과 피격은 중요한 개념입니다. 이번 과정에서는 공격과 피격을 구현하는 방법을 학습합니다. ##### 참고 가이드/레퍼런스 [AttackComponent](/apiReference/Components/AttackComponent) [HitComponent](/apiReference/Components/HitComponent
[task] | code [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | Vector4 타입의 프로퍼티를 정의하고 OnBeginPlay()에서 w 성분을 포함한 모든 값이 10으로 생성되는지 확인한 후 w 값을 30으로 변경하여 log()로 출력하는 스크립트를 작성해줘 [answer] | Vector4 타입의 프로퍼티를 정의하고, `OnBeginPlay()`에서 초기값을 확인한 뒤 `w` 성분을 30으로 변경하여 출력하는 스크립트는 다음과 같습니다. ```lua Property: [None] Vector4 MyVector4 = Vector4(10, 10, 10, 10) Method: [server only] void OnBeginPlay() { -- 생성된 값 확인 (모든 성분이 10이어야 함) log(self.MyVector4) -- w 성분만 30으로 변경 self.MyV [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 3차원 벡터와 위치를 표현합니다. **Vector2**와 마찬가지로 프로퍼티 또는 변수에 할당 시 참조됩니다. **Vector2**에서 멤버 변수 z가 추가됩니다. **TransformComponent**의 **Position**에 할당할 때를 제외하곤 주로 **Vector2**를 사용합니다. 값의 생성과 참조, 그리고 멤버 변수 x, y, z에 접근하는 방법은 아래를 참고합니다. ```lua Method: [Server Only] void OnBeginPlay() { local tmpVector3 = Vector3(10,
[task] | code [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | Vector3 타입의 프로퍼티를 정의하고 OnBeginPlay()에서 x, y, z 값을 10으로 초기화한 뒤 각 성분을 20으로 수정하여 log()로 출력하는 스크립트를 작성해줘 [answer] | Vector3 타입의 프로퍼티를 정의하고, `OnBeginPlay()`에서 초기값을 설정한 뒤 값을 수정하여 출력하는 스크립트는 다음과 같습니다. ```lua Property: [None] Vector3 MyVector = Vector3(0, 0, 0) Method: [server only] void OnBeginPlay() { -- x, y, z 값을 10으로 초기화 self.MyVector.x = 10 self.MyVector.y = 10 self.MyVector.z = 10 lo [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 3차원 벡터와 위치를 표현합니다. **Vector2**와 마찬가지로 프로퍼티 또는 변수에 할당 시 참조됩니다. **Vector2**에서 멤버 변수 z가 추가됩니다. **TransformComponent**의 **Position**에 할당할 때를 제외하곤 주로 **Vector2**를 사용합니다. 값의 생성과 참조, 그리고 멤버 변수 x, y, z에 접근하는 방법은 아래를 참고합니다. ```lua Method: [Server Only] void OnBeginPlay() { local tmpVector3 = Vector3(10,
[task] | instruction_following [pass] | ⏳ 미판정 [title] | 공격과 피격 [question] | 크리티컬 대미지 계산 시 CalcCritical()과 GetCriticalDamageRate() 함수를 조합하는 로직을 코드만 출력하고 설명은 주석으로 작성해 주세요. [answer] | ```lua Method: [server only] -- 크리티컬 발생 여부(30% 확률)를 반환하는 함수 override boolean CalcCritical (Entity attacker, Entity defender, string attackInfo) { return _UtilLogic:RandomDouble() < 0.3 } [server only] -- 크리티컬 공격 시 적용되는 대미지 배율(기본 대미지의 2배)을 반환하는 함수 override number GetCriticalDamageRate () { r [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | # 학습 과정 소개 메이플스토리에서 플레이어는 모험가가 되어 메이플 세계를 탐험하고 몬스터를 만나 전투를 합니다. 전투 과정에서 다른 몬스터를 공격하거나 피격당합니다. 메이플스토리뿐만 아니라 다양한 게임에서 공격과 피격은 중요한 개념입니다. 이번 과정에서는 공격과 피격을 구현하는 방법을 학습합니다. ##### 참고 가이드/레퍼런스 [AttackComponent](/apiReference/Components/AttackComponent) [HitComponent](/apiReference/Components/HitComponent
[task] | single_turn [pass] | ⏳ 미판정 [title] | 프로퍼티 [question] | Vector2 프로퍼티의 x 값을 수정했는데 원래 값이 그대로 유지되는 것 같은데, 참조 변수를 통해 수정할 때 주의해야 할 점이 무엇인가요? [answer] | Vector2 프로퍼티는 테이블 형태로 제공되며, 프로퍼티 또는 변수에 할당 시 **참조**됩니다. 따라서 `local` 변수에 Vector2 프로퍼티를 할당하여 참조 변수를 만든 후, 이 참조 변수를 통해 `x`나 `y` 값을 수정하면 원본 프로퍼티의 값도 함께 변경됩니다. 참고 문서의 예제에서 이를 확인할 수 있습니다. `self.Vector2Property`를 `calcu`라는 지역 변수에 할당한 뒤 `calcu.x`와 `calcu.y`를 수정했더니, 원본인 `self.Vector2Property`의 값도 `(20.000, [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | #### table 루아 테이블을 참조합니다. 동기화(Sync)는 되지 않습니다. ```lua Property: [None] table TableProperty = {1,2,3,4,5} Method: [Server Only] void OnBeginPlay() { local tmpTable = self.TableProperty self.TableProperty[1] = 10 log("tmpTable : "..tmpTable[1]) --콘솔 창에 "tmpTable : 10" 출력 log("TablePr
[task] | two_turn [pass] | ⏳ 미판정 [title] | 몬스터를 스폰해 보자 [question] | `SpawnMonster()` 함수에서 `_SpawnService:SpawnByModelId`의 두 번째 인자로 `"SpawnedMonster"`라는 이름 문자열을 전달하고 있는데, 동일한 모델 ID로 여러 마리의 몬스터를 연속 스폰할 때 이 이름 인자가 충돌을 일으키거나 특정 엔티티를 덮어쓰는 문제가 발생하나요? [answer] | 제공된 문서에는 `_SpawnService:SpawnByModelId` 메서드의 두 번째 인자(이름)가 동일한 모델 ID로 여러 엔티티를 스폰할 때 발생하는 충돌이나 덮어쓰기 문제에 대한 설명이 없습니다. 따라서 문서에 근거하여 이 인자가 충돌을 일으키는지, 특정 엔티티를 덮어쓰는지 여부는 알 수 없습니다. [followup] | 다중 스폰 시 엔티티 식별을 위해 고유한 이름(예: `Monster_1`, `Monster_2`)을 동적으로 생성하여 전달하는 mLua 코드 패턴을 작성해 주세요. [followup_answer] | 제공된 문서에는 다중 스폰 시 엔티티 식별을 위해 고유한 이름을 동적으로 생성하여 전달하는 코드 패턴이나 관련 API 설명이 포함되어 있지 않습니다. 문서에는 `SpawnMonster()` 함수 내에서 `_SpawnService:SpawnByModelId`의 두 번째 인자로 고정된 문자열 `"SpawnedMonster"`을 전달하는 예제만 제시되어 있으며, 이를 동적으로 변경하거나 고유 이름을 생성하는 방법에 대한 내용은 없습니다. [scores] | [judge_reason] | [source_doc] | 지정한 영역 안에서 랜덤한 위치에 몬스터를 스폰하는 기능을 추가하겠습니다. 위에 확인했던 좌표를 기준으로 스폰 영역을 정의하고, 영역 내 랜덤한 위치에 몬스터를 스폰해 봅시다. 1. **MonsterSpawn**을 스크립트 에디터에서 열고 **Vector3** 타입의 새 함수 `GetRandomPosition()`을 추가합니다. 2. `GetRandomPosition()` 함수를 다음과 같이 작성합니다. 위에서 기록해두었던 좌표를 함수에 활용합니다. ```lua Vector3 GetRandomPositio
[task] | code [pass] | ⏳ 미판정 [title] | 몬스터를 스폰해 보자 [question] | x, y 좌표 범위가 주어진 영역 내에서 랜덤한 위치를 계산하여 _SpawnService:SpawnByModelId() 의 매개 변수로 전달하는 랜덤 스폰 위치 생성 로직이 포함된 SpawnMonster() 함수를 작성해줘. [answer] | 제공된 참고 문서에는 `_SpawnService:SpawnByModelId()` 함수의 사용법과 매개 변수(`Entry ID`, 이름, `Vector3` 좌표, 부모 엔티티)에 대한 정보가 포함되어 있습니다. 또한, 몬스터가 스폰될 좌표 범위를 설정하고 그 사이에서 스폰되도록 한다는 설명이 있습니다. 그러나, **랜덤 수를 생성하는 함수(예: `math.random`)나 랜덤 위치 계산 로직에 대한 구체적인 mLua API 또는 코드 예제는 제공된 문서에 포함되어 있지 않습니다.** mLua는 Lua 5.3 기반이므로 표준 Lua [followup] | [followup_answer] | [scores] | [judge_reason] | [source_doc] | 이제 스크립트를 통해 몬스터 스폰 기능을 만들어보겠습니다. 우선은 특정 좌표에 몬스터를 스폰해 보고 점차 기능을 확장해 봅시다. 1. 새 스크립트 컴포넌트 **MonsterSpawn**을 생성합니다. 2. **MonsterSpawn** 스크립트를 열어 `SpawnMonster()` 함수를 추가합니다. 3. `SpawnMonster()` 함수를 다음과 같이 작성합니다. ```lua void SpawnMonster() { local parent = _EntityServ