본문 바로가기
Learn

전투 애니메이션 로직 - (1) 내가 만든 애니메이션 적용하고 HitBox 붙히기

by Roblox_개발자 2021. 4. 26.

작성자 : CHAN

최종 업데이트 : 2021.04.26

기타 : 애니메이션 적용 과정을 순서대로 업로드할 예정

이 예제에서는 내가 로블록스에 저장시킨 애니메이션들을 불러와 마우스로 공격 했을 때, 해당 애니메이션들을 실행시킬 수 있다. 마우스 한번클릭, 2번클릭에 따라 다른 애니메이션을 적용 시킨다.
그후 HitBox를 생성하여 해당 부위에 붙혀준다.

주석에 서술한 내용이 많으므로 주석을 잘 읽어볼 것 

사전준비

- 로블록스에 저장된 애니메이션들 ( 주먹공격 애니메이션, 발차기 애니메이션 등등 2개 이상 )

- roblox-blog.tistory.com/28 글의 3-6 (로블록스에 애니메이션 저장하기)까지 진행하고 이어서 하면 됨

 

- 이 글에서 사용한 애니메이션 (태권소년)

www.roblox.com/library/6729954651/Taekwon2

www.roblox.com/library/6730003404/Taekwon3

www.roblox.com/library/6729998143/Taekwon4

 

 

1. 스크립트 생성하기 

좌 : 탐색창 / 우 : Taekwon4 애니메이션 properties 내용

- StarterPlayer - StarterCharacterScripts 안에서 진행( 왜 이 폴더 안에서 코딩해야할까? )

1. LocalScript 생성( Combat2 으로 이름 변경)

2. Combat2 안에  Script 생성 ( CombatSystem2 으로 이름 변경 )

3. CombatSystem2 안에 Folder 생성 ( Animations 으로 이름 변경 )

4. Animations 안에 내가 만든 애니메이션 추가하기 
   1) Animations 옆 "+" 버튼클릭 ->  animation을 검색해서 추가한다.
   2) properties 에서 AnimationsId 에 내가 로블록스에 올린 애니메이션 링크를 넣는다 ( 자동으로 rbxasset...으로 변경된다) 
   3) 애니메이션 이름을 변경한다.


- ReplicatedStorage 안
1. Remote Event를 생성한다. ( HitEvent 로 이름 변경)



- Combat2 :  전투 관련 클라이언트 코드
- CombatSystem2 : 전투 관련 서버 코드
- HitEvent : 전투시 클라 , 서버를 연결할때 발생시키는 이벤트 ( Remote event 공부 )

 

 

 

2. 클라이언트 부분 ( Combat2 ) 

StarterPlayer / StarterCharacterScripts / combat2(LocalScript)
공부해야할 사항: TweenService, RemoteEvent, CFrame, UserInputService

-1. 인스턴스 생성 및 변수 선언

local Player = game.Players.LocalPlayer                  --goyou_chan
local Character = Player.Character                       --goyou_chan
local Humanoid = Character:FindFirstChild("Humanoid")    --Humanoid
local UserInputService = game:GetService("UserInputService") --inputObject
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local HitEvent = ReplicatedStorage:WaitForChild("HitEvent")
--local TweenService = game:GetService("TweenService")


local combo = 1   -- 진행시킬 콤보
local current = 0	-- 현재 마우스 클릭 시간
local prev = 0		-- 이전 마우스를 클릭 시간
local debounce = false	-- 애니메이션이 실행중인지 판별하는 변수 (애니메이션 중복 플레이 못하도록 하는데 사용)
서비스 연결 및 전투관련 스크립트에 필요한 인스턴스들을 생성해준다.
tween은 추후에 추가될 예정

 

-2. 마우스 클릭 시 서버로 이벤트 전송

--유저의 입력을 받았을 때 일어나는 함수 
UserInputService.InputBegan:Connect(function(input)
    
    --유저가 어떤 키를 누르는지 다 알 수 있게 해준다.
    --print(input.UserInputType) 
    
    --사용자가 마우스 왼쪽클릭 한 경우
    if input.UserInputType == Enum.UserInputType.MouseButton1  then
        
        --애니메이션 실행중이 아닌 경우
        if not debounce then
            debounce = true
            prev = current		-- 이전 클릭 시간
            current = tick()	-- 현재 클릭 시각 
            
            local gap = current - prev
            warn("Gap : "..gap)     --마우스 클릭 간격 차이
            
            -- 콤보 계산 로직
            -- 콤보 수마다 코드가 달라져야 하므로 나중에 변경되야함 지금은 2콤보 기준으로 작성
            -- 클라에서 서버로 combo라는 변수값을 보내서 1이면 콤보1 실행, 2면 콤보 2 실행 
            if gap > 0.8 then	-- 이전 클릭과의 시간 gap이 0.8초 초과면 콤보 해제
                combo = 1
                HitEvent:FireServer(combo)
                combo = combo + 1
            elseif combo == 1 and gap <= 0.8 then
                HitEvent:FireServer(combo)
                combo = combo + 1
            elseif combo == 2 then
                HitEvent:FireServer(combo)
                combo = 1
            end
            
            --애니메이션 끝난 후 애니메이션 진행중이 아니라고 상태 저장
            debounce = false
        end
    end
end)

 

현재는 2콤보 기준으로 작성되었다

print(input.UserInputType) 으로 유저가 어떤 키(키보드,마우스 등등)를 입력하는지 다 알 수 있다. 
1. 마우스 클릭 간격차이로 combo 값을 서버로 보낸다. 
2. 서버에서 받은 combo값에 따라 실행시키는 애니메이션이 달라진다. 


마우스 왼쪽 클릭 시 
Enum.UserInputType.MouseButton1,
우클릭시 
Enum.UserInputType.MouseButton2 등등, 키보드도 알 수있다.  

tick() 은 1970.1.1 부터 기록되는 시간값이다. 시간계산할때 용이하게 쓰인다.

Enum은 뭔지 잘 모르겠다.

 

 

 

 

3. 서버 부분 ( CombatSystem2 )

StarterPlayer / StarterCharacterScripts / combatSystem2(Script)
공부해야할 사항: TweenService, RemoteEvent, CFrame, Debris, WeldConstraint, Animator

-1. 클라에서 보낸 이벤트에 응답해 애니메이션 실행시키기

--전투 관련 서버 코드

local Player = game:GetService("Players").LocalPlayer
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local HitEvent = ReplicatedStorage:WaitForChild("HitEvent")
local Animations = script:WaitForChild("Animations")
local Debris = game:GetService("Debris") -- Hitbox에서 사용( 공부하기 )

------------------------------------------------------

--[[애니메이션 모음 테이블 ]]--
local anims = {  -- 유저 공격 순서(콤보)
    --Animations:WaitForChild("Taekwon2"),
    Animations:WaitForChild("Taekwon3"),
    Animations:WaitForChild("Taekwon4")
}



--마우스 클릭 했을때 HitEvent받는 부분 / 자동으로 Player 가 첫번째 파라미터에 추가됨
HitEvent.OnServerEvent:Connect(function(Player, combo)
    
    print("마우스 왼쪽클릭시 서버에서 받음")
    
    local Character = Player.Character
    local Humanoid = Character:FindFirstChild("Humanoid")
    local Humrp = Character:WaitForChild("HumanoidRootPart")
    local Animator = Humanoid:FindFirstChildOfClass("Animator")
    
    --받은 콤보변수값에 따라 다른 애니메이션을 실행시킨다.
    local attack = Animator:LoadAnimation(anims[combo])
    attack:Play()
    
end)
    
1. 전투 시 필요한 인스턴스들을 변수로 받아온다.
2. HitEvent.OnServerEvent:Connect 부분은 Combat2 (클라이언트) 에서 FireServer() 으로 발생시킨 이벤트를 받는 부분이다.

로컬에서 서버로 이벤트를 발생시킬때는 항상 player가 첫번째 파라미터로 들어온다. 서버측에서 누가 보낸건지 알아야 하기 때문이다. 

HumanoidRootPart는 왜 쓰일까? 
꼭 애니메이션을 폴더 안에 저장해놓고 써야하나? 필요할 때 인스턴스를 생성해서 사용하면 편하지 않을까?

 

이제 플레이를 해보자. 내 캐릭터에 내가 만든 애니메이션이 적용 된 것을 확인할 수 있다.

만약 애니메이션이 실행이 안된다면, AnimationID, AnimationName을 잘 확인해 볼 것 


 

-2. HitBox 생성하기

HitBox도 결국에는 하나의 Part이다.
추후에는 투명도를 0으로 하여 범위를 조절하면 될 것같다.

1) 먼저, Part를 만든 뒤, ReplicatedStorage에 추가한다.  (이름을 Hitbox로 변경한다 )

좌 : "Hitbox" part의 속성 / 우 : 해당 part의 위치

 

 

--[[애니메이션 모음 테이블 ]]--
local anims = {  -- 유저 공격 순서(콤보)
    --Animations:WaitForChild("Taekwon2"),
    Animations:WaitForChild("Taekwon3"),
    Animations:WaitForChild("Taekwon4")
}


-- 여기부터 추가하는 내용입니다!!!!!!!!!!!!

-- 해당 부분 추가하기
-- 유저가 타격할 때 사용한 부위. ( 콤보순서랑 순서가 맞아야함 + 애니메이션 만들때 어느 부위인지 기억)
-- 해당 부위에 HitBox가 생성될 예정
local limbs = {
    "LeftFoot",
    "RightFoot"
}




-------------[[로컬 함수]]-----------------------------
local function CreateHitbox(Player,combo)
    local Character = Player.Character
    local Limb = Character:WaitForChild(limbs[combo]) -- combo에 따라 어느 부위로 때렸는지 가져온다.
    
    --플레이어 이름의 폴더를 생성한다. 
    local folder = Instance.new("Folder", Character) 
    folder.Name = Player.Name
    
    -- 객체를 복사해준다. (복사해주는 이유는 계속 생성됐다가 사라지는걸 반복해야 하기 때문에 본 객체는 건들면 안된다.)
    local Hitbox = ReplicatedStorage:WaitForChild("Hitbox"):Clone()
    
    Hitbox.CFrame = Limb.CFrame   -- Hitbox의 위치를 타격할때 사용한 부위와 일치시킨다. 
    Hitbox.Parent = folder      -- ?? 왜 parent 지정해주는지 모르겠음
    Debris:AddItem(folder, .5)  -- Hitbox를 0.5초 후 사라지게 한다.

    local weld = Instance.new("WeldConstraint")
    weld.Part0 = Hitbox   -- Part0은 용접할 첫번째 Part ( 히트박스와 플레이어가 맞닿는 부위 )
    weld.Part1 = Limb     -- Part1은 용접할 두번째 Part ( 플레이어의 부위 )
    weld.Parent = workspace
    
    Debris:AddItem(weld, .5) -- 마찬가지로 용접부위도 0.5초후 사라지게 함

    return Hitbox
    
    
end





--마우스 클릭 했을때 HitEvent받는 부분 / 자동으로 Player 가 첫번째 파라미터에 추가됨
--애니메이션을 실행시키고, Hitbox를 추가하는 코드
HitEvent.OnServerEvent:Connect(function(Player, combo)
    
    print("마우스 왼쪽클릭시 서버에서 받음")
    
    local Character = Player.Character
    local Humanoid = Character:FindFirstChild("Humanoid")
    local Humrp = Character:WaitForChild("HumanoidRootPart")
    local Animator = Humanoid:FindFirstChildOfClass("Animator")
    
    --받은 콤보변수값에 따라 다른 애니메이션을 실행시킨다.
    local attack = Animator:LoadAnimation(anims[combo])
    attack:Play()
    
    
    -- Hitbox 생성 ( 이 부분 추가 )
    local Hitbox = CreateHitbox(Player, combo)	
    
end)
    
<추가되는 코드 설명 >

1. limbs 라는 테이블을 생성하고, 내가 Hitbox를 붙힐 파트의 위치들을 저장한다. 
이때, anims 테이블의 애니메이션순서와  limbs 테이블의 타격 위치가 일치해야 한다. 
(내가 만드는 애니메이션의 동작과 갯수에 따라 달라질 수 있다. )


2. CtreateHitbox() - 히트박스를 만들어주는 함수를 추가한다. (파라미터로 플레이어와 콤보가 들어간다)
(WeldConstraint, CFrame 모른다면 공부하기)

3. HitEvent.OnServerEvent:Connect 부분에 애니메이션 플레이 후 HibBox를 생성하는 함수를 사용한다.

 

이제 플레이를 해보면 내가 만든 애니메이션에 Hitbox가 추가된 모습을 확인 할 수 있다. 
다음편에서는 Hitbox에 데미지를 넣을 것이다. 

 

 

댓글