클라이언트 스크립트

Chain2D.UI

화면. 구조는 문서고 변화는 스크립트입니다 — 릴리스가 들고 있는 `client/**/ui/<이름>.json` 을 열어 바꾸고, 미리 적어 둘 수 없는 목록에서 만들어지는 화면만 스크립트가 직접 짓습니다. Axmol 은 여기 나오지 않습니다. 그 API 는 남의 일정으로 바뀌고, 거기에 대고 쓴 게임은 바뀔 때마다 깨집니다. 만드는 쪽은 모두 손잡이를 돌려줍니다. 숫자 id 를 그대로 주면 창작자가 그것으로 무엇을 할 수 있는지 알 수 없고, 실수로 다른 id 를 넘겨도 아무도 모릅니다. 손잡이는 얼려 두어 가리키는 것이 바뀌지 않습니다. 아래에서 요소 하나의 손잡이를 `Element`, `UI.load` 가 돌려주는 것을 `Screen` 이라고 적습니다 — 엔진의 타입 이름이 아니라 이 문서가 쓰는 이름입니다.

UI.load

선언
Chain2D.UI.load(name: string, parent: Element?) -> Screen
매개변수
namestring릴리스 안 `client/**/ui/<이름>.json` 의 파일 이름, 확장자 없이. `ui` 라는 폴더 안의 `.json` 만 화면으로 셉니다. 폴더는 이름에 들어가지 않으므로 `client/boot/ui/lobby.json` 과 `client/ui/lobby.json` 은 둘 다 `"lobby"` 이고, 그때 스크립트가 열 수 있는 것은 먼저 도착한 하나뿐입니다.
parentElement?문서를 어느 요소 안에 지을지. 생략하면 화면 전체에 대해 놓입니다. 요소의 손잡이가 아닌 것을 주면 거부되고, 이미 지워진 것을 주면 "the parent has been destroyed" 가 옵니다.
반환

Screen`root` 와 `find` 와 `maybe` 를 가진, 얼려 둔 표.

릴리스가 들고 있는 화면을 열어 트리에 짓습니다. 있던 것을 지우지 않고 더합니다 — 문서로 지은 화면에 스크립트가 덧붙이는 것이 보통이고, 두 문서를 한 화면에 같이 올리는 것도 그렇게 합니다. 없는 이름은 소리 내어 거부합니다. 조용히 아무것도 짓지 않으면 검은 화면이 남고, 그것은 파일이 없다는 뜻이 아니라 게임이 시작하지 못했다는 것처럼 보입니다. 문서가 읽히지 않을 때도 어느 화면의 무엇이 잘못됐는지와 함께 거부합니다. 로비의 화면은 `client/boot/ui/` 에 두세요. 로그인 전에 내려가는 것은 boot 번들뿐이고, 로비는 로그인 전에 그려집니다.

local screen = Chain2D.UI.load("lobby")
screen.find("action"):set({ text = "시작", visible = true })

screen.root

선언
screen.root: Element

문서의 첫 노드. 문서가 맨 위에 여럿을 적었다면 그중 처음 지어진 것 하나뿐입니다 — 나머지 최상위 노드는 그 아래에 있지 않으므로, `root` 를 감추거나 지워도 그것들은 그대로 남습니다. 화면 하나를 한 번에 잡으려면 문서의 최상위를 하나로 두고 나머지를 그 자식으로 적으세요.

local screen = Chain2D.UI.load("lobby")
screen.root:set({ visible = false })

screen.find

선언
screen.find(name: string) -> Element
매개변수
namestring문서에서 그 노드에 적은 `name`. 이름이 없는 노드는 찾을 수 없습니다 — 화면의 대부분은 배경입니다.
반환

Element그 노드의 손잡이.

이름으로 집습니다. 순서로 찾으면 위에 라벨 하나만 끼워 넣어도 모든 스크립트가 조용히 다른 것을 가리킵니다. 없는 이름은 오류입니다 — `nil` 을 돌려주면 그 다음 줄에서 터지고, 그때는 어느 이름이 없었는지가 더는 보이지 않습니다. 찾는 표는 `load` 가 돌아오는 그 순간 트리에 살아 있던 이름 있는 노드 전부로 만들어집니다 — 이 문서가 지은 것만이 아닙니다. 앞서 올려 둔 문서의 이름도 여기서 잡히고, 이름이 겹치면 나중에 지어진 것 하나만 남습니다. 뒤에 올린 문서의 이름은 이 표에 없으니 그쪽 `load` 가 돌려준 것으로 찾으세요. 스크립트가 만든 요소는 이름이 없어 어느 표에도 오르지 않습니다.

local screen = Chain2D.UI.load("lobby")
local status = screen.find("status")
status:set({ text = "다시 연결하는 중" })

screen.maybe

선언
screen.maybe(name: string) -> Element?
매개변수
namestring문서에서 그 노드에 적은 `name`.
반환

Element?있으면 손잡이, 없으면 `nil`. 오류를 내지 않습니다.

있는지만 묻습니다. 문서를 고쳐 가며 만드는 동안에는 아직 없는 이름을 부르는 것이 정상이라, 그때는 오류가 아니라 답이 필요합니다. 보는 표는 `find` 가 보는 그 표이니, 어디까지가 이 표인지는 `find` 에 적어 두었습니다.

local screen = Chain2D.UI.load("lobby")
local hint = screen.maybe("hint")
if hint then
    hint:set({ visible = false })
end

UI.Frame

선언
Chain2D.UI.Frame(props: { [string]: any }?) -> Element
매개변수
props{ [string]: any }?`anchor` `x` `y` `width` `height` `text` `fontSize` `image` `colour`(`color` 도 같이 받습니다) `visible` `onPressed` `parent`. 아는 것은 이것뿐이고, 만들 때도 모르는 이름은 `set` 과 똑같이 거부됩니다. 생략하면 기본값으로 만듭니다. 표가 아닌 것이나 인자 두 개는 거부됩니다.
반환

Element만들어진 요소의 손잡이.

사각형 하나를 만듭니다. 다른 것들을 담는 자리이고, `colour` 를 주면 그 색으로 칠해집니다. `props.parent` 에 다른 요소의 손잡이를 주면 그 안에 놓이고, 배치는 부모의 사각형이 먼저 정해진 뒤 그것에 대해 붙습니다. 같은 부모 안에서는 나중에 만들어진 것이 위에 그려지고, 눌림도 위에 있는 것이 가져갑니다. 요소가 아닌 것을 부모로 주거나 이미 지워진 것을 주면 거부됩니다.

local panel = Chain2D.UI.Frame({
    anchor = "center", width = 380, height = 200,
    colour = { 0.09, 0.10, 0.13, 0.95 },
})

UI.Text

선언
Chain2D.UI.Text(props: { [string]: any }?) -> Element
매개변수
props{ [string]: any }?`Frame` 과 같은 속성. 글자는 `text`, 크기는 `fontSize`, 색은 `colour` 입니다.
반환

Element만들어진 요소의 손잡이.

글자 하나를 만듭니다. 나중에 바뀌는 글자 — 상태 줄, 남은 시간 — 는 손잡이를 들고 있다가 `set` 으로 바꿉니다.

local panel = Chain2D.UI.Frame({ width = 380, height = 200 })
Chain2D.UI.Text({
    parent = panel, anchor = "top", y = -34,
    text = "내 게임", fontSize = 18,
})

UI.Image

선언
Chain2D.UI.Image(props: { [string]: any }?) -> Element
매개변수
props{ [string]: any }?`Frame` 과 같은 속성. `image` 는 릴리스 안의 그림 경로입니다.
반환

Element만들어진 요소의 손잡이.

그림 하나를 만듭니다. `image` 는 릴리스 안의 경로이고, 그것을 실제로 여는 것은 그리는 쪽입니다 — 이 트리는 어떤 파일을 그릴지만 들고 있습니다.

local panel = Chain2D.UI.Frame({ width = 380, height = 200 })
Chain2D.UI.Image({
    parent = panel, image = "client/art/logo.png",
    width = 96, height = 96,
})

UI.Button

선언
Chain2D.UI.Button(props: { [string]: any }?) -> Element
매개변수
props{ [string]: any }?`Frame` 과 같은 속성에 `onPressed`. `onPressed` 는 눌렸을 때 불릴 함수이고, `Button` 만 씁니다. 문서에는 적을 수 없습니다 — 함수는 파일에 담기지 않습니다.
반환

Element만들어진 요소의 손잡이.

누를 수 있는 것을 만듭니다. 누름은 겹친 것 중 맨 위의 버튼이 가져갑니다. 보이지 않는 것은 아예 배치되지 않으므로, 숨긴 패널 안의 버튼은 눌리지 않습니다. `onPressed` 를 만들 때 주지 않았어도 나중에 `set` 으로 줄 수 있습니다 — 다만 핸들러가 없는 버튼도 그 누름을 가져가고, 뒤에 있는 게임까지 내려보내지 않습니다.

Chain2D.UI.Button({
    anchor = "bottom", y = 28, width = 240, height = 40,
    text = "시작",
    onPressed = function() Chain2D.join("") end,
})

element.set

선언
element:set(props: { [string]: any }?) -> Element
매개변수
props{ [string]: any }?바꿀 속성만. 적지 않은 것은 그대로 둡니다 — 하나를 바꾸려고 나머지를 다시 적게 하지 않습니다. `colour` 는 `color` 로 적어도 받습니다.
반환

Element자기 자신. 만든 자리에서 이어 부를 수 있습니다.

요소의 속성을 바꿉니다. 모르는 속성은 거부합니다 — 조용히 무시하면 창작자는 줄을 썼고, 그 줄은 아무 일도 하지 않았고, 어디에도 그 사실이 적히지 않습니다. `set` 으로 준 `onPressed` 가 바로 그렇게 버려진 적이 있고, 버튼은 오류 없이 반응하지 않았습니다. 모르는 `anchor` 이름도 가운데로 되돌리는 대신 거부합니다. 이미 지워진 요소에 대고 부르면 "this UI element no longer exists" 가 옵니다 — 무엇을 바꾸려 했든 그 일은 일어나지 않기 때문입니다. `parent` 는 아는 속성이라 거부되지는 않지만, `set` 이 읽는 것에 들어 있지 않으므로 부모가 바뀌지는 않습니다.

local status = Chain2D.UI.Text({ text = "연결 중" })
status:set({ text = "연결됨", colour = { 0.4, 0.9, 0.5, 1 } })
status:set({ visible = false })

element.destroy

선언
element:destroy()

요소와 그 아래의 모든 것을 지웁니다. 자식이 같이 가는 이유는, 부모가 사라진 노드는 자리를 잡을 기준이 없어져 화면 전체에 대해 배치되기 때문입니다 — 지운 패널의 버튼이 구석에 다시 나타납니다. 이미 지워진 것을 다시 지우는 것은 아무 일도 하지 않습니다.

local panel = Chain2D.UI.Frame({ width = 200, height = 120 })
Chain2D.UI.Text({ parent = panel, text = "잠깐" })
panel:destroy()