룸 정의 레퍼런스
stateMachine 룸에서는 GS2-StateMachine에 등록한 룰(GSL)을 룸 안에서 실행하여, 공유 오브젝트의 생성·갱신, 조작 검증, 시간 경과 처리를 수행합니다. 이 페이지에서는 그 룰을 작성하기 위한 사양을 정리합니다.
룰은 C#으로 작성하여 GSL을 생성하는 것이 기본입니다. C# 기술 형식(상태 record, Step 등)은 StateMachine과 공통이므로 C# 정의 레퍼런스도 함께 참조해 주세요. 절차를 따라 시험해 보려면 튜토리얼을 참조해 주세요.
룰의 전체 구조
| 대상 | 배치 위치 | 예 |
|---|---|---|
| 룰 | GS2-StateMachine의 StateMachineMaster(버전 관리된다) | 적의 생성, 공격 검증, 부활 |
| 룸의 진행 상태 | 룰의 상태 record. 클라이언트에는 배포되지 않는다 | 플로어 번호, 적의 오브젝트 ID |
| 공유하는 값 | 공유 오브젝트. 모든 참가자에게 배포된다 | 위치, HP, 보스의 페이즈 |
| 표시 | Unity의 GameObject | HP 바, 위치 보간 |
룸의 상태와 공유 오브젝트는 룸이 동작하는 동안에만 유지됩니다. 룸이 끝나면 사라지며, 영구 저장되지 않습니다.
C#으로 작성하는 방법
룰 클래스에는 다음 3종류의 메서드를 작성합니다.
| 메서드 | 호출되는 시점 |
|---|---|
public static Step Start() | 룸 시작 시 한 번 |
public static Step Interact(TState state, int objectId, int method) | 플레이어가 SharedObject.Interact(method)를 전송했을 때 |
public static Step Timer(TState state, int objectId, int eventId) | Room.After로 예약한 시각이 되었을 때 |
public static Step Tick(TState state, int tick) | DefineTickRate로 지정한 간격마다(틱) |
Start에서는 공유 오브젝트의 종류를 정의하고 초기 오브젝트를 생성한 후, Step.WaitFor(nameof(Interact), state).Or(nameof(Timer))로 입력을 기다립니다. Interact와 Timer는 처리 후에 Step.Stay(state)로 같은 입력을 계속 기다립니다.
플레이어가 전송할 수 있는 조작은 Interact뿐입니다. 조작의 종류는 method 값으로 구분합니다. C#에 public 메서드를 추가해도 클라이언트에서 호출할 수 있게 되지는 않습니다.
생성 설정에서는 hostProfile에 RealtimeRoomV1을 지정합니다. 이 프로파일에서는 GameRewards(보상)와 GameRandom(난수)을 사용할 수 없습니다.
Room API
C#에서는 Room.*, GSL의 Lua에서는 같은 이름의 room.*을 호출합니다. 값은 모두 int32입니다.
오브젝트의 종류 정의하기
룸의 초기화 중(Start부터 첫 입력 대기까지)에만 호출할 수 있습니다.
| API | 내용 |
|---|---|
DefineServerPrefab<TSchema>() | 동기화 스키마 TSchema의 종류를 서버가 관리하는 오브젝트로 정의한다 |
DefineLeasedPrefab<TSchema>(leaseMilliseconds) | 동기화 스키마 TSchema의 종류를 플레이어에게 조작 권한을 대여하는 오브젝트로 정의한다 |
DefineServerPrefab(prefab, fields) | Prefab ID와 동기화할 필드의 비트 마스크를 직접 지정하여 정의한다 |
DefineLeasedPrefab(prefab, fields, leaseMilliseconds) | 위와 동일. 조작 권한의 대여 기간을 밀리초로 지정한다 |
DefineSimulatedPrefab<TSchema>() / DefineSimulatedPrefab(prefab, fields) | 플레이어가 생성하고 룰이 틱마다 움직이는 오브젝트로 정의한다(틱) |
DefineRuleSpawnedPrefab<TSchema>() / DefineRuleSpawnedPrefab(prefab, fields) | DefineSimulatedPrefab과 같지만 플레이어는 생성할 수 없고, 룰의 SpawnOwned만 생성한다(탄 등) |
DefineTickRate(ticksPerSecond) | Tick을 초당 몇 번 실행할지 지정한다(1~60) |
DefineHitbox<TSchema>(radius) / DefineHitbox(prefab, radius) | 그 종류의 오브젝트에 판정 영역(반경 radius의 구. 2D에서는 원)을 붙인다. DefineTickRate 다음에 호출한다(지연 보상) |
DefineRelevance<TSchema>(distance) / DefineRelevance(prefab, distance) | 그 종류의 오브젝트의 갱신을 받는 플레이어로부터 멀수록 줄여서 보낸다. DefineTickRate 다음에 호출한다(전송 빈도) |
| 관리 방법 | 값을 변경할 수 있는 것 | 용도의 예 |
|---|---|---|
| 서버 관리 | 룰의 Room.Set뿐 | 적의 HP, 문, 보스의 페이즈, 스코어 |
| 조작 권한 대여 | 조작 권한을 얻은 1개의 접속뿐. 룰에서는 변경할 수 없다 | 들고 옮기는 상자, 패들, 조작석 |
| 시뮬레이션 | 룰의 Room.Set뿐. 소유한 플레이어는 입력을 보낸다 | 플레이어 캐릭터, 탈것 |
오브젝트를 생성하고 값 읽고 쓰기
| API | 내용 |
|---|---|
Spawn<TSchema>(x, y, z) / Spawn(prefab, x, y, z) | 오브젝트를 생성하고 오브젝트 ID를 반환한다. x y z는 위치 필드의 초기값이며, 다른 필드는 0에서 시작한다 |
SpawnOwned<TSchema>(ownerObjectId, x, y, z) / SpawnOwned(prefab, ownerObjectId, x, y, z) | Simulated 오브젝트를 ownerObjectId 오브젝트를 소유한 플레이어의 소유물로 생성한다. 그 플레이어의 입력으로 움직인다(탄이나 리스폰에 사용). 그 플레이어가 이미 64개를 소유하고 있으면 생성하지 않고 0을 반환한다 |
Despawn(objectId) | 서버 관리·대여·Simulated 오브젝트를 삭제하고 그 타이머도 취소한다. 플레이어 오브젝트는 삭제할 수 없다 |
Get(objectId, field) | 필드의 값을 읽는다 |
Set(objectId, field, value) | 필드의 값을 쓴다. 서버 관리 오브젝트와 Simulated 오브젝트만 |
field에는 동기화 스키마의 enum 값(EnemyFields.Health 등)이나 RoomFields.PositionX 등의 표준 필드를 지정합니다. 룰 안에서 읽고 쓴 값은 같은 입력의 처리 안에서 즉시 Get에 반영됩니다.
위치 값은 단위 간격으로 나눈 정수입니다. 표준 단위 간격(0.01)에서는 50이 Unity의 0.5에 해당합니다.
시간 경과로 처리하기
| API | 내용 |
|---|---|
After(objectId, milliseconds, eventId) | milliseconds 후에 Timer(state, objectId, eventId)를 한 번만 호출하도록 예약한다 |
CancelTimer(objectId) | 해당 오브젝트의 예약을 취소한다 |
예약은 1개의 오브젝트당 1개이며, 같은 오브젝트에 After를 호출하면 예약을 대체합니다. 일정 간격으로 반복하는 처리는 Timer 안에서 다음 After를 예약합니다. 적의 행동 간격, 문이 닫힐 때까지의 시간, 부활까지의 대기 시간 등에 사용합니다.
타이머는 룸 안에서 약 50밀리초마다 처리됩니다. Unity의 렌더링 프레임과는 동기화되지 않으며, 정확한 시각에서의 실행을 보장하지는 않습니다. 발화한 타이머는 룰이 거부·실패하더라도 소비됩니다.
일정 간격으로 시뮬레이션하기(틱)
초기화에서 DefineTickRate를 호출하면, 룸은 초당 정해진 횟수만큼 Tick(state, int tick)을 실행합니다. 플레이어 캐릭터의 이동 등을 서버가 입력으로부터 계산하는 경우에 사용합니다. 클라이언트는 위치를 쓸 수 없으므로 이동 속도 변조나 벽 통과를 막을 수 있습니다.
| API | 내용 |
|---|---|
Input(objectId, field) | Simulated 오브젝트의 소유자가 현재 틱용으로 보낸 입력 값을 읽는다(슬롯 0~31). Simulated 이외의 오브젝트를 지정하면 오류가 된다 |
ObjectCount<TSchema>() / ObjectCount(prefab) | 그 종류의 오브젝트 수(이 처리에서 생성한 것을 포함) |
ObjectAt<TSchema>(index) / ObjectAt(prefab, index) | 그 종류의 index번째 오브젝트 ID(ID가 작은 순) |
// Position is the standard XYZ (fields 0..2, 0.01 units); custom fields start at 6.
[RoomSchema(5, StandardFields = RoomSync.Position)]
public enum Character { Hits = 6 }
public static Step Start() {
Room.DefineTickRate(30);
Room.DefineSimulatedPrefab<Character>();
return Step.WaitFor(nameof(Tick), new State(0));
}
public static Step Tick(State state, int tick) {
int count = Room.ObjectCount<Character>();
for (int i = 0; i < count; i++) {
int id = Room.ObjectAt<Character>(i);
int dx = System.Math.Max(-1, System.Math.Min(1, Room.Input(id, 0)));
int dy = System.Math.Max(-1, System.Math.Min(1, Room.Input(id, 1)));
Room.Set(id, RoomFields.PositionX, Room.Get(id, RoomFields.PositionX) + dx * 10);
Room.Set(id, RoomFields.PositionY, Room.Get(id, RoomFields.PositionY) + dy * 10);
}
return Step.Stay(state);
}Tick의 인수 이름은tick(1부터 시작하는 틱 번호)으로 고정입니다.DefineTickRate를 사용하는 정의에Tick이 없으면 컴파일 오류가 됩니다.Tick을 받아들이는 입력 대기 동안에만 실행됩니다.Interact나Timer와 함께 기다릴 수도 있습니다(.Or(nameof(Interact))). 시뮬레이션을 계속할 모든 입력 대기에Tick을 포함하세요. 포함하지 않는 동안에도 틱 번호는 진행됩니다.Tick이 오류가 된 틱은 아무것도 변경되지 않으며, 다음 틱은 평소대로 실행됩니다.DefineSimulatedPrefab의 종류는 플레이어 오브젝트와 마찬가지로 클라이언트가 생성하고 그 플레이어가 소유합니다. 클라이언트가 생성할 수 있는 것은 종류마다 하나까지이며, 요청에서 사용되는 것은 위치와 회전뿐입니다(다른 필드는 0에서 시작합니다). 값을 쓰거나 삭제할 수 있는 것은 룰뿐이며, 소유자의 쓰기와 삭제는 거부됩니다. 소유자가 퇴장하면 삭제됩니다.Input·DefineHitbox·DefineRelevance·Raycast·Overlap을 사용하는 정의에DefineTickRate가 없으면 컴파일 오류가 됩니다.- 플레이어는 “어느 틱에 사용할 입력인지"를 붙여 입력을 보냅니다. 그 틱의 입력이 도착하지 않았으면 직전의 입력이 사용됩니다.
- 틱에서 변경한 값은 정기 전송(UDP를 사용할 수 있을 때는 UDP)으로 클라이언트에 전달됩니다.
- 처리가 늦어졌을 때는 한 번에 최대 3틱까지 모아서 실행하고 나머지는 건너뜁니다.
- 룰의 계산은 정수만 사용합니다. 위치는 단위 간격으로 나눈 정수(기본은 0.01 단위)로 다룹니다.
쏜 쪽의 화면대로 맞히기(지연 보상)
플레이어의 화면에 보이는 다른 캐릭터는 통신 지연과 보간만큼 조금 과거의 위치입니다. 그대로 판정하면 화면에서는 맞았는데 빗나갑니다. 클라이언트는 입력마다 “그때 화면이 얼마나 늦었는지"를 보내므로, 룸은 쏜 플레이어가 보고 있던 위치에서 판정할 수 있습니다.
public static Step Start() {
Room.DefineTickRate(30);
Room.DefineSimulatedPrefab<Character>();
Room.DefineHitbox<Character>(50); // DefineTickRate 다음
return Step.WaitFor(nameof(Tick), new State(0));
}
static void Fire(int id) {
if (Room.Input(id, 2) == 0) return;
int target = Room.Raycast<Character>(id,
Room.Get(id, RoomFields.PositionX), Room.Get(id, RoomFields.PositionY), 0,
Room.Input(id, 3), Room.Input(id, 4), 0, 2000);
if (target != 0) Room.Set(target, Character.Hits, Room.Get(target, Character.Hits) + 1);
}| API | 내용 |
|---|---|
Raycast<TSchema>(shooterId, x, y, z, dx, dy, dz, distance) | (x, y, z)에서 (dx, dy, dz) 방향으로 distance까지 나아가는 선에 처음 맞은 오브젝트의 ID. 없으면 0 |
Overlap<TSchema>(shooterId, x, y, z, radius) | 반경 radius의 구와 겹치는 가장 가까운 오브젝트의 ID. 없으면 0 |
- 판정 영역은 위치 필드(0~2번. 표준 Position)를 중심으로 한 구입니다. 2D 게임에서는 Z를 0으로 하면 원으로 다룰 수 있습니다.
shooterId가 Simulated 오브젝트이면, 다른 오브젝트는 그 소유자가 현재 틱의 입력을 보냈을 때 보고 있던 위치로 되돌려 판정합니다. 틱 사이는 보간합니다. 되돌릴 수 있는 것은 최대 500밀리초입니다.shooterId자신은 판정 대상이 되지 않습니다.shooterId에는 존재하는 오브젝트를 지정합니다(0은 지정할 수 없습니다).- 쏘는 위치와 방향은 인수의 값이 그대로 사용됩니다. 쏘는 쪽의 현재 위치를 전달하세요(쏘는 쪽의 위치는 소유자의 클라이언트가 예측하므로 화면과 일치합니다).
[Predict]처리에서는 호출할 수 없습니다. 클라이언트에서는 이펙트만 먼저 표시하고, 대미지는 룸의 결과가 도착한 후에 반영하세요.
먼 오브젝트의 갱신 줄이기
인원이 많은 룸에서는 모든 사람의 위치를 모든 사람에게 매 틱 보내면 통신량이 커집니다. DefineRelevance를 선언하면 그 종류의 오브젝트의 갱신은 받는 플레이어의 캐릭터로부터 멀수록 적은 빈도로 도착합니다.
Room.DefineRelevance<Character>(300); // DefineTickRate 다음| 받는 플레이어의 캐릭터로부터의 거리 | 전송 빈도 |
|---|---|
distance 이내 | 매 틱 |
| 2배 이내 | 2틱마다 |
| 4배 이내 | 4틱마다 |
| 그보다 멀다 | 8틱마다 |
- 거리는 그 플레이어가 소유한 Simulated 오브젝트 중 가장 가까운 것으로부터 위치 필드(0~2번)로 측정합니다. 자신의 캐릭터는 매 틱 도착합니다.
- 줄인 동안 바뀐 값은 다음에 보낼 때 최신 값으로 도착합니다. 표시는 보간되므로 먼 캐릭터도 부드럽게 움직입니다.
- Simulated 오브젝트를 갖지 않은 플레이어(관전 등)에게는 매 틱 보냅니다.
- 1초마다의 재전송과 RPC 등의 직전에 보내는 값은 줄이지 않습니다.
- 예: 64명이 20m 사방에 흩어져 모두 움직이는 경우(
distance300), 갱신의 통신량은 약 3분의 1이 됩니다.
클라이언트에서 먼저 움직이기(예측)
오브젝트 하나를 움직이는 처리를 private 보조 함수로 분리하고 [Predict(typeof(스키마))]를 붙이면, 소유자의 클라이언트도 그 처리를 실행하여 자신의 캐릭터를 입력한 순간에 움직일 수 있습니다.
public static Step Tick(State state, int tick) {
int count = Room.ObjectCount<Character>();
for (int i = 0; i < count; i++)
Move(Room.ObjectAt<Character>(i), tick);
return Step.Stay(state);
}
[Predict(typeof(Character))]
static void Move(int id, int tick) {
int dx = System.Math.Max(-1, System.Math.Min(1, Room.Input(id, 0)));
Room.Set(id, RoomFields.PositionX, Room.Get(id, RoomFields.PositionX) + dx * 10);
}- 대상 보조 함수는
private static void 이름(int objectId)또는(int objectId, int tick)형태로 합니다.[Predict]는 스키마 하나당 하나입니다. 룸에서는Tick에서 평소대로 호출됩니다. - 컴파일러는 이 보조 함수와 거기서 호출하는 보조 함수를 Unity용 C#(
unity/<룰 클래스 이름>Prediction.cs)으로도 출력합니다. 값의 계산은 룸과 같은 정수 연산이 됩니다. - 예측하는 처리에서 사용할 수 있는 것은 int와 bool 값,
System.Math.Min과Max, 룰 클래스의 보조 함수,Room.Get·Set·Input·ObjectCount·ObjectAt·SpawnOwned·Despawn입니다. 계산은 모두 int나 bool로 합니다(스키마의 필드는 이름으로 지정하며, enum으로의 캐스트는 할 수 없습니다). 그 밖의 것(float, 정적 필드, 상태state의 읽기 등)을 사용하면 컴파일 오류가 됩니다. - 예측하는 처리 안에서
SpawnOwned를 호출하면(탄 발사 등) 쏜 플레이어의 화면에는 탄이 바로 표시됩니다(예측 스폰). 룸이 생성한 탄이 도착하면 표시 중인 같은 오브젝트에 그대로 인계됩니다. 룸이 생성하지 않은 경우에는 사라집니다. 탄을 올바르게 대응시키기 위해SpawnOwned는 예측하는 처리 안에서만 호출하고, 판정은 룸에서만 실행하는 처리로 나누세요. - 클라이언트에서는 다른 플레이어의 입력은 0으로, 다른 오브젝트의 값은 룸에서 도착한 최신 값으로 계산합니다. 캐릭터끼리 부딪히는 경우 등에는 예측이 빗나갈 수 있지만, 룸의 결과로 보정됩니다.
조작한 사용자 확인하기
| API | 내용 |
|---|---|
SenderUserId() | 조작한 플레이어의 userId. 액세스 토큰으로부터 취득한 값이며, 클라이언트가 변경할 수 없다 |
SenderConnectionId() | 조작한 접속의 ID. 재접속하면 바뀐다 |
Start와 Timer에는 조작한 플레이어가 없으므로 SenderUserId()는 빈 문자열, SenderConnectionId()는 0을 반환합니다. 타이머 처리에서 「누가 시작한 조작인지」가 필요하다면 Interact 처리에서 상태 record에 저장해 둡니다.
플레이어별 소유물이나 순서를 관리할 때는 재접속해도 바뀌지 않는 SenderUserId()를 사용해 주세요.
동기화 스키마
공유 오브젝트의 동기화할 값은 RoomSchema 속성을 붙인 enum으로 선언합니다. 이 선언으로부터 룰의 정의와 Unity의 동기화 스키마(.realtimeschema)가 모두 생성됩니다.
[RoomSchema(7, StandardFields = RoomSync.Position)]
public enum EnemyFields
{
Health = 6,
}
[RoomSchema(8)]
public enum SwitchFields
{
[RoomField(typeof(bool))] IsOpen = 6,
[RoomField(typeof(float), Precision = 0.01f)] Progress = 7,
}| 항목 | 내용 |
|---|---|
RoomSchema(prefab) | Prefab ID. 양의 int32이며, 다른 동기화 스키마와 중복될 수 없다 |
StandardFields | 위치·회전의 표준 필드. RoomSync.Position(필드 0RoomSync.Rotation(3RoomSync.Transform(양쪽 모두) |
PositionPrecision / RotationPrecision | 표준 필드의 단위 간격. 기본값은 0.01과 360 / 4096도 |
| enum 값 | 통신상의 필드 번호(0~31). 표준 필드의 번호와 겹치지 않도록 6 이상을 사용한다 |
RoomField(typeof(...)) | Unity에서의 타입. int(기본값), bool, float |
Precision | float의 단위 간격. 룰에서는 정수로 다루고, Unity에서는 「정수 × 단위 간격」으로 받는다 |
위의 예에서 Progress에 125를 Set하면 Unity에서는 1.25f로 받습니다. bool에는 0 또는 1을 설정합니다.
필드 번호는 통신상의 식별자입니다. 릴리스한 후에 번호를 바꾸거나, 순서 변경으로 번호가 어긋나지 않도록 해 주세요.
입력 1회의 처리와 취소
1회의 입력(Interact 또는 Timer)으로 수행한 변경은 한꺼번에 확정하거나 한꺼번에 취소합니다.
| 룰의 결과 | 동작 |
|---|---|
Step.Next | 같은 입력의 처리로서 다음 처리로 진행한다 |
입력 대기 도달(WaitFor / Stay)·Step.End | 상태, 공유 오브젝트, 타이머의 변경을 확정하고 전원에게 배포한다 |
Step.Ignore | 이 입력으로 수행한 변경을 모두 취소한다. 이유는 클라이언트에 전달된다 |
| 예외, 실행 시간·전이 횟수의 상한 초과 | 이 입력으로 수행한 변경을 모두 취소한다. 클라이언트에는 InvalidOperation이 전달된다 |
Step.Ignore의 이유 값은 조작한 클라이언트의 CommandCompleted에 그대로 전달됩니다. 값 1~7은 시스템 결과 코드(MissingObject 등)와 같은 번호이므로, 이유에는 1000 이상의 값을 사용해 주세요. 클라이언트가 받는 결과 목록은 Unity 클라이언트를 참조해 주세요.
Step.End는 룰의 실행을 끝낼 뿐이며, 공유 오브젝트의 삭제나 룸의 종료는 수행하지 않습니다.
생성과 등록
| 순서 | 내용 |
|---|---|
| 생성 | project.json의 hostProfile에 RealtimeRoomV1을 지정하여 생성한다. 설정 항목은 C# 정의의 생성 설정을 참조 |
| 등록 | 생성한 main.gsl의 내용을 GS2-StateMachine의 UpdateStateMachineMaster의 payload에 지정한다. mainStateMachineName은 룰의 클래스명 |
| 룸에 설정 | Realtime 네임스페이스에서 서버 타입 stateMachine과 룰의 저장 위치 네임스페이스(stateMachineSetting.namespaceId)를 지정한다. 버전(stateMachineSetting.version)은 생략하면 최신을 사용하며, 고정하고 싶을 때만 지정한다 |
생성물은 다음과 같습니다.
| 파일 | 용도 |
|---|---|
main.gsl | 등록할 룰 |
package.zip | main.gsl만 넣은 배포용 파일. 등록할 때는 안의 main.gsl을 읽어 payload에 지정한다 |
unity/<스키마 이름>.realtimeschema | Unity의 NetworkEntity에 지정하는 동기화 스키마 |
source-map.json | 생성된 위치로부터 원래 C#을 찾기 위한 개발용 파일. 등록에는 사용하지 않는다 |
룸은 할당되었을 때 룰을 한 번만 읽어 들입니다. 버전을 생략한 경우에는 그 시점의 최신 룰을, 지정한 경우에는 해당 버전을 읽어 들입니다. 저장 위치에 룰이 1건도 등록되어 있지 않은 경우나, 읽기 또는 초기화에 실패한 경우에는 룸이 사용되지 않습니다. 동작 중인 룸은 끝까지 같은 버전을 사용하며, 새로운 룰은 그 이후에 할당되는 룸부터 사용됩니다.
룰(GSL)과 동기화 스키마는 같은 C# 정의로부터 동시에 생성한 것을 조합하여 배포해 주세요.
상한값
| 대상 | 상한 |
|---|---|
| 1룸의 참가자 | 64접속 |
| 1룸의 공유 오브젝트 | 4096개 |
| 오브젝트의 종류 | 초기화 중의 정의는 64종류까지. 룸 전체에서 4096종류까지 |
| 1개 오브젝트의 필드 | 32개(번호 0~31) |
| 조작 권한의 대여 기간 | 50밀리초~1일(86,400,000밀리초) |
After의 대기 시간 | 50밀리초~1일(86,400,000밀리초) |
After의 eventId | 1 이상 |
| 예약 중인 타이머 | 1오브젝트당 1개, 룸 전체에서 4096개 |
| 입력 1회당 변경 | 생성·값 변경은 각각 64오브젝트까지. 타이머 예약·취소는 64오브젝트까지 |
| 입력 1회당 처리 | 실행 시간 100밀리초, Next에 의한 전이 32회까지 |
| 1회의 처리 주기에서 실행하는 타이머 | 64개까지. 초과한 분은 다음 주기로 넘어간다 |
| 틱 빈도 | 초당 1~60회 |
| 1틱당 변경 | 값 변경·생성·삭제는 각각 1024오브젝트까지. 한 명이 소유할 수 있는 것은 64오브젝트까지 |
| 판정 되감기 | 500밀리초까지(위치 기록은 1초분) |
| 미리 보낼 수 있는 입력 | 1초 분량의 틱까지 |
| 룰(GSL)의 크기 | 1 MiB |
사용할 수 없는 기능
룸의 룰은 룸 안에서 완결되는 처리만 실행합니다. 다음 기능은 사용할 수 없습니다.
GameRewards(보상 부여),GameRandom(난수)- GSL의
Script(GS2-Script 호출) - 외부 API, 파일, 네트워크 접근
- Unity API나 임의의 .NET 라이브러리
룰에서 사용할 수 있는 C# 구문은 C# 정의 레퍼런스를 참조해 주세요.
GSL을 직접 작성하는 경우
C#을 사용하지 않고 GSL을 직접 작성할 수도 있습니다. GSL의 기본 서식은 GSL 포맷을 참조해 주세요. 룸에서 실행하는 GSL에는 다음 규칙이 있습니다.
상태 변수는
map run과map execution2개입니다.입력을 기다리는
WaitTask에서 받는 이벤트는 다음 시그니처로 합니다.이벤트 파라미터 Interactint revision, int objectId, int methodTimerint revision, int objectId, int eventIdTickint revision, int tickTask의 Payload에 작성한 Lua에서
room.Spawn등의 Room API를 호출합니다.Script에 의한 GS2-Script 호출은 사용할 수 없습니다.