1. 소개


지난 시간에 이어서 takeObject()를 좀 더 활용해보도록 합시다. 제가 업로드한 하트 오브 크라운 한글 (스크립트)를 예로 들자면, 게임 시작 버튼을 눌렀을 때, 각 플레이어에게 시작용 덱이 구성됩니다. 하지만 각 플레이어별로 시작용 덱의 구성이 달라 서로 다른 카드가 주어져야 합니다만 이들 카드는 모두 하나의 Bag에 들어있습니다. Bag에서 원하는 카드만을 골라 특정 플레이어에게 나눠줘야 하는 상황이 발생합니다. 이를 해결하는게 takeObject()의 Parameter를 활용하는 것입니다. 오늘은 이를 연습해 보도록 하겠습니다.

2. 준비

2-1. 구상

자판기를 생각해 봅시다. 음료수 버튼을 누르면, 자신이 누른 음료수가 아래쪽을 통해 나옵니다. 나오는 곳이 하나고 버튼이 여러개인 상태입니다. 이번 예제에서는 버튼을 여러개 만들고, 어떤 버튼을 눌렀을 때, 그 버튼에 해당하는 오브젝트가 주머니에서 나오도록 하는 스크립트를 구성해 보겠습니다.

2-2. 테이블 준비

동일한 Red Square를 5개 생성해 봅시다. 그리고 각 오브젝트를 마우스 우클릭하여 Color Tint를 눌러 색상을 수정합시다. 빨간색, 파란색, 초록색, 검은색, 흰색으로 총 5개의 색으로 하겠습니다.

각 오브젝트를 구분하기 위해 이름도 붙입니다. 마우스 우클릭하여 Name란에 각각 'Red Cube', 'Blue Cube', 'Green Cube', 'Black Cube', 'White Cube'라 이름을 붙입니다. 이 오브젝트들이 들어갈 Bag을 하나 생성합니다. 이번 스크립트 역시 Bag에 작성할 예정이므로 Bag을 우클릭하여 Script>Scripting Editor를 눌러 bag-1516a3.lua 페이지를 생성합니다. 마지막으로 5개의 정육면체 오브젝트를 모두 Bag 안에 넣어줍니다. 여기까지 완료했다면 테이블을 저장합니다.

3. 스크립트 작성

3-1. 버튼 만들기

우선 버튼을 만들어 봅시다. 오늘 예제에서는 총 5가지 버튼을 만들어야 합니다. 5개의 버튼을 만드려면 5개의 Parameter가 필요합니다. 하지만 이 버튼들은 실행할 함수만 다를 뿐, 크기가 동일한 버튼들입니다. 또, 버튼을 만드는 Parameter는 버튼을 생성할 때만 필요할 뿐, 버튼이 생성된 뒤에는 필요없는 정보가 됩니다. 그러므로 이번에는 하나의 Parameter를 재활용해 비슷한 여러가지 버튼을 만들어 보겠습니다.
우선 첫 번째 버튼을 위한 Parameter를 만들어 줍시다.

button_parm = {
    click_function = 'takeRed',
    function_owner = self,
    label = '빨간색',
    width = 800,
    height = 500,
    position = {0, 0.5, 2.5},
    font_size = 230,
}

빨간색 정육면체를 꺼내는 기능을 할 버튼의 Parameter입니다. 이어서 파란색 정육면체를 꺼내는 버튼을 위한 Parameter를 작성한다고 하면, 위 Parameter와 click_function, label, position이 세 요소만 달라지고 나머지는 동일할 것입니다. 그러므로 빨간색 버튼을 만든 뒤에 이 Parameter의 세 가지 값만 수정한 후, 재활용할 수 있습니다. 우선 onload()함수에 빨간색 버튼을 생성합니다.

function onload()
    self.createButton(button_parm)
end

Lua 테이블 조작
Parameter는 테이블 자료형을 갖고 있습니다. 테이블의 각 요소는 key(키)와 value(값)를 갖습니다. 예를 들어 위에 작성한 button_parm에서 'click_function'이라는 키에 'takeRed'라는 값을 갖는 것입니다. 알기 쉬운 예시를 들자면 주소와 그 주소에 살고있는 사람을 생각하시면 됩니다. 키는 사람이 살고있는 주소이고, 값은 그 주소에 살고있는 사람인 셈입니다. 테이블에 있는 값을 수정하기 위해서는 그 값이 있는 키를 지정해주어야 합니다. Lua에서 테이블의 값을 수정하는 방법에는 여러 가지가 있는데, 그 중 두 가지를 소개하고자 합니다.

    Parameter.key = value
    Parameter['key'] = value

위 두 가지 문장은 동일하게 작용합니다. 이 중 첫 번째 줄의 내용이 지난 시간에 배운 내용과 일치합니다. interactable을 소개할 때를 기억해보세요.

interactable은 함수가 아닌 오브젝트를 구성하는 요소이므로 수정방법이 다릅니다.

interactable은 오브젝트를 구성하는 요소라고 알려드렸습니다. 이를 수정하려면 위와 같은 방법으로 수정해야 하는것이죠. 그렇다면 onload()함수에 button_parm의 요소를 수정하는 코드를 이어서 작성해 봅시다. 아래와 같이 추가합니다.

button_parm.click_function = 'takeBlue'
button_parm.label = '파란색'
button_parm.position = {-2, 0.5, 2.5}
self.createButton(button_parm)

button_parm의 세가지 요소만 수정한 뒤 재활용하여 파란색 버튼을 만들었습니다. 동일한 방법으로 나머지 버튼도 추가해 줍시다.

button_parm.click_function = 'takeGreen'
button_parm.label = '초록색'
button_parm.position = {-4, 0.5, 2.5}
self.createButton(button_parm)

button_parm.click_function = 'takeBlack'
button_parm.label = '검은색'
button_parm.position = {2, 0.5, 2.5}
self.createButton(button_parm)

button_parm.click_function = 'takeWhite'
button_parm.label = '흰색'
button_parm.position = {4, 0.5, 2.5}
self.createButton(button_parm)
3-2. Bag 내용물 꺼내기

함수 인자(Parameter)
createButton(Parameter) 함수는 Parameter를 통해 생성할 버튼의 여러가지 요소를 수정할 수 있습니다. 마찬가지로 사용자가 직접 작성하는 함수에도 Parameter를 이용하여, 한 가지 함수가 여러가지 방식으로 실행되도록 할 수 있습니다. 이 때, 함수가 받아들이는 값을 Parameter라고 합니다. 이번에 만든 각 버튼은 꺼내는 내용물만이 다를 뿐, 내용물을 꺼낸다는 점에서는 동일하게 작동하는 함수입니다. 그렇다면 버튼을 누를 때 각기 다른 parameter를 한 가지 함수에 전달해, 내용물을 꺼낸다는 동작을 각기 다른 오브젝트에 수행하도록 할 수 있습니다. takeFromBag(Parameter)함수를 작성해보도록 합시다.

function takeFromBag(targetName)

end

이처럼 평소에 함수를 작성하는 것과 다르게 ()안에 Parameter를 넣었습니다. 이제 takeFromBag(Parameter)함수를 실행하면 Parameter에 넣어준 값이 targetName 변수에 저장되어 함수를 실행하는 동안 사용할 수 있습니다. 이때 전달받는 Parameter는 지역변수로 작용해서 함수 실행이 끝나면 사라집니다.

지역변수와 전역변수
Lua에서 변수는 크게 지역변수와 전역변수 두 종류로 나뉩니다. 전역변수는 해당 스크립트 전체에서 사용 가능한 변수입니다. 지금까지 예제에서 사용해 온 변수들이 이에 해당합니다.
지역변수는 해당 함수에서만, 혹은 해당 구간에서만 사용되는 변수입니다. 위 takeFromBag()함수의 Parameter인 targetName도 그 중 하나로, takeFromBag()함수의 실행이 끝나면 사라집니다. 그 외에 함수 내에서 지역변수를 선언할 때에는 함수의 이름 앞에 local을 붙이면 됩니다.

local 변수명 = 값

오브젝트 검색
takeObject(Parameter)함수와 그 Parameter를 이용해 Bag에서 특정 오브젝트만 꺼낼 수 있도록 스크립트를 작성해 봅시다. takeObject의 Parameter에 대해 다시 소개하자면,

  • position = 좌표 : 꺼낸 오브젝트가 올 위치입니다.
  • rotation = 좌표 : 꺼낸 오브젝트의 회전값입니다.
  • flip = boolean : 꺼낸 오브젝트가 카드라면 뒤집을지 뒤집지 않을지를 정합니다. (rotation을 입력했다면 무시됩니다)
  • guid = 문자열 : 꺼낼 오브젝트의 GUID입니다. 이를 이용해 무엇을 꺼낼지 정할 수 있습니다.
  • index = 숫자 : 몇 번째 오브젝트를 꺼낼지 정합니다. index는 0부터 시작합니다. 이를 이용해 무엇을 꺼낼지 정할 수 있습니다. (guid와 index는 동시에 입력할 수 없습니다)

안타깝게도 꺼낼 오브젝트의 이름을 지정하는 Parameter는 존재하지 않습니다. 그래서 우선 Bag에서 꺼낼 오브젝트의 이름을 이용하여 해당 오브젝트의 index를 알아내도록 합시다. getObejcts()함수를 통해 Bag안에 있는 오브젝트의 목록을 작성합니다. 이후, for문과 if문을 통해서 Bag에 있는 오브젝트의 이름을 하나씩 비교하여, 찾는 이름과 같을 경우 그 오브젝트의 index를 받아옵니다.

bagObjects = self.getObjects()

for i=1, #bagObjects do
    objectName = bagObjects[i]["name"]

    if objectName == targetName then
        targetIndex = bagObjects[i]["index"]
        break
    end
end

위 스크립트는 이같은 방법으로 작동합니다: bagObjects에 Bag에 있는 오브젝트의 정보를 순서대로 작성합니다. 여기에는 각 오브젝트의 이름, index, guid 등등을 포함합니다. 그리고 for문을 통해 1부터 Bag에 있는 오브젝트의 갯수만큼 반복합니다. 반복하는 내용은 objectName에 현재 확인중인 오브젝트의 이름을 넣고 targetName과 비교하여 같을 경우, targetIndex에 해당 오브젝트의 index값을 대입합니다. 그리고 이름이 같은 오브젝트를 찾았다면 더 이상 찾아볼 필요가 없으므로 break를 통해 for문을 빠져나옵니다.
찾아낸 index를 이용해 takeObject()함수를 실행하여 targetObject에 대입합니다. Parameter요소가 index 단 하나이므로, 테이블을 따로 정의하지 않고 직접 함수에 입력하도록 하겠습니다. for문 아래에 작성합니다.

targetObject = self.takeObject({index = targetIndex})

주의할 점은 index를 반드시 {}로 감싸 테이블로 전달해야 한다는 점입니다. takeObject(Prameter)는 테이블을 전달받기 때문입니다.

return
takeObject()함수는 Bag에서 오브젝트를 꺼내는 동작도 하지만, 꺼낸 오브젝트를 반환하기도 합니다. 그래서 위에 작성한 코드는 Bag에서 오브젝트를 꺼내고, 그 오브젝트를 targetObject에 대입해주기까지 합니다. 이처럼 함수는 코드를 실행하기도 하지만, 실행한 결과물을 내놓기도 합니다. 이는 마찬가지로 사용자가 작성한 함수에도 적용 가능합니다. 함수의 실행 결과로 반환되는 값을 return이라고 합니다. 아래와 같이 takeFromBag()함수의 마지막줄에 추가합니다.

return targetObject

정리
여기까지 따라오셨다면 takeFromBag()함수 전문은 아래와 같습니다.

function takeFromBag(targetName)
    bagObjects = self.getObjects()

    for i=1, #bagObjects do
        objectName = bagObjects[i]["name"]

        if objectName == targetName then
            targetIndex = bagObjects[i]["index"]
            break
        end
    end

    targetObject = self.takeObject({index = targetIndex})

    return targetObject
end

takeFromBag(targetName)함수는 다음과 같이 작동합니다: targetName을 Parameter로 전달받아 targetName과 같은 이름을 갖는 오브젝트를 찾아 Bag에서 꺼냅니다. 그리고 꺼낸 오브젝트를 반환합니다.

3-3. 버튼 함수

각 색상의 버튼을 누르면 실행될 함수를 작성합니다. 기반은 모두 작성했으므로 짧은 함수가 될 것입니다. 빨간색을 예로 들면, 버튼이 눌렸을 때, takeFromBag()함수를 실행하되 전달할 Parameter는 'Red Cube'가 됩니다. 그리고 이를 setPosition()함수를 이용해 지정된 위치로 옮겨주도록 합시다.

function takeRed()
    takenObject = takeFromBag('Red Cube')
    takenObject.setPosition({0.00, 1.46, -6.00})
end

동일한 방법으로 나머지 함수도 작성합니다.

function takeBlue()
    takenObject = takeFromBag('Blue Cube')
    takenObject.setPosition({-2.00, 1.46, -6.00})
end

function takeGreen()
    takenObject = takeFromBag('Green Cube')
    takenObject.setPosition({-4.00, 1.46, -6.00})
end

function takeBlack()
    takenObject = takeFromBag('Black Cube')
    takenObject.setPosition({2.00, 1.46, -6.00})
end

function takeWhite()
    takenObject = takeFromBag('White Cube')
    takenObject.setPosition({4.00, 1.46, -6.00})
end

여기까지 작성하셨다면 완성입니다.

4. 스크립트 전문


button_parm = {
    click_function = 'takeRed',
    function_owner = self,
    label = '빨간색',
    width = 800,
    height = 500,
    position = {0, 0.5, 2.5},
    font_size = 230,
}

function onload()
    self.createButton(button_parm)

    button_parm.click_function = 'takeBlue'
    button_parm.label = '파란색'
    button_parm.position = {-2, 0.5, 2.5}
    self.createButton(button_parm)

    button_parm.click_function = 'takeGreen'
    button_parm.label = '초록색'
    button_parm.position = {-4, 0.5, 2.5}
    self.createButton(button_parm)

    button_parm.click_function = 'takeBlack'
    button_parm.label = '검은색'
    button_parm.position = {2, 0.5, 2.5}
    self.createButton(button_parm)

    button_parm.click_function = 'takeWhite'
    button_parm.label = '흰색'
    button_parm.position = {4, 0.5, 2.5}
    self.createButton(button_parm)
end

function takeFromBag(targetName)
    bagObjects = self.getObjects()

    for i=1, #bagObjects do
        objectName = bagObjects[i]["name"]

        if objectName == targetName then
            targetIndex = bagObjects[i]["index"]
            break
        end
    end

    targetObject = self.takeObject({index = targetIndex})

    return targetObject
end

function takeRed()
    takenObject = takeFromBag('Red Cube')
    takenObject.setPosition({0.00, 1.46, -6.00})
end

function takeBlue()
    takenObject = takeFromBag('Blue Cube')
    takenObject.setPosition({-2.00, 1.46, -6.00})
end

function takeGreen()
    takenObject = takeFromBag('Green Cube')
    takenObject.setPosition({-4.00, 1.46, -6.00})
end

function takeBlack()
    takenObject = takeFromBag('Black Cube')
    takenObject.setPosition({2.00, 1.46, -6.00})
end

function takeWhite()
    takenObject = takeFromBag('White Cube')
    takenObject.setPosition({4.00, 1.46, -6.00})
end

5. 마무리


이것으로 takeObject()의 Parameter를 활용하는 강좌를 마치겠습니다. 사실 takeObject보다 중요한 것은 사용자가 작성하는 함수에도 Parameter를 통한 값의 전달과, return을 활용한 값의 반환의 이용이 되겠습니다. 이를 잘 활용하시면 동일한 기능을 하는 함수를 여러번 작성할 필요 없이 간결한 스크립트를 작성하실 수 있습니다. 궁금하신 점 댓글로 달아주시면 아는 범위에서 최대한 답변드리도록 하겠습니다. 읽어주셔서 감사합니다.

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기