h662

메이플스토리 월드 강의 / 무한 점프맵 만들기 / 3화

무한 점프맵 만들기 3화, 첫 코드로 계단 만들기

2026년 10월 8일

무한 점프맵 만들기 3화, 첫 코드로 계단 만들기 완성 화면

첫 스크립트를 만들고, 테이블과 반복문으로 발판 일곱 개를 계단 모양으로 옮깁니다. 실행 중에 옮긴 발판을 밟으려면 무엇이 필요한지, 움직이는 발판만 있을 때 맵 경계를 어떻게 정하는지도 함께 다룹니다.

지난 글에서는 발판 일곱 개를 손으로 놓아 탑 1층을 만들었습니다. 이번에는 매번 손으로 놓는 대신, 코드로 발판을 옮겨 계단을 만들어 봅니다.

첫 스크립트

먼저 코드를 담을 스크립트를 만듭니다. 스크립트는 실행할 명령을 순서대로 적어 둔 코드 파일입니다. 메이플스토리 월드에서는 루아(Lua)를 메이플스토리 월드 환경에 맞게 다듬은 mLua 로 코드를 씁니다.

  1. 오른쪽 Workspace 창에서 MyDesk 를 오른쪽 클릭하고, Create Scripts 에서 Create Component 를 고릅니다.
  2. 이름을 FloorBuilder 로 짓습니다. 층을 만드는 스크립트라는 뜻입니다.
MyDesk 를 오른쪽 클릭해 컴포넌트 스크립트를 만든다
MyDesk 를 오른쪽 클릭해 컴포넌트 스크립트를 만든다

스크립트를 열면 기본 틀이 들어 있습니다. 여기에 OnBeginPlay 를 넣습니다. 이름 그대로 게임이 시작될 때 한 번 불리는 함수입니다. Method 옆의 + 버튼에서 Event Method 의 OnBeginPlay 를 고르면 틀이 바로 들어갑니다. 그 안에 log 한 줄을 적습니다. log 는 괄호 안의 글을 콘솔 창에 찍어 주는 함수로, 코드가 제대로 도는지 확인할 때 씁니다.

스크립트는 만든 것만으로는 아무 일도 하지 않습니다. 어딘가에 붙여야 돌아갑니다. Hierarchy 에서 맵 map01 을 고르고, Property 창 맨 아래의 Add Component 로 FloorBuilder 를 찾아 붙입니다.

실행하면 화면 아래 Console 창에 방금 적은 글이 찍힙니다. 같은 글이 두 줄 찍히는 것은 서버와 클라이언트에서 한 번씩 실행됐기 때문입니다. 이 이야기는 뒤에서 자세히 다룹니다.

실행하면 Console 창에 log 가 찍힌다
실행하면 Console 창에 log 가 찍힌다

편집기 오른쪽 아래의 Plain Text 를 누르면 파일에 실제로 저장된 코드를 볼 수 있습니다. 평소 보는 Script 보기는 같은 코드를 메이커가 보기 좋게 정리한 화면입니다. 이 강의는 Script 보기로 진행합니다.

배치표 (테이블)

이제 발판을 놓을 자리를 표로 적습니다. 메이플스토리는 가로와 세로만 있는 2D 게임이라, 발판 하나의 자리는 X 와 Y 두 숫자면 됩니다.

여기서 말하는 표는 루아의 테이블입니다. 여러 값을 한 번에 담아 두는 상자라고 생각하면 됩니다. 두 숫자를 중괄호로 묶으면 한 줄이 되고, 그 줄들을 다시 중괄호로 묶으면 표가 됩니다. 계단처럼 오른쪽으로 갈수록 0.5 씩 올라가게 적고, X 는 2.9 씩 늘립니다. 발판 길이가 2.4 라 발판 사이가 0.5 가 됩니다. 지난 글에서 올라가는 곳은 발판 사이를 0.5 정도로 좁힌다고 한 규칙 그대로입니다.

표에서 값을 꺼내는 법은 이렇습니다.

  • 표 이름 뒤에 대괄호와 번호를 적으면 그 번호의 줄이 나옵니다. 예를 들어 layout[1] 이 첫 줄입니다.
  • 루아는 다른 언어와 달리 번호를 0 이 아니라 1 부터 셉니다.
  • 꺼낸 줄에서 X 는 .x, Y 는 .y 를 붙여 가져옵니다.

1 번부터 7 번까지 log 로 한 줄씩 찍어 보면, 콘솔에 일곱 발판의 자리가 차례로 나옵니다. 아직 발판은 안 움직였지만 코드가 표를 제대로 읽고 있다는 것은 확인했습니다.

반복문

방금 적은 log 일곱 줄을 다시 보면 대괄호 안의 번호만 1 부터 7 까지 바뀌고 나머지는 모두 같습니다. 이렇게 같은 일을 번호만 바꿔 되풀이할 때 쓰는 것이 반복문입니다.

루아에서는 for 를 씁니다. for 와 ipairs 를 함께 쓰면 표의 첫 줄부터 마지막 줄까지 차례로 하나씩 꺼내 줍니다. ipairs 는 번호와 그 줄의 값, 두 가지를 동시에 꺼내 주고, 이를 for 뒤의 i 와 spot 이 받습니다. i 는 흔히 쓰는 인덱스의 약자이고 spot 은 우리가 지은 이름입니다. 반복할 내용은 do 와 end 사이에 적습니다.

그래서 log 일곱 줄은 반복문 안의 log 한 줄이 됩니다. layout[1].x 대신 spot.x, layout[1].y 대신 spot.y 를 쓰면 됩니다. 코드는 일곱 줄에서 세 줄로 줄었는데 결과는 같고, 발판이 백 개로 늘어나도 이 세 줄은 그대로입니다.

반복문으로 바꾼 뒤에도 같은 결과가 찍힌다
반복문으로 바꾼 뒤에도 같은 결과가 찍힌다

표대로 발판 옮기기

이제 표대로 발판을 옮깁니다. 발판들은 map01 아래에 Platform1 부터 Platform7 까지 이름이 붙어 있습니다. 코드에 나오는 말들을 하나씩 보면 이렇습니다.

  • self 는 이 스크립트 자신, FloorBuilder 입니다. self.Entity 는 스크립트가 붙은 대상이라 여기서는 map01 입니다. 맵, 발판, 캐릭터처럼 월드에 놓이는 것을 모두 엔티티라고 부릅니다.
  • GetChildByName 은 이름으로 바로 아래 자식 엔티티를 찾아 줍니다.
  • 번호를 일일이 적는 대신 반복문의 i 를 씁니다. 점 두 개(..)는 글자를 이어 붙이는 기호라, "Platform" .. i 는 i 가 1 일 때 Platform1 이 됩니다.
  • TransformComponent 는 엔티티의 위치, 크기, 회전을 담은 컴포넌트이고, 그 안의 Position 이 위치입니다. 지난 글에서 발판을 클릭했을 때 Property 창에 나오던 그 숫자입니다.

마지막으로 OnBeginPlay 오른쪽의 점 세 개 버튼에서 Execution Space Setting 을 열고 ServerOnly 를 고릅니다. 실행 공간(ExecSpace)은 이 코드를 어디서 실행할지 정하는 설정이고, ServerOnly 는 서버에서만 실행하라는 뜻입니다. 발판 자리는 서버가 한 번 정하면 접속한 모두에게 똑같이 보입니다.

RootDesk/MyDesk/FloorBuilder.mlua
@Component
script FloorBuilder extends Component

    @ExecSpace("ServerOnly")
    method void OnBeginPlay()
        local layout = {
            { x = 0,    y = 0 },
            { x = 2.9,  y = 0.5 },
            { x = 5.8,  y = 1.0 },
            { x = 8.7,  y = 1.5 },
            { x = 11.6, y = 2.0 },
            { x = 14.5, y = 2.5 },
            { x = 17.4, y = 3.0 },
        }
        for i, spot in ipairs(layout) do
            local platform = self.Entity:GetChildByName("Platform" .. i)
            platform.TransformComponent.Position = Vector3(spot.x, spot.y, 0)
        end
    end

end

그런데 실행해 보면 발판은 계단 모양으로 옮겨졌는데, 캐릭터가 발판 위에 서지 못하고 발판 속에 파묻힙니다.

그림은 옮겨졌지만 밟는 선(빨간 선)은 옛 자리에 남아 있다
그림은 옮겨졌지만 밟는 선(빨간 선)은 옛 자리에 남아 있다

메이플스토리 월드의 발판은 그림과 밟는 선이 따로 있습니다. 빨간 선이 캐릭터가 실제로 밟는 선입니다. 보통 발판은 저장할 때 밟는 선이 맵에 구워지기 때문에, 실행 중에 그림을 옮겨도 선은 따라오지 않습니다. 실행 중에 옮길 발판은 미리 움직일 수 있는 발판으로 바꿔 둬야 합니다.

  1. Hierarchy 에서 Shift 를 누른 채 발판 일곱 개를 모두 고릅니다.
  2. Property 창의 CustomFootholdComponent 에서 IsDynamicFoothold 를 켭니다.
발판 일곱 개를 한꺼번에 골라 IsDynamicFoothold 를 켠다
발판 일곱 개를 한꺼번에 골라 IsDynamicFoothold 를 켠다

다시 실행하면 이번에는 캐릭터가 제자리에서 꼼짝도 하지 않습니다. 콘솔에는 시작점이 맵 영역 밖에 있다는 경고가 떠 있습니다. 지난 글에서 맵 경계는 놓아 둔 발판들을 감싸는 범위로 정해진다고 했는데, 움직이는 발판은 경계를 정하는 데 들어가지 않습니다. 일곱 개를 모두 바꿨으니 경계를 정할 발판이 하나도 남지 않은 것입니다.

이럴 때는 경계를 직접 정합니다. map01 의 MapComponent 에서 UseCustomBound 를 켜고, 왼쪽 아래 LeftBottom 을 -2, -3 으로, 오른쪽 위 RightTop 을 20, 6 으로 적습니다. 발판이 놓인 곳보다 넉넉한 값입니다.

UseCustomBound 를 켜고 경계를 숫자로 정한다
UseCustomBound 를 켜고 경계를 숫자로 정한다

이제 실행하면 코드가 옮긴 계단을 하나씩 밟고 꼭대기까지 올라갈 수 있습니다. 손으로 하나하나 놓던 발판을 표 한 장으로 놓을 수 있게 됐습니다.

정리

  • 스크립트를 만들어 맵에 붙이고, OnBeginPlay 와 log 로 첫 코드를 돌렸습니다.
  • 테이블에 발판 자리를 적고, for 와 ipairs 반복문으로 차례로 꺼냈습니다.
  • GetChildByName 으로 발판을 찾아 TransformComponent 의 Position 을 바꿨습니다.
  • 실행 중에 옮길 발판은 IsDynamicFoothold 를 켜야 하고, 그러면 맵 경계는 UseCustomBound 로 직접 정해야 합니다.

다음 글에서는 이 표를 손으로 적는 대신 규칙으로 만듭니다. 실행할 때마다 다른 모양의 층이 나오도록 발판 자리를 무작위로 정합니다.

막힌 곳이 있으면 디스코드에 몇 화의 어느 부분인지 같이 적어 주세요.