프로젝트 개요
LuaFlow는 Lua 스크립트 기반의 Unity 컷신 시스템입니다. UniTask와 Lua-CSharp를 기반으로 구축되었으며, JSON 기반 컷신 관리의 불편함을 해결하기 위해 Lua 스크립팅을 도입한 혁신적인 시스템입니다.
Note
이 시스템은 Lilium 프로젝트의 일부에서 파생되었으며, MIT 라이센스로 오픈소스화되었습니다 !
최신 릴리즈: v1.4.0 (2025년 6월 25일) | 라이센스: MIT
개발 동기
Unity Inspector에서 JSON 기반으로 컷신을 관리하는 것이 너무 번거롭고 복잡해서, 대안을 찾던 중 Lua 스크립팅을 발견했습니다. 컷신 관리에 Lua를 사용하는 것이 간지가 날것 같아서 이 시스템을 만들게 되었습니다!
Lilium 프로젝트의 실제 컷신 예시
LuaFlow를 사용한 Lilium 프로젝트의 실제 컷신 코드입니다:
--- Chap1-01 -> Chap1-02 Cutscene
-- 필요한 게임 오브젝트들의 참조를 가져옴local player = get("Player")local camera1 = get("CameraMove_1")local camera2 = get("CameraMove_2")
function playCutscene() -- camera1을 0.5 속도로 따라가기, 도착까지 대기 camera1:camera():follow(0.5, true)
-- 화면 페이드 아웃 camera1:cinematic():fadeOut() camera2:camera():follow(0.03, true)
-- 플레이어 움직임 정지 함수 실행 player:action():exec("PlayerMoveStop")
-- 챕터 전환 이벤트 호출 player:event():exec("MovePlayerToNextChapter")
-- 플레이어 방향을 오른쪽으로 플립 player:animation():flip(true) player:camera():follow(1, true)
-- 플레이어 낙하 애니메이션 재생 player:animation():play("FallDown")
-- 화면 페이드 인 player:cinematic():fadeIn()
-- 3초 대기 wait(3.0)
-- 플레이어 일어나는 애니메이션 재생 및 완료까지 대기 player:animation():play("GetUp", true) player:animation():play("Idle")
-- 플레이어 제어 함수 재활성화 player:action():exec("PlayerMoveStart")end
핵심 아키텍처
Interface 기반 설계
LuaFlow는 Interface 기반 설계를 통해 높은 유연성과 확장성을 제공합니다.
// Interface/ICameraManager.cspublic interface ICameraManager{ Vector3 PositionOffset { get; set; } Transform transform { get; } void ChangeCameraTarget(Transform target, float followSpeed = 0.1f);}
public class CameraManager : MonoBehaviour, ICameraManager{ public static CameraManager Instance { get; private set; }
private void Awake() { if (Instance == null) { Instance = this; LuaFlowServiceLocator.Register<ICameraManager>(this); DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } }
// ICameraManager 인터페이스 구현 // ...}
Service Locator 패턴
LuaFlowServiceLocator
는 다양한 매니저 인스턴스에 접근하기 위한 중앙 레지스트리를 제공합니다.
// 매니저 클래스의 Awake 메소드에서LuaFlowServiceLocator.Register<ICameraManager>(this);
// 매니저가 파괴될 때 등록 해제private void OnDestroy(){ LuaFlowServiceLocator.Unregister<ICameraManager>();}
// 어디서든 등록된 매니저 인스턴스에 접근ICameraManager cameraManager = LuaFlowServiceLocator.Get<ICameraManager>();cameraManager.ChangeCameraTarget(targetTransform, 0.5f);
주요 시스템
Game Entity 관리
GameEntityManager
는 다양한 씬에서 게임 오브젝트를 중앙에서 등록, 접근, 관리할 수 있는 방법을 제공합니다.
// 게임 오브젝트 등록 (일반적으로 Awake나 Start에서)GameEntityManager.RegisterGameObject("Player", playerGameObject);
// 등록된 게임 오브젝트 가져오기GameObject player = GameEntityManager.Instance.GetGameObject("Player");
// 더 이상 필요하지 않을 때 게임 오브젝트 등록 해제GameEntityManager.UnregisterGameObject("Player");
-- Lua에서 등록된 게임 오브젝트 가져오기local player = get("Player")
Custom Events 시스템
LuaCustomEventManager
는 C# 스크립트와 Lua 스크립트 간의 이벤트 시스템을 제공합니다.
// 매개변수 없는 이벤트 구독LuaCustomEventManager.Subscribe("PlayerDied", () => { Debug.Log("Player died event received!");});
// 매개변수가 있는 이벤트 구독LuaCustomEventManager.Subscribe<int>("ScoreChanged", (score) => { Debug.Log($"Score changed to: {score}");});
-- 매개변수 없는 이벤트 발행myObject:event():exec("PlayerDied")
-- 매개변수가 있는 이벤트 발행myObject:event():execP("ScoreChanged", 100)
Custom Actions 시스템
LuaCustomActionManager
는 Lua 스크립트에서 호출할 수 있는 C# 함수를 등록하는 시스템을 제공합니다.
// 매개변수 없는 간단한 함수 등록LuaCustomActionManager.RegisterFunction("ShowGameOver", () => { gameOverPanel.SetActive(true);});
// 매개변수가 있는 함수 등록LuaCustomActionManager.RegisterFunction<int>("UpdateHealth", (health) => { playerHealth.SetHealth(health);});
// 비동기 함수 등록 (UniTask 사용)LuaCustomActionManager.RegisterAsyncFunction("FadeToBlack", async () => { await fadeScreen.FadeToBlackAsync(2.0f);});
-- 매개변수 없는 등록된 함수 실행myObject:action():exec("ShowGameOver")
-- 매개변수가 있는 등록된 함수 실행myObject:action():exec("UpdateHealth", 50)
-- 비동기 함수 실행 (두 번째 매개변수가 true면 완료까지 대기)myObject:action():execAsync("FadeToBlack", true)
설치 및 사용법
Unity Package Manager를 통한 설치
- Package Manager 창을 엽니다 (Window > Package Manager)
- 좌측 상단의 ”+” 버튼을 클릭합니다
- “Add package from git URL…”을 선택합니다
https://github.com/Snow0406/LuaFlow.git?path=Assets/LuaFlow
를 입력합니다- “Add” 버튼을 클릭합니다
Note
패키지 종속성(Lua-CSharp 및 UniTask)은 package.json에 정의되어 있어 자동으로 설치됩니다.
컷신 생성하기
Assets/Cutscene/Chap{X}/
디렉토리에 새 Lua 스크립트를 생성합니다- 다음 템플릿을 사용하여 시작합니다:
--- Test Cutscene
-- GameEntityManager에 등록된 게임 오브젝트들의 참조 가져오기local tg1 = get("Target1")local tg2 = get("Target2")
-- 메인 컷신 함수function playCutscene() log("Test Cutscene Start")
-- 2초 대기 wait(2.0)
-- 카메라가 Target1을 부드럽게 따라가도록 전환 tg1:camera():follow(0.03, true)
-- 카메라가 Target2를 따라가도록 전환 tg2:camera():follow(0.03, true)
log("Test Cutscene End")end
컷신 트리거하기
CutsceneTrigger
컴포넌트를 사용하여 컷신을 트리거할 수 있습니다:
- 씬에 빈 GameObject를 생성합니다
- CircleCollider2D 컴포넌트를 추가합니다
CutsceneTrigger
스크립트를 추가합니다- Chapter와 Cutscene Name 필드를 설정합니다
- 플레이어가 트리거 영역에 들어가면 컷신이 재생됩니다
시스템 확장하기
커스텀 커맨드 추가
새로운 커맨드 클래스를 생성하여 LuaFlow를 확장할 수 있습니다:
[LuaObject]public partial class LuaDialogueCommand : BaseLuaCommand{ public LuaDialogueCommand(GameObject targetObject) : base(targetObject) { }
[LuaMember("say")] public void Say(string text) { // 구현 내용 }}