프로퍼티

# 학습 과정 소개

이번 과정에서는 메이플스토리 월드의 프로퍼티가 무엇인지 알아봅니다. 또한 프로퍼티 선언 및 초깃값 설정, 프로퍼티 동기화 방법 등 프로퍼티 관련 정보를 살펴보겠습니다.

# MSW의 프로퍼티

프로퍼티는 컴포넌트의 멤버 변수로써, 엔티티 별로 다른 값을 가질 수 있습니다. 프로퍼티는 세 가지 특징이 있습니다.

  * 루아 변수와 달리 메이플스토리 월드의 프로퍼티는 선언할 때 타입을 설정하고, 값을 대입하거나 참조할 수 있습니다.
  * 컴포넌트 내 외부에서 프로퍼티에 접근할 수 있고, 각 함수에서 값을 읽거나 쓸 수 있습니다.
  * 프로퍼티 동기화 설정으로 서버 - 클라이언트 간 동기화 여부를 설정할 수 있습니다.



# 프로퍼티 선언과 제거

프로퍼티 선언 및 삭제 방법을 알아봅시다.

#### 프로퍼티 선언

  1. Workspace - MyDesk의 콘텍스트 메뉴에서 Create Scripts - Create Component를 클릭해 새 스크립트 컴포넌트를 생성합니다. [png](1687496291799b05a128af08748b8bc69f2956c9de929)   

  2. 컴포넌트 이름을 입력합니다. 생성된 컴포넌트를 더블클릭하여 스크립트 에디터를 엽니다. [png](1635329393515dacd9e3446ac472e92b77b2f787c160d)   

  3. [+] 버튼을 클릭해 새 프로퍼티를 추가합니다. [png](1635329401326a69e30631ae4401784a4862c7711aada)   

  4. 프로퍼티 이름, 타입, 초깃값, 그리고 서버-클라이언트 간 동기화 여부를 설정합니다. [png](1635329412870e2a6e47b832645ed89b9768119cc89bc)



#### 프로퍼티 삭제

[⋮] 메뉴 - Remove를 클릭해 프로퍼티를 삭제합니다. [png](1635329420391af78791adc6e4e3e97380dea88d3b4ad)

# 프로퍼티 타입

프로퍼티는 타입을 설정해야 합니다. 루아 기본 문법과는 조금 다르지만, 프로퍼티 값 전달과 동기화를 위해 타입을 반드시 설정해야 합니다. 프로퍼티 이름 왼쪽의 타입을 클릭해 적절한 타입을 설정합니다. [png](1673599399633ba4e45d5b2694a838e3454621f4b9d4c)

#### string

string 타입은 문자열 값을 받을 수 있습니다.
    
    
    @Component
    script StartupStringLogger extends Component
        @Sync
        property string StringProperty = "Hello"
    
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
        self.StringProperty = self.StringProperty .. " World!!"
        log(self.StringProperty) --콘솔 창에 문자열 "Hello World!!" 출력
        self.StringProperty = "Hello MSW!!"
        log(self.StringProperty) --콘솔 창에 문자열 "Hello MSW!!" 출력
        end
    end
    

#### number

number 타입은 숫자 값을 받을 수 있습니다. 정수, 실수 상관없이 모든 숫자 값을 받습니다.
    
    
    @Component
    script NumberModifier extends Component
        @Sync
        property number NumberProperty = 100
    
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
        self.NumberProperty = self.NumberProperty + 83
        log(self.NumberProperty) --콘솔 창에 숫자 183 출력
        self.NumberProperty = 3.28
        log(self.NumberProperty) --콘솔 창에 숫자 3.28 출력
        end
    end
    

#### integer

integer 타입은 정숫값을 받을 수 있습니다. 실숫값을 받으면 소수부는 버립니다.
    
    
    @Component
    script IntegerConversionDemo extends Component
        @Sync
        property integer IntegerProperty = 100
    
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
        self.IntegerProperty = self.IntegerProperty + 83
        log(self.IntegerProperty) --콘솔 창에 숫자 183 출력
        self.IntegerProperty = 3.28
        log(self.IntegerProperty) --콘솔 창에 숫자 3 출력
        end
    end
    

#### boolean

프로퍼티에 true 또는 false 값을 할당할 수 있습니다.
    
    
    @Component
    script BeginPlayLogger extends Component
        @Sync
        property boolean BooleanProperty = false
    
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
        self.BooleanProperty = true
    
        if self.BooleanProperty == true then
        self.BooleanProperty = false
        log("Hello MSW!!")
        end
        end
    end
    

#### table

루아 테이블을 참조합니다. 동기화(Sync)는 되지 않습니다.
    
    
    @Component
    script TableReferenceDemonstrator extends Component
        property table TableProperty = {1,2,3,4,5}
    
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
        local tmpTable = self.TableProperty
        self.TableProperty[1] = 10
        log("tmpTable : "..tmpTable[1]) --콘솔 창에 "tmpTable : 10" 출력
        log("TableProperty : "..self.TableProperty[1]) --콘솔 창에 "TableProperty : 10" 출력
    
        self.TableProperty = {100,200,300,400}
        log("tmpTable : "..tmpTable[1]) --콘솔 창에 "tmpTable : 10" 출력
        log("TableProperty : "..self.TableProperty[1]) --콘솔 창에 "tmpTable : 100" 출력
        end
    end
    

#### SyncTable< v >

루아의 table 형태지만 제약이 있는 타입으로 `next` 함수는 사용할 수 없습니다. 일반 table 프로퍼티와 다르게 정해진 타입 중 한 가지만 설정할 수 있으며 동기화(Sync)할 수 있습니다. 다만 일반 table 보다는 다소 느리다는 점을 참고하기 바랍니다. v는 타입입니다. 목록에서 원하는 타입을 선택할 수 있습니다. [png](168610513116799d1be6022794e1582c2b9227100a178)

#### SyncTable< k, v >

루아의 table 형태지만 제약이 있는 타입으로 `next` 함수는 사용할 수 없습니다. 일반 table 프로퍼티와 다르게 key, value에 각각 정해진 타입만 설정할 수 있으며 동기화(Sync)할 수 있습니다. 다만 일반 table 보다는 다소 느리다는 점을 참고하기 바랍니다. k, v는 타입입니다. 목록에서 원하는 타입을 선택할 수 있습니다.

#### Vector2

2차원 벡터와 위치를 표현합니다. 테이블 형태로 제공되며 프로퍼티 또는 변수에 할당 시 참조됩니다. 주로 2차원 공간에서의 위치 좌표를 나타내는 데 사용하며, x좌표와 y좌표를 나타내는 멤버 변수 x와 y를 포함하고 있습니다. 값의 생성과 참조, 그리고 멤버 변수 x, y에 접근하는 방법은 아래를 참고합니다.
    
    
    @Component
    script VectorPropertyUpdater extends Component
        @Sync
        property Vector2 Vector2Property = Vector2(0,0)
    
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
        local tmpVector2 = Vector2(10,10) --벡터를 생성하여 지역변수 tmpVector2에 할당
        self.Vector2Property = tmpVector2 --tmpVector2의 값을 프로퍼티 Vector2Property에 할당
        log(tmpVector2) --콘솔 창에 (10.000, 10.000)으로 출력
        log(self.Vector2Property) --콘솔 창에 (10.000, 10.000)으로 출력
    
        local calcu = self.Vector2Property
        calcu.x = 20 --멤버 변수 x의 값을 설정
        calcu.y = 50 --멤버 변수 Y의 값을 설정
        log(calcu) --콘솔 창에 (20.000, 50.000)으로 출력
        end
    end
    

#### Vector3

3차원 벡터와 위치를 표현합니다. Vector2와 마찬가지로 프로퍼티 또는 변수에 할당 시 참조됩니다. Vector2에서 멤버 변수 z가 추가됩니다. TransformComponent의 Position에 할당할 때를 제외하곤 주로 Vector2를 사용합니다. 값의 생성과 참조, 그리고 멤버 변수 x, y, z에 접근하는 방법은 아래를 참고합니다.
    
    
    @Component
    script VectorReferenceTest extends Component
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
        local tmpVector3 = Vector3(10,10,10)  --벡터를 생성하여 지역변수 tmpVector3에 할당
        local Vt1 = tmpVector3
        local Vt2 = tmpVector3
        log(Vt1)  --콘솔 창에 (10.000, 10.000, 10.000)으로 출력
        log(Vt2)  --콘솔 창에 (10.000, 10.000, 10.000)으로 출력  
    
        tmpVector3.x = 20
        tmpVector3.y = 20
        tmpVector3.z = 20
    
        log(tmpVector3)  --콘솔 창에 (20.000, 20.000, 20.000)으로 출력
        log(Vt1)  --콘솔 창에 (20.000, 20.000, 20.000)으로 출력
        log(Vt2)  --콘솔 창에 (20.000, 20.000, 20.000)으로 출력
        end
    end
    

#### Vector4

4차원 벡터와 위치를 표현하는 참조 타입입니다. Vector3에서 멤버 변수 w가 추가됩니다. 값의 생성과 참조, 그리고 멤버 변수 x, y, z, w에 접근하는 방법은 아래를 참고합니다.
    
    
    @Component
    script Vector4ReferenceTest extends Component
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
        local tmpVector4 = Vector4(10,10,10,10)  --벡터를 생성하여 지역변수 tmpVector3에 할당
        local Vt1 = tmpVector4
        local Vt2 = tmpVector4
        log(Vt1)  --콘솔 창에 (10.000, 10.000, 10.000, 10.000)으로 출력
        log(Vt2)  --콘솔 창에 (10.000, 10.000, 10.000, 10.000)으로 출력  
    
        tmpVector4.x = 20
        tmpVector4.y = 20
        tmpVector4.z = 20
        tmpVector4.w = 20        
    
        log(tmpVector4)  --콘솔 창에 (20.000, 20.000, 20.000, 20.000)으로 출력
        log(Vt1)  --콘솔 창에 (20.000, 20.000, 20.000, 20.000)으로 출력
        log(Vt2)  --콘솔 창에 (20.000, 20.000, 20.000, 20.000)으로 출력
        end
    end
    

#### Entity / EntityRef

엔티티를 가리키는 프로퍼티입니다. Entity와 EntityRef의 기본 사용법은 동일하며, 참조 방식이 다릅니다.

  * Entity: 엔티티를 직접 참조합니다. 다른 맵으로 이동했다가 다시 돌아온 경우 참조가 끊어져 프로퍼티를 사용할 수 없게 됩니다.
  * EntityRef: 엔티티의 키 값을 가지고 있다가 실제 접근할 때 엔티티를 찾습니다. 다른 맵에 갔다 왔을 때 원래 가리키던 엔티티에 문제없이 접근할 수 있습니다. EntityRef는 엔티티에 접근할 때마다 엔티티를 찾기 때문에 Entity보다 느립니다.


    
    
    @Component
    script EntityNameLogger extends Component
        @Sync
        property Entity EntityProperty = nil
        property EntityRef EntityRefProperty = nil
    
        @ExecSpace("ClientOnly")
        method void OnBeginPlay()
        log(self.EntityProperty.Name)
        log(self.EntityRefProperty.Name)
        end
    end
    

#### Component / ComponentRef

컴포넌트 가리키는 프로퍼티입니다. 프로퍼티 타입을 Component 선택 후, 버튼을 눌러 특정 컴포넌트를 타입으로 설정할 수 있습니다. Component와 ComponentRef의 기본 사용법은 동일하며, 참조 방식이 다릅니다.

  * Component: 컴포넌트를 직접 참조합니다. 다른 맵에 갔다 오면 참조가 끊어지고 프로퍼티를 사용할 수 없게 됩니다.

  * ComponentRef: 컴포넌트의 키 값을 가지고 있다가 실제 접근할 때 컴포넌트를 찾습니다. 다른 맵에 갔다 왔을 때 원래 가리키던 컴포넌트에 문제없이 접근할 수 있습니다. ComponentRef는 컴포넌트에 접근할 때마다 컴포넌트를 찾기 때문에 Component보다 느립니다.

[png](1635329448310478cc03be91a4882900e39683a801fd7)



    
    
    @Component
    script ReferencePositionLogger extends Component
        property any ComponentProperty = nil
        property any ComponentRefProperty = nil
    
        @ExecSpace("ClientOnly")
        method void OnBeginPlay()
        log(self.ComponentProperty.Position)
        log(self.ComponentRefProperty.Position)
        end
    end
    

#### any

어떤 타입이든 참조할 수 있는 타입입니다. 위 기본 타입들을 대신해서 쓰기보다는, Entity나 Component를 참조할 때 주로 활용합니다.
    
    
    @Component
    script EntityPropertyLogger extends Component
        property any EntityProperty = nil
        property any ComponentProperty = nil
    
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
        self.EntityProperty = _EntityService:GetEntityByPath("/maps/map01/entityA")
        self.ComponentProperty = self.EntityProperty.TransformComponent
        log(self.EntityProperty.Name) --콘솔 창에 해당 엔티티의 이름 출력
        log(self.ComponentProperty.Position) --콘솔 창에 컴포넌트의 프로퍼티 값 출력. (Position은 Vector3 타입이므로, (x, y, z) 형태로 출력)
        end
    end
    

# 프로퍼티 초깃값 설정

프로퍼티 초깃값은 기본적으로 스크립트에서 설정합니다. 예를 들면 아래와 같이 각 프로퍼티의 타입에 따라 "Hello"라는 문자열 값을 넣거나, 또는 100과 같은 숫자 값을 넣을 수 있습니다. Table 타입의 프로퍼티라면 테이블을 초깃값으로 설정할 수도 있습니다. 프로퍼티 초깃값은 해당 컴포넌트가 적용된 엔티티의 기본값이 되기도 합니다. 각 프로퍼티에 어떠한 값도 대입하거나 참조하지 않은 상태에서 각 함수에서 접근하면 프로퍼티의 초깃값을 읽어옵니다.
    
    
    @Component
    script StartupPropertyLogger extends Component
        property string StringProperty = "Hello!!"
    
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
        local getStringProperty = self.StringProperty 
        --self.StringProperty의 값인 "Hello!!"를 getStringProperty에 대입
        log(getStringProperty) 
        --콘솔 창에 "Hello!!" 출력
        end
    end
    

  
엔티티별로 다른 초깃값을 설정할 수도 있습니다. 엔티티 별로 다른 초깃값을 설정하려면 스크립트보다는 프로퍼티 에디터에서 설정하는 것이 효율적입니다. 프로퍼티 에디터에서 설정한 프로퍼티 값은 스크립트의 프로퍼티 초깃값보다 우선순위가 높기 때문입니다. 따라서 엔티티에 컴포넌트를 추가한 후 프로퍼티 에디터에서 초깃값을 설정하면, 엔티티별로 다른 프로퍼티 초깃값을 가지게 됩니다. [png](1683001932653498b70b84e0242c7a8b8902a8643b144) 

# 프로퍼티 접근

함수에서 프로퍼티에 접근하는 방법을 알아봅시다. 먼저 같은 컴포넌트에서는 다음과 같이 접근할 수 있습니다.
    
    
    @Component
    script PropertyAccessor extends Component
        property string StringProperty = "Hello!!"
    
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
        local stringProperty = self.StringProperty 
        --"self.프로퍼티명"으로 해당 프로퍼티를 가져옴
        end
    end
    

  
다른 컴포넌트의 프로퍼티는 다음과 같이 접근할 수 있습니다. 아래 상황에서 접근하는 방법을 알아봅시다. 

  * 엔티티 "A"와 "B"가 존재하고, 각각 엔티티에 "ComponentA"와 "ComponentB"가 적용된 상황.
  * "ComponentA"에는 "PropertyA"라는 이름의 프로퍼티가, "ComponentB"에는 "PropertyB"라는 이름의 프로퍼티가 선언되어 있다.
  * 이때 "ComponentB"에서 "ComponentA"의 프로퍼티 "PropertyA"의 값을 읽어와서 "PropertyB"에 대입시킨다.


    
    
    @Component
    script StringValueHolder extends Component
        property string PropertyA = "HelloMSW!!"
    end
    
    
    
    @Component
    script PropertyCopier extends Component
        property string PropertyB = ""
    
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
        local entityA = _EntityService:GetEntityByPath("/maps/map01/A")
        local componentA = entityA.ComponentA
        self.PropertyB = componentA.PropertyA
        log(self.PropertyB) --콘솔 창에 HelloMSW!!로 출력되는 것을 확인합니다.
        end
    end
    

> 더 알아보기 엔티티와 컴포넌트 참조에 대한 설명은 다음 가이드를 참고하세요. 

# _T 프로퍼티

프로퍼티는 컴포넌트 내 멤버 변수로, 함수에서 쓰고 읽어도 종료되지 않고 함수에서 설정한 값이 보존됩니다. 따라서 함수를 통해 어떤 값을 담아놓거나 함수를 반복 호출하여 값을 누적할 때 활용될 수 있습니다. 다음은 3초 간격으로 콘솔 창에 "HelloMSW"를 출력하는 예제입니다.
    
    
    @Component
    script IntervalTimer extends Component
        @Sync
        property number AccTime = 0
    
        @ExecSpace("ServerOnly")
        method void OnUpdate(number delta)
        self.AccTime = self.AccTime + delta  
        --OnUpdate가 호출될 때마다 AccTime 프로퍼티에 delta 값을 더합니다.
    
        if self.AccTime >= 3 then  
        -- AccTime 프로퍼티의 누적값이 3 이상인지 체크, 3 이상이면 콘솔 창에 "HelloMSW"를 출력
            self.AccTime = 0
            log("HelloMSW")
        end
        end
    end
    

  
위 예제처럼 컴포넌트 내부에서 누적된 값을 담아놓을 수 있습니다. 하지만 불필요한 프로퍼티 선언과 동기화를 계속하기 때문에 번거롭습니다. 이를 해결하기 위해 메이플스토리 월드에서는 **_T 프로퍼티**를 제공합니다. **_T 프로퍼티**의 특징은 다음과 같습니다. 

  * 일반 프로퍼티와 달리 함수 내에서 처음 쓴 시점에 생성됩니다.
  * 내부 컴포넌트는 물론 외부 컴포넌트에서도 접근할 수 있습니다.
  * 동기화를 할 수 없기 때문에 동일한 공간에서만 사용할 수 있습니다.



_T 프로퍼티는 "_T.프로퍼티 명" 형식으로 사용합니다. 정확한 사용법을 알아보기 위해 위의 예제를 _T 프로퍼티를 사용하여 다시 작성해 보겠습니다.
    
    
    @Component
    script IntervalTimer extends Component
        @Sync
        property number AccTime = 0  -- 이 프로퍼티 대신 _T 프로퍼티를 사용
    
        @ExecSpace("ServerOnly")
        method void OnUpdate(number delta)
        if self._T.accTime == nil then self._T.accTime = 0 end  
        --_T 프로퍼티는 쓰는 시점에 바로 선언됨. 내부 컴포넌트에서 선언되었기 때문에 앞에 self를 붙임
    
        self._T.accTime = self._T.accTime + delta  
        --OnUpdate가 호출될 때마다 _T.accTime에 delta 값을 더함
    
        if self._T.accTime >= 3 then  
        -- _T.accTime의 누적값이 3 이상인지를 체크하고, 3이상이면 콘솔 창에 "Hellow MSW"를 출력
            self._T.accTime = 0
            log("HelloMSW")
        end
        end
    end