A bunch or reusable classes, utilities and tools that I'm using in my games in Unity.
Since this project ignores Unity's .meta
files, you should NEVER add any MonoBehaviour
classes from this project directly to your scene objects as components, because as GUID
for those scripts change between projects, you might have "Missing Behaviour" error.
Instead, just create a new class in your local project, extend the required class from ZTK and then add it as a component.
- Follow integration guide on HeyZap website to setup all the IDs
- Make sure you take age rating into account when setting up ads in all networks mediation:
- Chartboost: go to publishing campaigns, open Advanced Targeting and check Block 17 http://d.pr/i/1cvAb. Remember to do this for all campaigns
- AdMob: unfortunatelly, AdMob does not have an age restriction setting, but you can filter ads categories using this guide: https://support.google.com/admob/answer/3150235?hl=en
- AppLovin: go to managing apps, select your app and then check or uncheck Children's App setting, then check settings under Ad Filtering
- Unity Ads: in Unity Editor, go to Services, Age Designation and select appropriate option. Then, go to app setting in Unity Ads dashboar, select Ads Filtering and select option under Ages: http://d.pr/i/171V8
- HeyZap: select your app on dashboard, go to Publisher Setting and select appropriate option under Family Friendly Filter
- Make sure video interstitials are skippable:
- Chartboost: no settings
- AdMob: no settings
- AppLovin: go to app settings and select appropriate option under Video Settings and Playable Ad Settings
- Unity Ads: no settings
- HeyZap: go to publisher settings for the app and select skippable options
- Make sure reward videos rewards are set correctly for ad networks:
- Chartboost: no settings
- AdMob: no settings
- AppLovin: go to app settings and set correct values under Rewarded Video
- Unity Ads: no settings
- HeyZap: no settings
- Import HeyZap, Chartboost, AdMob, UnityAds and AppLovin SDKs to project. You can just copy them from one of the projects that already have them, or download from HeyZap integration guide (make sure to follow the additional instructions there!)
- Add
API_ADS_HEYZAP
to project build settings - Add
API.Ads.Use(APIs.Ads.HeyZap, 0f, "<your id>");
intInitAPI()
in yourAppController
class. Replace<your id>
with the one provided by HeyZap - Add
AppController.Instance.API.Ads.ShowTestUI(true);
somewhere in options screen button callback - Add ads placements to
IDs
class, the most generic ones are:between_level
,crosspromo
and various placements for incentivized ads:free_coins
,xp_boost
, etc - Add caching calls in
AppController.OnPostInit()
(like this:API.Ads.CacheIntersitials(IDs.Ads.Interstitials.BetweenLevel)
) - Between level ads: Just use this call when user presses Restart button in game over UI
AppController.Instance.API.Ads.ShowBetweenLevelAd(IDs.Ads.Interstitials.BetweenLevel, OnInterstitialClosed, _adBlock)
._adBlock
should be a UI gameobject that blocks all input. API will set it active and inactive automatically - Reward calls: just use this call:
AppController.Instance.API.Ads.ShowRewardedVideo(IDs.Ads.RewardVideos.FreeCoins, null, OnVideoRewardCompleted, coinPack.ID)
- If you want to use cross-promo placement:
- Add call
AppController.Instance.API.Ads.ShowPromo(IDs.Ads.Interstitials.PromoOnPlayButton, OnPromoEnd, _adBlock)
where needed. Also - Enable promo ads in API Settings in game data and set delay in minutes
- Add call
- Setup IAPs in iTunesConnect
- Enable UnityPurchases in Services tab
- Add
API.Store.Use(APIs.IAPs.Unity, 0f)
toAppController
- Add
API_IAP_UNITY
to project build settings - Expand
IAPProduct
class inGameData
to create your own products - Override
protected override StoreProduct[] ProductList
getter inAppController
, but don’t forget to includebase.ProductList
in your new list! - Add
protected override void OnProductPurchaseFinished(string productID, bool success)
override toAppController
- Remember to add
AppController.Instance.API.Store.RestorePurchases()
to options screen - If you support Remove Ads, add
AppController.Instance.PurchaseAdsRemoval()
to options screen - ❗ Xcode: Remember to enable IAPs support in Xcode
Troubleshooting:
- Sometimes sandbox requests take too long to process during testing, that’s okay
- Setup achievements and leaderboards in iTunesConnect
- Create achievements generic conditions in
GameData
in Unity - Add achievements and leaderboards data to
GameData
in Unity - Add
API.Score.Use(APIs.Score.GameCenter, 0f)
toAppController
- Add Prime31 GameCenter plugin
- Add
API_SCORE_GAMECENTER
to project build settings - Add all achievements conditions IDs to
Settings.IDs
file for easier reference - Add
AppController.Instance.API.Score.SubmitScore(<score>, AppController.Instance.Data.Game.DefaultLeaderboard.CurrentPlatformID)
calls where needed, replace leaderboard ID if needed - Add
AppController.Instance.Data.Player.AchievementsTracker.UpdateConditionParameter()
calls where needed - Add
protected override bool OnCheckCustomAchievementCondition(int achievementID, object parameterValue)
override toAppController
if any custom conditions for achievements are requered - Add
AppController.Instance.API.Score.DisplayLeaderboardsList()
andAppController.Instance.API.Score.DisplayAchievementsList()
calls where needed - ❗ Xcode: Remember to enable GameCenter support in Xcode
Troubleshooting:
- Achievements and leaderboards sometimes updated slow in sandbox
- Add Prime31 iCloud plugin to the project
- Add
API_SYNC_ICLOUD
to build settings - Add
API.Sync.Use(APIs.Sync.iCloud, 0f, "<filename>.dat")
(change the filename to the one you like though) - Make sure all your custom player data model classes have
Merge()
method implemented - Override
protected virtual bool DisplaySyncConfirmUI(System.Action syncConfirmedHandler, System.Action syncDeniedHandler)
in yourAppController
. If UI is not ready to display popup right now, return false. In that case, API will try to display this popup in 0.5 sec again. - Add sync button to options screen and change sync state using
AppController.Instance.Data.Player.APIState.ChangeSyncState(!AppController.Instance.Data.Player.APIState.SyncEnabled)
- Don’t forget to call
AppController.Instance.API.Sync.ApplyLoadedData()
if sync is enabled again in options screen. Here's a sample code for options screen flow:
public void OnSyncButtonPress()
{
AppController.Instance.Data.Player.APIState.ChangeSyncState(!AppController.Instance.Data.Player.APIState.SyncEnabled);
if (AppController.Instance.Data.Player.APIState.SyncEnabled)
{
AppController.Instance.API.Sync.ApplyLoadedData();
}
UpdateSyncButtonState();
}
private void UpdateSyncButtonState()
{
if (_icloudButtonText != null)
{
_icloudButtonText.text = AppController.Instance.Data.Player.APIState.SyncEnabled ? AppController.Instance.Localisation.Localise(IDs.Localisation.Buttons.ICLOUD_ON) : AppController.Instance.Localisation.Localise(IDs.Localisation.Buttons.ICLOUD_OFF);
}
}
- If you need to know when new data was applied from cloud, listen to
Zedarus.ToolKit.Settings.IDs.Events.CloudSyncFinished
event - ❗ Xcode: Remember to enable iCloud support in Xcode with iCloud Documents support
- ❗ Xcode: Sometimes Xcode switches off document support for iCloud, even though iCloud is still turned on. Make sure to check and correct that!
- Make sure HeyZap plugin is already in and implemented, including Ads controller from above
- Make sure
API_ADS_HEYZAP
is added to build settings - Add
API.RemoteData.Use(APIs.RemoteData.HeyZap, 0f)
toAppController
- To test locally, you can add
OnRemoteDataReceived(Resources.Load<TextAsset>("Data/RemoteData").text)
inPostInit()
inAppController
class - You can use http://jsonlint.com to validate your JSON
- Remember to remove test JSON from HeyZap dashboard after done testing
- If using version of Unity prior to 5.4, add
API_CRASH_UNITY
to build settings and follow this guide: http://d.pr/i/14nPZ - If using Unity 5.4, just enalbe reporting in Services
- Add Prime31 social plugin, but delete all the Twitter and Facebook related stuff. Basically, you need
SharingBinding
andSharingManager
files some files in Editor folder for Prime31 - Add
API_SHARE_NATIVE
to build settings - Add
API.Share.Use(APIs.Sharing.Native, 0f)
toAppController
- Add helper method from Promo helpers to share score text (see description below)
- Enable Unity Analytics in Unity project's services tab
- Add
API_ANALYTICS_UNITY
to build settings - Add
API.Analytics.Use(APIs.Analytics.Unity, 0f)
toAppController
- Add
AppController.Instance.API.Analytics.LogEvent()
calls where needed
- Make sure to add MasterAudio plugin first
- Setup MasterAudio instances in boot scene and make sure they are kept across scenes
- Then add
AUDIO_MASTER_AUDIO
to build settings - Use
AppController.Instance.Audio
to get access to audio controller - Use
AppController.Instance.Audio.ToggleSound()
andAppController.Instance.Audio.ToggleMusic()
if you need to toggle sounds or music mute - Listen to
Zedarus.ToolKit.Settings.IDs.Events.AudioStateUpdated
event where needed to update UI button state, etc - Use
AppController.Instance.Audio.SoundEnabled
andAppController.Instance.Audio.MusicEnabled
if you need to check if sounds or music are enabled - Override
Zedarus.ToolKit.UI.Elements.UIButtonAudio
class in your custom class and add it to all game objects withButton
component you have in your UI
-
Add this localisation package to the game: http://www.m2h.nl/files/LocalizationPackage.pdf
-
Override
LocaliseText
andLocaliseTextEditor
in your local scripts to avoid meta files missup -
Override
protected abstract LocalisationManager LocManagerRef { get; }
inLocaliseText
. It should return reference toAppContoller.Instance.Localisation
(your local controller). But make sure thatAppController
has instance ready first. Here's the code:protected override Zedarus.ToolKit.Localisation.LocalisationManager LocManagerRef { get { if (AppController.HasInstance) { return AppController.Instance.Localisation; } else { return null; } } }
-
Add
LocaliseText
component to UIText
components that you want to localise without code -
Add localisation ids to your
IDs
class -
Use
AppController.Instance.Localisation.Localise()
method to localise strings in code -
Add
API_LOC_M2H
to build settings -
Language change is logged automatically in analytics (just search for
IDs.Events.SetLanguage
event in ZTK code) -
Remember to add lproj files to Xcode project for all supported langauges before final build
-
TODO: create localisation packages wrappers system
- Setup your app on Batch.com using this guide: https://batch.com/doc/unity/prerequisites.html
- Generate push certificate and provision profiles: https://batch.com/doc/ios/prerequisites.html#_creating-a-certificate
- Download and add Bathc.com plugin to project. Make sure you put the C# files into Plugin folder, not root folder
- Add
API_PROMO_BATCH
to project's build settings - Add
API.Promo.Use(APIs.Promo.Batch, 0f, "<your_dev_or_live_key>")
to yourAppController
- Call
AppController.Instance.API.Promo.RequestNotificationsPermission()
somewhere at the start of the game. For example, when player click “Play” button or with some delay in main menu - Test remote push notifications on this page: https://dashboard.batch.com/app/7265/settings#/push-settings
- ❗ REMEMBER to use live API key before making final build!
- ❗ Xcode: Make sure you make build using correct certificate and provision profile, even if just making development build!
- ❗ Xcode: Enable push notifications in Xcode
- ❗ Xcode: if you are planning to add promo codes support or redeem links, add Batch's custom scheme in
- Setup push notifications first
- Setup unlock inventory in your app settings on batch.com and follow documentation: https://batch.com/doc/unlock/overview.html
- Override
OnProcessRemoteUnlockFeature(string feature, string value)
,OnProcessRemoteUnlockResource(string resource, int quantity)
andOnProcessRemoteUnlockParams(Dictionary<string, string> parameters)
in yourAppController
to correctly process remote unlocks. important:UIManager
might not be available at the time of this call, so make sure you cache the message and display it later if it doesn't. TODO: handle this the same way as in iCloud permission popup - If you are planning to use redeem links from Bathc, make sure to add special URL scheme in Xcode, more on this here: https://batch.com/doc/unlock/overview.html
- Add
AppController.Instance.API.Promo.RestoreRewards()
call where needed (along with IAPs restore?) - Add
AppController.Instance.API.Promo.RedeemCode(code.text)
if requered, but read this: - REMEMBER: We recommend against using promocodes on iOS. Apple guidelines indicate that promocodes are not permitted. Though some apps do successfully integrate this functionality, Batch does not recommend the practice.
- Add and setup local notifications in game data
- Note: local notifications are canceled, scheduled and rescheduled in base AppController class everytime app is started or comes back from background
- Override
OnProcessRewardFromLocalNotification(int rewardID)
in yourAppController
to handle rewards from local notifications
SendContactsEmail()
- opens email composition window with email and subject specified in settings in game dataOnMoreLevelsPress()
- opens URL that was specified in settings in game data and returntrue
. Returnsfalse
otherwise so you can do something else, like display rate me popupOnMoreGamesPress()
- either opens URL or display interstitial ad with custom placement (all specified in settings in game data)OnFacebookButtonPress()
- opens URL that was specified in settings in game data
- Override
InitExtentions()
method in yourAppController
-
Add
AddExtention(new SecondChancePopup(...));
in yourInitExtentions()
override inAppController
. Class constructor is documented, so just follow instructions for each parameter to setup extention correctly. -
Add this to your
GameData
class and set setting you like in game data editor in Unity[SerializeField] [DataTable(15, "Second Chance Popup Settings", typeof(SecondChancePopupData))] private SecondChancePopupData _seconChancePopupSettings;
-
If you haven't already, add call
AppController.Instance.RegisterGameSessionStart()
andAppController.Instance.RegisterGameSessionEnd()
when game session starts and end. Important to note, that both should only be called when actual session starts or ends, not when second chance was used. SoRegisterSessionStart()
should not be called when player's character was resurrected for example, andRegisterSessionEnd()
should not be called exactly on player's death, because he might use second chance. Instead, you need first to check if player used second chance, and only then call this method -
Call this before you enter game over state:
AppController.Instance.GetExtention<SecondChancePopup>().DisplayPopup( UIManager.Instance, <player's score>, <callback> );
This returns
true
if second chance popup is pesented (and so you need to wait for player's choice before finally entering game over state), andfalse
if not and you can safely enter final game over state in that case. Callback should be a method that receivesbool
parameter. If parameter istrue
then second chance should be activated. Iffalse
then user decinded not use second chance. :exclamation: Please not that it is important to passadBlock
object – this should be a UI invisible image object that lays on top of all your UI and blocks user's clicks while ad is loaded and displayed.
-
Add
AddExtention(new Zedarus.ToolKit.Extentions.OneTapGames.DoubleCoinsPopup.DoubleCoinsPopup(...));
in yourInitExtentions()
override inAppController
. Class' constructor is documented, so just follow instructions for each parameter to setup extention correctly. -
Add this to your
GameData
class and set setting you like in game data editor in Unity[SerializeField] [DataTable(5, "Double Coins Popup Settings", typeof(DoubleCoinsPopupData))] private DoubleCoinsPopupData _doubleCoinsPopupData;
-
If you haven't already, add call
AppController.Instance.RegisterGameSessionStart()
andAppController.Instance.RegisterGameSessionEnd()
when game session starts and end. Important to note, that both should only be called when actual session starts or ends, not when second chance was used. SoRegisterSessionStart()
should not be called when player's character was resurrected for example, andRegisterSessionEnd()
should not be called exactly on player's death, because he might use second chance. Instead, you need first to check if player used second chance, and only then call this method -
Call this before you enter game over state:
AppController.Instance.GetExtention<DoubleCoinsPopup>().DisplayPopup(UIManager.Instance, <earned coins during session>, <callback>);
This returns
true
if popup is pesented (and so you need to wait for player's choice before finally entering game over state), andfalse
if not and you can safely enter final game over state in that case. Callback should be a method that receivesbool
parameter. If parameter istrue
then player chose to double coins. Iffalse
then user decinded not to double coins. :exclamation: Please not that it is important to passadBlock
object – this should be a UI invisible image object that lays on top of all your UI and blocks user's clicks while ad is loaded and displayed.
- Add
AddExtention(new Zedarus.ToolKit.Extentions.OneTapGames.FreeRemoveAds.FreeRemoveAds(Data.Game, Data.Player, API, "<double_coins_video_ad_id>"));
in yourInitExtentions()
override inAppController
- Set correct settings in API settings section in Game Data DB
- Call
DisplayPopup()
method onFreeRemoveAds
instance and pass all required parameters. ❗ Please not that it is important to passadBlock
object – this should be a UI invisible image object that lays on top of all your UI and blocks user's clicks while ad is loaded and displayed.
-
Add
AddExtention(new Zedarus.ToolKit.Extentions.OneTapGames.RateMePopup.RateMePopup(...));
in yourInitExtentions()
override inAppController
. Class' constructor is documented, so just follow instructions for each parameter to setup extention correctly. -
Add this to your
PlayerData
class inSetupModelsList()
method overrideAddModel<RateMePopup>();
-
Add this to your
GameData
class and set setting you like in game data editor in Unity[SerializeField] [DataTable(5, "Rate Me Popup Settings", typeof(RateMePopupData))] private RateMePopupData _rateMePopup;
-
Call this where you need it:
AppController.Instance.GetExtention<RateMePopup>().DisplayPopup(UIManager.Instance, <callback>, <between_level>, <skip_reward>);