MSW 기본 학습 교안 8. Event와 컴포넌트 확장

bi_sz·2022년 9월 27일
0

MSW

목록 보기
8/11
post-thumbnail

Event는 객체와 객체 간에 주고 받는 형식이다. MSW에서도 이벤트가 많이 사용되어 MSW에서는 Entity Event System을 제공한다. Entity Event System은 MSW에서 이벤트 시스템을 쉽게 활용할 수 있도록 기본적으로 제공하는 API이다.

Event 시스템에 해당하는 3가지 구성 요소

  • Event : 로직 상에서 사건의 발생을 의미 ( Event의 종류의 식별 정보, 추가 정보 소유 )
  • Handler : 해당 Event를 받았을 때 처리하는 행동의 주체
  • Sender : 해당 이벤트를 발송하는 객체

Event 시스템의 장점

  • 다른 Component나 기능 단위에서 결합성이 떨어진다.
  • 행위에 대한 액션 추가 희망 시 행위 수행하는 곳 수정 없이 추가 가능하다.
  • 다른 Component의 정보를 필요로 하지 않는다.

Event 시스템의 단점

  • 사건 발생 시 전체적 플로우를 찾기 어렵다. (각각 처리하는 로직으로 인해 실행되는 시점에서 알 수 없다.)
  • 디버깅이 어렵다.
  • 순차적 행위 수행이 어렵다.

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

아래는 Event를 생성하는 과정이다.

1. LogEvent 객체 생성

2. LogEvent에 message Property 생성

3. myComponent 생성 및 Handler와 Function 추가


다음은 Log로 이벤트를 주고받는 과정이다. 쏘는 주체와 받는 주체가 같은 컴포넌트인 경우의 예시이다.

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

2. 로그 이벤트 받겠다는 핸들러 -> 자신의 엔티티 쪽으로 등록


각 엔티티들은 컴포넌트들을 포함하고 있다. 엔티티 이벤트 시스템은 다음과 같이 동작한다.

가장 먼저 컴포넌트는 각 엔티티를 중계자로 사용할 수 있으며, 각 컴포넌트는 엔티티를 통해 핸들러를 등록한다. ( 이벤트 발생 역시 엔티티를 통해 가능하다. )

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

정리를 해보자면, 어떤 Event가 왔을 때 이것을 처리하는 부분을 Component내의 로직에 넣는다.
그리고 이벤트 수신 등록을 Register, AddListener등으로 하며, MSW에서는 Entity에 등록을 하는 구조이다.


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

Ex) Component1에서 이벤트 발생 시, Component3의 이벤트를 실행시키고 싶을 때를 가정한다.
Component간에 연관이 없기 때문에 Component에서 바로 호출하지 않고 엔티티를 통해 호출하는 구조이다.

1. Component1이 특정 타이밍에 의해 엔티티로 발송

2. 로그 이벤트를 수신하겠다고 등록한 컴포넌트들에게 이벤트 발송

3. 해당 컴포넌트들은 이벤트를 받아서 처리

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

Ex) RabbitEntity쪽으로 이벤트를 쏘면 RabbitComponent가 수신해서 RabbitComponent안의 로그 메시지가 출력되는 구조이다.

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

2. 받는 엔티티 쪽에서 신호를 받고 이벤트 수행


이를 구현하는 방법은 두 가지 이다.

방법 1

1. RabbitComponent 에서 로그 이벤트 등록

2. MyComponent에서 RabbitEntity쪽으로 로그 이벤트 발송 등록


방법 2

1. RabbitComponent에서 로그 메시지 핸들 이벤트와 출력 등록 ( 이 때 BeerEntity로 이벤트 발송 등록x)

2. BeerComponent에서등록한 로그 이벤트를 RabbitEntity로 보내 로그 출력

  • 정리를 해 보았을 때, 분리되어 있는 환경에서 통신을 할 수 있게 해주는 것을 우리는 Event라고 할 수 있다.

이벤트 처리는 아래의 순서로 이루어 진다.

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

2. 핸들러 로직 추가

핸들러 로직이 이루어 지는 방법
  1. Entity Event Handler 추가
  2. 핸들러 상단 이벤트 중계자 설정
  3. 이벤트 처리 로직

3. 이벤트 발생 로직 추가


  • 아래 소스코드는 핸들러 로직을 추가하여 해가 떴을 때 Hp를 증가시키는 HunterComponent와, 해가 떴을 때 Hp를 감소시키는 VampireComponent에 대한 예제 코드이다.
Property : 
    [Sync]
    boolean isSunrise = false
    [Sync]
    number Hp = 0

    Method : 
        [server Only]
        void OnUpdate (number delta)
        {
            if self.isSunrise == true then --해가 떴는지 체크한다.
                self.Hp = self.Hp + delta --해가 떠 있을 동안 Hp가 증가한다.
                log("Hunter Hp : "..self.Hp) --현재 체력을 Console 창에 표시한다.
                if self.Hp >= 200 then self.Hp = 200 end --Hp가 200까지 증가했다면 증가를 멈춘다.
            end
        }

    Entity Event Handler : 
        entity map01 (/maps/map01)
        HandlerSunriseEvent(SunriseEvent event)
        {
            -- Parameters
            local isSunrise = event.isSunrise
            self.isSunrise = isSunrise
        }

Property : 
    [Sync]
    boolean isSunrise = false
    [Sync]
    number Hp = 0

    Method : 
        [server Only]
        void OnUpdate (number delta)
        {
            if self.isSunrise == true then --해가 떴는지 체크한다.
                self.Hp = self.Hp - delta --해가 떠 있을 동안 Hp가 감소한다.
                log("Vampire Hp : "..self.Hp) --현재 Hp를 Console 창에 표시한다.
                if self.Hp < 0 then self.Hp = 0 end --Hp가 0까지 감소했다면 감소를 멈춘다.
            end
        }

    Entity Event Handler : 
        Entity map01 (/maps/map01)
        HandlerSunriseEvent(SunriseEvent event)
        {
            -- Parameters
            local isSunrise = event.isSunrise
            self.isSunrise = isSunrise
        }
위의 코드에서 이벤트 발생 로직 추가의 경우 아래와 같이 해가 뜨고 지는 이벤트 로직을 추가할 수 있다.
Property : 
        [Sync]
        boolean isSunrise = false

    Method : 
        [server only]
        void OnUpdate (number delta)
        {
            if self._T.Time == nil then self._T.Time = 0 end
            self._T.Time = self._T.Time + delta

            if self._T.Time >= 5 then --5초마다 번갈아 해가 뜨고 진다.
                self._T.Time = 0
                if self.isSunrise == true then
                    self.isSunrise = false
                else
                    self.isSunrise = true --해가 떠 있는 상태 외에 나머지 상태는 isSunrise가 false이다.
                end
                log(self.isSunrise)
                self:SendEvent(self.isSunrise)
            end
        }

        [server]
        void SendEvent (boolean isSunrise)
        {
            local event = SunriseEvent()
            event.isSunrise = isSunrise
            self.Entity:SendEvent(event)

            self.isSunrise = isSunrise
            self._T.Time = 0
        }

완성된 컴포넌트를 map에 AddComponent를 통해 등록시켜준 후 이벤트 호출을 위한 로직을 추가해준다. 실습 강의의 경우 HunterComponentHandleKeyDownEvent를 추가해주었고, Z 키보드 사용 시 이벤트가 호출되도록 하였다.

Property : 
        [Sync]
        boolean isSunrise = false

    Method : 
        [server only]
        void OnUpdate (number delta)
        {
            if self._T.Time == nil then self._T.Time = 0 end
            self._T.Time = self._T.Time + delta

            if self._T.Time >= 5 then --5초마다 번갈아 해가 뜨고 진다.
                self._T.Time = 0
                if self.isSunrise == true then
                    self.isSunrise = false
                else
                    self.isSunrise = true --해가 떠 있는 상태 외에 나머지 상태는 isSunrise가 false이다.
                end
                log(self.isSunrise)
                self:SendEvent(self.isSunrise)
            end
        }

        [server]
        void SendEvent (boolean isSunrise)
        {
            local event = SunriseEvent()
            event.isSunrise = isSunrise
            self.Entity:SendEvent(event)

            self.isSunrise = isSunrise
            self._T.Time = 0
        }

업로드중..

엔티티 생성

  • MSW에서는 엔티티를 생성할 수 있는 함수인 _SpawnService를 제공해준다.

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

  • 맵 상에 복제 대상이 되는 템플릿 엔티티가 반드시 존대해야 한다.
--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 : 워크스페이스에 추가된 모델 중 한가지 모델을 지정해 엔티티를 생성해 주는 함수

  • 모델 리스트에 있는 모델을 엔티티로 생성하고자 할 때 사용한다.
-- void SpawnByModelId()
    --SpawnByModelId의 파라미터값들을 설정한다.
    local id = "maplestorymapobject$002be76" 
    -- 워크스페이스 -> Model 하위에 추가된 모델이 있으며, 
    -- 모델 -> 우클릭 -> Copy Model IDID를 복사해서 가져올 수 있다. 
    -- 앞에 "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

엔티티 삭제

  • MSW에서는 엔티티를 생성할 수 있는 함수인 _EntityService:Destroy 또는 Entity:Destroy를 제공해주며, 삭제하고자 하는 엔티티를 위와 같이 지정해 삭제해줄 수 있다.
--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

엔티티 유효성 체크

  • MSW에서는 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
self.SpawnedEntity:Destroy() --_EntityService:Destroy 대신 Entity:Destroy로 교체.
end

본문 : https://maplestoryworlds-developers.nexon.com/ko

0개의 댓글