Stack is one of those simple, silly, addictive games that you can lose an entire afternoon to if you're not careful. It became hit in hypercasual game industry in 2016. This is a clone of Stack.The goal of the game is to stack the moving blocks over each other as high as you can.
In this project, we explain the use of Unity Plugin of Huawei Mobile Services on this game.
The HMS Unity plugin helps you integrate all the power of Huawei Mobile Services in your Unity game.
In this project we are using those kits:
- Account
- IAP
- Ads
- Push
- Game Service
- Analytics
- Crash Service
- Remote Configuration
- Auth Service
- App Linking
- In App Comment
- App Messaging
- Nearby Service
- Cloud DB
- Scan
- Location
- APM
- Drive
- Cloud Storage
Purpose In Project
: Sign in automatically.
Use In Project
: By calling this method HMSAccountKitManager.Instance.SignIn();
at GameManager.cs 121
#region Unity: Start
private void Start()
{
GLog.Log($"Start", GLogName.GameManager);
HMSAccountKitManager.Instance.SignIn();
}
#endregion
Purpose In Project
: Selling the product of "Remove Ads".
Use In Project
:
- By clicking "Remove Ads" button in game menu.This button calls this method
HMSAccountKitManager.Instance.SignIn();
at UIView.cs 231
#region ButtonClick: BuyProduct
public void ButtonClick_BuyProduct(string productId)
{
DelegateStore.BuyProduct?.Invoke(productId);
}
#endregion
DelegateStore.BuyProduct?.Invoke(productId);
command invokesOnBuyProduct(string productID)
at IAPManager.cs 154
#region Events: OnBuyProduct
private void OnBuyProduct(string productID)
{
HMSIAPManager.Instance.BuyProduct(productID);
}
#endregion
- If purchase request return success
OnBuyProductSuccess(PurchaseResultInfo obj)
callback method will be called automatically.In this method we handle the having "Remove Ads" item.
#region Events: BuyProductSuccess
private void OnBuyProductSuccess(PurchaseResultInfo obj)
{
if (obj.InAppPurchaseData.ProductId == "1006")
{
GLog.Log($"OnBuyProductSuccess {obj.InAppPurchaseData.ProductName}", GLogName.IAPManager);
Warehouse.RemoveAds = true;
}
}
#endregion
Purpose In Project
: Monetizing with Interstial Ads and Banner Ads.
Use In Project
:
- Banner Ads : By calling this method
ShowBanner()
at AdsManager.cs 102.We are calling this method at first,and you can hide banner ads by callingHideBanner()
at AdsManager.cs 112.
#region Ads: Banner
public void ShowBanner()
{
if (!IsAdsActive)
return;
GLog.Log($"ShowBanner", GLogName.AdsManager);
HMSAdsKitManager.Instance.ShowBannerAd();
}
public void HideBanner()
{
GLog.Log($"HideBanner", GLogName.AdsManager);
HMSAdsKitManager.Instance.HideBannerAd();
}
#endregion
- Interstial Ads : By calling this method
ShowInterstitial()
at AdsManager.cs 145.We are calling this method at the end of sessions.
#region Ads: Interstitial
public void ShowInterstitial()
{
if (!IsAdsActive)
return;
GLog.Log($"ShowInterstitial", GLogName.AdsManager);
HMSAdsKitManager.Instance.ShowInterstitialAd();
}
#endregion
Purpose In Project
: Send notificatons to users when we want.
Use In Project
: We can send notificaton at App Gallery Connect.
Purpose In Project
: Handle the success of users.
Use In Project
:
- Leaderboard : We can see leaderboard by clicking leaderboard button in game menu ui.Button click calls
OnShowLeaderboard()
at ScoreManager.cs 292 and we can handle score by callingSendScore(int score)
at ScoreManager.cs 277.
#region Event: ShowLeaderboard
private void OnShowLeaderboard()
{
Debug.Log("OnShowLeaderboard");
HMSLeaderboardManager.Instance.ShowLeaderboards();
}
#endregion
- Achievement : By calling this method
SendScore(int score)
at ScoreManager.cs 226.We can handle the passing 10 points achievement.
public void SendScore(int score)
{
HMSLeaderboardManager.Instance.SubmitScore(LeaderBoardId, score);
if (score >= 10)
{
HMSAchievementsManager.Instance.UnlockAchievement(AchievementId);
}
}
Purpose In Project
: Analyse the session starts and ends count.
Use In Project
: With those callbacks OnSessionStart() , OnSessionEnd()
at AnalyticsManager.cs 15,26.
#region Events: OnSessionStart
protected override void OnSessionStart(object sender, GEvent<object> eventData)
{
base.OnSessionStart(sender, eventData);
HMSAnalyticsKitManager.Instance.SendEventWithBundle("Session_Start", "Session_Start", "OK");
}
#endregion
#region Events: OnSessionEnd
protected override void OnSessionEnd(object sender, GEvent<object> eventData)
{
base.OnSessionStart(sender, eventData);
HMSAnalyticsKitManager.Instance.SendEventWithBundle("Session_End", "Session_End", "OK");
}
#endregion
Purpose In Project
: Learn the cause of unexpected exits of application.
Use In Project
: We can read the crash reports from App Gallery Connect.
Purpose In Project
: Modifying the game without deploying a new version.
Use In Project
: We are fetching the values at the beginning of application.Then we are using that values in game mechanic.(GameManager.cs 147)
#region HMS: RemoteConfig
private void RemoteConfig()
{
void OnFecthSuccess(ConfigValues config)
{
HMSRemoteConfigManager.Instance.Apply(config);
var _blockMoveTimeCoeff = HMSRemoteConfigManager.Instance.GetValueAsDouble("BlockMoveTimeCoeff");
var _backgroundColorIndex = HMSRemoteConfigManager.Instance.GetValueAsLong("BGColor");
backgroundColorIndex = int.Parse(_backgroundColorIndex.ToString());
blockMoveTimeCoeff = (float)_blockMoveTimeCoeff;
Debug.Log($"coeff {blockMoveTimeCoeff}");
Debug.Log($"BG Color {_backgroundColorIndex}");
remoteConfigIsOk = true;
}
void OnFecthFailure(HMSException exception)
{
Debug.Log($" fetch() Failed Error Code => {exception.ErrorCode} Message => {exception.WrappedExceptionMessage}");
}
HMSRemoteConfigManager.Instance.OnFecthSuccess = OnFecthSuccess;
HMSRemoteConfigManager.Instance.OnFecthFailure = OnFecthFailure;
HMSRemoteConfigManager.Instance.Fetch(5);
}
#endregion
Purpose In Project
: Potential need of login with
- Apple
Purpose In Project
: Provide users to share game application with their friends.
Use In Project
: By clicking sharing button in game menu,this calls the ShareLongLink()
at AppLinkingManager.cs 177.
public void ShareLongLink()
{
Debug.Log("ShareLongLink");
AGConnectAppLinking.ShareLink(longLink);
}
Purpose In Project
: Providing users to submit ratings and make comments for app without leaving the application.
Use In Project
: Automatically running at the session end callback OnSessionEnd
at GameManager.cs 245.
#region Events: OnSessionEnd
private void OnSessionEnd(object sender, GEvent<object> eventData)
{
GLog.Log($"OnSessionEnd", GLogName.GameManager);
InAppComment.ShowInAppComment();
}
#endregion
Purpose In Project
: Remind the user of "Remove Ads" IAP product.
Use In Project
: Automatically running with Session_End
analytic event.You can arrange this from AGC.
Purpose In Project
: Broadcasting the game as if it is being played live on another device by transferring input
Use In Project
: In "Pre" unity-scene , there is a NearbyDeviceManager gameobject.This includes NearbyDeviceManager
, NearbyServer
and NearbyClient
scripts.We are transferring input to other device by these scripts.These scripts subscribes Touch and TapToPlay events at DelegateStore
.
Purpose In Project
: Save all game session scores with session number (sequential) and display this at popup.
Use In Project
: We are saving session at CloudDBManager
157, AddSession method.
public void AddSession(int score)
{
GLog.Log($"AddSession", GLogName.CloudDBManager);
var gameSession = new GameSessions();
var token = Guid.NewGuid().ToString();
var sessionNumber = ++SessionNumber;
gameSession.HuaweiIdMail = GameManager.Instance.Uid;
gameSession.Id = token;
gameSession.SessionNumber = sessionNumber;
gameSession.Score = score;
cloudDBManager.ExecuteUpsert(gameSession);
}
We are showing ScoreTable popup by clicking ScoreTable Button at menu.This button calls following command UIView
88.
Btn_ShowScoreTablePopup.onClick.AddListener(delegate ()
{
DelegateStore.ShowPopup?.Invoke(PopupType.ScoreTable);
});
This command query sessions from CloudDB by calling QuerySessions method at CloudDBManager
141.
public void QuerySessions()
{
GLog.Log($"QuerySessions", GLogName.CloudDBManager);
queryIsOK = false;
gameSessionsList.Clear();
CloudDBZoneQuery mCloudQuery =
CloudDBZoneQuery.Where(
new AndroidJavaClass(GameSessionsClass));
mCloudQuery.EqualTo("huaweiIdMail", GameManager.Instance.Uid);
cloudDBManager.ExecuteQuery
(
mCloudQuery,
CloudDBZoneQueryCloudDBZoneQueryPolicy.CLOUDDBZONE_CLOUD_CACHE
);
}
Purpose In Project
: Set user "NoAds" item owner by QR Code.
Use In Project
: We are saving session at ScanKitManager
114, OnScanSuccess method.
Firstly we click the scan button ,this button calls following command UIView
88.
Btn_ScanKit.onClick.AddListener(delegate ()
{
DelegateStore.Scan?.Invoke();
});
then following command is called then start to scan ScanKitManager
75.
private void OnScan()
{
Debug.Log($"OnScan");
HMSScanKitManager.Instance.Scan(HmsScanBase.ALL_SCAN_TYPE);
}
When scan is success then this method is called.If code is true user will have No Ads item.
#region Callbacks
private void OnScanSuccess(string text, HmsScan hmsScan)
{
Debug.Log($"OnScanSuccess {text}");
scanResult = text;
if (text == "StackCode_NoAds")
{
Warehouse.RemoveAds = true;
}
}
#endregion
Purpose In Project
: Show our users content based on their location in the world.
Use In Project
: We are fetching location at LocationKitManager
147, at GetLastKnownLocation method.
var latitude = location.GetLatitude();
var longitude = location.GetLatitude();
_latitude = latitude;
_longitude = longitude;
DelegateStore.ShowLocation?.Invoke(latitude, longitude);
then we are checking location if it is in Turkey at the UIView
140.
Then we are displaying the message the users that live in Turkey.
var _lat = LocationKitManager.Instance.Latitude;
var _long = LocationKitManager.Instance.Longitude;
if (_lat <= 42 && _lat >= 36)
{
if (_long <= 45 && _long >= 26)
{
TxtLocation.text = "KEYİFLİ OYUNLAR !!!";
await Task.Delay(2000);
TxtLocation.gameObject.SetActive(false);
}
}
Purpose In Project
: We are wondering if our users are experiencing performance and slowness problems in the game.
Use In Project
: We are enabling monitoring the performance at APMKitManager
43, Start method.
HMSAPMManager.Instance.EnableCollection(true);
HMSAPMManager.Instance.EnableAnrMonitor(true);
Purpose In Project
: We want our users to automatically save the screenshots of their important moments in the game and then be able to see them.
Use In Project
: We are calling the capture screenshot command DriveKitManager
109, OnSessionEnd callback.
StartCoroutine(captureScreenshot(filePath, fileName));
Then we are creating file at cloud drive with there commands.
yield return new WaitForEndOfFrame();
var file = HMSDriveKitManager.Instance.CreateFiles(MimeType.MimeTypeFromSuffix(".png"), filePath, fileName);
Purpose In Project
: We want to reduce application size by fetching prefab and its texture content from remote with Huawei Cloud Storage and Unity Asset Bundle technology. Also this provides us a a flexible structure, with this structure we can easily change this prefab when we want without new release.
Use In Project
: We are calling the GetAssetBundle Ienumarator method of CloudStorageManager
67 at app initialize.
bool state1 = File.Exists(fullPathFile1);
bool state2 = File.Exists(fullPathFile2);
DownloadTask task1;
DownloadTask task2;
if (!state1)
task1 = HMSCloudStorageManager.Instance.DownloadFile("/gameoverCanvas", "", "");
if (!state2)
task2 = HMSCloudStorageManager.Instance.DownloadFile("/gameoverCanvas.manifest", "", "");
These commands are deserializing commands that transition from Asset Bundle file format to Unity scene object.
AssetBundle orjinalAssetBundle = AssetBundle.LoadFromFile(fullPathFile1);
var effectPrefab = (GameObject)orjinalAssetBundle.LoadAsset(fileName);
var gameoverCanvas = Instantiate(effectPrefab);
gameoverCanvas.SetActive(false);
gameOverCanvas = gameoverCanvas;
DontDestroyOnLoad(gameOverCanvas);