본문 바로가기

Coding/멋쟁이사자처럼

[멋쟁이사자처럼 X 넥슨] MOD Supporters Hackathon 3주차 회고 (8. Event와 컴포넌트 확장)

[7월 19일]

 

● Event 시스템

- 객체와 객체 간에 주고 받는 형식이며 MOD에서도 이벤트가 많이 사용된다.

- MOD에서 이벤트 시스템을 쉽게 활용할 수 있도록 Entity Event System이라는 API를 기본으로 제공한다.

 

○ Event : 로직 상에서 사건의 발생을 의미 (Event의 종류의 식별 정보, 추가 정보 소유)

○ Handler : 해당 Event를 받았을 때 처리하는 행동의 주체 : 해당 이벤트가 불렸을 때 불리는 함수 (listener, subscriber 등의 비슷한 용어가 있다.)

○ Sender : 해당 Event를 발송하는 객체 (emitter, dispatcher 등의 비슷한 용어가 있다.)

 

학급 전체에 공지문을 우편 배달한다고 가정하면, 이때 Event는 공지문이고, Handler는 학급 내 학생들, Sender는 우편 배달원이 되는 것이다.

 

Event 시스템 설명
장점 * 다른 Component나 기능 단위에서 결합성이 떨어짐
* 행위에 대한 액션 추가 희망 시 행위 수행하는 곳 수정 없이 추가 가능
* 다른 Component의 정보를 필요로 하지 않는다.
단점 * 사건 발생 시 전체적 플로우 찾기 어렵다.
* 각각 처리하는 로직으로 인해 실행되는 시점에서 알 수 없다.
* 디버깅이 어려우며, 순차적 행위 수행이 어렵다.

 

● LogEvent

- 우리가 이름 붙이는 Event로, Log 간의 이벤트를 집어넣는데 사용될 것이다.

 

- MyDesk - Create EventType : LogEvent 객체 생성

- LovEvent에 message property 생성

 

 

- myComponent 생성 및 Handler와 function 추가

 

 

Log로 이벤트를 주고받는 과정 - 쏘는 주체와 받는 주체가 같은 Component인 경우

 

1. 자신이 엔티티 쪽으로 이벤트를 쏨

 

 

2. 로그 이벤트를 받는 핸들러 → 자신의 Entity 쪽으로 등록

 

 

● 핸들러 등록 구조

- 각 Entity들은 Component를 포함하고 있으며, Entity Event System은 다음과 같이 동작한다.

 

 

- Component는 각 Entity를 중계자로 사용할 수 있음.

- 각 Component는 Entity를 통해 Handler를 등록한다. (Event 발생 역시 Entity를 통해 가능하다.)

 

 

- sender 역시 Entity를 통해 이벤트를 발생시키는 것이 가능하며, 이 때 Entity는 Handler들에게 해당 이벤트를 전송하는 역할을 한다.

 

- 어떤 Event가 왔을 때 이것을 처리하는 부분을 Component 내의 Logic에 넣고, 이벤트 수신 등록을 Register, AddListener 등으로 하며, MOD에서는 Entity에 등록하는 구조이다.

 

● 이벤트를 주고 받을 때

 

○ 같은 엔티티에서 이벤트를 주고 받을 때

 

- component1에서 이벤트 발생 시, component3의 이벤트를 실행시키고 싶을 때를 가정.

- 두 component간에 연관이 없기 때문에 component에서 바로 호출하지 않고 엔티티를 통해 호출하는 구조.

    - 1. component1이 특정 타이밍에 의해 Entity로 발송

    - 2. Log Event를 수신하겠다고 등록한 Component들에게 Event 발송

    - 3. 해당 Component들은 Event를 받아서 처리

○ 다른 엔티티에서 이벤트를 주고 받을 때

 

 

- rabbitEntity 쪽으로 이벤트를 쏘면 rabbitComponent가 수신해서 rabbitComponent 안의 로그 메시지가 출력되는 구조

    - 1. 신호를 보내는 Entity 쪽에서 Component들이 이벤트 발송

    - 2. 받는 Entity 쪽에서 신호를 받고 Event 수행

 

구현하는 방법에는 두 가지가 있다.

- 첫 번째 방법 : RabbitComponent에서 로그 이벤트 등록 후 MyComponent에서 rabbitEntity 쪽으로 Log Event 발송 등록

- 두 번째 방법 : Rabbitcomponent에서 로그 메시지 핸들 이벤트와 출력 등록(이 때 BeerEntity로 이벤트 발송 등록X) 후, BeerComponent에서 등록한 로그 이벤트를 RabbitEntity로 보내 로그 출력

 

* 분리되어 있는 환경에서 통신을 할 수 있게 해주는 것을 Event라고 할 수 있겠다.

 

● 이벤트 처리

- 1. 이벤트를 처리할 컴포넌트 & 엔티티 생성

- 2. 핸들러 로직 추가

    - 핸들러 로직이 이루어지는 방법

    - 1. Entity Event Handler 추가

    - 2. 핸들러 상단 이벤트 중계자 설정

    - 3. 이벤트 처리 로직

- 3. 이벤트 발생 로직 추가

 

이벤트 시스템과 일반 로직의 차이

- "행위가 일어난 주체"에서 일을 처리(로직)할 것인지, "행동을 실행해야 할 주체"에서 일을 처리(이벤트 시스템)할 것인지에 대한 차이입니다.

 

● Entity

- Entity 생성

    - MOD에서는 Entity를 생성할 수 있는 함수인 _SpawnServie를 제공해준다.

    - SpawnByEntityTemplate : 배치된 Entity와 동일한 Entity를 생성하는, Entity를 복제해주는 역할을 수행

        - 맵 상에 복제 대상이 되는 템플릿 Entity가 반드시 존재해야 한다.

 

Parameter Type Description
entity Entity * 복제할 엔티티를 지정합니다.
name string * 생성할 엔티티의 이름을 설정합니다.
spawnPosition Vector3 * 엔티티가 생성될 위치 좌표를 설정합니다.
parent(Optional) Entity * 생성된 엔티티의 부모가 될 엔티티를 넣어줍니다.
includeChild(Optional) boolean * 복제할 엔티티의 하위 엔티티도 함께 복제할 것인지 설정합니다.

 

--void SpawnByEntityTemplate()
    --SpawnByEntityTemplate의 파라미터값들을 설정합니다.
    local entityTemplate = _EntityService:GetEntityByPath("/maps/map01/object-49_1") -- 맵에 배치한 엔티티를 받아옵니다. 워크스페이스 -> 엔티티 -> 우클릭 -> Copy Entity Path로 패스를 가져올 수 있습니다.
    local name = entityTemplate.Name .. "Copy" -- 생성될 엔티티의 이름을 설정합니다.
    local spawnPosition = Vector3(0,0,0) -- 생성될 때의 위치 좌표를 설정합니다.

    local spawnedEntity = _SpawnService:SpawnByEntityTemplate(entityTemplate, name, spawnPosition) --스폰한 엔티티를 변수로 받으면, 해당 엔티티에 대한 후처리를 할 수 있습니다.
    if isvalid(spawnedEntity) == false then log("Spawn Failed") end

 

    - SpawnByModelID : 워크 스페이스에 추가된 모델 중 한 가지 모델을 지정해 엔티티를 생성해 주는 함수

        - 모델 리스트에 있는 모델을 Entity로 생성하고자 할 때 사용한다.

 

Parameter Type Description
id string * 워크스페이스에 추가된 모델 중 새로 생성할 엔티티의 템플릿이 될 Model ID를 넣어줍니다.
* Model ID는 Workspace - 모델(생성할 엔티티의 템플릿의 되는) - Copy Model ID를 선택해 가져올 수 있습니다.
name string * 생성할 엔티티의 이름을 설정합니다.
spawnPosition Vector3 * 엔티티가 생성될 위치 좌표를 설정합니다.
parent Entity * 생성된 엔티티의 부모가 될 엔티티를 넣어줍니다.

 

- 반환 값

    - 스폰 성공 시 Entity를 반환합니다.

    - 스폰 실패 시 nil을 반환한다.

 

- 사용 예시

    - 개발자 센터 Docs를 참고해보자.

 

-- void SpawnByModelId()
    --SpawnByModelId의 파라미터값들을 설정합니다.
    local id = "maplestorymapobject$002be76" -- 워크스페이스 -> Model 하위에 추가된 모델이 있으며, 모델 -> 우클릭 -> Copy Model ID로 ID를 복사해서 가져올 수 있습니다. 앞에 "model://"은 제거해줍니다.
    local name = "SpawnedEntity" -- 생성될 엔티티의 이름을 설정합니다.
    local spawnPosition = Vector3(0,0,0) -- 생성될 때의 위치 좌표를 설정합니다.
    local parent = _EntityService:GetEntityByPath("/maps/map01") -- 생성될 엔티티의 부모 엔티티입니다.
    local ownerId = nil -- 엔티티의 소유권을 가질 플레이어의 ID(Name)를 넣어줍니다. 일반적으로 nil로 설정합니다.

    local spawnedEntity = _SpawnService:SpawnByModelId(id, name, spawnPosition, parent, ownerId) --스폰한 엔티티를 변수로 받으면, 해당 엔티티에 대한 후처리를 할 수 있습니다.
    if isvalid(spawnedEntity) == false then log("Spawn Failed") end

 

- Entity 삭제

    - MOD에서는 Entity를 삭제할 수 있는 함수인 _EntityService:Destroy 또는 Entity:Destroy를 제공해주며, 삭제하고자 하는 엔티티를 위와 같이 지정해 삭제해줄 수 있다.

 

Parameter Description
entity * 유효성 체크 대상이 되는 엔티티를 넣어줍니다.

 

- 반환 값

    - 파라미터로 넘겨진 Entity가 존재하는 Entity면 true를, Entity가 nil이거나 삭제 대기 중,

      또는 삭제된 Entity라면 false를 반환

 

--void OnUpdate(number delta) [server only]
if isvalid(self.SpawnedEntity) == false then return end
if self._T.time == nil then self._T.time = 0 end

self._T.time = self._T.time + delta

if self._T.time >= 3 then
_EntityService:Destroy(self.SpawnedEntity)
end

 

--void OnUpdate(number delta) [server only]
if isvalid(self.SpawnedEntity) == false then return end
if self._T.time == nil then self._T.time = 0 end

self._T.time = self._T.time + delta

if self._T.time >= 3 then
self.SpawnedEntity:Destroy() --_EntityService:Destroy 대신 Entity:Destroy로 교체.
end

 

- Entity 유효성 체크

    - MOD에서는 isValid를 사용해 유효성을 체크한다.

 

--void OnUpdate(number delta) [server only]
if isvalid(self.SpawnedEntity) == false then return end
if self._T.time == nil then self._T.time = 0 end

self._T.time = self._T.time + delta

if self._T.time >= 3 then
    local isvalidValue = isvalid(self.SpawnedEntity)
    log("삭제 전 : "..tostring(isvalidValue)) -- 콘솔 창에 "삭제 전: true" 출력
    self.SpawnedEntity:Destroy()
    isvalidValue = isvalid(self.SpawnedEntity)
    log("삭제 후 : "..tostring(isvalidValue)) -- 콘솔 창에 "삭제 후: false" 출력
end

 

회고 :

 

*해당 게시글은 Supporters Hackathon 참여자에게 제공되는 MOD 학습 강의와 기본 학습 교안의 내용을 기반으로 작성되었음을 알립니다.