상위 문서: Roblox Studio
게임 목록 | 이벤트 | 2차 창작 | 유료 서비스 Roblox Corporation 평가 (긍정적) | 문제점 및 비판 | 논란 및 사건 사고 | Roblox Studio스크립트 유료 모델 |
1. 개요2. 기본 문법3. 기본 스타일4. 스크립트 종류5. 자료형6. 라이브러리
6.1. bit326.2. buffer6.3. coroutine6.4. debug6.5. math6.6. os
7. 글로벌8. 서비스9. 클래스 목록10. 팁6.6.1. 함수
6.7. string6.7.1. 함수
6.8. table6.9. task6.9.1. 함수
6.10. utf81. 개요
로블록스 스튜디오의 스크립트에 대해 설명하는 문서다. 로블록스 스튜디오 스크립트의 사용 언어는 Lua의 파생 언어인 Luau[1]이다.2. 기본 문법
로블록스 스튜디오 스크립트가 사용하는 언어인 Luau 가 Lua의 파생형인 만큼 대부분의 문법이 Lua와 일치한다. 하지만 Lua에는 없는 문법이 다수 존재한다.2.1. 변수
변수는 어떠한 데이터를 담는 공간이다. 일반적으로 변수 생성은 local 로 가능하다.[2]#!syntax lua
local a = 123 -- number형
local b = "Hello World!" -- string형
local c = true -- boolean형
위 코드처럼 변수는 다양한 자료형들을 담을 수 있다.변수의 출력은 일반적으로 print 를 사용한다.
#!syntax lua
local a = 1
print(a) -- 1 출력
local b = "Hello World!"
print(b) -- Hello World! 출력
변수끼리의 연산도 가능하다. 단, boolean같이 서로 연산이 불가능한 자료형도 있다.#!syntax lua
local a = 1
local b = 2
local c = a + b
local d = (b / a - b * a)^a + b
print(c) -- 3
print(d) -- 2
local 을 사용하지 않고 생성한 변수는 스크립트 내 아무 범위에서나 사용이 가능하다. 즉 전역 변수가 된다. 반대로 local을 사용하여 생성된 변수는 전역 스코프에서 선언하면 전역 변수가 되는 것이고 지역 스코프 내에서 선언하면 지역 변수가 된다. 개념만으로는 이해가 어려우니 아래 예제 코드를 살펴보자.#!syntax lua
a = 1 -- 전역 변수, 어디에서나 사용이 가능하다.
local b = 2 -- 이 또한 전역 변수, 어디에서나 사용이 가능하다.
do
local c = 3 -- 지역 변수, do 스코프 내에서만 사용이 가능하다.
end
하지만 local 없이 변수를 선언하는 건 삼가야 한다. local 과 함께 선언한 변수가 local 없이 선언한 변수보다 더 빠르고, 해커들이 바이트코드 상태에서 찾기 더 힘들기 때문이다.[3]2.2. 연산자
- 산술 연산자
+ : 더하기
- : 빼기
\* : 곱하기
/ : 나누기
// : 버림 나누기
^ : 제곱
% : 나머지
(피연산자 앞의) - : 마이너스 부호[4]
- 논리 연산자
and : 두 조건이 모두 true 일시 true 로 치환됨
or : 두 조건중 한 조건이라도 true 일시 true 로 치환됨
not : 조건의 반대로 치환됨
- 관계 연산자[5]
== : 같다
~= : 같지 않다
\> : 더 크다
\< : 더 작다
\>= : 더 크거나 같다
<= : 더 작거나 같다
- 복합 대입 연산자
+= : 대입 후 더하기
-= : 대입 후 빼기
\*= : 대입 후 곱하기
/= : 대입 후 나누기
//= : 대입 후 버림 나누기
%= : 대입 후 나머지
^= : 대입 후 제곱
..= : 대입 후 연결[6]
- 기타 연산자
.. : 문자와 문자를 연결
# : 테이블의 길이[7]
2.3. 조건문
스크립팅을 하다보면 필연적으로 사용해야 하는 부분이 있다. 바로 조건문이다. 아래는 조건문이 활용될 수 있는 간단한 예이다.[8]- 플레이어의 체력이 10% 이하가 되면 이동속도를 50% 증가시킨다.
- 플레이어가 채팅에 금지어를 입력할시 플레이어가 추방된다.
- 플레이어가 10점을 얻을시 게임에서 승리한다.
스크립트는 이러한 조건들을 다루기 위해 조건문을 사용한다. 조건문은 앞서 나열한 연산자들을 사용한다. 다음은 간단한 조건문을 사용한 예제 코드이다.
#!syntax lua
if true then
print("첫번째 조건 통과") -- 실행된다.
end
if 1 == 2 then
print("두번째 조건 통과") -- 실행되지 않는다.
end
if not 1 >= 3 and true then
print("세번째 조건 통과") -- 실행된다.
end
이처럼 조건문은 기본적으로 'if 조건 then 조건 통과시 실행될 코드 end' 로 구성된다. 이러한 조건문엔 추가로 else, elseif 를 덧붙일 수 있는데, 아래 예제 코드를 통해 살펴보자.#!syntax lua
if false then
print("첫번째 조건 통과") -- 실행되지 않는다.
elseif false or 1 == 2 then
print("두번째 조건 통과") -- 실행되지 않는다.
else
print("세번째 조건 통과") -- 실행된다.
end
elseif 와 else 는 if 조건문이 통과되지 않았을시 실행되는 추가 조건문들이다. elseif 는 처음 if 조건문이 통과되지 않았을시 그 다음 if의 역할을 한다. else 는 if 와 elseif 조건들이 모두 통과되지 않았을시 무조건 실행되는 예외처리 역할을 한다.[9] elseif 는 if 다음에 연속적으로 붙을 수 있는 반면, else 는 마지막 조건으로만 사용이 가능하다.추가로 간이 조건문도 사용이 가능하다. 아래 예제 코드들을 통해 사용방법을 익혀보자.
#!syntax lua
-- 구버전
local a = true and 1 -- 1이 저장된다.
local b = false or 2 -- 2가 저장된다.
-- 최신 버전
local a = if true then 1 -- 1이 저장된다.
local b = if false then nil else 2 -- 2가 저장된다.
간단한 간이 조건문은 구버전을 사용해도 문제없지만, 복잡한 간이 조건문은 최신 버전을 사용하는 게 좋다. 그러나 간이 조건문을 과도하게 사용할시 가독성에 문제가 생길수 있다. 따라서 간이 조건문은 상황에 따라서 정말 필요할 때만 사용하도록 하자.2.4. 반복문
아래 코드를 살펴보자.#!syntax lua
print(1)
print(2)
print(3)
print(4)
print(5)
print(6)
print(7)
print(8)
print(9)
그저 1 2 3 4 5 6 7 8 9 를 출력하는 코드인데 무려 9줄이나 소요했다. 이러한 반복되는 코드를 효율적으로 줄이기 위해서 반복문이 존재한다. Luau에서 반복문은 크게 3가지로 나뉜다. 먼저 while 반복문을 알아보자. while 반복문은 'while 조건 do 실행할 코드 end' 형태로 이루어져있다. 메커니즘은 꽤나 간단하다. 그저 조건이 false 가 될 때까지 실행할 코드를 반복한다. 아래 예제 코드는 위 9줄의 코드를 while 반복문으로 간편하게 줄인것이다.#!syntax lua
local num = 1
while num < 9 do
print(num)
num += 1
end
while 반복문은 조건이 false 가 될 때까지 무한히 반복하기 때문에 실행되는 코드가 중간에 정지 없이 과도하게 오랫동안 반복해서 실행된다면 성능에 치명적인 손상을 입힐수 있다. 그러므로 while 반복문을 사용할 때는 항상 실행되는 코드가 1초 또는 그 이상동안 반복될 수 있는지 체크해야 한다. 만약 그렇다면 task.wait(n초) 를 넣어 반복되는 코드마다 조금씩 간격을 놓자. task.wait(n초) 를 조건에 넣을 수도 있는데, 이는 false 가 되는 조건을 아예 없애는 것이기 때문에 실행될 코드가 무한하게 반복된다.이제 repeat 반복문을 알아보자. repeat 반복문은 while 반복문과 상당히 비슷한데, 'repeat 실행할 코드 until 조건' 으로 이루어져 있다. repeat 반복문이 while 반복문과 다른점은 조건이 true 가 될 때까지 반복된다는 것이다. 이번엔 repeat 반복문으로 위 9줄의 코드를 간편하게 줄여보자.
#!syntax lua
local num = 1
repeat
print(num)
num += 1
until num > 9
repeat 또한 while 과 같이 1초 또는 그 이상동안 반복될시 중간정지 없이 쓰인다면 성능에 치명적인 손상을 입힐수 있으니 task.wait() 을 넣는 걸 추천한다. 이러한 repeat 은 일반적으로 while 과 전혀 다르게 쓰인다. while 반복문이 주로 코드를 무한하게 반복하려고 쓰인다면, repeat 반복문은 주로 조건이 존재할 때까지 기다리는 용도로 쓰인다.마지막으로 for 반복문을 알아보자. for 반복문은 'for 변수 = 시작수, 끝수, 증가수 do 실행할 코드 end' 로 이루어져있다. for 반복문이 위의 두 반복문과 다른점은 for 스코프 내에서 변수를 선언하고 실행할 코드에서 직접 사용할 수 있다는 것이다.[10] 그리고 실행할 코드가 끝날시 증가수를 변수에 더하고 다시 코드를 실행한다. 만약 변수가 끝수에 도달하면 반복문이 종료된다. 이번엔 for 반복문을 이용하여 위 9줄의 코드를 간편하게 줄여보자.[11]
#!syntax lua
for num = 1, 9, 1 do
print(num)
end
for 반복문도 마찬가지로 과도하게 실행될것으로 예상된다면 실행될 코드내에 task.wait(n초)를 넣는 것이 현명하다. for 반복문은 다른 형태로도 사용이 가능한데, 이는 차후에 설명할 배열과 관련있는 부분이므로 현재로선 생략한다.2.5. 함수
함수란 특정 작업을 수행하는 코드의 집합체이다. 함수를 사용하면 코드의 재사용성, 가독성 및 유지보수성을 높일 수 있다. 또한 함수를 이벤트에 연결해 콜백으로 지정할 수도 있다. 다음은 매우 기초적인 함수를 나타낸 코드이다.#!syntax lua
function a()
print("a")
end
이렇듯 함수의 구조는 'function 이름() 실행할 코드 end' 로 이루어진다. 함수는 매게변수라는 특정한 변수를 받을 수 있다. 매게변수란 함수가 호출될 때 전달되는 값으로 매게변수를 통해 함수의 실행 결과에 변화를 줄수 있다. 그렇다면 함수의 호출은 어떤 식으로 행해질까? 함수의 호출은 '함수의 이름(매게변수들[12])' 로 가능하다. 글로만 설명하는 것은 복잡하니 다음 예시 코드를 보며 이해해보자.#!syntax lua
function printName(name) -- 여기서 name 은 출력할 이름을 결정짓는 매게변수이다.
print(`Hello {name}!`)
end
printName("Roblox Studio") -- Roblox Studio 라는 매게변수와 함께 함수를 호출한다.
또한 함수는 값을 리턴할 수 있다. 여기서 리턴이란 값을 반환한다는 것이다. 지금까지의 함수 a와 printName 은 리턴값이 nil 이였다. 만약 함수가 값을 반환하게 된다면(값을 리턴하게 된다면) 함수의 호출 부분에서 함수가 반환한 값을 받을 수가 있다. 받는 방법은 간단하다. 변수같은 데이터를 저장할 수 있는 공간을 두고 그 안에 함수의 리턴값이 들어오게 하는 것이다. 또는 다른 함수 안에 함수 호출 부분을 집어넣어서 다른 함수의 매게변수로 바로 넘겨버릴 수도 있다. 다음 예제 코드를 통해 더 자세히 알아보자.#!syntax lua
function returnGreetingText(name)
return `Hello {name}!` -- string(문자열)을 반환한다.
end
local robloxGreetingText = returnGreetingText("Roblox") -- 변수안에 함수의 리턴값을 저장한다.
print(robloxGreetingText) -- 리턴값이 담긴 변수를 출력한다.
print(returnGreetingText("Roblox")) -- 함수의 리턴값을 바로 print 함수의 매게변수로 넘긴다.
Luau 프로그래밍을 처음 접해보는 사람이라면 후자가 더 짧고 편리하다고 느낄수 있지만 변수의 재사용성과 코드의 가독성을 고려한다면 전자를 선택하는 것이 현명한 판단이라고 볼 수 있다.글 초반에 함수를 이벤트에 연결해 콜백으로 만드는 것이 가능하다고 언급하였는데, 이 또한 원리는 매우 간단하다. 함수를 만들거나 무명함수[13]를 사용해 이벤트의 연결 메서드 매게변수로 넘겨주는 것이다. 그리하여 이벤트는 넘겨준 함수를 이벤트가 트리거될 때마다 발생하는 콜백으로 이용하는 것이다. 함수와 이벤트를 많이 접해보지 않은 사람에겐 미숙할 수 있지만, 현재로선 그저 함수가 이벤트에 사용된다 라는 것만 알아두어도 된다.
지금까지의 예제 코드들은 모두 local 을 붙이지 않고 함수를 선언해왔지만 사실 이런식으로 함수를 선언하는 것은 옳지 않다.[14][15] local을 붙인 함수가 붙이지 않은 함수와 성능에 차이가 나기 때문이다.
2.6. 테이블
변수가 한 개의 데이터만을 담는 공간이라면 테이블은 여러개의 데이터들을 담는 공간이다. 테이블은 {}로 만들수 있으며 그 안에 nil 이 아닌 여러개의 자료형들을 담을 수 있다. 테이블은 두가지 종류로 나뉘는데, 바로 배열(Array) 과 딕셔너리(Dictionary) 이다. 먼저 배열에 대하여 알아보자.배열은 순서가 정해진 데이터들의 리스트이다. 이는 플레이어 목록같이 데이터들의 집합을 저장하기에 용이하다. 배열을 구축하려면 중괄호({}) 안에 차례대로 원하는 데이터 값들을 넣으면 된다. 다음은 기본적인 배열의 형태이다.
#!syntax lua
local array = { "Hello World!", 123, false, workspace.Camera }
배열에서 원하는 데이터 값을 읽는 방법은 배열에 대괄호([]) 를 치고 데이터가 들어간 순서(index) 를 넣으면 된다. 이때 데이터의 순서는 1부터 시작해서 2, 3, 4... 로 증가한다.[16] 다음은 전에 만든 배열의 값들을 읽는 코드이다.#!syntax lua
local string = array[1] -- Hello World!
local number = array[2] -- 123
local boolean = array[3] -- false
local instance = array[4] -- workspace.Camera
값을 읽는법을 배웠으니 이제는 값을 쓰는법을 배울 차례다. 배열에서 값을 쓰는법은 배열에 대괄호를 치고 데이터가 들어갈 순서를 넣고 =(대입 연산자) 을 통해 새로운 값을 넣거나 기존 값을 교체할 수 있다. 다음은 기존 array 에 데이터를 쓰는 코드이다.#!syntax lua
array[2] = 12345 -- 2번째 순서의 값이 12345로 대체됨.
array[5] = true -- 5번째 순서의 값이 새로 생겨남.
이러한 배열은 내장 라이브러리 table 을 통해 더 다양하게 활용할 수 있다. 다음은 table 라이브러리를 사용한 예제 코드이다.#!syntax lua
table.insert(array, "New Value") -- New Value 값을 (마지막 순서 + 1)에 집어넣는다.
-- => { "Hello World", 123, false, workspace.Camera, "New Value" }
table.remove(array, 1) -- 첫번째 순서의 값을 제거하고 뒤의 값들을 앞으로 집어넣는다.
-- => { 123, false, workspace.Camera, "New Value" }
또한 배열은 #(기타 연산자) 을 통해 몇개의 데이터들이 저장되어 있는지 확인할 수 있고 print 를 통해 출력할 수도 있다.이제 딕셔너리에 관하여 알아보자. 딕셔너리란 배열의 확장형으로 키(key)-값(value) 형식으로 되어 있다. 이때 키와 값에는 여러 자료형들이 들어갈 수 있다. 딕셔너리를 구축하려면 중괄호({}) 안에 원하는 키를 넣고 =(대입 연산자) 를 값과 연결시켜야 한다. 다음 코드는 기본적인 딕셔너리의 형태이다.
#!syntax lua
local dictionary = {
name = "apple", -- 여기서 키는 name, 값은 apple 이다.
color = "red", -- 키가 단순 문자(영어 기준)가 아니라면 대괄호([]) 와 함께 넣어야 한다.
enchanted = false -- 마지막 값에는 콤마(,) 를 넣지 않아도 된다.
}
이제 딕셔너리를 읽는법을 알아보자. 딕셔너리를 읽는법은 배열을 읽는법과 유사한데, 딕셔너리에 대괄호([]) 를 치고 키 값을 넣으면 된다. 다음은 전 코드의 딕셔너리의 값들을 읽는 코드이다.#!syntax lua
local name = dictionary["name"] -- apple
local color = dictionary["color"] -- red
local isEnchanted = dictionary["enchanted"] -- false
읽는법을 배웠으니 이젠 쓰는법을 배워보자. 딕셔너리를 쓰는법도 배열을 쓰는법과 유사한데, 딕셔너리에 대괄호([]) 를 치고 키 값을 넣고 =(대입 연산자) 를 통해 원하는 값을 넣으면 된다. 원하는 값의 키가 기존에 있다면 기존에 있던 값이 교체되고 기존에 없다면 새로운 키와 값이 생성된다. 다음은 딕셔너리의 값들을 쓰는 코드이다.#!syntax lua
dictionary["enchanted"] = true -- enchanted 키의 값을 true 로 변경.
dictionary["golden"] = true -- golden 이라는 새로운 키를 true 값과 함께 생성.
또한 딕셔너리에서 원하는 값을 제거하려면 쓰는법과 동일하게 대괄호와 제거하려는 값의 키 값, = 을 넣고 nil 을 대입해주면 된다. nil을 넣음으로서 값이 nil로 교체되고 가비지 컬렉터(gc) 가 수거해가는 원리.2.7. 주석
- 단일 주석
{{{#!syntax lua
}}}
- 여러줄 주석
{{{#!syntax lua
이것은 여러줄 주석이다.
]]}}}
- 여러줄 주석 변형
{{{#!syntax lua
이것은 여러줄 주석의 변형이다.
]=]}}}
- TODO 주석
TODO 부분이 자동으로 볼드체가 되며, 스크립트를 메모장처럼 쓸 때 자주 쓰인다.
{{{#!syntax lua
}}}
3. 기본 스타일
본 스타일 가이드는 기본적으로 로블록스의 공식 Lua 스타일 가이드를 따르며 제대로된 스타일 체계가 잡혀있지 않은 스크립터들에게 도움을 주기 위함을 밝힌다.3.1. 스크립트
- 스크립트를 파트 안, 모델 안 등 스크립트와 관련 없는 개체 안에 놓는 것은 삼가야 한다.[17]
- 워크스페이스는 스크립트를 놓으라고 마련된 공간이 아니다. 가능한 워크스페이스에 스크립트를 배치하는 것을 삼가자.
- 일반적으로 하나의 스크립트가 여러개의 개체들을 조작해야 한다. 스크립트를 여러개 만들어 하나의 스크립트가 하나의 개체를 조작하게 하는 것은 옳지 않다.
- 스크립트 구조와 스타일(디자인)은 게임의 컨텐츠와 본인의 실력에 따라 신중하게 선택해야 한다. 대표적인 구조로는 대다수의 스크립터들이 선택하는 multi script architecture (다중 스크립트 구조)와 실력있는 소수의 스크립터들이 선택하는 single script architecture (단일 스크립트 구조)가 있다. 또한 대표적인 프로그래밍 스타일(디자인)으로는 oop (object oriented programming) 과 fop (functional oriented programming), pop (procedure oriented programming), dod (data oriented design)[18] 등을 선택할 수 있다.
3.2. 네이밍
- 스크립트의 이름은 어떠한 케이스를 선택해도 상관없으며 스크립트가 하는 행위를 간단 명료하게 설명할 수 있어야 한다.
- 변수든 함수든 딕셔너리의 키든 모든 이름은 언제나 의미를 가져야 한다. 작명하기 귀찮다는 이유로 이름을 a, b, c, d 등 한 문자로 짓는 것은 옳지 않다.[19]
- 불변성 변수 (require 한 모듈, 서비스 등 변하지 않는 값들) 은 PascalCase 로 작명한다.
- 가변성 변수 (변할 수 있는 값들, 일반적으로 대부분의 변수) 은 camelCase 로 작명한다.
- 설정값 변수 (Offset 등 스크립트 내에서 선언하는 불변성 설정값들) 은 SCREAMING_SNAKE_CASE 로 작명한다.
- for 반복문의 변수는 camelCase 로 작명하며 가독성을 위해 n, i, v 등 알파벳 하나로 작명하는 것을 피한다. 그러나 변수에 큰 의미가 없다면 n, i, v 로 작명하여도 된다.
- 매게변수는 camelCase 로 작명한다.
- 함수는 camelCase 또는 snake_case 로 작명한다.
- 딕셔너리의 키(메서드나 이벤트가 될 수도 있음, 딕셔너리의 키에 포함되는 모든것을 속함) 는 상황에 따라서 camelCase, PascalCase 중 하나를 선택한다.
- 초기에 변수를 선언할 때 불변성 변수, 가변성 변수, 설정값 변수 등으로 체계적으로 나누어 줄을 띄어쓰는 것도 좋다.
- 한 케이스(표기법)만을 추구하는 것은 옳지 않다. 상황에 따라서 올바른 케이스를 쓰는 것이 현명하다.
- 약자(줄여쓰기)를 쓰는 것은 옳지 않다.[20]
- 이름이 스크립트 내 단어와 겹친다면 camelCase, PascalCase, snake_case 등 다른 케이스로 작명하는 것이 좋다.[21]
- private 딕셔너리 키는 이름앞에 _를 붙일 수 있다. 이는 메서드나 이벤트에도 포함된다.
- 사용하진 않지만 튜플의 한계상 넣어야 하는 변수는 이름 앞에 _ 를 붙이거나 아예 _로 표기한다.[22]
- 일반적인 상황에서 한글로 작명하는 것은 피한다.
- 위의 수칙들은 기본적인 네이밍 스타일이며, 상황에 따라서 위의 수칙들을 지키지 않아도 된다. 그러나 개인의 취향을 위하여 단독적으로 자신만의 스타일만을 고집해 나가는 것은 추천하지 않는다.
{{{#!folding [ 네이밍 스타일 예제 ]
#!syntax lua
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local SUBSCRIPTION_ID = "EXP-0"
local function checkSubscriptionHistory(player: Player)
local subscriptionHistory = {}
local success, err = pcall(function()
subscriptionHistory = MarketplaceService:GetUserSubscriptionPaymentHistoryAsync(player, SUBSCRIPTION_ID)
end)
if not success then
warn(`Error while checking subscription history: {err}`)
return
end
if next(subscriptionHistory) then
print(`Player {player.Name} has subscribed to {SUBSCRIPTION_ID} before:`)
for entryNum, paymentEntry in subscriptionHistory do
local paymentStatus = tostring(paymentEntry.PaymentStatus)
local cycleStartTime = paymentEntry.CycleStartTime:FormatLocalTime("LLL", "en-us")
local cycleEndTime = paymentEntry.CycleEndTime:FormatLocalTime("LLL", "en-us")
print(`{entryNum}: {paymentStatus} ({cycleStartTime} - {cycleEndTime})`)
end
else
print(`Player {player.Name} has never subscribed to {SUBSCRIPTION_ID} before.`)
end
end
for _, player in ipairs(Players:GetPlayers()) do
checkSubscriptionHistory(player)
end
Players.PlayerAdded:Connect(checkSubscriptionHistory)
}}} - [ 네이밍 스타일 예제 2 ]
#!syntax lua local MarketplaceService = game:GetService("MarketplaceService") local Players = game:GetService("Players") local teleporter = script.Parent local showModal = true local TELEPORT_POSITION = Vector3.new(1200, 200, 60) local function teleportPlayer(player) player:RequestStreamAroundAsync(TELEPORT_POSITION) local character = player.Character if character and character.Parent then local currentPivot = character:GetPivot() character:PivotTo(currentPivot * CFrame.new(TELEPORT_POSITION)) end end teleporter.Touched:Connect(function(otherPart) local player = Players:GetPlayerFromCharacter(otherPart.Parent) if not player then return end if not player:GetAttribute("CharacterPartsTouching") then player:SetAttribute("CharacterPartsTouching", 0) end player:SetAttribute("CharacterPartsTouching", player:GetAttribute("CharacterPartsTouching") + 1) if player.MembershipType == Enum.MembershipType.Premium then teleportPlayer(player) else if not showModal then return end showModal = false task.delay(5, function() showModal = true end) MarketplaceService:PromptPremiumPurchase(player) end end) teleporter.TouchEnded:Connect(function(otherPart) local player = Players:GetPlayerFromCharacter(otherPart.Parent) if player and player:GetAttribute("CharacterPartsTouching") then player:SetAttribute("CharacterPartsTouching", player:GetAttribute("CharacterPartsTouching") - 1) end end) Players.PlayerMembershipChanged:Connect(function(player) warn("User membership changed; new membership is " .. tostring(player.MembershipType)) if player.MembershipType == Enum.MembershipType.Premium and player:GetAttribute("CharacterPartsTouching") > 0 then teleportPlayer(player) end end)
3.3. 코드
3.4. 라이브러리
스크립팅을 할 때 대다수의 사람이 겪는 불편한 문제는 바로 비슷한 내용의 코드를 반복해서 짜야 한다는 것이다. 똑같은 내용이 아니라 비슷한 내용의 코드여서 복사 붙여넣기도 할 수 없고, 그렇다고 비슷한 내용의 코드를 또 짜기에는 너무 비효율적이라 난감한 상황에 놓인다. 이럴때 유용하게 쓸 수 있는 것이 라이브러리인데, 라이브러리는 반복되는 작업을 줄여줄뿐만 아니라, 우리가 설계하지 못하는 유용하고 고급적인 코드를 가져와 게임에 마치 아이템처럼 장착할 수 있다. 이러한 라이브러리는 스크립팅에서 매우 필수적인 요소인데, 많은 사람들이 잘못된 라이브러리를 선택하고 있어 이 문단을 만든다. 다음은 분야별 올바른 라이브러리의 목록이다.- 데이터스토어 : 대다수의 초심자들이 스크립팅을 시작할 때 로블록스 측에서 제공하는 기본 데이터스토어 서비스를 선택하는데, 이것은 잘못된 행위이다. 초심자들이 기본 데이터스토어 서비스를 제대로 활용하지 못할뿐더러, 안전한 세이브를 하려면 초심자들이 다루기 어려운 개념들을 사용해야 하기 때문에 꼭 라이브러리를 활용해야 한다. 그렇다고 해서 숙련자들이 데이터스토어 라이브러리를 사용하지 않아도 된다는 것은 아니다. 오히려 더욱 활용할 수 있어야 한다. 아무리 그들이 숙련자라고 할지어도, 데이터스토어 라이브러리를 만든 사람들은 오랫동안 그 분야를 연구해오며 최상급의 코드를 짠건데 일반적인 숙련자가 하루아침에 뚝딱 만들어서 그들의 라이브러리를 뛰어넘는 코드를 절대 짜지 못한다. 우리는 당연하게도 그런 최상급의 코드를 아무런 대가도 없이 게임에 막 집어넣을 수 있다는 것이다. 데이터스토어 라이브러리는 일반적으로 로블록스의 데이터스토어 서비스를 극도로 활용해 우리에게 쉽게 사용할 수 있도록 만들어준 하나의 모듈 스크립트이다. 그러므로 아무리 자신이 잘하는 숙련자라고 해도 그러한 혜택을 거부하는 건 어리석은 행위라는 것이다.[24]
초심자 : 데이터스토어 2 - 사용하기 매우 간편하다. 튜토리얼도 보지 않고 사용할 수 있을정도. 그러나 단점은 타 라이브러리에 비해 성능이 떨어진다는 점.[25]
중급자 : 프로필서비스 - 최상급 성능을 보유한다. 가장 잘 알려진 데이터스토어 라이브러리이기도 하다. 그러나 큰 단점은 사용하는 난이도에 있다. 사용할 수 있는 기능은 수도없이 많지만 기능 하나하나가 사용 난이도가 매우 어렵다. 사용 난이도가 어렵기 때문에 튜토리얼[26]을 정독하길 추천한다.
숙련자 : 데이터킵 - 사용하기 매우 편리하고[27] 성능도 프로필서비스에 미칠 정도로 좋다. 특징은 프로미스 라이브러리를 이용해 효율성을 극대화한다는 점이다. 프로필서비스의 프로미스 버전이라고 불리기도 한다.
라피스 - 이 라이브러리는 데이터킵과 비슷하게 사용하기 매우 편리하고 매우 효율적이다.[28] 그러나 라피스는 꽤나 특이하고 호불호가 갈리는 라이브러리인데, 장점으로는 다른 라이브러리들과는 다른 migration, validation 이라는 독특한 기능이 있고 가독성이 그 어떤 라이브러리 보다도 깔끔하고 좋은 점이 있다. 단점은 숙련자들에게도 어려운 사용 난이도[29]와 부족한 기능이 있다.
- 비동기 - 프로미스 : 현재 스크립트에서 비동기를 사용할 수 있는 방법은 task 라이브러리의 비동기 함수들을 사용하는 것과, coroutine 라이브러리를 사용할 수 있다. 그러나 이 라이브러리들은 복잡한 비동기를 구현하려면 코드를 불필요하게 길게 짜야 한다. 그러나 evaera 가 제작한 비동기 라이브러리 프로미스 를 사용한다면 복잡한 비동기 코드를 단순하게 짤 수 있을뿐만 아니라 다양한 기능들을 사용해 매우 편리하고 효율적으로 코드를 짤 수 있다. 사용법만 잘 터득한다면 아예 모든 비동기를 프로미스로 대체도 가능하다. 사실상 프로미스 라이브러리는 프로젝트를 시작하면 가장 우선으로 설치해야 할 라이브러리이다. 사실상 현존하는 라이브러리의 1/3이 프로미스를 기반으로 만들어졌다 해도 과언이 아닐 정도로 수많은 스크립터들에게 각광받는 현재 최고의 라이브러리이다.
- 네트워킹 : 네트워킹이란 서버와 클라이언트간의 상호작용을 뜻하며 이는 리모트 이벤트나 리모트 펑션을 통해 가능하다. 이때 네트워킹 라이브러리는 리모트 개체를 극도로 최적화해 활용하여 사용자에게 더 빠르고 가벼운 네트워킹을 할 수 있게한다. 그러나 네트워킹 라이브러리의 사용은 찬반이 꽤나 갈리는 주제이다. 네트워킹 라이브러리 사용에 반대하는 편의 주장은 네트워킹 라이브러리는 과시용 라이브러리라는 것이다. 그 이유는 네트워킹을 가볍고 빠르게 하길 원한다면 라이브러리를 사용해 불필요한 코드를 추가하지 않아도 리모트를 통해 전송하는 데이터의 크기를 줄일 수 있기 때문이다. 찬성하는 편의 주장은 실제로 네트워킹 라이브러리는 리모트보다 더 간편한 사용방식을 가지고 있으며 그냥 리모트 개체를 사용하는 것 보다는 라이브러리를 사용해 성능을 끌어올리는 게 더 낫다는 것이다. 그러나 사실상 네트워킹 라이브러리의 사용 여부는 개인의 취향이며 무엇이 맞고 틀리다는 타인이 판단해서는 안 된다. 또한 네트워킹 라이브러리를 사용하지 않아도 애용하는 프레임워크가 있다면 그 프레임워크의 네트워킹 시스템을 사용할 수도 있다.
초심자, 중급자 : 이때는 라이브러리를 사용하지 않고 리모트 개체들을 통해서 네트워킹을 배우고 익히는 것이 우선이므로 네트워킹 라이브러리의 사용은 웬만해선 피하도록 하자.
숙련자 : 먼저 간단하고 쉽게 사용할 수 있는 라이브러리는 와프가 있다.[30] 그에 대비되는 바이트넷은 복잡하고 사용하는데 어려움이 있지만 성능이 최우수하다.[31] 이외에는 잽과 Remo가 있다. 잽은 잽만의 고유한 문법으로 사용자가 지정하는 데이터 타입과 데이터 크기로 자신만의 최적의 네트워킹 모듈 스크립트를 만들어낼수 있고 Remo 는 극도의 효율성과 타입 체킹 호환성을 보여주는 라이브러리이다.
- 클린업 : 로블록스 스크립팅에서 클린업은 꽤나 중요한 요소이다. 클린업이 되지않은 데이터가 메모리 누락을 일으키고 예상치 못한 에러를 가져올수 있기 때문이다. 이러한 클린업을 쉽고 간편하게 만들어주는 클린업 라이브러리들은 대개 OOP(Object Oriented Programming) 와 함께 쓰인다. 다음은 대표적인 클린업 라이브러리와 그 설명을 나열한 것이다.
메이드 : NevermoreEngine 측에서 개발한 클린업 라이브러리. 현재까지 가장 많이 쓰이며 대표적이다. NevermoreEngine 프레임워크를 사용한다면 이 클린업 라이브러리를 사용하는 것을 추천한다.
트루브 : 유명 개발자 Sleitnick 이 만든 클린업 라이브러리. 이 또한 Knit 프레임워크를 사용한다면 패키지안에 딸려오니 같이 사용하는 것을 추천한다.
제니터 : howmanysmall 이 만든 클린업 라이브러리.메이드를 이기려고 온갖 기능을 넣어놨다.
- UI : gui 스크립팅과 애니메이팅, 심지어 gui 제작까지도 다룰수 있는 ui 라이브러리는 최근 들어 더욱 애용되고 있는 추세이다. 사실상 ui 라이브러리는 단점이 없기 때문인데, gui 제작도 더욱 효율적이게 할 수 있고 그에 따라 스크립팅, 애니메이팅도 실력만 있다면 더욱 고급지고 편리하게 할 수 있다. 그러나 대부분의 ui 라이브러리는 웹 프로그래밍 형식인데, 이 때문에 웹 프로그래밍을 접해보지 못한 개발자들에게는 매우 어려운 접근 난이도를 가지고 있어 대체로 웹 프로그래밍을 접해본 스크립터가 사용한다.
로액트 : 로블록스 + 리액트. ui 라이브러리 하면 대다수의 사람들이 대표적으로 로액트를 떠올릴것이다. 그러나 현재의 로액트는 deprecated 된 상태로 웬만하면 사용하는 것을 피해야 한다. 후술할 리액트 루아가 훨씬 더 가독성 있고 더 능률적으로 코드를 짤 수 있기 때문이다.
리액트 루아 : 리액트 루아는 로액트가 deprecated 된 후 출시된 라이브러리인데 이는 ReactJs의 17.x 버전 업데이트를 따라가기 위한 것이다. 그래서 로액트의 클래스 형식 ui 구축 방식이 함수 형식 방식으로 바뀌기도 했다. 리액트 루아는 장점도 설명할 수 없을만큼 많지만 단점도 매우 크다. 그 이유는 입문 난이도와 사용 난이도 때문인데 사실상 리액트에 접해보지 않은 사람은 손 대기도 어려울 정도로 심각한 입문 난이도를 가지고 있고 리액트와는 다른 리액트 루아만의 고유한 문법[32]으로 리액트를 이미 접해본 사람에게도 선사하는 최악의 사용 난이도를 가지고 있기 때문이다.[33] 그러나 제대로된 사용 방법과 올바른 코드를 짤 수만 있다면 ui 개발의 경지에 다가갈 수 있다.[34] 추가로 리액트 루아를 사용해 ui 애니메이팅을 하고 싶다면 리액트(로액트) 스프링을 추천한다.
퓨전 : 퓨전은 ui 제작 및 스크립팅, ui 상태 관리, ui 애니메이팅을 가능하게 해주는 복합형 ui 라이브러리이다. 퓨전의 리액트 루아와 대비되는 가장 큰 장점은 배우기 쉽고 사용하기 쉽다는 것이다. 리액트 루아가 악몽같은 입문 난이도를 자랑한다면 퓨전은 스크립터가 아닌 개발자도 충분히 배우고 사용할 수 있을만큼 입문 난이도가 간단하고 쉽다는 것이다. 심지어 공식 문서도 충분한 예제 코드와 설명을 가지고 있어 입문에 매우 도움된다. 하지만 좋은 약이 입에 쓴법, 퓨전의 단점은 웹 프로그래밍과는 거리가 멀다는 것이다. 그래서 타 프레임워크나 라이브러리들과 잘 결합하여 쓸 수 있는 리액트 루아와 달리 타 라이브러리와 함께 쓰기가 쉽지 않다. 즉, 퓨전에 ui 관리의 모든것을 맡기지 않는 사람에겐 잘 맞지 않는다는 것이다.
- 상태 관리 : 상태 관리 (State Management) 는 데이터를 관리하는 것을 의미한다. 상태 관리는 스크립팅에 있어 매우 중요한 요소 중 하나이다. 상태 관리 라이브러리로 게임의 데이터들을 제어하는 것이 필수적이지는 않지만 몇몇 에러가 시스템에 치명적 손상을 끼칠수 있는 게임에서는 상태 관리 라이브러리를 사용하는 것이 좋다. 다음은 대표적이며 현재로서 가장 좋은 성능을 발휘하는 상태 관리 라이브러리들이다.
로덕스 : 로블록스 + 리덕스. 대부분의 사람들이 로덕스 하면 ui 전용 상태 관리 라이브러리로 인지하고 있지만, 주로 로덕스는 게임의 전반적인 상태를 관리하는 용도로 쓰인다.[35] 현재는 후술할 리플렉스에 밀리는 추세이다.[36] 그래도 로덕스를 사용하고 싶다면 로덕스 보완용으로 rodux-utils 을, 리액트 루아 지원용으로는 react-redux 를 사용하는 것을 추천한다.
리플렉스 : 로덕스와 같은 역할을 하지만 리액트 루아의 상태 관리도 지원한다. 로덕스보다 대부분의 면에서 더 우세해 상태 관리 라이브러리를 찾는다면 이 라이브러리를 선택하는 것을 추천한다. 로덕스와 달리 타입스크립트 버전과 서버-클라이언트 동기화를 지원하는 등 최신 트렌드를 적극적으로 따라가고 있다.
3.5. 프레임워크
프레임워크란 스크립트의 전체적인 틀을 정해진 형식으로 짤 수 있게 하고 부가적인 기능들을 제공하는 하나의 뼈대이다. 게임에 프레임워크를 쓰는 것은 필수적이지는 않지만 큰 규모의 게임일수록 프레임워크의 필요성이 높아지는 것은 사실이다. 프레임워크는 타인이 만든것을 가져올수도 있고 자신이 만들수도 있다. 이는 상황에 따라서 갈리는데, 특정한 프레임워크[37]를 만들 때는 타인이 만든것을 사용하는 것보다 자신이 직접 만들어 쓰는 게 더 독특성 있고 효율적일 수도 있다. 하지만 스크립트의 틀을 잡기 위해 사용할 프레임워크는 실력있는 스크립터가 오랫동안 심혈을 기울여 만든 좋은 프레임워크를 쓰는 게 옳다. 다음은 대표적이고 현재로서 가장 뛰어난 프레임워크들을 나열한 것이다.- Knit : 가장 대표적인 프레임워크이자 AeroGameFramework 의 업그레이드 버전. SSA(Single Script Architecture/단일 스크립트 구조) 를 기반으로 한다. 서비스(서버) & 컨트롤러(클라이언트) 구조로 되어 있다. 서비스와 컨트롤러간의 네트워킹과 미들웨어도 지원한다. 또한 패키지 안에 딸려오는 부가적인 라이브러리[38]들을 제공한다.
- Nevermore : Knit 과 비슷한 형식으로 되어 있는 프레임워크. Knit 과 대부분의 구조가 비슷하지만 이 프레임워크의 포인트는 어마어마한 양의 라이브러리들을 제공한다는 것이다.
- Matter : ECS 를 기반으로 하는 프레임워크. ECS 를 접해보지 않은 사람에게는 초반에 제대로 사용하기가 불가능할 정도로 입문/사용 난이도가 매우 높다. 또한 시스템을 통해 데이터를 제어하는 DOD 디자인이 사용되었다.
4. 스크립트 종류
4.1. 로컬 스크립트
로컬 스크립트 [LocalScript]
[로컬]
서버측이 아닌 클라이언트, 개인 컴퓨터에서 구동 되는 스크립트다. 로컬 스크립트에서 변동된 값은 대부분 해당 클라이언트에게만 적용 된다. 그러나 예외가 있다. 서버에 복제되는 항목인 재생중인 애니메이션 트랙이나 자신이 소유중인 고정되지 않은 파트[40]의 값 변경 등에 대해선 그 변경이 다른 클라이언트에도 그대로 복제된다.
이러한 특성 때문에 서버의 연산이 불필요한 항목[41]은 로컬 스크립트에서 연산하게 하는 것이 현명하다.
로컬 스크립트는 개인 컴퓨터의 자원을 소모하기 때문에 복잡한 연산을 하면 컴퓨터에 렉이 걸려 FPS가 떨어진다. 그러므로 불필요한 연산은 로컬 스크립트에서 구동하게 하되 최대한 최적화를 해서 FPS가 떨어지지 않도록 하는 것도 중요하다.
로컬 스크립트는 당연히도 클라이언트 측에서 작동되는 스크립트 이므로 클라이언트와 관련된 개체 밑에서만 작동을 한다.[42]
또한 로컬스크립트에서만 작동되는 서비스나 이벤트들이 있는데, 플레이어의 입력을 감지하는 UserInputService와 함수가 프레임 마다 실행될 수 있게 하는 RenderStepped 이벤트 등이 있다.
4.2. 서버 스크립트
서버 스크립트 [Server Script]
[서버]
서버측에서 구동되는 스크립트이다. 서버측 스크립트는 클라이언트측 스크립트와 다르게 복제되는 항목[44]의 값 변경시 그 값이 모든 클라이언트에게도 복제되어 변경된다. 그러나 이러한 변경 및 연산은 서버의 자원을 소모하고, 너무 많은 자원을 소모하게 되면 궁극적으로 핑이 높아지고 렉이 걸리기 때문에 중요하지 않은 연산이나 값 변경 등은 클라이언트에게 맡겨야 한다.
4.3. 모듈 스크립트
모듈 스크립트 [Module Script]
반복적인 코드들을 정리하고 재사용할 수 있게 하는 특별한 류의 스크립트이다.
모듈 스크립트는 기본적으로 테이블 형태로 되어 있어 자동적으로 실행되지 않는다.
또한 서버와 로컬 공용으로 쓰이며 호환되지는 않는다.
다음은 모듈 스크립트의 기본 사용예제이다.
- [ 모듈 스크립트 사용 예시 ]
#!syntax lua --모듈 스크립트 local module = {} module.a = 1 function module.b() print("b") end function module:c() print("c") end return module -- 모듈 스크립트는 값을 반환해야 함. --서버 or 로컬 스크립트 local module = require(모듈_경로) -- 모듈 볼러오기 print(module.a) -- 1 출력 module.b() -- b 출력 module:c() -- c 출력
5. 자료형
로블록스에는 수많은 자료형들이 있지만 그 많은 자료형 중 몇몇만 자주 사용된다.- Axes : 개체의 아크핸들(스튜디오에서 BasePart를 클릭했을 때 나오는 회전축)의 회전축을 제어한다.
- BrickColor : 로블록스에서 기본으로주는 색깔들
- boolean : 참/거짓
- CatalogSearchParams
- CFrame : Vector3 처럼 위치를 나타내는 스크립트지만 CFrame은 개체의 피벗을 기준으로 한다.
- Color3 : RGB색
- ColorSequence
- ColorSequenceKeypoint
- Content
- DateTime
- DockWidgetPluginGuiInfo
- Enum : 거의 모든 자료형의 담고 있는 자료
- EnumItem
- Enums : Enum과 같음
- Faces
- FloatCurveKey
- Font : 글꼴
- Instance : 개체 [45]
- nil : 아무것도 없는 값 [46]
- number : 숫자
- NumberRange
- NumberSequence
- NumberSequenceKeypoint
- OverlapParams
- PathWaypoint
- PhysicalProperties
- Random : 무작위 난수나 방향
- Ray : 한 방향으로는 유한하지만 다른 방향으로는 무한한 반직선 백터
- RaycastParams
- RaycastResult
- RBXScriptConnection
- RBXScriptSignal : 주로 Event로 알려져있고, 리스너에 콜백 함수를 달수 있다.
- Rect
- Region3 : 직육면체 지역 공간의 볼륨
- Region3int16
- SharedTable
- TweenInfo : TweenService를 이용한 동작을 지정한 개체
- UDim
- UDim2 : 2차원 위치 단위 [47]
- Vector2
- Vector2int16
- Vector3 : 3차원 위치 단위 [48]
- Vector3int16 : Vector3와 유사하나 이건 각각의 좌표들을 16진법으로 나타낸다.
5.1. BrickColor
로블록스에서 기본으로 제공하는 색깔은 다음과 같다.- [ 펼치기 · 접기 ]
- * White 흰색
- Grey 회색
- Light yellow : 연노랑
- Brick yellow : 브릭 옐로우
- Light green(Mint) : 연초록(민트)
- Light reddish violet : 연붉은 보라색
- Pastel bule : 파스텔 블루
- Light orange brown : 밝은 브라운 오랜지
- Nougat : 누가(견과류)
- Bright red : 밝은 빨강
- Med. reddish violet : 중간정도로 붉은 보라색
- Bright bule : 밝은 파랑
- Bright yellow : 밝은 노랑
- Earth orange : 지구 오랜지
- Black : 검정
- Dark grey : 짙은 회색
- Dark green : 짙은 초록
- Medium green : 중간 초록
- Lig. Yellowich orange : 밝은 노랏빛 오랜지
- Brignt green : 밝은 초록
- Dark orange : 짙은 주황
- Light bluish violet : 밝은 청자색
5.2. CFrame
5.2.1. 속성
- Position : CFrame의 좌표
- Rotation : CFrame 복사본을 정리 없이 복사한 것
- X : CFrame의 x좌표
- Y : CFrame의 y좌표
- Z : CFrame의 z좌표
- RightVector : CFrame의 오른쪽 방향 구성
- UpVector : CFrame의 위쪽 방향 구성
- LookVector : CFrame의 정면 방향 구성
5.2.2. 메서드
- Lerp(CFrame, 비율) : CFrame의 Position과 Angle을 두 CFrame의 사이로 간다. 비율에 클수록 괄호안에 넣은 CFrame에 가까워진다.[49]
5.3. Vector3
- 속성들
- X : X축의 값
- Y : Y축의 값
- Z : Z축의 값
- Magnitude : 원점과 주어진 Vector3 사이의 직선 거리
- Unit : 크기는 1인 닮음 크기의 Vector3
5.4. UDim2
5.4.1. 메서드
- Lerp(UDim2, 비율) : 위에 서술한 CFrame:Lerp와 같은 메서드
5.4.2. 속성
- X : UDim2의 x축 속성
- Y : UDim2의 y축 속성
5.5. TweenInfo
5.5.1. 속성
- EasingDirection : EasingStyle이 실행되는 방향
- Time : 에니메이션의 지속시간
- DelayTime : 에니메이션이 시작하기 까지 걸리는 딜레이
- RepeatCount : 반복 횟수(정수)[50]
- EasingStyle : 에니메이션의 스타일
- Reverses : 돌아가는 에니메이션 사용 여부
6. 라이브러리
- bit32
- buffer
- coroutine
- debug
- math (여러가지 연산이나 숫자)
- os (시간 관련 라이브러리)
- string (문자열)
- table (테이블)[51][주의할점]
- task
- utf8
6.1. bit32
6.2. buffer
6.3. coroutine
- coroutine.create(함수) : 괄호 안에 넣은 함수를 코루틴으로 만들어 준다.
- coroutine.resume(코루틴, 매개변수...) : 괄호 안에 넣은 코루틴을 실행한다.
- coroutine.yield() : coroutine 개체:resume()으로 만든 코루틴을 일시정지 시켜준다.
- 코루틴을 쓴 것
{{{#!syntax lua
{{{#!folding [ 코루틴을 쓴 스크립트와 안 쓴 스크립트 비교 펼치기 · 접기 ]
local b =1
local PlusA = coroutine.creat(function()
while true do
end)a += 1
endlocal PlusB = coroutine.creat(function()
while true do
end)b += 1
endcoroutine.resume(PlusA)
coroutine.resume(PlusB)
}}} -> a와 b 둘다 1식 증가한다.
- 코루틴을 안 쓴 것
{{{#!syntax lua
local b =1
local function PlusA()
while true do
enda += 1
endlocal function PlusB()
while true do
endb += 1
endPlusA()
PlusB()
}}} -> a만 1식 증가한다 -> PlusA()를 무한히 실행하기 때문에 이 다음 명령어인 PlusB()를 실행 못한다.
}}}
- coroutine 개체:wrap(함수) : 괄호 안에 넣은 함수를 함수처럼 쓸 수 있는 코루틴으로 만들어 실행시켜준다.
6.4. debug
6.5. math
6.5.1. 함수
- math.abs(값) : 값의 절댓값을 반환한다.
- math.ceil(값) : 천장함수. 값을 올림한 값을 반환한다.
- math.clamp(값, 최솟값, 최댓값) : 값이 최솟값과 최댓값 사이에 있다면 값 반환, 아니라면 최솟값과 최댓값 중 값과 가까운 것을 반환한다.
- math.floor(값) : 바닥함수. 값을 내림한 값을 반환한다.
- math.max(값1, 값2... , 값n) : 여러개의 값중 가장 큰 값을 반환한다.
- math.min(값, 값2... , 값n) : 여러개의 값중 가장 작은 값을 반환한다.
- math.random(최솟값, 최댓값) : 최솟값과 최댓값사이의 정수값중 무작위로 하나를 반환한다.
- math.round(값) : 반올림. 값을 반올림한 값을 반환한다.
6.5.2. 속성
- math.huge : 로블록스에서 쓸 수 있는 가장 큰 수를 반환해준다. (약 2¹⁰²⁴) 그냥 무한이라 생각해도 된다.
- math.pi : 원주율
6.6. os
os는 UTC에 맞춰 흘러가는 시간 시스템이다.6.6.1. 함수
- os.time(테이블[53]) : 1970년 1월 1일 자정 0시로부터 현제 몇 초가 지났는지 숫자로 반환한다.
- os.difftime(숫자₁, 숫자₂) : 두개의 숫자가 모두 옳게 time_t 형식을 갖추었다면 두개의 숫자의 차이만큼의 초를 반환한다.
- os.data(포멧스트링, 숫자) : 문자열을 os.time의 테이블 형식에 맞춰 반환한다. 문자열의 속성들은 다음과 같다.
- %a : 요일 이름의 약자 (Mon 반환)
- %A : 요일 이름의 풀네임 (Monday 반환)
- %b : 월 이름의 약자 (Jan 반환)
- &B : 월 이름의 풀네임 (January 반환)
- &c : 날짜와 시간
- %d : 해당 월의 일(day)
- %H : 24시간을 기준으로 하는 시간 값
- %I : 12시간을 기준으로 하는 시간 값(13시 → 1시)
- %j : 올해의 일
- %m : 달(month)
- %M : 분
- %p : 오전, 오후 구분
- %S : 초
- %U : 몇번째 주(week)(일요일을 기준으로 삼음)
- %w : 주말
- %W : 몇번째 주(월요일을 기준으로 삼음)
- %x : 날짜
- &X : 시간(2024→24)
- %y : 연도 앞 두자리
- %Y : 연도
- &z : 현위치에서의 시간과 UTC+0과의 시간 차이(+1분=1, +1시간=100)
{{{#!folding [ 문자열 형식 ]
- os.clock : CPU가 실행 이후 현재 몇초가 지났는지 반환한다. 수밀리초 까지 알아챌 정도로 정교하다.
6.7. string
6.7.1. 함수[54]
- string.len(문자열) : 문자열의 길이를 반환해준다. 문자열 앞에 #을 붙히는 것으로도 대체 가능하다.
- string.upper(문자열) : 문자열의 모든 글자를 대문자로 만들어준다.
- string.lower(문자열) : 문자열의 모든 글자를 소문자로 만들어준다.
- string.reverse(문자열) : 문자열의 배치를 뒤집어준다.
- string.split(문자열, 기준글자) : 문자열을 기준글자를 기점으로 나누어 테이블에 담아 반환해준다. 이걸 이용해서 string.split(문자열, "")으로 글자 하나하나를 쪼개 테이블에 담을 수 있다.
- string.find(문자열, 찾는글자) : 문자열에서 찾는 글자를 찾은다음 몇번째 글자인지 반환해준다. 만약 찾는글자가 문자열에 아예 없다면 nil을 출력해준다.[55]
- string.match(문자열, 찾는글자) : 문자열에서 찾는글자를 찾아 string으로 반환해준다.
- string.sub(문자열, 시작번호, 끝번호) : 문자열에서 시작번호 부터 끝번호 까지를 string으로 반환해준다.[56][57]
- string.gsub(문자열,찾는글자,바꿀글자) : 문자열에서 찾는글자를 모두 바꿀글자로 바꿔서 string 으로 반환해준다.
- string.format(적용할 문자열, 삽입할 문자열₁, 삽입할 문자열₂,...) : 적용할 문자열에다가 삽입할 문자열들을 삽입한다. 포매팅 기호는 %s로 쓰고, n번째 삽입할 문자열은 n번째 포매팅 자리에 삽입된다.
{{{#!folding [ 예시 ]
#!syntax lua
local Message = "%s %s개를 %s원 주고 구매했다."
local Output = string.format(Message, "과자", 6, 80000)
print(Output) → 과자 6개를 80000원 주고 구매했다.
}}}
6.8. table
6.8.1. 함수
- table.insert(테이블, 자료, 순서) : 지정한 테이블에다가 자료의 순서를 정해서 넣는다. 순서를 안 써놓으면 자동으로 마지막 순서로 넣어진다.
- table.remove(테이블, n번째) : 지정한 테이블에서 n전째 항목을 없엔다.
- table.maxn(테이블) : 테이블중에서 가장 큰 숫자를 반환한다. 테이블 안에 숫자가 아예 없을땐 nil을 반환한다.
6.8.2. 속성
- #테이블 : 테이블 안에 들어 있는 자료 개수
- 테이블[n] : 테이블의 n번째 자료
6.9. task
6.9.1. 함수
- task.cancel(thread : coroutine) : void
스레드를 중지시킨다.
- task.delay(duration : number, functionOrThread : function | coroutine) : coroutine
스레드를 생성하고 해당 스레드를 반환한뒤 괄호에 넣은 시간이 다 지난 직후의 프레임에서 스레드를 실행한다.
- task.spawn(함수) : coroutine
spawn() 함수와 같이 스레드를 생성한뒤 즉시 실행하고 해당 스레드를 반환한다.
- task.wait(duration : number) : number
괄호에 넣은 시간동안 현재 스레드를 양보하고 실제로 양보한 시간을 반환한다. wait와 달리 보다 정밀하고 쓰로틀링이 없다.
6.10. utf8
7. 글로벌
로블록스 내 어느 코드에서나 쓸 수 있는 함수 또는 속성들로, 로블록스 스튜디오의 코드는 Lua 기반이기에 Lua 내 대부분의 글로벌 또한 들어간다.7.1. 함수
- error(튜플) = print와 비슷하지만 이것은 문자열을 error 형태로 내보낸다.
- elapsedTime()
- gcinfo()
- pcall(함수) : 괄호안에 넣은 함수가 에러가 나도 아랫줄이 실행될 수 있도록 하는 함수다.
- PluginManager()
- print(문자열) = 문자열을 Console에 출력한다.[58]
- require(모듈스크립트)
- settings()
- spawn(함수)[최적화별로] : 괄호 안에 넣은 함수를 스레드로 만들어 실행한다.
- tick()
- time()
- typeof(개체)
- UserSettings()
- version()
- warn(튜플)
- wait(숫자) = (숫자)초만큼 기다린다. 수십 밀리초 정도 딜레이가 있다.[60] 현재는 거의 사용되지 않는 항목으로, 더 정밀한 task.wait()으로 대체되었다.
7.2. 속성
- Enum : true, false, nil을 재외한 모든 속성
- game
- plugin
- shared
- script
- workspace
- _G : 기본 모듈 스크립트다. 이 뒤에 다른 속성을 붙여 모듈스크립트처럼 쓸 수 있다.
- _VERSION : 로블록스 스튜디오 루아의 버젼
8. 서비스
8.1. Debris
스크립트를 직접적으로 정지하지 않고 스케줄된 개체 삭제를 수행할 수 있게 하는 서비스.- Debris:AddItem(Instance, number) : Instance 개체를 number 초 뒤에 삭제한다. 개체 삭제 방식은 Instance:Destroy() 와 똑같다. 사실상 메커니즘은 아래 코드와 같다. {{{#!syntax lua
Instance:Destroy()
}}} 그러나 이 코드에선 task.wait() 를 사용함으로서 스레드를 정지하게 한다. 즉 정지한 동안은 아무런 동작도 할 수 없다는 것이다. 추가적으로 코드를 실행해야 하는 경우에는 불가피하게 이 메커니즘은 사용할 수 없게 된다. 따라서 아래 코드처럼 새로운 스레드를 만들어 코드를 실행하게 하면 본 스레드에서는 정지를 피할 수 있다.
#!syntax lua
task.delay(number, function()
Instance:Destroy()
end)
하지만 이 방법에도 치명적인 단점이 있다. 실행되고 있는 스레드의 부모 스크립트가 삭제될시 그 스레드도 삭제된다는 것이다. 간단한 예시를 들자면, 닿으면 3초뒤에 닿은 플레이어가 죽는 검 코드를 짜고 있다고 가정해보자, 만약 위 코드를 사용할시 모종의 이유로 스크립트가 삭제당하면[61] 위 스레드는 전부 무효화된다. 따라서 이때 Debris 서비스가 진가를 발휘할 수 있다. #!syntax lua
Debris:AddItem(Instance, number)
위 코드 한줄이면 새 스레드를 생성하지도 않고, 개체 삭제를 스케줄할 수 있다. 추가로 이 메소드는 최대 수용 한도가 1000개 라는 것을 명심해두자.8.2. MarketplaceService
- 주로 게임패스, VIP 등의 로벅스 사용 관련 컨텐츠를 만들 때 쓰인다.
8.3. RunService
- IsServer() : boolean
실행한 환경이 서버인지 반환한다. - IsClient() : boolean
실행한 환경이 클라이언트인지 반환한다. - IsStudio() : boolean
실행한 환경이 스튜디오인지 반환한다.
8.4. TweenService
- TweenService 서비스:Create(개체, TweenInfo, 테이블) : Tween
Tween을 반환한다. 개체엔 Tween의 대상을, TweenInfo엔 Tween의 방식을 정하는 TweenInfo를, 테이블엔 Tween의 목표인 개체의 속성과 속성의 값이 들어간다.
{{{#!folding [ 예제 펼치기 · 접기 ]
{{{#!syntax lua
local TweenService = game:GetService("TweenService")
local part = workspace.Part
local goal = { Position = Vector3.new(0, 50, 0) }
local tween = TweenService:Create(part, tweenInfo, goal)
tween:Play()
}}}}}}* Tween:Play() : Tween을 재생한다.* Tween:Cancel() : Tween을 취소한다.* Tween:Pause() : Tween을 일시중지한다.* Tween.Completed :
8.5. BadgeService
- BadgeService 서비스:AwardBadge(유저 ID, 배지 ID) : 유저에게 배지를 수령하는 메서드다.
9. 클래스 목록
- [모든 클래스 펼치기 · 접기 ]
- * Accessory : 플레이어의 장신구
- AccessoryDescription
- Accoutrement
- Actor
- AdGui
- AdPortal
- AdService
- AdvancedDragger
- AirController
- AlignOrientation
- AlignPosition
- AnalyticsService
- AngularVelocity
- Animation : 에니메이션
- AnimationClip
- AnimationClipProvider
- AnimationController
- AnimationFromVideoCreatorService
- AnimationRigData
- AnimationTrack
- Animator : 에니메이션을 재생시키는 개체
- ArcHandles
- AssetDeliveryProxy
- AssetPatchSettings
- AssetService
- Atmosphere
- Attachment
- AudioAnalyzer
- AudioChorus
- AudioCompressor
- AudioDeviceInput
- AudioDeviceOutput
- AudioDistortion
- AudioEcho
- AudioEmitter
- AudioEqualizer
- AudioFader
- AudioFlanger
- AudioListener
- AudioPages
- AudioPitchShifter
- AudioPlayer
- AudioReverb
- AudioSearchParams
- AvatarCreationService
- AvatarEditorService
- Backpack : 플레이어 인밴토리
- BackpackItem
- BadgeService
- BallSocketConstraint
- BasePart
- BasePlayerGui
- BaseRemoteEvent
- BaseScript
- BaseWrap
- Beam
- BevelMesh
- BillboardGui
- BinaryStringValue
- BindableEvent
- BindableFunction[최적화별로]
- BlockMesh
- BloomEffect
- BlurEffect
- BodyAngularVelocity
- BodyColors : 플레이어의 몸 색깔
- BodyForce : 개체를 workspace를 기준으로 움직이는 힘[구버전]
- BodyGyro : 개체를 돌리는 돌림힘[구버전]
- BodyMover : BodyForce, BodyGyro 등의 상위 개체[구버전]
- BodyPartDescription
- BodyPosition : 개체를 어디를 기준으로 힘을 가할지 정할 수 있는 힘
- BodyThrust
- BodyVelocity : 개체에 일정한 속도로 가하는 힘
- Bone
- BoolValue
- BoxHandleAdornment
- BrickColorValue
- BrowserService
- BubbleChatConfiguration
- BubbleChatMessageProperties
- BuoyancySensor
- CacheableContentProvider
- Camera
- CanvasGroup
- CaptureService
- CatalogPages
- CFrameValue
- ChangeHistoryService
- CharacterAppearance
- CharacterMesh
- Chat
- ChatInputBarConfiguration
- ChatWindowConfiguration
- ChorusSoundEffect
- ClickDetector
- ClientReplicator
- ClimbController
- Clothing
- Clouds
- ClusterPacketCache
- CollectionService
- Color3Value
- ColorCorrectionEffect
- CommandService
- CompressorSoundEffect
- ConeHandleAdornment
- Configuration
- ConfigureServerService
- Constraint
- ContentProvider
- ContextActionService
- Controller
- ControllerBase
- ControllerManager
- ControllerPartSensor
- ControllerSensor
- ControllerService
- CookiesService
- CoreGui
- CoreScriptDebuggingManagerHelper
- CornerWedgePart
- CurveAnimation
- CustomEvent
- CustomEventReceiver
- CylinderHandleAdornment
- CylinderMesh
- CylindricalConstraint
- DataModel
- DataModelMesh
- DataModelSession
- DataStore
- DataStoreGetOptions
- DataStoreIncrementOptions
- DataStoreInfo
- DataStoreKey
- DataStoreKeyInfo
- DataStoreKeyPages
- DataStoreListingPages
- DataStoreObjectVersionInfo
- DataStoreOptions
- DataStorePages
- DataStoreService
- DataStoreSetOptions
- DataStoreVersionPages
- Debris
- DebugSettings
- Decal
- DepthOfFieldEffect
- Dialog
- DialogChoice
- DistortionSoundEffect
- DockWidgetPluginGui
- DoubleConstrainedValue
- DraftsService
- DragDetector
- Dragger
- DraggerService
- DynamicRotate
- EchoSoundEffect
- EditableImage
- EditableMesh
- EmotesPages
- EqualizerSoundEffect
- EulerRotationCurve
- ExperienceInviteOptions
- ExperienceNotificationService
- Explosion : 폭발
- FaceControls
- FaceInstance
- Feature
- File
- FileMesh
- Fire : 불
- Flag
- FlagStand
- FlagStandService
- FlangeSoundEffect
- FloatCurve
- FloorWire
- Folder : 폴더
- ForceField
- FormFactorPart
- Frame
- FriendPages
- FriendService
- FunctionalTest
- GamepadService
- GamePassService
- GameSettings
- GenericSettings
- Geometry
- GeometryService
- GetTextBoundsParams
- GlobalDataStore
- GlobalSettings
- Glue
- GoogleAnalyticsConfiguration
- GroundController
- GroupService
- GuiBase
- GuiBase2d
- GuiBase3d
- GuiButton
- GuidRegistryService
- GuiLabel
- GuiMain
- GuiObject
- GuiService
- HandleAdornment
- Handles
- HandlesBase
- HapticService
- Hat
- HeightmapImporterService
- HiddenSurfaceRemovalAsset
- Highlight
- HingeConstraint
- Hint
- Hole
- Hopper
- HopperBin
- HSRDataContentProvider
- HttpRbxApiService
- HttpService
- Humanoid
- HumanoidController
- HumanoidDescription
- IKControl
- ILegacyStudioBridge
- ImageButton
- ImageHandleAdornment
- ImageLabel
- IncrementalPatchBuilder
- InputObject
- InsertService
- Instance
- InstanceAdornment
- IntConstrainedValue
- IntersectOperation
- IntValue : 정수값
- InventoryPages
- JointInstance
- JointsService
- KeyboardService
- Keyframe
- KeyframeMarker
- KeyframeSequence
- KeyframeSequenceProvider
- LanguageService
- LayerCollector
- Light
- Lighting : 일반 빛
- LinearVelocity
- LineForce
- LineHandleAdornment
- LocalizationService
- LocalizationTable
- LocalScript
- LoginService
- LogService
- LuaSettings
- LuaSourceContainer
- LuaWebService
- ManualGlue
- ManualSurfaceJointInstance
- ManualWeld
- MarkerCurve
- MarketplaceService
- MaterialService
- MaterialVariant
- MemoryStoreHashMap
- MemoryStoreHashMapPages
- MemoryStoreQueue
- MemoryStoreService
- MemoryStoreSortedMap
- MemStorageConnection
- MemStorageService
- MeshContentProvider
- MeshPart
- Message
- MessagingService
- Model
- ModuleScript
- Motor
- Motor6D
- MotorFeature
- Mouse
- MouseService
- MultipleDocumentInterfaceInstance
- NegateOperation
- NetworkClient
- NetworkMarker
- NetworkPeer
- NetworkReplicator
- NetworkServer
- NetworkSettings
- NoCollisionConstraint
- NotificationService
- NumberPose
- NumberValue : 실수값
- ObjectValue
- OpenCloudApiV1
- OpenCloudService
- OrderedDataStore
- OutfitPages
- PackageLink
- PackageService
- Pages
- Pants
- Part
- PartAdornment
- ParticleEmitter
- PartOperation
- PartOperationAsset
- PatchBundlerFileWatch
- PatchMapping
- Path
- PathfindingLink
- PathfindingModifier
- PathfindingService
- PermissionsService
- PhysicsService
- PhysicsSettings
- PitchShiftSoundEffect
- PlacesService
- Plane
- PlaneConstraint
- Platform
- Player
- PlayerGui
- PlayerMouse
- Players
- PlayerScripts
- PlayerViewService
- Plugin
- PluginAction
- PluginCapabilities
- PluginDebugService
- PluginDragEvent
- PluginGui
- PluginGuiService
- PluginManagementService
- PluginManager
- PluginManagerInterface
- PluginMenu
- PluginMouse
- PluginToolbar
- PluginToolbarButton
- PointLight
- PointsService
- PolicyService
- Pose
- PoseBase
- PostEffect
- PrismaticConstraint
- ProcessInstancePhysicsService
- ProximityPrompt
- ProximityPromptService
- PublishService
- PVAdornment
- PVInstance
- QWidgetPluginGui
- RayValue
- ReflectionMetadata
- ReflectionMetadataCallbacks
- ReflectionMetadataClass
- ReflectionMetadataClasses
- ReflectionMetadataEnum
- ReflectionMetadataEnumItem
- ReflectionMetadataEnums
- ReflectionMetadataEvents
- ReflectionMetadataFunctions
- ReflectionMetadataItem
- ReflectionMetadataMember
- ReflectionMetadataProperties
- ReflectionMetadataYieldFunctions
- RemoteDebuggerServer
- RemoteEvent : 리모트 이벤트
- RemoteFunction[최적화별로][보안취약][68] : 리모트 함수
- RenderingTest
- RenderSettings
- ReplicatedFirst
- ReplicatedStorage
- ReverbSoundEffect
- RigidConstraint
- RocketPropulsion
- RodConstraint
- RopeConstraint
- Rotate
- RotateP
- RotateV
- RotationCurve
- RunningAverageItemDouble
- RunningAverageItemInt
- RunningAverageTimeIntervalItem
- RunService
- ScreenGui
- ScreenshotHud
- Script
- ScriptBuilder
- ScriptContext
- ScriptDocument
- ScriptEditorService
- ScriptService
- ScrollingFrame
- Seat : 의자 파트
- Selection
- SelectionBox
- SelectionHighlightManager
- SelectionLasso
- SelectionPartLasso
- SelectionPointLasso
- SelectionSphere
- SensorBase
- ServerReplicator
- ServerScriptService
- ServerStorage
- ServiceProvider
- ServiceVisibilityService
- SharedTableRegistry
- Shirt
- ShirtGraphic
- ShorelineUpgraderService
- SkateboardController
- SkateboardPlatform
- Skin
- Sky : 하늘
- SlidingBallConstraint
- Smoke : 연기
- SmoothVoxelsUpgraderService
- Snap
- SocialService
- SolidModelContentProvider
- Sound : 소리
- SoundEffect
- SoundGroup
- SoundService
- Sparkles
- SpawnerService
- SpawnLocation
- SpecialMesh
- SphereHandleAdornment
- SpotLight : 원뿔형 빛
- SpringConstraint
- StandalonePluginScripts
- StandardPages
- StarterCharacterScripts
- StarterGear
- StarterGui
- StarterPack
- StarterPlayer
- StarterPlayerScripts
- StartupMessageService
- Stats
- StatsItem
- Status
- StopWatchReporter
- StringValue : 문자열
- Studio
- StudioService
- StudioTheme
- StyleBase
- StyleDerive
- StyleLink
- StyleRule
- StyleSheet
- StylingService
- SunRaysEffect
- SurfaceAppearance
- SurfaceGui
- SurfaceGuiBase
- SurfaceLight : 사각뿔형 빛
- SurfaceSelection
- SwimController
- SyncScriptBuilder
- TaskScheduler
- Team
- TeamCreateData
- TeamCreateService
- Teams
- TeleportAsyncResult
- TeleportOptions
- TeleportService
- Terrain
- TerrainDetail
- TerrainRegion
- TestService
- TextBox
- TextBoxService
- TextButton
- TextChannel
- TextChatCommand
- TextChatConfigurations
- TextChatMessage
- TextChatMessageProperties
- TextChatService
- TextFilterResult
- TextFilterTranslatedResult
- TextLabel
- TextService
- TextSource
- Texture
- TimerService
- Tool : 툴
- Torque : 토크
- TorsionSpringConstraint
- TotalCountTimeIntervalItem
- TouchInputService
- TouchTransmitter
- Trail
- Translator
- TremoloSoundEffect
- TriangleMeshPart
- TrussPart
- Tween
- TweenBase
- TweenService
- UGCValidationService
- UIAspectRatioConstraint
- UIBase
- UIComponent
- UIConstraint
- UICorner
- UIFlexItem
- UIGradient
- UIGridLayout
- UIGridStyleLayout
- UILayout
- UIListLayout
- UIPadding
- UIPageLayout
- UIScale
- UISizeConstraint
- UIStroke
- UITableLayout
- UITextSizeConstraint
- UnionOperation
- UniversalConstraint
- UnreliableRemoteEvent
- UserGameSettings
- UserInputService
- UserNotification
- UserNotificationPayload
- UserNotificationPayloadAnalyticsData
- UserNotificationPayloadJoinExperience
- UserNotificationPayloadParameterValue
- UserService
- UserSettings
- ValueBase
- Vector3Curve
- Vector3Value
- VectorForce
- VehicleController
- VehicleSeat
- VelocityMotor
- VideoCaptureService
- VideoFrame
- VideoService
- ViewportFrame
- VirtualInputManager
- VirtualUser
- VisibilityCheckDispatcher
- VisibilityService
- Visit
- VoiceChatService
- VRService
- VRStatusService
- WedgePart
- Weld : 용접(구버전)
- WeldConstraint
- Wire
- WireframeHandleAdornment
- Workspace
- WorldModel
- WorldRoot
- WrapLayer
- WrapTarget
모든 클래스가 Instance의 속성을 가진다.
9.1. BasePart
9.1.1. 속성
자주 쓰이는 속성은 다음과 같다.- Anchored[Bool] : 앵커(고정) 여부
- BrickColor : 파트 색깔
- CanCollide[Bool] : 통과 여부
- CanTouch[Bool] : Touched와 TouchEnded 이밴트 감지 가능 여부
- CanQuery[Bool] : 공간 쿼리 작업(마우스 포인테 위에 파트가 있는지 감지 등) 가능 여부
- Color[Color3] : 파트의 색 BrickColor와 다른점은 BrickColor에는 없는 색상까지 사용 할 수 있다.
- Mass[Number] : 질량
- Massless[Bool] : 이 값이 true라면 파트의 질량이 0이 된다.
- Material : 질감
- Orientation[Vector3] : 파트의 회전
- Position[Vector3] : 파트의 위치
- Size[Vector3] : 파트의 크기
- Transparency[Number] : 투명도
9.1.2. 이벤트
- BasePart 개체.Touched : CanTouch가 켜져있는 다른 BasePart 개체가 닿았을 때 상호작용하는 이벤트. 닿는 두 BasePart중 어느 하나라도 CanTocuh가 꺼져있으면 호출되지 않는다. 쿨다운을 설정 안 하면 한 번에 여러 번 호출되니 조심. 인자엔 닿은 개체가 들어온다.[80]
{{{#!folding [ 스크립트 예시 펼치기 · 접기 ] - 닿으면 죽는 KillPart 스크립트
#!syntax lua
local Cooldown = false --쿨다운 변수 만드는 부분
script.Parent.Touched:Connect(function(hit)
local Humanoid = hit.Parent:FindFirstChild("Humanoid") -- 변수 만드는 부분
if Humanoid and not Cooldown then --휴머노이드 개체가 있는지 확인하는 부분 + 쿨다운이 아닌지 확인하는 부분
Cooldown = true
Humanoid.Health = 0 --닿은 플레이어의 체력
task.wait(1) --1초 기다림
Cooldown = false
end
end)
}}}
- BasePart 개체.TouchEnded : Touched의 반대로 CanTouch가 켜진 BasePart 개체가 더 이상 닿지 않을 때 상호작용하는 이벤트. 마찬가지로 닿는 두 BasePart중 어느 하나라도 CanTouch가 켜져있지 않으면 호출되지 않는다. 이것도 쿨다운을 설정 안 하면 한 번에 여러 번 호출되니 조심. 인자엔 더 이상 닿지 않는 개체가 들어온다.
{{{#!folding [ 스크립트 예시 펼치기 · 접기 ] - 더 이상 닿지 않을 때 죽는 스크립트(Touched 예시 스크립트에서 이벤트만 바꾼 버전이다)
{{{#!syntax lua
script.Parent.TouchEnded:Connect(function(hit)
local Humanoid = hit.Parent:FindFirstChild("Humanoid") --변수 만드는 부분
if Humanoid and not Cooldown then --휴머노이드 개체가 있는지 확인하는 부분 + 쿨다운이 아닌지 확인하는 부분
end)if Humanoid and not Cooldown then --휴머노이드 개체가 있는지 확인하는 부분 + 쿨다운이 아닌지 확인하는 부분
Cooldown = true
Humanoid.Health = 0 --닿은 플레이어의 체력
task.wait(1) --1초 기다림
Cooldown = false
endHumanoid.Health = 0 --닿은 플레이어의 체력
task.wait(1) --1초 기다림
Cooldown = false
}}}
}}}
9.1.3. 메서드
PVInstance의 메서드를 사용함9.1.4. 기타
파트의 미끄러움 정도는 다음과 같이 계산된다.파트1과 파트2가 서로 닿는 경우에 파트1의 마찰계수를 [math(a)], 파트2의 마찰계수를 [math(b)]라 두고, 파트1의 마찰무게(friction weight)를 [math(a')], 파트2의 마찰무게를 [math(b')]라고 둔다면 총 마찰계수는 다음과 같다.
[math(마찰계수 = \dfrac{aa'+bb'}{a'+b'})]
9.2. Instance
9.2.1. 속성
자주 쓰이는 속성은 다음과 같다.9.2.2. 이벤트
9.2.3. 메서드
- AddTag(문자열) : 태그를 추가한다.
- CleaeAllChildren() : 개체 안에 있는 모든 아이들(개체 안에 속해 있는 개체들)을 삭재한다.
- Clone() : 개체를 복제한다.
- Destroy() : 개체를 제거한다.
- FindFirstAncestor(문자열) : 해당 이름을 가진 자신의 부모를 찾는다.
- FindFirstAncestorOfClass(문자열) : 해당 클래스에 속한 자신의 부모를 찾는다.
- FindFirstAncestorWhichIsA(문자열) : 해당 상위 클래스(BasePart 등)에 속한 자신의 부모를 찾는다.
- FindFirstChild(문자열, 재귀적 여부) : 해당 이름을 가진 자신의 아이를 찾는다. 만약 두번째 인자가 true라면 개체 안의 개체라도 찾을 수 있다.
- FindFirstChildOfClass(문자열) : 해당 클래스에 속하는 자신의 아이를 찾는다.
- FindFirstChildWhichIsA(문자열, 재귀적 여부) : 해당 상위 클래스에 속한 자신의 아이를 찾는다.
- GetChildren() : 자신의 아이들을 배열로 반환한다.
- GetDescendants() : 자신의 아이들을 배열로 반환하지만 이 메서드는 아이안에 있는 개체도 배열에 넣어 반환한다.
- WaitForChild(문자열, 대시 시간) : 일정 시간동안 해당 이름을 가진 자신의 아이를 감지 할 때까지 기다리는 함수다. 만약 두번째 인자가 없다면 무한히 기다린다.
9.3. Players
9.4. RemoteEvent
9.4.1. 속성
Instance의 속성을 가진다.9.4.2. 이벤트
- OnClientEvent : 클라이언트에서 리모트 이벤트가 감지 됐을 때 발동하는 이벤트다. 로컬 스크립트에서만 사용 가능하다.
- OnServerEvent : 서버에서 리모트 이젠트가 감지 됐을 때 발동하는 이벤트다. 서버 스크립트에서만 사용 가능하다.
9.4.3. 메서드
- FireAllClient(튜플) : 모든 클라이언트에 리모트 이벤트를 보낸다.
- FireClient(플레이어 개체, 튜플) : 특정한 플레이어에게 리모트 이벤트를 보낸다.
- FireServer(튜플) : 서버에 리모트 이벤트를 보낸다.
9.4.4. 기타
- 리모트이벤트를 너무 빠른 간격으로 보낼 경우 리모트이벤트 몇개가 누락 될 수도 있다.
10. 팁
10.1. Luau 관련
- 로블록스 엔진 API 참조 문서에 들어가면 여러 로블록스내 존재하는 오브젝트의 속성이나 메서드 등을 찾을 수 있고, 그 오브젝트에 대한 메서드의 코드 예제도 볼 수 있다.
- Loadlibrary 스크립트는 더 이상 사용되지 않으며 삭제되었다. 이를 쓰는 개발자는 Loadlibrary 대체방법(영문)을 참고해서 대체하면 된다.
- if 조건문은 elseif로 무한대 연장이 가능하다.[83]
- 보통 개체를 움직일 때 CFrame이나 position을 반복해 부드러운 움직임을 연출하는데. 이런 더 부드러운 움직임을 원한다면 RenderStepped 서비스를 이용해보자. 이것도 성에 차지 않는다면 TweenService를 사용해보자.[유튜브]
- 최근 AI 스크립팅 기능이 추가되어 굳이 어려운 스크립트를 작성하는 법을 알기 위해 유튜브를 뒤져야 하는 수고가 없어졌다. 단 아직 초기 단계이므로 너무 좋은 성능은 기대하지 않는 것이 좋다. 그리고 한국어 지원도 해주지만 아직은 영어가 더 정확도가 높다.[85]
- 스튜디오 베타에서 스크립트에 기본적으로 비엄격 모드 적용을 활성화 해서 주석 표시 옆에다 !nocheck, !nonstrict, !strict중 하나를 적어서 타입 체킹 모드를 변경할 수 있다.
- 자신이 짠 코드에 버그나 실수가 많다면 타입 체킹을 적극적으로 활용하자.
- 제대로 스타일링 되고 구조화된 코드를 짜고싶다면 vscode 를 활용해보자. rojo 를 통해 스튜디오와 연결하고 wally 를 통해 편리하게 라이브러리들을 설치할 수 있다. 게다가 vscode 설정이나 외부 익스텐션으로 코드를 짜는데 편리한 기능들을 추가할 수 있다.
- roblox-ts 를 통해 luau 의 TypeScript 버전을 사용할 수 있다.
- 또, 루아우 네이티브 코드를 활성화 한 뒤 주석 표시 옆에다 !native를 적어 코드의 성능을 끌어올릴 수 있다. 그러나 네이티브를 써도 코드의 속도가 올라가지 않을 수도 있다. 이에 대해선 밑의 최적화 문단 참고.
10.2. 최적화 관련
- 스크립트를 여러개의 개체 내부에 일일이 넣는 것 보다는 단일 스크립트를 ServerScriptService 같은데에 넣어서 개체들을 테이블이나 폴더에 묶은 뒤에 for i, v 루프를 사용하여 이벤트 처리 등을 하는 것이 유지 보수와 최적화 면에서 훨씬 좋다.
- 아니면 아예 로컬 스트립트와 리모트 이벤트로 이벤트 처리를 할 수 도 있다. 단, 이 경우엔 자신이 정말 코드를 잘 짜서 보안을 잘 관리 할 수 있는 경우가 아니면 하지 말자.
- 로블록스에 기본적으로 내재되어 있는 서버와 클라이언트 간 통신 처리용 리모트 이벤트, 리모트 함수 말고 Zap, ByteNet 같이 네트워킹 라이브러리를 쓰면 성능 향상을 할 수 있다.[86]
- 테이블의 요소에 접근할 때 테이블.키 나 테이블[키], table.find(테이블, 요소) 이 세가지 방법으로 접근할 수 있는데 점으로 접근하는 것과 대괄호로 접근하는 거나 성능면에선 큰 차이가 없다. 그러나 table.find의 경우엔 테이블을 순회하면서 찾기에 속도가 더 떨어진다.
- 영어를 잘 한다면 Luau 성능 관련 문서나 공식 엔진 API 문서를 읽는 것도 좋다.
- 빛이나 불같은 효과들은 많이 있으면 랙이 유발되므로 캐릭터가 광원이나 불로 부터 멀리 떨어진다면 빛이나 불을 로컬스크립트로 끄는 것도 도움이 된다.
- 파트들을 웬만하면 앵커 해 놓는 것이 좋다. 파트를 앵커 안 해놓으면 매 프레임마다 물리 계산이 되기 때문에 파트를 앵커 시켜 물리 계산을 줄일 수 있다.
- 개체를 찾을때 (개체).(찾을 개체 이름), (개체)[찾을 개체 이름], (개체):FindFirstChild(찾을 개체 이름), (개체):WaitForChild(찾을 개체 이름) 이 네가지 방법중 하나를 쓸텐데, 개체를 찾는데 걸리는 시간은 점으로 찾기 = 대괄호로 찾기 < FindFirstChild < WaitForChild 순이다. 그러나 점과 대괄호로 개체를 찾을때 만약 찾는 개체가 없으면 오류를 낸다. 따라서 반드시 있는 개체[87]를 찾을땐 점과 대괄호로, 있을수도 있고 없을수도 있는 개체를 찾을땐 FindFirstChild로, 있을때 까지 기다려야만 하는 개체를 찾을땐 WaitForChild로 찾아야 한다.[88]
- --!native로 네이티브 코드 생성을 활성화 시킨 상태에서 코드의 속도를 더 높이려면 타입 선언을 제대로 해놓는게 중요하다. 네이티브가 켜진 상태에선 코드를 더 빠르게 하기 위해서 타입 추론을 하는데 이때 타입 선언을 제대로 해놓지 않으면 속도가 느려지는 경우가 발생한다. 예를 들어 Vector3의 X, Y, Z값을 모두 더해 반환하는 함수를 만든다고 하자. 그러면 다음과 같은 함수가 나올 것이다.
{{{#!syntax lua
local function sumOfVector3Components(v)
return v.X + v.Y + v.Z
end
}}}
그러나 이 경우 타입 추론이 인자의 유형을 테이블로 추론해 속도가 느려지는 경우가 발생할 수 있다. 이를 방지하기 위해선 다음과 같이 타입 선언을 해둬야 한다.
{{{#!syntax lua
local function sumOfVector3Components(v:Vector3)
return v.X + v.Y + v.Z
end
}}}
10.3. 보안 관련
- 게임 내에 핵 사용자가 창궐하는 것을 막으려면 리모트 이벤트 처리할 때 조건문을 많이 넣는 것이 좋다. 아니면 최소한 클라이언트 측으로라도 막자. 클라이언트 측 핵 방지 스크립트는 무용지물이라는 말이 있긴 하나 이건 그나마 좀 머리 돌아가는 핵쟁이나 뚫지, 해피모드 이런 거 쓰는 잼민이들은 거의 못 뚫기에 아예 클라이언트 측 핵 방지 스크립트도 안 짜는 것 보단 낫다.
- 위에서 얘기 했듯 클라이언트 측 보안은 아예 안 하는 것 보단 낫긴 하지만 결국 뚫리기 쉽다. 그렇기에 보안을 100% 클라이언트에 떠넘기면 나중에 보안이 숭숭 뚫려서 폭망한다. 클라이언트를 무조건적으로 신뢰하지 않고 계속 의심하고 코드를 짜는 것이 보안의 핵심이다.
- 총 스크립트를 짠다고 할 때, 리모트 이벤트 처리를 하면서 무엇을 인자로 받을지 결정할텐데, 이때 마우스의 Hit을 인자로 받아서 레이캐스팅 하는 것 보단 그냥 클라이언트에게 레이캐스팅 떠넘기고 피격 결과를 인자로 받으면 성능면에서 좋긴 하겠지만, 이러면 핵이 벽 뒤에 있는 대상까지도 인자로 FireServer 메소드로 보내서 대미지를 주게끔 하는 일이 벌어질게 불 보듯 뻔하다. 성능을 위해 클라이언트에게 계산을 맡길거면 최소한 계산 결과에 대해 조건문, 예를 들어서 위 처럼 피격 결과 테이블을 인자로 받는다 하면 테이블의 피격 파트와 플레이어 간의 거리 같은 것들을 검사 하면서 처리를 해야 그나마 안전하다.
10.4. 개발 관련
- 개발을 할 때 도움을 받고 싶다면 로블록스 크리에이터 허브에서 알아보기 > 엔진 > 가이드에 들어가 필요한 정보를 얻으면 도움이 될거다.
- 개발자 허브를 보고도 잘 모르겠다면 개발자 포럼에 가서 질문하는 것도 도움이 될거다.
- 혹은 영어가 잘 안되거나 신속한 정보를 얻고 싶다면 유튜브에서 검색하는 것도 도움이 될거다. 국내 로블록스 스튜디오 유튜버로는 노페어와 옐롯 등이 있다.[89]
[1] 로블록스에서 자체 개발한 언어이다.[2] local 없이도 변수 생성이 가능하기는 하다, 그러나 추천하지는 않는다.[3] 이와 비슷한 예로는 _G의 사용이 있다.[4] 정확하게는 단항 부정 연산자[5] 비교 연산자라고 불리기도 한다.[6] 문자와 문자를 연결해준다.[7] 테이블에 들어 있는 데이터의 수를 의미한다.[8] 사실상 아래 예 말고도 조건문은 무궁무진하게 활용된다.[9] 즉 조건을 넣을 수 없다.[10] 물론 변수는 read-only, 읽기전용이다.[11] 3가지의 반복문 중에서 위 코드를 줄이기에는 for 반복문이 가장 용이하다.[12] 매게변수가 존재하지 않아도 된다.[13] 이름없는 함수라고도 알려져 있다.[14] 무명함수는 제외.[15] 예제 코드에 local을 붙이지 않은 건 함수에 처음 접해보는 초심자들의 혼동을 예방하기 위한 약간의 배려라고 볼 수 있다.[16] 대부분의 타 언어와 달리 순서가 1부터 시작하니 주의하자.[17] 그 예로 킬파트를 만드려고 스크립트를 파트 안에 놓는 경우가 있다. 점프맵류 게임을 만들 때 대다수의 초심자 스크립터들이 하는 실수인데, 현명한 방법으로는 ServerScriptService 에 하나의 스크립트를 놓고 여러개의 킬파트를 조작하게 할 수 있다.[18] 대표적으로 ecs가 있다.[19] 단, 딱히 아무런 의미도 가지지 않는 변수는 한 문자로 짓는 것이 가능하다.[20] 그 예로 초심자들이 player 를 plr 로 줄여쓰는 것이 있다. 간혹가다 줄여 쓰는 것이 편리하고 간단해 보여 더 좋다고 주장하는 사람들이 있지만, 아마도 이들은 대부분 프로그래밍을 처음 배워보거나 접한지 얼마 지나지 않은 사람들일것이다. 이미 수많은 공식 문서에서 네이밍할 때 약자를 쓰는 것이 좋지 않다고 언급해왔다.[21] 예로 local tweenInfo = TweenInfo.new() 가 있다.[22] 예로 for _, v do end 가 있다.[23] 출처 : 예제 1, 예제 2[24] 실제로 천상계 스크립터들은 그러한 혜택을 극대화해 사용하여 스크립트들의 대부분이 다른 사람이 만든 라이브러리나 프레임워크가 되기도 한다.[25] 데이터 로스도 종종 일어난다.[26] 유튜브 동영상이나 공식 문서, 데브포럼에 있는 글 등[27] 물론 숙련자 기준이다. 초심자나 중급자에겐 프로미스가 익숙하지 않을 수도 있다.[28] 또한 프로미스 라이브러리를 기반으로 한다.[29] 그러나 어느 정도 체득하면 꽤나 편리하다.[30] 성능은 준수하다.[31] 같은 개발자가 만든 브릿지넷이나 브릿지넷2는 모두 deprecated 되었으니 사용하지 않길 바란다.[32] React Roblox 등[33] 리액트 루아 공식 문서로는 사용 방법을 터득하기도 쉽지 않고 잘못된 사용방법을 쓸 확률이 높다.[34] 그 예로 리액트 루아의 ts 버전을 토대로 개발한 slither이 있다.[35] ui 전용 상태 관리 라이브러리는 로액트-로덕스이다.[36] 그래도 로덕스를 고집하는 사람들도 있다.[37] 예: 총 프레임워크, 자동차 프레임워크 등[38] Knit 내에서는 유틸(Util) 이라 불린다.[로컬] LocalScript에서만 읽기 및 호출이 가능하다.[40] 좋은 예시로 플레이어의 캐릭터. 캐릭터의 파트들은 로블록스 엔진에 의해 자동으로 해당 플레이어가 소유하게 된다. 이러한 캐릭터의 파트들의 물리 연산은 전적으로 클라이언트가 계산하기에 클라이언트 측에서 캐릭터의 파트를 고정할 시 고정된게 그대로 다른 클라이언트에도 복제된다.[41] 파티클, 기후 등[42] 예로 gui, player, character 안에서 작동이 된다.[서버] 서버측 스크립트에서만 읽기 및 호출이 가능하다.[44] 예시로 Workspace나 ReplicatedStorage 안에 들어 있는 모든 항목들.[45] 서비스 및 BasePart와 같은 추상 클래스를 제외한 모든 클래스의 상위 클래스인 Instance 클래스의 생성자를 보유하고 있는 자료형이다.[46] 타 언어의 null 과 같다.[47] gui 상에서 많이 사용된다.[48] CFrame과 함께 위치를 나타내는 단위이다.[49] 예시) script.Parent.CFrame = script.Parent:Lerp(CFrame1, 0.7) --> script.Parent.CFrame과 CFrame1의 Position 간격이 1(100%)이라하면 Position이 0.7(70%) 되는 지점에 온다.[50] 반복 횟수를 0으로 설정하면 1번 움직이고, 음수로 설정하면 무한 반복한다.[51] 다차원 테이블(테이블 안에 있는 테이블)도 가능하며 key-value 형의 딕셔너리(for i, v in ipairs(테이블) do)도 가능하다.[주의할점] 루아 스크립트에서는 테이블의 번호를 1번 부터 센다.[53] 테이블의 형식은 {year=년도, month=월, day=일, hour=시, min=분, sec=초}다.[54] 한글은 한글자가 깨진글자 3개로 이루어져 있어 한글로 하면 버그가 생길수도 있다. 예) string.len("안녕하세요") -> ��������������� -> 15출력[55] 찾는글자가 두글자라면 시작지점을 반환해준다. 예) string.find("11223344", "22") -> 3출력[56] 예) string.sub("12345678", 4, 8) -> " "45678"[57] 시작번호나 끝번호에 음수를 넣으면 글자 끝에서 뒤로간다. -1이면 마지막 글자, -2면 마지막 2번째 글자 이런식이다.[58] 디버깅할 때 말고는 별로 쓸모가 없다.[최적화별로] [60] 괄호 안에 아무것도 안넣으면 기본값으로 0.03초를 기다린다.[61] 캐릭터가 죽거나, 스크립트를 자식으로 두고 있던 부모가 삭제되거나 (예: 검 모델)[최적화별로] [구버전] 현재에는 다른 것으로 대체되었다.[구버전] [구버전] [최적화별로] [보안취약] [68] 이는 서버 -> 클라이언트 구조에만 해당됨, 불가피할시 타임아웃을 걸어두는 걸 추천함[Bool] true와 false 값을 가진다.[Bool] [Bool] [Bool] [Color3] RGB 색상코드[Number] 숫자를 값으로 가진다.[Bool] [Vector3] Vector3(좌표)를 값으로 가진다.[Vector3] [Vector3] [Number] [80] Touched 이벤트는 뭔가가 닿여을 때 한 번만 작동하고 만약 어떤 파트가 Touched 를 감지하는 파트 안으로 들어오면 Touched 를 감지 하지 못한다. 예를 들어 Touched 가 감지될 때마다 플레이어의 채력을 5씩 깎는 파트가 있다고 하면, 플레이어가 처음 파트에 닿였을 때는 채력이 깎이지만 데미지 파트 안에서는 아무리 움직여도 데미지를 받지 않는다. 만약 데미지 파트 안에서도 데미지를 받게 만들고 싶다면 무한 반복문을 통해 플레이어의 위치가 파트 안인지 감지해주는 스크립트를 만들어준다.[String] 문자열을 값으로 가진다.[String] [83] 한글로 치면 만약 ㅇㅇ이 6 이면 ㅇㅇ을 해라. 아니라면 ㅇㅇ을 해라. 아니라면 ㅇㅇ을 해라. 아니라면 ㅇㅇ을 해라. 아니라면 ㅇㅇ을 해라....[유튜브] 유튜브에 적용법이 나온다.[85] --을 사용해 주석을 치고 원하는 내용을 적고 Alt+\를 누르면 된다. 스크립트를 쓰다가 기다리면 자동으로 추천해주기도 한다. Tab으로 추천된 코드를 완성시킬 수 있다.[86] 자세한 건 라이브러리 문단의 네트워킹 부분을 참고.[87] 예시: ServerStorage 내부에 있는 개체 (서버 스크립트 측)[88] 참고로 pcall을 통해서 있을수도 있고 없을수도 있는 개체를 찾는 방법은 WaitForChild보다 더 느리다.[89] 옐롯은 주로 프리모델의 사용법을 알려주기 때문에 스크립트를 배우고 싶다면 노페어의 영상을 보늨 것을 추천한다.