Integrating a new game: Full Documentation - chanhpomme/ugp-wiki-temp GitHub Wiki
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.
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.
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
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
.
You can now drag and drop the _GameName
folder into the Unity Gaming Platform project.
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.
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".
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, 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.
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
.
Scripts will need to be modified to work correctly in the framework. Here are the things that need to be changed:
- Calls to
PlayerPrefs
andBinaryPrefs
need to be replaced byGP.PlayerPrefs
andGP.BinaryPrefs
: it would be too painful to check all the calls toPlayerPrefs
andBinaryPrefs
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 whatGP.PlayerPrefs
andGP.BinaryPrefs
do basically. - Calls to
DontDestroyOnLoad
need to be replaced byGP.Object.DontDestroyOnLoad
: games often useDontDestroyOnLoad
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 whatGP.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 theDontDestroyOnLoad
sub-scene to see if there are no objects that are not destroyed. - Calls to
LayerMask
API need to be replaced byGP.LayerMask
. If there are anyGP.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 byGP.Random
. Just change calls related to gameplay or level design. - Be careful with the
Time.TimeScale
value. Make some tests withPauseGame
andResumeGame
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 changeDatabaseParser.cs
so that it does not use theResources
folder, but a direct reference to TextAsset.
You also need to change theDatabaseInfo.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.
Use the Addressables Analyze
window.
(Needs to be done for all the game already integrated)
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
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
).
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
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.
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.