몇 개의 스크립트를 만든 후에는 서로 간에 일부 코드를 재사용하고 싶어질 것입니다. 위치에 따라, ModuleScripts는 클라이언트-서버 경계의 서로 다른 쪽 또는 동일한 쪽에서 스크립트 간에 코드를 재사용할 수 있도록 해줍니다.
모듈 스크립트는 스크립트와 동일한 위치에 배치할 수 있지만, ReplicatedStorage는 인기 있는 위치입니다. 여기에서 모듈 스크립트를 저장하면 서버와 클라이언트 간에 코드를 재사용할 수 있습니다.
모듈 스크립트의 구조
Roblox Studio에서 ReplicatedStorage에 모듈 스크립트를 추가하고 이름을 PickupManager로 변경합니다. 각 ModuleScript는 다음 코드를 시작합니다:
local module = {}return module
이 코드는 빈 Luau 테이블을 생성하고 모듈 스크립트를 요구하는 모든 스크립트에 반환합니다.
반환 값은 nil을 제외한 모든 데이터 유형이 될 수 있지만, 대부분의 모듈 스크립트는 함수, 테이블 또는 함수의 테이블을 반환합니다. 모듈 스크립트는 물론 다른 모듈 스크립트를 요구하는 것을 포함하여 임의의 코드를 실행하여 반환 값을 생성할 수 있습니다.
다음 예제는 getPickupBonus라는 단일 함수를 가진 테이블을 반환합니다. 이를 새 모듈 스크립트에 붙여넣으세요:
-- ReplicatedStorage의 ModuleScript
local PickupManager = {}
local defaultMultiplier = 1.25
local rarityMultipliers = {
common = 10,
uncommon = 20,
rare = 50,
legendary = 100
}
-- PickupManager 테이블에 getPickupBonus 함수 추가
PickupManager.getPickupBonus = function(rarity)
local bonus = rarityMultipliers[rarity] * defaultMultiplier
return bonus
end
return PickupManager
함수를 테이블에 추가하는 것은 엄밀히 필요하지 않습니다. 단순히 함수 자체를 반환할 수도 있지만, 이를 따르면 이해하기 쉬운 구문을 제공하며 시간이 지남에 따라 모듈 스크립트에 더 많은 함수를 쉽게 추가할 수 있습니다.
모듈 스크립트 요구하기
모듈 스크립트를 로드하려면 require() 함수를 호출합니다. ReplicatedStorage에 새 스크립트를 추가하고 RunContext를 Client로 변경합니다. 그런 다음 PickupManager.getPickupBonus 함수를 호출하기 위해 다음 코드를 추가합니다:
ReplicatedStorage의 클라이언트 스크립트local ReplicatedStorage = game:GetService("ReplicatedStorage")-- ModuleScript가 반환한 값을 가져옵니다.local PickupManager = require(ReplicatedStorage:WaitForChild("PickupManager"))-- ModuleScript 함수 호출local bonus = PickupManager.getPickupBonus("legendary")print(bonus) --> 125
모듈 스크립트를 ReplicatedStorage에 저장하면 서버와 클라이언트 간에 코드를 공유할 수 있으므로, ServerScriptService에서 스크립트를 요구하는 데 같은 코드를 사용할 수 있습니다:
ServerScriptService의 스크립트local ReplicatedStorage = game:GetService("ReplicatedStorage")local PickupManager = require(ReplicatedStorage:WaitForChild("PickupManager"))
ModuleScript에서 require()를 호출하면 한 번 실행되고 참조로서 단일 항목을 반환합니다. require()를 다시 호출하면 정확히 동일한 참조가 반환되며, 반환된 테이블 또는 Instance를 수정하면 이후의 require() 호출은 수정된 참조를 반환합니다. 모듈 자체는 여러 번 실행되지 않습니다.
클라이언트-서버 경계의 양쪽에서 ModuleScript를 요구하면, 위의 예와 같이 ModuleScript는 각 쪽에 대해 고유한 참조를 반환합니다.
패턴
모듈 스크립트에는 게임의 크기와 복잡성이 증가함에 따라 코드를 단순화하고 함정을 피하는 데 사용할 수 있는 몇 가지 공통 패턴이 있습니다.
데이터 공유
개별 객체와 데이터를 연결하려면 속성을 할당하거나 Configuration 폴더를 생성하여 StringValue 또는 IntValue와 같은 값 객체를 사용할 수 있습니다. 하지만 이러한 접근 방식은 수십 개의 객체 또는 데이터 값을 추가하거나 수정하려고 할 때 어려움이 있습니다. 또한 테이블이나 함수를 저장할 수 없습니다.
동일한 데이터로 여러 복사본의 동일한 객체를 수정하거나 서로 다른 객체에 대해 동일한 데이터를 재사용하려면 ModuleScripts에 데이터를 저장하세요. 이는 다른 스크립트에서 데이터를 재사용하는 더 쉬운 방법이며, 테이블과 함수를 저장할 수 있습니다.
다음 예제 ModuleScript는 ReplicatedStorage에 있으며, 일반 총기의 구성 값을 저장합니다:
ReplicatedStorage의 ModuleScriptlocal GunConfig = {}GunConfig.MagazineSize = 20GunConfig.AmmoCount = 100GunConfig.Firerate = 600GunConfig.Damage = {["Head"] = 50;["Torso"] = 40;["Body"] = 25;}return GunConfig
사용자 정의 이벤트
사용자 정의 이벤트는 스크립트가 서로 통신할 수 있게 해주지만, 개별 BindableEvent 객체에 대한 참조를 유지해야 하는 것은 코드가 복잡해질 수 있습니다.
ModuleScripts를 사용하여 사용자 정의 이벤트를 저장하고 ModuleScript의 메서드에 직접 연결된 사용자 정의 이벤트 핸들러를 제공할 수 있습니다.
다음 ModuleScript는 ReplicatedStorage에 있으며, 스위치 상태가 변경될 때 발생하는 사용자 정의 이벤트를 포함하고 있습니다:
ReplicatedStorage의 ModuleScript
local Switch = {}
-- 스위치 상태 변경을 위한 Bindable 생성
local bindableEvent = Instance.new("BindableEvent")
Switch.Changed = bindableEvent.Event
local state = false
function Switch.flip()
state = not state
bindableEvent:Fire(state)
end
return Switch
다음 클라이언트 스크립트는 Switch.Changed 이벤트가 발생할 때 호출할 함수를 연결합니다.
ReplicatedStorage의 스크립트
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Switch = require(ReplicatedStorage:WaitForChild("Switch"))
Switch.Changed:Connect(function(newState)
print("스위치 상태가 이제", newState)
end)
-- 몇 번 스위치를 전환 테스트
task.wait(1)
Switch.flip()
task.wait(1)
Switch.flip()
캡슐화
캡슐화는 객체나 스크립팅 로직에 대한 추상화 계층을 만들어 복잡성을 숨기는 관행입니다. ModuleScripts를 사용하여 Robinlox 객체를 사용자 정의 Luau 함수로 캡슐화하여 코드를 단순화할 수 있습니다.
예를 들어, 캡슐화를 사용하여:
- 단일 RemoteEvent 객체로 네트워크 간 통신을 단순화합니다.
- DataStoreService와 같은 민감한 서비스에 대해 에러 처리 코드를 추가합니다.
- Roblox 객체 기능을 제어하거나 확장하기 위한 사용자 정의 메서드를 정의합니다.
게임에서 네트워크 구현을 위해 수십 개의 개별 RemoteEvent 객체를 추적하는 것은 어렵습니다. ModuleScript를 사용하여 단일 RemoteEvent 객체를 캡슐화하여 이 문제를 단순화할 수 있습니다. 고유한 id 인수를 포함시킴으로써 여전히 여러 네트워크 메시지를 보낼 수 있으면서 단일 RemoteEvent만 사용할 수 있습니다.
다음 예제에서 ModuleScript는 NetworkManagerClient라는 이름의 클래스로, RemoteEvent:FireServer() 메서드를 캡슐화하여 추가 id 인수를 포함합니다. 또한, 이 ModuleScript는 코드의 다른 부분에서 참조할 필요가 없도록 RemoteEvent 객체 자체를 참조합니다. 이 ModuleScript를 요구하기만 하면 네트워크 메시지를 보내고 나머지 코드베이스에서 RemoteEvent 객체를 처리할 필요가 없습니다.
다음 ModuleScript는 ReplicatedFirst에 있으며 클라이언트 스크립트에서 네트워크 메시지를 보내기 위해 호출할 수 있는 캡슐화된 함수를 제공합니다:
네트워크 모듈
-- ReplicatedFirst의 ModuleScript, 이름: NetworkManagerClient
local NetworkManagerClient = {}
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:WaitForChild("RemoteEvent")
-- 원격 객체의 FireServer 기능을 캡슐화
function NetworkManagerClient.FireServer(id, ...)
remoteEvent:FireServer(id, ...)
end
return NetworkManagerClient
다음 ModuleScript는 ServerScriptService에 있으며, 모든 스크립트가 특정 ID에 연결할 수 있도록 BindableEvents를 사용합니다. 클라이언트가 네트워크 메시지를 보내면 지정된 ID와 연결된 각 BindableEvent가 발생합니다.
-- ServerScriptService의 ModuleScript, 이름: NetworkManagerServer
local NetworkManagerServer = {}
local networkSignalList = {}
function NetworkManagerServer.GetServerEventSignal(id)
local bindableEvent = Instance.new("BindableEvent")
-- 새로운 BindableEvent를 id에 연결
table.insert(networkSignalList, {
id = id,
bindableEvent = bindableEvent,
})
return bindableEvent.Event
end
-- 연결
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteEvent = ReplicatedStorage:WaitForChild("RemoteEvent")
remoteEvent.OnServerEvent:Connect(function(player, id, ...)
-- 수신된 원격 이벤트의 ID와 일치하는 모든 바인더 이벤트 찾기
for _, signal in networkSignalList do
if signal.id == id then
signal.bindableEvent:Fire(player, ...)
end
end
end)
return NetworkManagerServer
다음 LocalScript는 ID RequestA로 메시지를 보내며 선택적으로 Hello 인수를 포함합니다.
-- ReplicatedFirst의 LocalScriptlocal ReplicatedFirst = game:GetService("ReplicatedFirst")local NetworkManagerClient = require(ReplicatedFirst:WaitForChild("NetworkManagerClient"))NetworkManagerClient.FireServer("RequestA", "Hello")
다음 Script는 네트워크 메시지 ID RequestA에 연결되고 요청을 받을 때 추가 매개변수와 함께 문장을 인쇄합니다.
-- ServerScriptService의 Script
local ServerScriptService = game:GetService("ServerScriptService")
local NetworkManagerServer = require(ServerScriptService:WaitForChild("NetworkManagerServer"))
NetworkManagerServer.GetServerEventSignal("RequestA"):Connect(function(player, ...)
print("Received RequestA from", player, ...)
end)