Integrating a new game: Full Documentation - chanhpomme/ugp-wiki-temp GitHub Wiki

Open the new game in its own Unity instance

Open the game project in its own Unity instance. Open it by using the same Unity version as the Unity Gaming platform project so that the meta will be of the same version, and you'll be able to fix any API changes issues.

Delete the VoodooSauce and other unnecessary plugins

We don't need the VoodooSauce in the games (we don't need ads, analytics, monetization, etc.). Delete the VoodooSauce folder and fix all compilation errors (it might be fastidious).
We don't need other plugins like Facebook or anything related to ads, analytics, social, etc.

Prepare the folder structure

In the game project, create the folders for the new game to match the folder structure defined in the Unity Gaming Platform project:
_GameName
_Project
_ProjectSettings

Copy all the folders inside Assets from the game into the _Project folder, except for some exceptions:
Do not include the Demigiant folder (DOTween). This plugin is DLL-based, so we cannot duplicate it.
Do not include the VoodooSauce folder.
Do not include the VoodooPackages that are already present (you might have to include new ones).
Do not include the TextMeshPro folder (it is included by the TextMeshPro package).
Do not include the PlayServicesResolver folder.
Do not include the MoPub folder.
You might have to manually merge the StreamingAssets folder to omit some data we don't need (like crashlytics config).
You might have to manually merge the Plugins folder to omit plugins we don't want. Here is a non-exhaustive list of plugins we don't want:
Facebook, Firebase, UnityPurchasing, UDP, AdjustPurchasing

Copy these 5 project settings into the _ProjectSettings folder:
DynamicsManager.asset
Physics2DSettings.asset
QualitySettings.asset
TagManager.asset
TimeManager.asset

N.b.: please note that the project settings need to be in text format (not binary format, to change this settings, go to Project Settings > Editor > Asset Serialization > Mode

Regenerate GUIDs

GUIDs are created when the assets are created, not when they are imported. If you import an asset with the same GUID as an asset in the destination project, the imported asset will have a new GUID, but the imported game will reference the asset from the destination project.

To avoid having assets pointing to the wrong asset when importing the game to the Unity Gaming Platform, regenerating GUIDs can be a great solution. It is not complete, though.

To use the GUIDs Regenerator tool, import Assets/GamingPlatformPackageResources/Editor/UnityGuidRegenerator.cs into the game project.

You should regenerate every asset that is only used in the game. For this matter, you can use GamingPlatform/Tools/GUID/Regenerate GUIDs for selection to regenerate the GUIDs of the selected assets.
/!\ You have to select every asset you want to process, not just the parent folder. Folders are assets too. To unfold a folder entirely, you can alt + click on Windows or command + click on macOS.

Some assets might already exist in the Unity Gaming Platform project. It might concerns assets like TextMeshPro fonts or VoodooPackages, or maybe some other stuff.
You want these assets to have the same GUIDs as their counterparts in the Unity Gaming Platform project. If the GUIDs do not match, you can regenerate the GUID of an asset with a specific GUID. To do so, use the tool in GamingPlatform/Tools/GUID/GUID Regenerator Window.

Copy the game to the Unity Gaming Platform project

You can now drag and drop the _GameName folder into the Unity Gaming Platform project.

Embed the new game code into Assembly Definitions

Many games use similar third parties or similar class names. To prevent these conflicts, we need to embed the game's code into a Unity Assembly Definition. It also allows us to exclude all the scripts from a game to be compiled and included in the build, saving some space. In the _Project folder, create _{GameName}.Runtime.asmdef.
In the Editor folder, create _{GameName}.Editor.asmdef. It must only include the Editor in the Platforms section.
In those asmdef, uncheck Auto Referenced.
Add a define constraint GAME_NAME (e.g.: FLAPPY_DUNK).
Add references to the needed Assembly Definitions.
Add this symbol to the Scripting Define Symbols in the Player Settings.

Move the Editor code to a unique Editor folder

As an Assembly Definition requires the code to be in the same folder, we need to move all the Editor folders to a unique Editor folder.
To automatically move all the editor folders to the Editor folder that should be located in Assets > _GameName > _Project > Editor, you can use the tool located in `"GamingPlatform > Tools >Move Project Editor Folders In The Selected Directory".

Rename all game shaders

For a better visibility, it's good to add a suffix at the end of the name of all shaders for each game.
To automatically add the suffix to all shaders (and script dependency) you can use the tool located in `"GamingPlatform > Tools > Shader suffix".

Create a new Addressable group and modify the game to use Addressable scenes

Create a new Addressable group, use the same settings for this group as the other game groups. Add all the used scenes of the game to the Addressable group. You might have to modify the game to handle loading scenes asynchronously from addressables. Use GP.SceneManager to load scenes asynchronously.

Create a new ProjectSettingsData for the new game

In GamingPlatformResources/ProjectSettingsData, right click, and create a new GamingPlatform/ProjectSettingsData. Fill in the necessary info. Drag and drop the entire _ProjectSettings folder in the ProjectSettingsData to import all the project settings from that game.
In the original Unity project, check in the Project Settings if the game uses custom default PhysicsMaterial and default PhysicsMaterial2D. Those default materials will need to be set in the ProjectSettingsData. Add this new ProjectSettingsData to the GamesData, accessible from the menu GamingPlatform/Manage Games.

Convert the game to be Gaming Platform ready

Scripts will need to be modified to work correctly in the framework. Here are the things that need to be changed:

  • Calls to PlayerPrefs and BinaryPrefs need to be replaced by GP.PlayerPrefs and GP.BinaryPrefs: it would be too painful to check all the calls to PlayerPrefs and BinaryPrefs to see if keys are duplicated. The easy solution is to prefix all calls with the game's name to make the keys unique. That's what GP.PlayerPrefs and GP.BinaryPrefs do basically.
  • Calls to DontDestroyOnLoad need to be replaced by GP.Object.DontDestroyOnLoad: games often use DontDestroyOnLoad to have objects persist from one scene to another. This is fine, but we don't want objects to persist from one game to another. That's basically what GP.Object.DontDestroyOnLoad does for you. It will cache all the objects that are persistent and destroy them when you unload the game.
    N.B.: to make sure that objects are destroyed correctly when switching game, test the game in the editor, unload it, and check the DontDestroyOnLoad sub-scene to see if there are no objects that are not destroyed.
  • Calls to LayerMask API need to be replaced by GP.LayerMask. If there are any GP.LayerMask used in public members in a MonoBehaviour script, please set the mask with the corresponding layers by hand.
  • Some calls to Random (not all) need to be replaced by GP.Random. Just change calls related to gameplay or level design.
  • Be careful with the Time.TimeScale value. Make some tests with PauseGame and ResumeGame methods from the proxy.
  • Some games might use a VoodooPackages tool called Database. It is used to save some data like currencies or unlocked shop items. First of all, you need to change DatabaseParser.cs so that it does not use the Resources folder, but a direct reference to TextAsset.
    You also need to change the DatabaseInfo.json of the game (_{Game}/_Project/Resources/Database/DatabaseInfo.json) and modify the paths so that it includes the name of the game, so that all paths are unique and not mixed up between games.
  • Remove all calls to Social (used for GameCenter stuff)

Remove all the unnecessary scenes (like Shop scenes, Skins scenes, Debug and Test scenes, etc.). We only need to keep the gameplay of the game. We also need to remove the unnecessary UI, not just disabling it, but deleting it from the game scene, so that the resources won't need to be loaded when loading the game, saving time and build space.
You might need to remove some prefabs from the game scene, like VoodooSauce, CrossPromoPrefab, and they will appear in red as MissingPrefab in Unity.

Making sure there are no duplicates in the Addressables

Use the Addressables Analyze window.
(Needs to be done for all the game already integrated)

Optimize the game (Resources and loading time)

First of all, we have to remove all calls to Resources.Load(), because we don't want any resources to be embedded with the Unity build. We want assets to be in the game bundles.
You might want to check the textures used by the game to find if there are absurdly large textures and decrease their size.
Some games might reference a lot of prefabs in public references in scripts, which will negatively affect the loading time. If a lot of prefabs are referenced, but only a few are initially needed, you might want to change those references to Addressables.AssetReference and change the implementation of how these assets are loaded.
Here is a link to Chanh's presentation about these matters: Unity Gaming Platform Optimizations

Integrate UI safe area

The sources we got or some features we had to add might cause some UI elements not to be displayed properly on every device (devices with notches).
To solve this problem, the easy solution is to put your sensible UI in a panel and attach a SafeArea script (located in the ThirdParty folder).
To quickly test on many devices type, you can use Unity device simulator (Window > General > Device Simulator).

Integrate the proxy methods and events

The proxy is the interface that allows the framework and the native app to communicate to each other
Here is the full documentation of the proxy: Proxy Documentation
Here are some explanations of the methods and events:
GameIsReadyToPlay: this event informs the native app that the game has finished loaded and that it can be started by the proxy or by the user (if AutoStart is set to true)
StartGame: can be called by the native app (will raise Proxy.OnStartGame) to indicate to the game that the user can now interact with the game (otherwise the input should be blocked). When the user actually starts playing the game, the proxy should send Proxy.GameplayStarted. If AutoStart is set to true, the game should call StartGame itself. You can listen to Proxy.OnStartGame or check Proxy.HasGameStarted (boolean) to know if the game has been started or not
EndGame: actually the same as StartGame but with the ending logic
UpdateScore: this event informs the native app of the new score. It should be called every time the score changes
PlayerDied: this event indicates the native app that the player has died. If Proxy.NbLives is not 0 (infinite lives) and the user has consumed all of his lives, EndGame should also be called. If the player is not out of life, the player should respawn after Proxy.RespawnTime, unless Proxy.RespawnTime is 0. In this case, the native app should manually call Respawn to make the player respawn
Respawn: should only be called by the native app to respawn the player, after he has died in the game
ActivateBooster: called by the native app to activate a booster
EncryptedEndGameScore: this event informs the native app of the score (encrypted) at the end of the game. Generally, it should be called with the OnEndGame action


Fix bad C# event management and miss-used singletons

Poorly designed code can subscribe to events and never unsubscribe to them. For instance, some games will subscribe to some event in the OnEnable / Awake / Start method, and never unsubscribe to them. It is important to clean that up so that the game can be unloaded and loaded again.
Singleton pattern is often misused. When testing the game, make sure that no objects are persisting when they should not.

Obscure sensible variables

Variables that could be hacked (memory hack) need to be secured in each game. It obviously concerns the variables that permit to compute the score, but also every variable that controls the gameplay (speed, acceleration, power, level, etc.).
To obscure variables, we use a Unity plugin called AntiCheatToolkit. First add the ACTk.Runtime AssemblyDefinition to the game AssemblyDefinition. Add the namespace CodeStage.AntiCheat.ObscuredTypes to scripts to use types like ObscuredInt, ObscuredFloat, ObscuredBool, etc.
/!\ Please note that if you change a public variable, its value in the editor will be reset to its default value.

⚠️ **GitHub.com Fallback** ⚠️