MapleStroy Worlds(MSW) - 인트로 재생 구현(캐릭터 자동 이동 & 대화창)
Programming Language/MapleStroy Worlds

MapleStroy Worlds(MSW) - 인트로 재생 구현(캐릭터 자동 이동 & 대화창)

728x90

※ 작성자도 MSW 초보자이므로 코드가 비효율적이거나 기술 구현이 많이 부족합니다!😅

 

구현 영상

동작 설명

  1. 게임에 처음 접속하는 플레이어(뉴비)일 경우, 인트로를 실행합니다.
  2. StartMap에서 IntroMap으로 플레이어 이동시킵니다.
  3. 플레이어를 자동 이동 시키며, 스토리에 대한 이해를 도울 대사를 대화창을 통해 주인공의 혼잣말 형식으로 보여줍니다.
  4. 인트로가 끝나면 StartMap으로 다시 이동시킵니다.
  5. 중간에 Skip 버튼 누를 시 인트로 기능이 중지됩니다.(자동 이동이 멈추고 대화창 꺼지며 StartMap으로 이동하게 됩니다.)
  6. 뉴비가 아니라면 접속 시 인트로가 실행되지 않습니다.
  7. 테스트 데이터를 초기화하고 다시 접속하면 뉴비로 인식되어 인트로 실행되는 것을 확인할 수 있습니다.

개선이 필요한 부분

  1. 자동 이동 시에 player 아바타가 자연스럽게 걸어가는 애니메이션을 하게 만들고 싶은데 실패..
  2. 대화창에 npc처럼 player Portrait로 쓸만한 이미지가 있으면 좋겠는데 이건 불가능할 듯

 

대화창을 나타내 줄 InfroUIGroup 추가

이 기능은 MapleStory Worlds-DEVELOPER의 Docs를 참고하여 만들었습니다.

Docs와 다른 점

'데이터 테이블 추가' 부분에서 데이터 테이블의 컬럼을 text 하나로 구성했습니다.

위의 docs와 다르게 인트로 구현 부분은 여러 명의 주체가 대화를 나누는 것이 아닌 주인공 혼잣말이라는 점, 주인공은 마땅히 사용할 수 있는 portrait가 없다는 점에서 name, portrait 컬럼 없이 text 컬럼만 생성했습니다.

이에 따라 데이터 테이블에서 정보를 가져와 대화창에 띄워주는 부분에서 코드 차이가 있습니다.

728x90

DefaultPlayer에 IntroComponent 추가

모든 코드 블록 첫 줄에 해당 코드가 어느 컴포넌트인지 쓰여 있습니다. 꼭 해당 코드가 어느 컴포넌트에 존재하는 코드인지 확인해주세요!

--IntroComponent Property

--@ BeginProperty
--@ SyncDirection=All
string isNP = ""true""
--@ EndProperty

--@ BeginProperty
--@ SyncDirection=None
any introTalkData = "nil"
--@ EndProperty

--@ BeginProperty
--@ SyncDirection=None
Entity uiNameEntity = "nil"
--@ EndProperty

--@ BeginProperty
--@ SyncDirection=None
Entity uiMessageEntity = "nil"
--@ EndProperty

--@ BeginProperty
--@ SyncDirection=None
Entity uiTalkPanel = "nil"
--@ EndProperty

--@ BeginProperty
--@ SyncDirection=None
Entity uiPortraitEntity = "nil"
--@ EndProperty

--@ BeginProperty
--@ SyncDirection=None
string startMap = """"
--@ EndProperty

게임에 처음 접속한 플레이어만 인트로 재생

--IntroComponent

--@ BeginMethod
--@ MethodExecSpace=ServerOnly
void isNewPlayer()
{
local userId = self.Entity.Name
local userDS = _DataStorageService:GetUserDataStorage(userId)

--userDS에서 isNewPlayer를 key값으로 가지는 value 가져오기 위한 CallBack 함수
local getCallBack = function(errorcode, key, value)
	--해당 key 값이 없으면 value에 nil이 들어감(뉴비란 소리이므로 value에 true 넣어줌
	if value == nil then value = "true" end
	if key == "isNewPlayer" then
		self.isNP = value
	end
end

--한 번 접속한 플레이어이므로 isNewPlayer에 false 넣어주기 위한 CallBack 함수
local setCallBack = function(errorcode, key)
	self.isNP = "false"
end

userDS:GetAsync("isNewPlayer", getCallBack)
--wait을 해주지 않으면 DataStorage를 가져오기 전에 뒤에 코드 실행됨(타이밍 이슈 발생)
wait(1)

--뉴비면 튜토리얼 실행
if self.isNP == "true" then
	log("튜토리얼 실행")
	--튜토리얼 실행 함수
	self:playIntro()
	--DataStorage에 뉴비가 아니라는 데이터 넣어줌
	userDS:SetAsync("isNewPlayer", "false", setCallBack)
	wait(3)
end

}
--@ EndMethod

인트로 재생

--IntroComponent

--@ BeginMethod
--@ MethodExecSpace=Client
void playIntro()
{
--IntroMap으로 이동(시작맵이 IntroMap이라면 없어도 됨)
self:teleportToIntroMap()
--wait이 없으면 맵 이동 후 캐릭터가 타일 위에 내려앉기 전에 움직이기 시작함.
--(공중부양한 채로 움직이게 됨)
wait(1)
--대사 데이터 table 불러오기
self.introTalkData = _DataService:GetTable("IntroTalk")
--대사창 ui entity들 불러오기
self.uiNameEntity = _EntityService:GetEntityByPath("/ui/IntroTalkGroup/TalkPanel/Name")
self.uiMessageEntity = _EntityService:GetEntityByPath("/ui/IntroTalkGroup/TalkPanel/Text")
self.uiTalkPanel = _EntityService:GetEntityByPath("/ui/IntroTalkGroup/TalkPanel")
self.uiPortraitEntity = _EntityService:GetEntityByPath("/ui/IntroTalkGroup/TalkPanel/Portrait")

--플레이어 이름 대사창에 띄우기
local name = _UserService.LocalPlayer.NameTagComponent.Name
self.uiNameEntity.TextComponent.Text = name
--플레이어가 오른쪽을 보게 함
self.Entity.PlayerControllerComponent.LookDirectionX = 1
--자동이동
self.Entity.IntroAutomaticMovementComponent.Enable = true
self.uiTalkPanel.Enable = true
--대화창 초상화 띄우는 entity
--플레이어의 초상화는 따로 없으므로 enable=false로 해둠
self.uiPortraitEntity.Enable = false

--대화를 3개만 지정해두어서 3까지만
for count = 1, 3 do
	--dataset에 넣어둔 대사 하나씩 불러오기
	--GetCall 아님. GetC'e'll임.(주의)
	local message = self.introTalkData:GetCell(count, 1)
	--불러온 대사 대화창에 띄우기
	self.uiMessageEntity.TextComponent.Text = message
	--대사 하나당 4초씩 보여주기
	wait(4)
	--대사 끝났으면
	if count == 3 then
		--대화창 끄고
		self.uiTalkPanel.Enable = false
		--자동이동 멈추고
		self.Entity.IntroAutomaticMovementComponent.Enable = false
		--플레이어가 다시 기본으로 왼쪽을 보게 함
		self.Entity.PlayerControllerComponent.LookDirectionX = -1
		--시작 맵으로 이동
		self:teleportToStartMap()
	end
end
}
--@ EndMethod

--@ BeginMethod
--@ MethodExecSpace=All
void teleportToStartMap()
{
--처음 시작했던 맵으로 이동
--Vertor3에 이동하고 싶은 좌표 넣기
_TeleportService:TeleportToMapPosition(_UserService.LocalPlayer, Vector3(-6.473167, -0.4881773, 1), self.startMap)
}
--@ EndMethod

--@ BeginMethod
--@ MethodExecSpace=All
void teleportToIntroMap()
{
--시작 맵 저장 후, 인트로맵으로 이동
--Vertor3에 이동하고 싶은 좌표 넣기
self.startMap = self.Entity.CurrentMapName
_TeleportService:TeleportToMapPosition(_UserService.LocalPlayer, Vector3(-6.618879, -0.5888717, 1), "IntroMap")
}
--@ EndMethod

 

DefaultPlayer에 IntroAutomaticMovementComponent 추가

자동 이동

--IntroAutomaticMovementComponent

--@ BeginMethod
--@ MethodExecSpace=ServerOnly
void OnUpdate(number delta)
{
local deltaX = 1
local pos = self.Entity.TransformComponent.Position
pos.x = pos.x + deltaX * delta
local deltaPos = Vector2(pos.x, pos.y)
self.Entity.MovementComponent:SetPosition(deltaPos)
}
--@ EndMethod

참고 문서

MapleStory Worlds-DEVELOPER Docs의 엔티티 이동

 

스킵 버튼 추가

위와 같이 되도록 UI 버튼 생성

UI IntroTalkGroup 부모-자식 관계

스킵 버튼 활성화

--IntroComponent

--@ BeginEntityEventHandler
--@ Scope=All
--@ Target=entity:4a02f0a7-c93c-4547-9a5f-66c0fb53dbf3
--@ EventName=ButtonClickEvent
HandleButtonClickEvent
{
-- Parameters
local Entity = event.Entity
--------------------------------------------------------
log("Skip")
self.uiTalkPanel.Enable = false
--자동 이동 멈춤
self.Entity.IntroAutomaticMovementComponent.Enable = false
--플레이어가 다시 기본으로 왼쪽 보게 함
self.Entity.PlayerControllerComponent.LookDirectionX = -1
--시작 맵으로 이동
self:teleportToStartMap()
}
--@ EndEntityEventHandler
728x90