{"id":27475383,"url":"https://github.com/snow0406/luaflow","last_synced_at":"2025-07-10T16:34:21.173Z","repository":{"id":287262535,"uuid":"962635052","full_name":"Snow0406/LuaFlow","owner":"Snow0406","description":"Unity Cutscene System + Lua Script","archived":false,"fork":false,"pushed_at":"2025-06-25T20:35:58.000Z","size":392,"stargazers_count":22,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-25T21:29:59.294Z","etag":null,"topics":["cutscene","lua","unity"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Snow0406.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-08T12:56:47.000Z","updated_at":"2025-06-25T20:35:11.000Z","dependencies_parsed_at":"2025-06-25T21:23:13.394Z","dependency_job_id":"76c2a724-3d91-40d6-9ff5-a3d4393ebbc3","html_url":"https://github.com/Snow0406/LuaFlow","commit_stats":null,"previous_names":["snow0406/luaflow"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/Snow0406/LuaFlow","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Snow0406%2FLuaFlow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Snow0406%2FLuaFlow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Snow0406%2FLuaFlow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Snow0406%2FLuaFlow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Snow0406","download_url":"https://codeload.github.com/Snow0406/LuaFlow/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Snow0406%2FLuaFlow/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264608184,"owners_count":23636688,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cutscene","lua","unity"],"created_at":"2025-04-16T06:43:11.274Z","updated_at":"2025-07-10T16:34:21.167Z","avatar_url":"https://github.com/Snow0406.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n## LuaFlow\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-skyblue.svg?style=for-the-badge\u0026logo=github)](LICENSE)\n![GitHub Repo stars](https://img.shields.io/github/stars/snow0406/luaFlow?style=for-the-badge\u0026logo=github\u0026color=%23ef8d9d)\n\n[Korean Docs](README_KR.md)\n\nLuaFlow is a Lua script-based Unity cutscene system built with [UniTask](https://github.com/Cysharp/UniTask) and [Lua-CSharp](https://github.com/kevthehermit/Lua-CSharp).\n\n\u003e This system is taken from part of the [Lilium](https://hyuki.dev/project/lilium/) project!\n\n\u003c/div\u003e\n\n---\n\n## Motivation\n\nThe motivation behind LuaFlow came from the frustration with JSON-based cutscene management. Managing cutscenes in Unity's inspector was just too cumbersome and complex, so I looked for alternatives and found Lua scripting and JSON. I thought using Lua for cutscene management would be really stylish, so I created this system !!\n\n### Architectural Background\n\nLuaFlow is from the [Lilium](https://hyuki.dev/project/lilium/) project, which employs a Scene Adaptive architecture. This approach separates game elements into different scenes (e.g., Chapter-specific scenes, Player scene, Manager scene) that can be loaded and unloaded independently.\n\nThis architecture influences LuaFlow's design in several key ways:\n\n- **Interface-based Design**: System components interact through interfaces rather than concrete implementations.\n\n- **Service Locator Pattern**: Access various manager instances through `LuaFlowServiceLocator`.\n\n- **Centralized Entity Management**: Since different scenes contain different game objects, the `GameEntityManager` provides a way to register and access objects across scene boundaries.\n\n## Cutscene Example from Lilium Project\n\nHere's an actual cutscene from the [Lilium](https://hyuki.dev/project/lilium/) project using LuaFlow:\n\n```lua\n--- Chap1-01 -\u003e Chap1-02 Cutscene\n\n-- Get references to required game objects\nlocal player = get(\"Player\")\nlocal camera1 = get(\"CameraMove_1\")\nlocal camera2 = get(\"CameraMove_2\")\n\nfunction playCutscene()\n    -- Follow camera1 at 0.5 speed, wait until arrival\n    camera1:camera():follow(0.5, true)\n\n    -- Screen fade out\n    camera1:cinematic():fadeOut()\n    camera2:camera():follow(0.03, true)\n\n    -- Execute player movement stop function\n    player:action():exec(\"PlayerMoveStop\")\n\n    -- Invoke chapter transition event\n    player:event():exec(\"MovePlayerToNextChapter\")\n\n    -- Flip player direction to right\n    player:animation():flip(true)\n    player:camera():follow(1, true)\n\n    -- Play player fall animation\n    player:animation():play(\"FallDown\")\n\n    -- Screen fade in\n    player:cinematic():fadeIn()\n\n    -- Wait 3 seconds\n    wait(3.0)\n\n    -- Play player getting up animation and wait until completion\n    player:animation():play(\"GetUp\", true)\n    player:animation():play(\"Idle\")\n\n    -- Re-enable player control function\n    player:action():exec(\"PlayerMoveStart\")\nend\n```\n\n## Getting Started\n\n### Installation\n\nYou can install LuaFlow through UPM (Unity Package Manager):\n\n1. Open Package Manager window (Window \u003e Package Manager)\n2. Click the \"+\" button in the upper-left corner\n3. Select \"Add package from git URL...\"\n4. Enter `https://github.com/Snow0406/LuaFlow.git?path=Assets/LuaFlow`\n5. Click \"Add\"\n\nPackage dependencies (Lua-CSharp and UniTask) are defined in package.json and will be installed automatically.\n\n\u003e [Nuget Lua-CSharp v0.4.2](https://github.com/nuskey8/Lua-CSharp) is included as dll files.\n\n### Project Structure\n\n```\nAssets/\n├── Cutscene/\n    └── Chap{X}/             # Lua script files organized by chapter\n        └── {cutscene_name}.lua\n\nLuaFlow/\n├── Runtime/\n│   ├── Base/            # Basic interfaces and classes\n│   ├── Command/         # Command implementations (animation, camera, etc.)\n│   ├── Core/            # Core functionality\n│   ├── Entity/          # Game entity wrappers\n│   ├── Integration/     # Custom action and event systems\n│   ├── Interface/       # System component interfaces\n\n```\n\n## Architecture\n\n### Interface-based Design\n\n\u003e `LuaFlow` provides high flexibility and extensibility through interface-based design.\n\n1. **Interface Definition:**\n\n```csharp\n// Interface/ICameraManager.cs\npublic interface ICameraManager\n{\n    Vector3 PositionOffset { get; set; }\n    Transform transform { get; }\n    void ChangeCameraTarget(Transform target, float followSpeed = 0.1f);\n}\n```\n\n2. **Implementation Class:**\n\n```csharp\n// CameraManager.cs\npublic class CameraManager : MonoBehaviour, ICameraManager\n{\n    public static CameraManager Instance { get; private set; }\n\n    private void Awake()\n    {\n        if (Instance == null)\n        {\n            Instance = this;\n            LuaFlowServiceLocator.Register\u003cICameraManager\u003e(this);\n            DontDestroyOnLoad(gameObject);\n        }\n        else\n        {\n            Destroy(gameObject);\n        }\n    }\n\n    // ICameraManager interface implementation\n    // ...\n}\n```\n\nThis approach makes other parts depend on interfaces rather than concrete classes, making them easier to change or replace.\n\n### Service Locator Pattern\n\n\u003e `LuaFlowServiceLocator` provides a central registry for accessing various manager instances. \u003cbr/\u003e\n\u003e This reduces coupling between command classes and managers.\n\n1. **Manager Registration:**\n\n```csharp\n// In the manager class's Awake method\nLuaFlowServiceLocator.Register\u003cICameraManager\u003e(this);\n\n// Unregister when the manager is destroyed\nprivate void OnDestroy()\n{\nLuaFlowServiceLocator.Unregister\u003cICameraManager\u003e();\n}\n```\n\n2. **Manager Usage:**\n\n```csharp\n// Access the registered manager instance from anywhere\nICameraManager cameraManager = LuaFlowServiceLocator.Get\u003cICameraManager\u003e();\ncameraManager.ChangeCameraTarget(targetTransform, 0.5f);\n```\n\n## Usage\n\n### Game Entity Management\n\n\u003e The `GameEntityManager` provides a centralized way to register, access, and manage game objects across different scenes. \u003cbr/\u003e\n\u003e You need to implement this directly. [Example](Assets/Scripts/GameEntityManager.cs)\n\n```csharp\n// Register a game object (typically in Awake or Start)\nGameEntityManager.RegisterGameObject(\"Player\", playerGameObject);\n\n// Get a registered game object (can be called from anywhere)\nGameObject player = GameEntityManager.Instance.GetGameObject(\"Player\");\n\n// Unregister a game object when no longer needed\nGameEntityManager.UnregisterGameObject(\"Player\");\n\n// Clear all registered objects (e.g., when changing scenes)\nGameEntityManager.Instance.RemoveAllEntities();\n```\n\nIn Lua scripts, you can access registered game objects using the `get` function:\n\n```lua\n-- Get a registered game object in Lua\nlocal player = get(\"Player\")\n```\n\n### Custom Events System\n\n\u003e The `LuaCustomEventManager` provides an event system that enables events between C# scripts and Lua scripts.\n\n**In C#:**\n\n```csharp\n// Subscribe to an event without parameters\nLuaCustomEventManager.Subscribe(\"PlayerDied\", () =\u003e {\n    Debug.Log(\"Player died event received!\");\n});\n\n// Subscribe to an event with a parameter\nLuaCustomEventManager.Subscribe\u003cint\u003e(\"ScoreChanged\", (score) =\u003e {\n    Debug.Log($\"Score changed to: {score}\");\n});\n\n// Unsubscribe from events\nLuaCustomEventManager.Unsubscribe(\"PlayerDied\", myCallback);\nLuaCustomEventManager.Unsubscribe\u003cint\u003e(\"ScoreChanged\", myScoreCallback);\n```\n\n**In Lua:**\n\n```lua\n-- Publish an event without parameters\nmyObject:event():exec(\"PlayerDied\")\n\n-- Publish an event with a parameter\nmyObject:event():execP(\"ScoreChanged\", 100)\n```\n\n### Custom Actions System\n\n\u003e The `LuaCustomActionManager` provides a system that lets you register C# functions that can be called from Lua scripts.\n\n**In C#:**\n\n```csharp\n// Register a simple function with no parameters\nLuaCustomActionManager.RegisterFunction(\"ShowGameOver\", () =\u003e {\n    gameOverPanel.SetActive(true);\n});\n\n// Register a function with a parameter\nLuaCustomActionManager.RegisterFunction(\"UpdateHealth\", (int health) =\u003e {\n    playerHealth.SetHealth(health);\n});\n\nprivate void UpdateHealth(int health)\n{\n    playerHealth.SetHealth(health);\n}\n\nLuaCustomActionManager.RegisterFunction\u003cint\u003e(\"UpdateHealth\", UpdateHealth);\n\n// Register an async function (using UniTask)\nLuaCustomActionManager.RegisterAsyncFunction(\"FadeToBlack\", async () =\u003e {\n    await fadeScreen.FadeToBlackAsync(2.0f);\n});\n\n// Unregister a function when no longer needed\nLuaCustomActionManager.UnRegisterFunction(\"ShowGameOver\");\n```\n\n**In Lua:**\n\n```lua\n-- Execute a registered function with no parameters\nmyObject:action():exec(\"ShowGameOver\")\n\n-- Execute a registered function with a parameter\nmyObject:action():exec(\"UpdateHealth\", 50)\n\n-- Execute an async function (will wait for completion if second parameter is true)\nmyObject:action():execAsync(\"FadeToBlack\", true)\n```\n\n### Creating a Cutscene\n\n1. Create a new Lua script in the `Assets/Cutscene/Chap{X}/` directory\n2. Use the following template to start:\n\n```lua\n--- Test Cutscene\n\n-- Get references to game objects registered in GameEntityManager\nlocal tg1 = get(\"Target1\")\nlocal tg2 = get(\"Target2\")\n\n-- Main cutscene function\nfunction playCutscene()\n    log(\"Test Cutscene Start\")\n\n    -- Wait 2 seconds\n    wait(2.0)\n\n    -- camera smoothly transition to follow Target1\n    tg1:camera():follow(0.03, true)\n\n    -- camera transition to follow Target2\n    tg2:camera():follow(0.03, true)\n\n    log(\"Test Cutscene End\")\nend\n```\n\n### Triggering a Cutscene\n\nYou can trigger cutscenes using the `CutsceneTrigger` component:\n\n1. Create an empty GameObject in your scene\n2. Add a CircleCollider2D component\n3. Add the `CutsceneTrigger` script\n4. Set the Chapter and Cutscene Name fields\n5. When the player enters the trigger area, the cutscene will play\n\n## Extending the System\n\n### Adding Custom Commands\n\n\u003e LuaFlow was designed with extensibility in mind. Adding new functionality is as simple as creating a new command class.\n\nYou can extend LuaFlow by creating new command classes:\n\n1. Create a new class that inherits from `BaseLuaCommand`\n2. Apply the `[LuaObject]` attribute\n3. Implement your methods with the `[LuaMember]` attribute\n4. Register your class in the appropriate entity wrapper\n\nExample:\n\n```csharp\n[LuaObject]\npublic partial class LuaDialogueCommand : BaseLuaCommand\n{\n    public LuaDialogueCommand(GameObject targetObject) : base(targetObject)\n    {\n    }\n\n    [LuaMember(\"say\")]\n    public void Say(string text)\n    {\n        // Implementation\n    }\n}\n```\n\n### Creating Custom Managers\n\n1. **Define a New Interface:**\n\n```csharp\n// Interface/IDialogueManager.cs\npublic interface IDialogueManager\n{\n    void ShowDialogue(string text);\n    void HideDialogue();\n    bool IsDialogueActive { get; }\n}\n```\n\n2. **Implement the Interface:**\n\n```csharp\n// DialogueManager.cs\npublic class DialogueManager : MonoBehaviour, IDialogueManager\n{\n    public static DialogueManager Instance { get; private set; }\n\n    [SerializeField] private GameObject dialoguePanel;\n    [SerializeField] private Text dialogueText;\n\n    public bool IsDialogueActive =\u003e dialoguePanel.activeSelf;\n\n    private void Awake()\n    {\n        if (Instance == null)\n        {\n            Instance = this;\n            LuaFlowServiceLocator.Register\u003cIDialogueManager\u003e(this);\n            DontDestroyOnLoad(gameObject);\n        }\n        else\n        {\n            Destroy(gameObject);\n        }\n    }\n\n    public void ShowDialogue(string text)\n    {\n        // ...\n    }\n\n    public void HideDialogue()\n    {\n        // ...\n    }\n\n    private void OnDestroy()\n    {\n        if (Instance == this)\n        {\n            LuaFlowServiceLocator.Unregister\u003cIDialogueManager\u003e();\n        }\n    }\n}\n```\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n---\n\nMade with ♥ by hy\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsnow0406%2Fluaflow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsnow0406%2Fluaflow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsnow0406%2Fluaflow/lists"}