로블록스 루아 스크립트 중급

정선호·2024년 4월 2일
1

Roblox-Lua

목록 보기
2/3

강의 재생목록

C계열 언어와 유니티를 익히고, 기초 강좌를 공부한 사람 기준으로 작성했습니다.


01.이벤트 Touched:Connect()

이벤트란 '누가 밟았을 때', '새 플레이어가 접속했을 때', '누가 버튼을 눌렀을 때' 등등 특정한 이벤트가 발생할 때 스크립트를 시작하게 만드는 것이다.

다른 파트가 닿을 때까지 기다린 후, 닿으면 색을 랜덤색으로 변경하는 이벤트 스크립트

part.Touched:Wait()
part.BrickColor = BrickColor.Random()

다른 파트와 맟닿을 시 ChangeColor함수를 호출하는 이벤트 스크립트
이 때 맟닿은 파트는 hit 매개변수로 입력되며 Destroy()가 호출된다.

local part = script.Parent

function ChangeColor(hit)
	part.BrickColor = BrickColor.Random()
	hit:Destroy()
end

part.Touched:Connect(ChangeColor)


02.FindFirstChild, 캐릭터에만 반응하는 이벤트

FindFirstChild

다음과 같이 workspace 내 파트를 찾을 시 파트가 없으면 에러가 발생한다.

local part = workspace.Part
print(part) // part가 없을 시 오류 출력

하지만 다음과 같이 파트를 찾을 시 파트가 없으면 nil이 출력된다.

local part = workspace:FindFirstChild("Part")
print(part)

이렇게 FindFirstChild함수를 이용해 파트를 찾게 되면 에러가 발생하지 않으므로 개체를 탐색하거나 파트의 없음을 확인할 때 사용하기 용이하다.

캐릭터에 반응하는 이벤트

위의 FindFirstChild를 이용해 맞닿은 파트가 플레이어 캐릭터인지 확인 후 선택적으로 반응하는 스크립트를 작성할 수 있다.

local part = script.Parent

function ChangeColor(hit)
	local humanoid = hit.Parent:FindFirstChild("Humanoid")
	if humanoid then
		part.BrickColor = BrickColor.Random()
	end
end

part.Touched:Connect(ChangeColor)

03.FindFirstChild의 형제들

다음과 같이 FindFirstChild의 플래그를 true로 변경하면 바로 아래 단계의 자식뿐 아니라 모든 단계의 자식들을 탐색한다.

print(workspace:FindFirstChild("Part", true))

자식이 아닌 부모를 찾아주는 함수는 FindFirstAncestor이다.
이 함수 또한 이름이 동일한 부모를 찾지 못하면 nil을 반환한다.

print(script:FindFirstAncestor("Part"))

자식 개체를 이름이 아닌 클래스네임으로 찾는 함수는 FindFirstChildOfClass이다.
파트 이름이 변경되어도 찾을 수 있다는 이점이 있다.

print(workspace:FindFirstChildOfClass("Part"))

특정 클래스를 상속한 클래스들을 찾기 위해 사용하는 함수는 FindFirstChildWhichIsA이다.
자식 파트들 중 BasePart클래스를 상속받는 Part, WedgePart, TrussPart들 중 위에 하나만을 찾기 위해 다음과 같이 사용할 수 있다.

print(workspace:FindFirstChildWhichIsA("BasePart"))

FindFirstAncestor함수 또한 OfClassWhichIsA함수가 존재한다.


04.변수 연산, 킬파트와 데미지 파트

대입연산자

Lua에서는 C계열과 마찬가지로 대입연산자를 사용할 수 있다.
대입 연산자는 어떠한 변수에 연산을 한 값을 다시 그 변수값으로 설정한다는 뜻이다.

local a = 10

a += 3   // a = a + 3
a -= 3   // a = a - 3
a *= 3   // a = a * 3
a /= 3   // a = a / 3

대입 연산자는 선언한 변수뿐만 아니라 파트의 인자값 등에도 사용할 수 있다.

킬파트와 데미지 파트

로블록스 플레이어의 Humanoid 파트에는 Health 속성이 있다. 이 값을 0으로 만들면 플레이어는 사망하게 된다.

닿은 플레이어를 죽게 만드는 킬 시스템은 다음과 같이 구현할 수 있다.

local part = script.Parent

function kill(hit)
	local humanoid = hit.Parent:FindFirstChild("Humanoid")
	if humanoid then
		humanoid.Health = 0
	end
end

part.Touched:Connect(kill)

데미지 파트는 humanoid.Health = 0 대신에 humanoid:TakeDamage(데미지값)을 사용하면 된다.


05.이름 없는 함수, 이벤트 쿨타임

이름 없는 함수

재사용성 없는 함수를 선언할 필요가 있을 때(하나의 이벤트에만 연결되는 함수 등) 이름 없는 함수를 선언과 동시에 사용할 수 있다.

local part = script.Parent

// Connect 안에 이름 없는 함수 선언
part.Touched:Connect(function(hit)
	local humanoid = hit.Parent:FindFirstChild("Humanoid")
	if humanoid then
		humanoid.Health -= 5
	end
end)

이벤트 쿨타임

이벤트 발생 조건이 상시로 부합되지만, 이벤트의 발생에 쿨타임을 두고 싶을 경우 다음과 같이 조건을 둘 수 있다.

local part = script.Parent

local Enabled = true

// 1초마다 한 번씩 발생하도록 조건을 걸어둠
part.Touched:Connect(function(hit)
	local humanoid = hit.Parent:FindFirstChild("Humanoid")
	if humanoid and Enabled then
		Enabled = false
		humanoid.Health -= 5
		wait(1)
		Enabled = true
	end
end)

06.모델 옮기기, MoveTo()

로블록스의 모델(유니티의 프리팹과 유사)은 일반적인 트랜스폼 개념이 존재하지 않는다. 따라서 모델은 이전에서 파트를 이동시키던 것처럼 트랜스폼 값을 변경해 이동시킬 수 없다.

대신 모델은 다음 함수를 이용해 특정 위치로 이동시킬 수 있다.

workspace.Model:MoveTo(Vector3.new(목표 좌표값))

해당 함수를 이용해 모델을 옮길 때 이미 다른 모델 혹은 파트가 자리에 위치해 있으면 이미 있던 모델/파트의 위로 이동하게 된다.
따라서 MoveTo를 이용해 모델을 옮길 때에는 목표 좌표값에 다른 모델이 있으면 안 된다.

MoveTo함수는 workSpace 안에 속해 있는 모델들에 한해서 작동된다. 따라서 ServerStorage에서 복사해 소환하는 오브젝트들은 워크스페이스에 소속시킨 후 MoveTo를 이용해 이동시켜야 한다.

local car = game.ServerStorage.Model
local clone = car:Clone()
clone.Parent = workspace
clone:MoveTo(Vector3.new(0,100,0))

모델을 MoveTo로 이동시킬 때 위치의 기준은 모델을 선택했을 때 나오는 파란 상자(Bounding Box)의 정중앙이다.
다만 모델의 Data-PrimaryPart를 지정했을 때는 PrimaryPart의 정중앙을 기준으로 이동하게 된다.


07.다양한 이벤트

ChildAdded

파트에 새 자식 개체가 생성 혹은 추가되었을 때 발생하는 이벤트이다.
매개변수에는 추가된 개체가 들어오게 된다.

local part = script.Parent

part.ChildAdded:Connect(function(child)

end)

Changed

파트의 속성 중 하나가 바뀌었을 때 발생하는 이벤트이다.
매개변수에는 바뀐 속성의 이름이 문자열로 들어온다.

part.Changed:Connect(function(property)
   if property == "Position" then

   end
end)

GetPropertyChangedSignal

파트의 특정 속성이 바뀌었음을 감지하기 위해 사용하는 함수이다.
매개변수는 아무것도 들어오지 않는다.

part:GetPropertyChangedSignal("Position"):Connect(function()
// position속성이 바뀌었을 때만 이벤트가 호출됨
end)

PlayerAdded

게임 내 '플레이어'파트에서만 사용할 수 있는 함수로 게임에 새 플레이어가 들어왔을 때 호출되는 함수이다.
매개변수로는 새로 들어온 플레이어 개체이다,

// 플레이어 이름이 nofair2002이면 해당 플레이어를 강제퇴장시킨다.
game.Players.PlayerAdded:Connect(function(plr)
   if plr.Name == "nofair2002" then
      plr:Kick()
   end
end)

플레이어 캐릭터가 스폰될 때 호출되는 CharacterAdded함수를 추가할 수도 있다.

game.Players.PlayerAdded:Connect(function(plr)
   plr.CharacterAdded:Connect(function(chr)
  
   end)
end)

08.서버와 클라이언트


09.로컬 스크립트, LocalPlayer

로컬 스크립트

지금까지 작성한 Script는 서버에서 작동되는 서버 스크립트였다. Script는 탐색기 내 Workspace와 ServerScriptService에서만 실행된다.

이와 반대로 로컬(각 클라이언트)에서만 작동되는 LocalScript는 ReplicatedFirst, StarterGui, StarterPack, StarterPlayer의 StarterCharacterScripts와 StarterPlayerScripts에서만 작동한다.
각각 위의 폴더에 넣은 스크립트들은 게임 실행시 플레이어 스폰과 동시에 다음과 같이 폴더에 추가된다.

LocalPlayer

로컬 스크립트에서는 로컬 플레이어에 접근할 수 있다. 로컬 플레이어는 클라이언트 내 본인의 플레이어를 자칭한다.

local localplayer = game.Players.LocalPlayer

서버 스크립트에서는 로컬 플레이어에 접근할 수 없지만 다음과 같이 이벤트를 발생시키는 플레이어를 특정할 수 있다.

game.Players.PlayerAdded:Connect(function(plr)
    // 플레이어가 맵에 추가될 때 호출될 기능 추가
end)

part = workspace.Baseplate
part.Touched:Connect(function(hit)
   // 특정 파트에 닿은 플레이어를 확인하는 기능
   local plr = game.Players:GetPlayerFromCharacter(hit.Parent)
end)

10.WaitForChild

문자열 합치기

Lua에서 문자열은 다음과 같이 문자열 사이에 ..를 추가해 합칠 수 있다.

print("문자열1".."문자열2")

숫자만 적혀있는 문자열들을 +하면 Lua는 문자열을 숫자로 인식해 실제 더한 값을 출력한다.

print("2"+"7")   // 9 출력
print("2".."7")  // 27 출력

WaitForChild

서버 스크립트는 맵 로딩이 완전히 끝날 때까지 작동하지 않는다.
맵 로딩이 완전히 끝나기 전에 스크립트가 작동하면 맵 내의 오브젝트를 찾지 못하는 오류가 발생하기 때문이다.

하지만 로컬 스크립트에서 workspace에 접근할 때에는 문제가 발생하는데, 로컬 스크립트는 맵 로딩이 끝나기 로컬에서 자체적으로 작동되기 때문이다.
따라서 로컬 스크립트에서 workspace로 접근할 때에는 WaitForChild함수를 사용해 원하는 파트가 소환될때까지 대기시킨다.

local part = workspace:WaitForChild("Baseplate")
part.BrickColor = BrickColor.new("Really black")

WaitForChild는 위와 같이 로컬에서 맵 로딩을 기다리는 용도 뿐 아니라, 게임이 돌아가는 도중에도 특정 파트가 소환될 때까지 기다리는 등에 사용할 수 있다.

다음과 같이 WaitForChild의 두 번째 매개변수로 시간(초)를 추가해주면 해당 함수는 지정해준 시간동안만 오브젝트 서칭을 하고, 그 안에 오브젝트가 소환되지 않으면 nil을 반환한다.

// 5초 동안만 Part를 찾고 못 찾으면 nil을 반환
local part = workspace:WaitForChild("Part", 5)
part.BrickColor = BrickColor.new("Really black")

위의 WaitForChild를 이용해 서버에서 소환하는 플레이트의 표면에 로컬에서만 보이는 글자를 추가하는 로컬 스크립트는 다음과 같다.

//워크스페이스의 서페이스 탐색
local part = workspace:WaitForChild("Part")
local surfaceGui = part:WaitForChild("SurfaceGui")
local TextLabel = surfaceGui:WaitForChild("TextLabel")
// 로컬 플레이어의 이름 가져오기
local player = game.Players.LocalPlayer
local playerName = player.Name
// 서페이스에 글자 작성
TextLabel.Text = playerName.."님 안녕하세요!"

캐릭터 또한 한번에 모든 파트가 생성되는 것이 아니라 하나하나씩 소환되는 것이기 때문에 캐릭터에 접근할 때도 WaitForChild를 사용해야 한다.

game.Players.PlayerAdded:Connect(function(plr)
   plr.CharacterAdded:connect(function(chr)
      local humanoid = chr:WaitForChild("Humanoid")
   end)
end)

11.바인더블 이벤트

바인더블 이벤트(Bindable Event)는 스크립트끼리 서로 신호를 주고받을 때 사용하는 이벤트이다.
1 : n, n : 1, n : m 개수의 스크립트로 이벤트를 주고받을 수 있다.

바인더블 이벤트 event에 이벤트를 추가하는 방법과 이벤트 실행 방법은 다음과 같다.

  • 이벤트 추가 : event.event:(원하는 이벤트 함수)
  • 이벤트 실행 : event:Fire()


위의 사진과 같이 ServerStorage에 babo라는 이름의 바인더블 이벤트를 추가하고, Workspace에 A와 B 스크립트가 있을 때, A스크립트에서 B스크립트의 이벤트를 발생시키는 법은 다음과 같다.

// A 스크립트(이벤트 실행)
local event = game.ServerStorage.babo
event:Fire()
// B 스크립트(이벤트 추가)
local event = game.ServerStorage.babo
event.Event:Connect(function()
   (추가할 기능)
end)

바인더블 이벤트는 다른 이벤트와 마찬가지로 매개변수를 추가할 수 있다.
아래 예시는 매개변수로 휴머노이드 파트를 전송해 해당 휴머노이드를 죽이는 이벤트 바인딩이다.

// A 스크립트(이벤트 실행)
local event = game.ServerStorage.babo
script.Parent.Touched:Connect(function(hit)
   local humanoid = hit.Parent:FindFirstChild("Humanoid")
   if humanoid then
      event:Fire(humanoid)
   end
end)
// B 스크립트(이벤트 추가)
local event = game.ServerStorage.babo
event.Event:Connect(function(humnoid)
   humnoid.Health =0
end)

바인더블 이벤트의 매개변수에 문자로 인덱싱된 테이블과 메타테이블은 보낼 수 없다.

tal = {}          // 테이블
tal[1] = "aaa"    // 숫자로 인덱싱된 테이블(매개변수 가능)
tal["a"] = "aaa"  // 문자로 인덱싱된 테이블(매개변수 불가능)

바인더블 이벤트는 추후 배울 바인더블 펑션과 다르게 값 반환(return)을 할 수 없다.
또한 바인더블 이벤트는 오직 서버-서버 스크립트에서만 사용할 수 있다.


12.CFrame, PivotTo()

CFrame은 Position과 같이 파트 속성이지만 오로지 스크립트로만 조작할 수 있는 속성이다.
Position은 위치만(유니티의 Transform.Location)맞춰준다면 CFrame은 위치와 회전까지 동일하게 맞춰준다.

// Part1의 위치만 Part2의 위치에 맞춰진다
workspace.Part1.Position = workspace.Part2.Position
// Part1의 위치와 회전이 Part2의 위치와 회전에 맞춰진다
workspace.Part1.CFrame = workspace.Part2.CFrame

모델을 특정 Position으로 이동시킬 때 MoveTo()함수를 사용한 것처럼 PivotTo()함수를 이용해 특정 위치와 회전으로 이동시킬 수 있다.
이 때 PivotTo()함수는 MoveTo()함수와 다르게 충돌을 무시하고 원하는 자리로 이동시켜준다.

// Position 이동
workspace.Model:MoveTo(workspace.Part1.Position)
// CFrame 이동
workspace.Model:PivotTo(workspace.Part1.CFrame)

CFrame은 Position처럼 덧셈연산이 가능하고 추가로 곱셈연산또한 가능하다

// 월드좌표를 기준으로 위로 10만큼 떨어진 곳으로 이동
workspace.Part1.Position = workspace.Part2.Position + Vector3.new(0,10,0)
// 월드좌표를 기준으로 회전을 유지한 채 위로 10만큼 떨어진 곳으로 이동
workspace.Part1.CFrame = workspace.Part2.CFrame + Vector3.new(0,10,0)
// Part2가 바라보는 방향을 기준으로 회전을 유지한 채 위로 10만큼 떨어진 곳으로 이동
workspace.Part1.CFrame = workspace.Part2.CFrame * CFrame.new(0,10,0)
// 모델이 바라보는 방향을 기준으로 회전을 유지한 채 위로 10만큼 떨어진 곳으로 이동
workspace.Model:PivotTo(workspace.Model:GetPivot() * CFrame.new(0,10,0))

13.리모트 이벤트(Remote Event)

리모트 이벤트는 로컬 스크립트와 서버 스크립트를 이어주는 매개체 역할을 한다.
리모트 이벤트는 로컬과 서버 스크립트 모두 접근할 수 있는 'ReplicatedStorage'에 추가한다.

StarterPlayerScripts에 있는 로컬 스크립트에서 Workspace에 있는 Part의 색깔을 바꿔주는 스크립트는 다음과 같이 작성할 수 있다.

// 로컬 스크립트
// 키 바인딩 서비스 불러오기
local contextActionService = game:GetService("ContextActionService")
// 리모트 이벤트 불러오기
local RemoteEvent = game.ReplicatedStorage:WaitForChild("ColorEvent")

// 리모트 이벤트 호출 함수 생성
function RPressed(actionName,  inputState, inputObject)
   if inputState == Enum.UserInputState.Begin then
      RemoteEvent:FireServer()
   end 
end

// 리모트 이벤트 호출 함수를 키에 바인드
contextActionService:BindAction("RPress", RPressed, true, Enum.KeyCode.R) 
// 서버 스크립트
// 리모트 이벤트 불러오기
local remoteEvent = game.ReplicatedStorage.ColorEvent

// 리모트 이벤트에 기능 추가
remoteEvent.OnServerEvent:Connect(function()
   workspace.Part.BrickColor = BrickColor.Random()
end)

로컬 스크립트에서는 RemoteEvent:FireServer()를 이용해 리모트 이벤트를 발동시켜 서버 스크립트에 신호를 줄 수 있다.
서버 스크립트에서는 신호를 받으면 remoteEvent.OnServerEvent에 바인딩 혹은 선언된 함수나 기능을 수행한다.

리모트 이벤트 또한 매개변수를 보낼 수 있다. 다만 OnServerEvent의 첫 번째 매개변수는 항상 이벤트를 보낸 로컬 플레이어 개체이다.

로컬 스크립트에서 문자열 매개변수를 보내 특정 문자에 따라 서버에 존재하는 파트의 색을 지정해 변경하는 스크립트 예시는 다음과 같다.

// 로컬 스크립트
local contextActionService = game:GetService("ContextActionService")
local RemoteEvent = game.ReplicatedStorage:WaitForChild("ColorEvent")

function RPressed(actionName,  inputState, inputObject)
 if inputState == Enum.UserInputState.Begin then
  RemoteEvent:FireServer("R")
 end 
end
function GPressed(actionName,  inputState, inputObject)
 if inputState == Enum.UserInputState.Begin then
  RemoteEvent:FireServer("G")
 end 
end
contextActionService:BindAction("RPress", RPressed, true, Enum.KeyCode.R) 
contextActionService:BindAction("GPress", GPressed, true, Enum.KeyCode.G) 
// 서버 스크립트
local remoteEvent = game.ReplicatedStorage.ColorEvent

remoteEvent.OnServerEvent:Connect(function(plr, key)
 if key == "R" then
  workspace.Part.BrickColor = BrickColor.Red()
 elseif key == "G" then
  workspace.Part.BrickColor = BrickColor.Green()
 end 
end)

리모트 이벤트의 매개변수로는 바인더블 이벤트와 마찬가지로 문자로 인덱싱된 테이블과 메타테이블은 보낼 수 없다.
또한 리모트 이벤트는 return을 사용해 값을 반환할 수는 있지만, 해당 값이 로컬 스크립트에 전송되지는 않는다.


14.팁(메모, 검색 기능)

주석

Lua스크립트의 한 줄 주석은 -- (주석글)으로 작성한다.
Lua스크립트의 여러줄 주석은 --[[ (주석글) ]]으로 작성한다

'ctrl' + '/' 키보드 단축키로 선택한 스크립트 라인을 즉시 주석처리할 수 있다.

검색


위의 사진과 같이 스크립트의 특정 단어들을 찾거나 바꿀 수 있게 하는 기능을 제공한다.
찾기의 키보드 단축키는 'ctrl' + 'F', 바꾸기의 키보드 단축키는 'ctrl' + 'H'이다.

또한 'ctrl' + 'shift' + 'F'를 이용해 모두 찾기/바꾸기를 수행할 수 있다.


15.GetServeice(), Debris

GetServeice()

만약 게임의 Players 개체에 접근하려 하면 다음과 같은 방법으로 접근할 수 있다.

  • game.Players
  • game:GetService("Players")

이 때 game:GetService()함수를 사용해 개체에 접근하면, 탐색기에 보이지 않는 개체들까지 접근할 수 있다. 이들을 '서비스'라 부른다.
탐색기에 보이지 않지만 game:GetService()함수를 사용해 접근하는 개체들의 예시는 다음과 같다.

  • game:GetService("MarketPlaceService") : 게임패스 판매 등 수익창출 관련 개체
  • game:GetService("ContextActionService") : 마우스/키보드 등 플레이어 입력 관련 개체

Debris

Debris는 '파편'이라는 뜻으로 게임내 개체들을 제거해주는 서비스이다.
Debris:AddItem(삭제할 개체, 시간)으로 사용한다.
workspace.Part개체를 삭제하는 스크립트 예시는 다음과 같다..

local debris = game:GetService("Debris")
debris:AddItem(workspace.Part)

Debris 서비스를 이용해 생성 2초 후에 제거되는 파트 생성 스크립트 예시는 다음과 같다.

local part = game.ServerStorage.Part
local debris = game:GetService("Debris")
for i=1, 50 do 
   local clone = part:Clone()
   clone.Parent = workspace
   clone.BrickColor = BrickColor.Black()
   debris:AddItem(clone, 2)
   wait()
end

16.간이 조건문, 삼항연산자

간이 조건문

local 변수 = a or b는 a에 값이 선언되어 있을 시 변수에 a의 값을, 아닐 시 b의 값을 추가하는 간이 조건문이다.

workspace에 파트가 존재할 시 해당 파트를 변수에 할당하고, 파트가 존재하지 않으면 새 파트를 생성하는 간이 조건문 예시는 다음과 같다.

local Part = workspace:FindFirstChild("Part") or Instance.new("Part", workspace)

로컬 스크립트에서 워크스페이스에 플레이어 캐릭터가 존재할 시 해당 캐릭터를 변수에 할당하고, 존재하지 않으면 플레이어 캐릭터가 생성될 때까지 기다리는 간이 조건문 예시는 다음과 같다.

local Player = game.Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()

-- if-else문으로 작성할 시의 스크립트
local Char
if Player.Character then
 Char = Player.Character
else
 Char = Player.CharacterAdded:Wait()
end

삼항연산자

local 변수 = 조건 and a or b의 삼항연산자를 사용할 수 있다.
이 때 조건이 true이면 a를 , false이면 b를 변수로 지정하게 된다.


17.break로 반복문 중단시키기

break를 이용해 for반복문 혹은 while반복문 중간에 특정 조건을 만족할 시 반복문을 중단 및 탈출할 수 있다.

for반복문의 break 예시는 다음과 같다.

for i=1, 10 do
   wait(0.3)
   print(i)
   if i ==5 then
      break
   end
end

while반복문의 break 예시는 다음과 같다.

while true do
   wait()
   workspace.Part.BrickColor = BrickColor.Random()
   if workspace.Part.BrickColor == BrickColor.new("Really red") then
      break
   end
end

18.CFrame Angles 각도 회전

오브젝트를 원하는 각도로 회전시키기 위해서는 CFrame.Angles()를 사용한다.

-- 오브젝트를 (1, 1, 1)각도로 회전
workspace.Part.CFrame = CFrame.Angles(1,1,1)
-- 오브젝트를 (5, 5, 5)위치로 이동시킨 후 (1, 1, 1)각도로 회전
workspace.Part.CFrame = CFrame.new(5,5,5) * CFrame.Angles(1,1,1)

CFrame.Angles()는 360도 기준이 아닌 라디안 기준의 값을 입력해야 한다.
따라서 오브젝트를 x축으로 30도 회전되게 설정하기 위해서는 다음과 같이 작성해야 한다.

workspace.Part.CFrame = CFrame.new(5,5,5) * CFrame.Angles(math.rad(30),0,0)

스크립트를 반복해서 실행할 때마다 각도 회전을 추가하기 위해서는 다음 예시와 같이 현재의 CFrame값에 원하는 각도를 곱해주면 된다.

workspace.Part.CFrame = workspace.Part.CFrame * CFrame.Angles(math.rad(30),0,0)

스크립트를 실행할 때마다 개체의 위치가 변경되게 하고 싶지 않으면, 개체의 현재 위치값만 가져와 다시 대입해 주면 된다.

workspace.Part.CFrame = CFrame.new(workspace.Part.CFrame.Position) * CFrame.Angles(math.rad(30),0,0)

19.math 모듈

math는 Lua의 수학 모듈로 여러 함수들이 내장되어 있다.
그 중에 입력 범위 내 무작위 값을 출력하는 math.random함수 또한 존재한다.

local n = math.random(1, 100) -- 1~100중 무작위 값이 n에 지정된다.

'매우 큰 수'를 뜻하는 math.huge값이 존재한다. 주로 wait(math.huge)처럼 작성해 스크립트를 무한대로 기다리게 하는 데에 사용한다.
혹은 오브젝트의 속성 중에 math.huge값을 도입할 수 있는 속성 또한 있다. 이들은 속성창에 'inf'라 표시된다.

원주율을 가리키는 math.pi가 존재한다. math.pi는 3.1415926535898까지 표시된다.
호, 구체 등 원과 관련된 계산을 할 때 자주 사용한다.

선택한 값들 중 가장 큰 값, 혹은 가장 작은 값을 반환해주는 math.max()함수와 math.min함수 또한 존재한다.

print(math.min(1, 5, 23, -26, 3, 34, 15, 5 ,1)) -- -26 출력
print(math.max(1, 5, 23, -26, 3, 34, 15, 5 ,1)) -- 34 출력

소수점 자릿수를 반올림해 정수로 반환해주는 math.round()함수도 존재한다.
올림 함수는 math.ceil(), 내림 함수는 math.floor()이다.


20.배열, array, table

배열의 기초

Lua의 배열은 다음과 같이 선언할 수 있다.

local array = {}

Lua의 배열은 한 배열 안에 여러 자료형의 값을 넣을 수 있다. 또한 배열 번호가 0이 아닌 1부터 시작한다.
배열의 수정은 C언어 계열과 동일하게 할 수 있다.

local array = {1234, 1222, "string", true}
print(array[2]) -- 1222출력
array[2] = 4366
print(array[2]) -- 4366출력

배열을 저장한 변수에 #을 붙이면 그 배열의 길이를 알 수 있다.

local array = {1234, 1222, "string", true}
print(#local) -- 4 출력

table.insert(배열, 배열 내 위치, 저장할 값)함수를 이용해 배열의 중간에 값을 추가할 수 있다.
이를 응용해 배열 맨 뒷자리에 값을 추가하는 스크립트 또한 작성할 수 있다.

-- 배열 중간에 값 추가하기
local array = {1234, "string", true}
table.insert(array, 2, 4355) -- 추가할 배열, 추가할 위치, 추가할 값 순으로 입력

-- 배열 맨 뒷자리에 값 추가하기
local array = {1234, "string", true}
table.insert(array, #array + 1, 4355) -- 맨 뒷자리에 추가
table.insert(array, 4355) -- 맨 뒷자리에 추가하는 경우 위치 생략 가능

table.remove(배열, 배열 내 위치)함수를 이용해 배열의 특정 위치에 있는 값을 삭제할 수 있다.
또한 nil을 이용해 배열 내 특정 위치의 값을 공백으로 남길 수 있다.

-- 배열 안의 값 삭제하기
local array = {1234, "string", true}
table.remove(array, 2)

-- 배열 안의 값 삭제하고 그 자리 공석으로 남기기
local array = {1234, "string", true}
array[2] = nil

배열 심화

Lua의 배열은 배열의 범위를 넘어가는 값을 지정하고 출력을 요구하면 nil값을 출력한다.

배열 안에 배열을 저장할 수 있다.

local array = {1234}
local array2 = {array}
print(array2[1][1]) -- 1234 출력

배열 안에 함수를 추가할 수 있다.

local array = {function()
 print("aaaaa")
end}
array[1]() -- aaaaa 출력

21.GetChildren(), 플레이어 목록 구하기

GetChildren()함수는 개체의 자식 개체들을 통합해서 배열로 반환해준다.
이때 스크립트, 모듈 등 모든 자식 개체들을 종류에 관계없이 반환해준다.

local model = script.Parent
local parts = model:GetChildren()

GetDecendent()함수는 2중, 3중으로 자식에 속해있는 자식 개체들까지 통합해서 배열로 반환해준다.

local model = script.Parent
local parts = model:GetDescendants()

Players 개체는 GetChildren함수보단 GetPlayers()함수를 통해 자식으로 소환되어 있는 캐릭터 개체를 구하는 것이 낫다.

local parts = game.Players:GetPlayers()

GetChildren()계열의 함수는 서로 다른 종류의 모든 자식 개체를 가져오기 때문에 반복문을 사용해 개체의 변수값을 일괄적으로 변경할 시 조건문을 활용해 각각의 자식 개체가 어떠한 개체 종류인지 확인해야 한다.
이 때 모든 개체에 사용할 수 있는 IsA()함수를 이용해 개체가 어떤 파트를 가지고 있는지 확인할 수 있다.

local model = script.Parent
local parts = model:GetChildren()

for i=1, #parts do
   if parts[i]:IsA("BasePart") then
      parts[i].CanCollide = false
      parts[i].BrickColor = BrickColor.Random()  
   end
end

Lua에서 C#의 'foreach'와 유사한 반복문을 사용할 수 있다.
위의 조건 반복 예시를 Lua의 foreach 형식으로 변경하면 다음과 같다.

for i, v in pairs(parts)do
   if v:IsA("BasePart") then
      v.CanCollide = false
      v.BrickColor = BrickColor.Random()  
   end
end

22.테이블, 딕셔너리

딕셔너리는 다음과 같이 선언 및 접근할 수 있다.

local dictionary = {
 aaa = "red";
 bb = "blue";
 c = "green";
}

print(dictionary["aaa"]) -- "red" 출력
print(dictionary.aaa) -- "red" 출력

딕셔너리의 키에는 모든 종류의 자료형이 들어갈 수 있다. 이미 변수로 선언된 값 또한 들어갈 수 있다. 단 nil값은 불가능하다.

local s= true
local dictionary = {
 aaa = "red";
 bb = "blue";
 c = "green";
 ["string"] = "blue";
 [s] = "yellow"
}

배열과 딕셔너리를 섞어 선언할 수도 있다.

local mixed = {"red"; ' blue'; 'green';
 aaa = "red";
 bb = "blue";
 c = "green";
}

배열의 foreach문은 다음과 같이 사용한다.

for i, v in ipairs(array)do
 print(i, v)
end

딕셔너리 혹은 혼합 배열의 foreach문은 다음과 같이 사용한다.

for i, v in pairs(dictionary)do
 print(i, v)
end

ipairs()는 오직 순수 배열에만 작동하는 함수이기 때문에 딕셔너리에 적용할 시 아무 것도 반환하지 않는다.
또한 배열 중간에 nil값이 있는 경우에 ipairs()nil값 이후의 값들은 반환을 하지 않는다.
하지만 ipairs()pairs()보다 효율적인 함수이다.

딕셔너리에는 배열에서 쓰던 remove, insert, #을 사용할 수 없다.


23.전역변수? Value 개체

로블록스에는 다음과 같이 생성할 수 있는 Value 개체가 있다.

로블록스에서는 이와 같이 Value개체에 값을 저장하고, 이 값을 스크립트들이 접근하는 방식으로 전역 변수처럼 사용한다.
Value 개체를 스크립트에서 접근 및 사용하는 방식은 다른 일반적인 개체들과 동일하다.

---스크립트A
local numValue = workspace.Num
numValue.Value = 19

또한 이렇게 설정한 값을 다른 스크립트에서 접근할 수 있다.

---스크립트B
wait(1)
print(workspace.Num.Value)

Value 개체 또한 다른 개체처럼 이벤트를 사용할 수 있다.

local numValue = workspace.Num

-- numValue 개체 내 value값이 변경될 때 호출됨
numValue.Changed:Connect(function(num)
   print(num)
end)

24.CFrame.lookAt(), 특정 파트 바라보게 하기

CFrame.lookAt()함수로 해당 오브젝트를 특정 오브젝트의 방향으로 회전시킬 수 있다.

local part = workspace.Part
local redPart = workspace.RedPart

part.CFrame = CFrame.lookAt(part.Position, redPart.Position) 

이를 이용해 나만을 바라보는 npc 머리 스크립트는 다음과 같이 작성할 수 있다.

local npcHead = workspace:WaitForChild("Head")
local MyHead = script.Parent:WaitForChild("Head")

while wait() do
   npcHead.CFrame = CFrame.lookAt(npcHead.Position, MyHead.Position)
end

CFrame.Rotation은 파트의 회전각이다.
이를 이용해 redPart가 part와 동일한 회전도가 되게 하는 방식은 다음과 같다.

local part = workspace.Part
local redPart = workspace.RedPart

redPart.CFrame = CFrame.new(redPart.Position) * part.CFrame.Rotation
profile
학습한 내용을 빠르게 다시 찾기 위한 저장소

0개의 댓글