A heavy modification of SMILE GAME BUILDER's Unity Exporter Runtime that makes manipulating the runtime state easier, fixes some problems with the original runtime, and adds support for embedding multiple SGB projects in one parent Unity project.
This has been built primarily for Cobysoft Joe's Dome-King Cabbage via the Naninovel extension that uses this package. I don't really intend to be active on this project outside of features / fixes necessary for Dome-King - though if you find this useful and have problems with it, don't be afraid to drop a ticket in the Issues tab.
- Installable from OpenUPM
- Support for multiple SGB Unity Exports into a single project
- SGB projects can be dynamically entered and exited at runtime
- Supports entry to the game as a whole, or a specific map
- Supports overriding how SGB reads / writes save file data streams
- Supports overriding what elements are available in SGB's pause menu
- Supports overriding what font SGB uses
- Supports manipulating SGB's volume settings and mixer routing
- Supports Unity's Input System
- Fixes visual visual bugs with the default SGB Unity Exporter Runtime
- Approve / Deny SGB loading maps for custom inter-scene navigation
openupm add net.bobbo.sgb.imod
See: OpenUPM's manual installation instructions.
- Export an SGB Project using the Unity Exporter.
- Open the exported Unity project at least once, with the same version of Unity that you'll use in your current project. This generates necessary assets.
- In the project that you'd like to import the project into, open the SGB Manager window by navigating to SGB_IMOD -> SGB Manager on the top of the Unity Editor.
- Click the Import button to open the SGB Import Wizard. Follow steps in the wizard to complete!
For more details see Runtime/Scripts/SGBManager.cs. For a real implementation see net.bobbo.sgb.imod.naninovel's usage in FrontendModeManager.cs.
// Enter the title screen of the game
await SGBManager.LoadSmileGameAsync("theNameOfTheGame");
// Load the first save file in the game
await SGBManager.LoadSmileGameAsync("someGame", 0);
// Load into the 2nd save file, at a specific map location
var mapLoadParams = new LoadSGBMapArgs {
MapName = "the_shack",
StartPosition = new Vector2Int(
MapStartPositionX.Value,
MapStartPositionY.Value
),
StartDirection = MapStartDirection.Value,
StartHeight = MapStartHeight.Value
};
await SGBManager.LoadSmileGameAsync("demoGame", 1, mapLoadParams);
// Exit the currently loaded SGB game
await SGBManager.UnloadSmileGameAsync();
For more details see Runtime/Scripts/SGBPauseMenuOptions.cs. For a real implementation see net.bobbo.sgb.imod.naninovel's usage in SGBSaveBridgeService.cs.
// Make the Exit Button invisible and non-interactable
SGBPauseMenuOptions.ExitButton.IsVisible = false
SGBPauseMenuOptions.ExitButton.IsInteractable = false
// Make the Save Button visible, but not-interactable
SGBPauseMenuOptions.SaveButton.IsVisible = true
SGBPauseMenuOptions.SaveButton.IsInteractable = false
For more details see Runtime/Scripts/SGBSaveManager.cs. For a real implementation see net.bobbo.sgb.imod.naninovel's usage in SGBSaveBridgeService.cs.
// Create a method that handles the raw binary data from SGB
void OnSgbSave(int saveIndex, Stream dataToSave)
{
Debug.Log($"Saving data for slot {saveIndex}");
// ...and do something with the data stream
}
// Tell SGB IMod that we want to use the above function to override how SGB handles saving
SGBSaveManager.SaveDataOverrideFunc = OnSgbSave;
// Create a method allows SGB to read information about a specific save slot
SGBSaveManager.SGBSaveInfo OnSgbReadSaveInfo(int saveIndex)
{
// Some logic for determining if there's not a save at this slot
if(/* we don't have a save at `saveIndex`*/)
{
return SGBSaveManager.SGBSaveInfo.NewEmpty(saveIndex);
}
// OTHERWISE, we DO have a save file, so
// do some logic reading info about the save
// and return it!
return SGBSaveManager.SGBSaveInfo.NewReal(saveIndex, /* DateTime of when the save was last written */);
}
// Tell SGB IMod that we want to use the above function to override how SGB handles reading information about a save file
SGBSaveManager.ReadSaveInfoOverrideFunc = OnSgbReadSaveInfo
// Create a method that allows SGB to read raw binary data from a specific save slot
Stream OnSgbLoad(int saveIndex)
{
// If we don't have a save file at this slot, EXIT EARLY
if(/* we don't have a save at `saveIndex` */)
{
return null;
}
// OTHERWISE, read the binary data!
return /* some way to read binary */
}
// Tell SGB IMod that we want to use the above function to override how SGB handles reading the raw binary data of a save slot
SGBSaveManager.LoadDataOverrideFunc = OnSgbLoad;
For more details see Runtime/Scripts/SGBFontManager.cs. For a real implementation see net.bobbo.sgb.imod.naninovel's usage in SGBFontBridgeService.cs.
// Load a font from somewhere
var someFont = Resources.Load<Font>("myCoolFont");
// Tell SGB IMod that we want to use the above font to override the font SGB will display text with
SGBFontManager.CurrentFont = someFont;
For more details see Runtime/Scripts/SGBAudioSettings.cs. For a real implementation see net.bobbo.sgb.imod.naninovel's usage in SGBAudioBridgeService.cs.
// Set background music volume to 50%
SGBAudioSettings.SetVolumeBGMRaw(50);
// Set background music volume to 20%
SGBAudioSettings.SetVolumeBGM(0.2);
// Set sound effect volume to -5db
SGBAudioSettings.SetVolumeSFXDecibels(-5);
// Set the sound effect volume to the background music volume
SGBAudioSettings.SetVolumeSFXRaw(SGBAudioSettings.GetVolumeBGMRaw());
AudioMixerGroup mixer = /* get some mixer group */;
// Set the mixer group of background music and sound effects
SGBAudioSettings.SetMixerGroupBGM(mixer, /* name of the volume handle */);
SGBAudioSettings.SetMixerGroupSFX(mixer, /* name of the volume handle */);
For more details see Runtime/Scripts/SGBMapLoadManager.cs. For a real implementation see net.bobbo.sgb.imod.naninovel's usage in NaniReturnService.cs.
// Create a method that SGB IMod will use to determine if a map loading operation should be approved or canceled
ApproveEventResult OnSgbMapPreload(SGBMapLoadManager.PreLoadApprovalArgs args)
{
// In this example funciton - we'll cancel the loading of any maps named "game_exit".
if(args.Map.name == "game_exit")
{
return ApproveEventResult.Cancel;
}
return ApproveEventResult.Approve;
}
// Tell SGB IMod that we want to use the above method to approve or deny an SGB map loading
SGBMapLoadManager.OnPreLoad.AddApprovalSource(OnSgbMapPreload);