Ejemplo n.º 1
0
        // Draw the array of leaderboards or achievements inside a foldout and the relevant buttons.
        void DrawGameServiceItemArray(string itemType, EMProperty myProp, ref bool isFoldout)
        {
            if (myProp.property.arraySize > 0)
            {
                EditorGUI.indentLevel++;
                isFoldout = EditorGUILayout.Foldout(isFoldout, myProp.property.arraySize + " " + myProp.content.text);
                EditorGUI.indentLevel--;

                if (isFoldout)
                {
                    // Update the string array of Android GPGPS ids to display in the leaderboards and achievements.
                    gpgsIds    = new string[gpgsIdDict.Count + 1];
                    gpgsIds[0] = EM_Constants.NoneSymbol;
                    gpgsIdDict.Keys.CopyTo(gpgsIds, 1);

                    System.Action <SerializedProperty> drawer;

                    if (itemType.Equals("Leaderboard"))
                    {
                        drawer = DrawGameServiceLeaderboard;
                    }
                    else if (itemType.Equals("Achievement"))
                    {
                        drawer = DrawGameServiceAchievement;
                    }
                    else
                    {
                        throw new System.Exception("Invalid itemType");
                    }

                    // Draw the array of achievements or leaderboards.
                    DrawArrayProperty(myProp.property, drawer);

                    // Detect duplicate names.
                    string duplicateName = EM_EditorUtil.FindDuplicateFieldInArrayProperty(myProp.property, GameServiceItem_NameProperty);
                    if (!string.IsNullOrEmpty(duplicateName))
                    {
                        EditorGUILayout.Space();
                        EditorGUILayout.HelpBox("Found duplicate name of \"" + duplicateName + "\".", MessageType.Warning);
                    }
                }
            }
            else
            {
                EditorGUILayout.HelpBox("No " + itemType + " added.", MessageType.None);
            }

            EditorGUILayout.Space();
            if (GUILayout.Button("Add New " + itemType, GUILayout.Height(EM_GUIStyleManager.buttonHeight)))
            {
                // Add new leaderboard.
                AddNewGameServiceItem(myProp.property);

                // Open the foldout if it's closed.
                isFoldout = true;
            }
        }
Ejemplo n.º 2
0
        // Generate a static class containing constants of IAP product names.
        void GenerateIAPConstants()
        {
            // First create a hashtable containing all the names to be stored as constants.
            SerializedProperty productsProp = IAPProperties.products.property;

            // First check if there're duplicate names.
            string duplicateName = EM_EditorUtil.FindDuplicateFieldInArrayProperty(productsProp, IAPProduct_NameProperty);

            if (!string.IsNullOrEmpty(duplicateName))
            {
                EM_EditorUtil.Alert("Error: Duplicate Names", "Found duplicate product name of \"" + duplicateName + "\".");
                return;
            }

            // Proceed with adding resource keys.
            Hashtable resourceKeys = new Hashtable();

            // Add the product names.
            for (int i = 0; i < productsProp.arraySize; i++)
            {
                SerializedProperty element = productsProp.GetArrayElementAtIndex(i);
                string             name    = element.FindPropertyRelative(IAPProduct_NameProperty).stringValue;

                // Ignore all items with an empty name.
                if (!string.IsNullOrEmpty(name))
                {
                    string key = "Product_" + name;
                    resourceKeys.Add(key, name);
                }
            }

            if (resourceKeys.Count > 0)
            {
                // Now build the class.
                EM_EditorUtil.GenerateConstantsClass(
                    EM_Constants.GeneratedFolder,
                    EM_Constants.RootNameSpace + "." + EM_Constants.IAPConstantsClassName,
                    resourceKeys,
                    true
                    );
            }
            else
            {
                EM_EditorUtil.Alert("Constants Class Generation", "Please fill in required information for all products.");
            }
        }
Ejemplo n.º 3
0
        //----------------------------------------------------------------
        // Draw Category Groups
        //----------------------------------------------------------------

        void DrawNotificationCategoryGroupsArray(EMProperty prop, ref bool isFoldout)
        {
            if (prop.property.arraySize <= 0)
            {
                EditorGUILayout.HelpBox(NotificationCategoryGroupIntro, MessageType.Info);
            }
            else
            {
                EditorGUILayout.HelpBox(NotificationCategoryGroupInvalidWarning, MessageType.None);
                EditorGUI.indentLevel++;
                isFoldout = EditorGUILayout.Foldout(isFoldout, prop.property.arraySize + " " + prop.content.text);
                EditorGUI.indentLevel--;

                if (isFoldout)
                {
                    // Draw the array of category groups.
                    DrawArrayProperty(prop.property, DrawNotificationCategoryGroup);

                    // Detect duplicate names.
                    string duplicateId = EM_EditorUtil.FindDuplicateFieldInArrayProperty(prop.property, NotificationCategoryGroup_Id);
                    if (!string.IsNullOrEmpty(duplicateId))
                    {
                        EditorGUILayout.Space();
                        EditorGUILayout.HelpBox("Found duplicate category group ID of \"" + duplicateId + "\".", MessageType.Warning);
                    }
                }
            }

            EditorGUILayout.Space();
            if (GUILayout.Button("Add New Category Group", GUILayout.Height(EM_GUIStyleManager.buttonHeight)))
            {
                // Add new category group.
                AddNewNotificationCategoryGroup(prop.property);

                // Open the foldout if it's closed.
                isFoldout = true;
            }
        }
Ejemplo n.º 4
0
        void IAPModuleGUI()
        {
            DrawModuleHeader();

            // Now draw the GUI.
            if (!isIAPModuleEnable.boolValue)
            {
                return;
            }

#if !EM_UIAP
            EditorGUILayout.Space();
            EditorGUILayout.BeginVertical(EM_GUIStyleManager.UppercaseSectionBox);
            EditorGUILayout.HelpBox(UnityIAPEnableInstruction, MessageType.Error, true);
            EditorGUILayout.EndVertical();
#else
            EditorGUILayout.Space();
            DrawUppercaseSection("IAP_AUTO_INIT_CONFIG", "AUTO INITIALIZATION", () =>
            {
                EditorGUILayout.PropertyField(IAPProperties.autoInit.property, IAPProperties.autoInit.content);
                if (!IAPProperties.autoInit.property.boolValue)
                {
                    EditorGUILayout.HelpBox(IAPManualInitInstruction, MessageType.Info);
                }
            });

            // Select target Android store, like using the Window > Unity IAP > Android > Target ... menu item.
            EditorGUILayout.Space();
            DrawUppercaseSection("ANDROID_TARGET_STORE_FOLDOUT_KEY", "TARGET ANDROID STORE", () =>
            {
                EditorGUI.BeginChangeCheck();
                IAPProperties.targetAndroidStore.property.enumValueIndex = EditorGUILayout.Popup(
                    IAPProperties.targetAndroidStore.content.text,
                    IAPProperties.targetAndroidStore.property.enumValueIndex,
                    IAPProperties.targetAndroidStore.property.enumDisplayNames
                    );
                if (EditorGUI.EndChangeCheck())
                {
                    SetTargetAndroidStore((IAPAndroidStore)IAPProperties.targetAndroidStore.property.enumValueIndex);
                }

                // Enable Amazon sandbox testing.
                androidStore = (IAPAndroidStore)IAPProperties.targetAndroidStore.property.enumValueIndex;
                if (androidStore == IAPAndroidStore.AmazonAppStore)
                {
                    EditorGUILayout.PropertyField(IAPProperties.enableAmazoneSandboxTesting.property, IAPProperties.enableAmazoneSandboxTesting.content);
                }
            });

            // Apple's Ask To Buy settings.
            EditorGUILayout.Space();
            DrawUppercaseSection("APPLE_ASK_TO_BUY_FOLDOUT_KEY", "APPLE ASK-TO-BUY", () =>
            {
                // Apple Ask To Buy simulation in sandbox.
                EditorGUILayout.PropertyField(IAPProperties.simulateAppleAskToBuy.property, IAPProperties.simulateAppleAskToBuy.content);
            });

            // Apple's promotional purchases settings.
            EditorGUILayout.Space();
            DrawUppercaseSection("APPLE_PROMOTIONAL_PURCHASES_FOLDOUT_KEY", "APPLE PROMOTIONAL PURCHASES", () =>
            {
                // Whether to intercept Apple's promotional purchases.
                EditorGUILayout.PropertyField(IAPProperties.interceptApplePromotionalPurchases.property, IAPProperties.interceptApplePromotionalPurchases.content);
            });

            // Receipt validation
            EditorGUILayout.Space();
            DrawUppercaseSection("RECEIPT_VALIDATION_FOLDOUT_KEY", "RECEIPT VALIDATION", () =>
            {
                EditorGUILayout.HelpBox("Unity IAP offers local receipt validation for extra security. Apple stores and Google Play store only.", MessageType.None);

                // iOS store.
                EditorGUI.BeginDisabledGroup(!isAppleTangleValid);
                IAPProperties.validateAppleReceipt.property.boolValue = EditorGUILayout.Toggle(IAPProperties.validateAppleReceipt.content, IAPProperties.validateAppleReceipt.property.boolValue);
                EditorGUI.EndDisabledGroup();

                // Always disable the option if AppleTangle is not valid.
                if (!isAppleTangleValid)
                {
                    IAPProperties.validateAppleReceipt.property.boolValue = false;
                }

                // Google Play store.
                bool isTargetingGooglePlay = androidStore == IAPAndroidStore.GooglePlay;
                EditorGUI.BeginDisabledGroup(!isGooglePlayTangleValid);
                IAPProperties.validateGooglePlayReceipt.property.boolValue = EditorGUILayout.Toggle(IAPProperties.validateGooglePlayReceipt.content, IAPProperties.validateGooglePlayReceipt.property.boolValue);
                EditorGUI.EndDisabledGroup();

                // Always disable the option if GooglePlayTangle is not valid.
                if (!isGooglePlayTangleValid)
                {
                    IAPProperties.validateGooglePlayReceipt.property.boolValue = false;
                }

                if (!isAppleTangleValid || (!isGooglePlayTangleValid && isTargetingGooglePlay))
                {
                    string rvMsg = "Please go to Window > Unity IAP > IAP Receipt Validation Obfuscator and create obfuscated secrets to enable receipt validation for Apple stores and Google Play store.";

                    if (!isAppleTangleValid)
                    {
                        rvMsg += " Note that you don't need to provide a Google Play public key if you're only targeting Apple stores.";
                    }
                    else
                    {
                        rvMsg = rvMsg.Replace("Apple stores and ", "");
                    }

                    if (isGooglePlayTangleValid || !isTargetingGooglePlay)
                    {
                        rvMsg = rvMsg.Replace(" and Google Play store", "");
                    }

                    EditorGUILayout.HelpBox(rvMsg, MessageType.Warning);
                }
            });

            // Product list
            EditorGUILayout.Space();
            DrawUppercaseSection("PRODUCTS_FOLDOUT_KEY", "PRODUCTS", () =>
            {
                EMProperty products = IAPProperties.products;

                if (products.property.arraySize > 0)
                {
                    EditorGUI.indentLevel++;
                    isIAPProductsFoldout = EditorGUILayout.Foldout(isIAPProductsFoldout, products.property.arraySize + " " + products.content.text, true);
                    EditorGUI.indentLevel--;

                    if (isIAPProductsFoldout)
                    {
                        // Draw the array of IAP products.
                        DrawArrayProperty(products.property, DrawIAPProduct);

                        // Detect duplicate product names.
                        string duplicateName = EM_EditorUtil.FindDuplicateFieldInArrayProperty(products.property, IAPProduct_NameProperty);
                        if (!string.IsNullOrEmpty(duplicateName))
                        {
                            EditorGUILayout.Space();
                            EditorGUILayout.HelpBox("Found duplicate name of \"" + duplicateName + "\".", MessageType.Warning);
                        }
                    }
                }
                else
                {
                    EditorGUILayout.HelpBox("No products added.", MessageType.None);
                }

                if (GUILayout.Button("Add New Product", GUILayout.Height(EM_GUIStyleManager.buttonHeight)))
                {
                    // Add new IAP product.
                    AddNewProduct(products.property);

                    // Open the foldout if it's closed.
                    isIAPProductsFoldout = true;
                }
            });

            // Constant generation.
            EditorGUILayout.Space();
            DrawUppercaseSection("IAP_CONTANTS_GENERATION_FOLDOUT_KEY", "CONSTANTS GENERATION", () =>
            {
                EditorGUILayout.HelpBox(IAPConstantGenerationIntro, MessageType.None);
                if (GUILayout.Button("Generate Constants Class", GUILayout.Height(EM_GUIStyleManager.buttonHeight)))
                {
                    GenerateIAPConstants();
                }
            });
#endif
        }
Ejemplo n.º 5
0
        void IAPModuleGUI()
        {
            EditorGUILayout.BeginVertical(EM_GUIStyleManager.GetCustomStyle("Module Box"));

            EditorGUI.BeginChangeCheck();
            isIAPModuleEnable.boolValue = EM_EditorGUI.ModuleToggle(isIAPModuleEnable.boolValue, IAPModuleLabel);
            if (EditorGUI.EndChangeCheck())
            {
                GameObject prefab = EM_EditorUtil.GetMainPrefab();

                if (!isIAPModuleEnable.boolValue)
                {
                    EM_PluginManager.DisableIAPModule(prefab);
                }
                else
                {
                    EM_PluginManager.EnableIAPModule(prefab);
                }
            }

            // Now draw the GUI.
            if (!isIAPModuleEnable.boolValue)
            {
                EditorGUILayout.Space();
                EditorGUILayout.HelpBox(IAPModuleIntro, MessageType.Info);
            }
            else
            {
                #if !EM_UIAP
                EditorGUILayout.Space();
                EditorGUILayout.HelpBox(UnityIAPEnableInstruction, MessageType.Error);
                #else
                // Select target Android store, like using the Window > Unity IAP > Android > Target ... menu item.
                EditorGUILayout.Space();
                EditorGUILayout.LabelField("[ANDROID] TARGET STORE", EditorStyles.boldLabel);

                EditorGUI.BeginChangeCheck();
                IAPProperties.targetAndroidStore.property.enumValueIndex = EditorGUILayout.Popup(
                    IAPProperties.targetAndroidStore.content.text,
                    IAPProperties.targetAndroidStore.property.enumValueIndex,
                    IAPProperties.targetAndroidStore.property.enumDisplayNames
                    );
                if (EditorGUI.EndChangeCheck())
                {
                    SetTargetAndroidStore((IAPAndroidStore)IAPProperties.targetAndroidStore.property.enumValueIndex);
                }

                // Receipt validation
                EditorGUILayout.Space();
                EditorGUILayout.LabelField("RECEIPT VALIDATION", EditorStyles.boldLabel);
                EditorGUILayout.HelpBox("Unity IAP offers local receipt validation for extra security. Apple stores and Google Play store only.", MessageType.None);

                // iOS store.
                EditorGUI.BeginDisabledGroup(!isAppleTangleValid);
                IAPProperties.validateAppleReceipt.property.boolValue = EditorGUILayout.Toggle(IAPProperties.validateAppleReceipt.content, IAPProperties.validateAppleReceipt.property.boolValue);
                EditorGUI.EndDisabledGroup();

                // Always disable the option if AppleTangle is not valid.
                if (!isAppleTangleValid)
                {
                    IAPProperties.validateAppleReceipt.property.boolValue = false;
                }

                // Google Play store.
                bool isTargetingGooglePlay = IAPProperties.targetAndroidStore.property.enumValueIndex == (int)IAPAndroidStore.GooglePlay;
                EditorGUI.BeginDisabledGroup(!isGooglePlayTangleValid);
                IAPProperties.validateGooglePlayReceipt.property.boolValue = EditorGUILayout.Toggle(IAPProperties.validateGooglePlayReceipt.content, IAPProperties.validateGooglePlayReceipt.property.boolValue);
                EditorGUI.EndDisabledGroup();

                // Always disable the option if GooglePlayTangle is not valid.
                if (!isGooglePlayTangleValid)
                {
                    IAPProperties.validateGooglePlayReceipt.property.boolValue = false;
                }

                if (!isAppleTangleValid || (!isGooglePlayTangleValid && isTargetingGooglePlay))
                {
                    string rvMsg = "Please go to Window > Unity IAP > IAP Receipt Validation Obfuscator and create obfuscated secrets to enable receipt validation for Apple stores and Google Play store.";

                    if (!isAppleTangleValid)
                    {
                        rvMsg += " Note that you don't need to provide a Google Play public key if you're only targeting Apple stores.";
                    }
                    else
                    {
                        rvMsg = rvMsg.Replace("Apple stores and ", "");
                    }

                    if (isGooglePlayTangleValid || !isTargetingGooglePlay)
                    {
                        rvMsg = rvMsg.Replace(" and Google Play store", "");
                    }

                    EditorGUILayout.HelpBox(rvMsg, MessageType.Warning);
                }

                // Product list
                EditorGUILayout.Space();
                EditorGUILayout.LabelField("PRODUCTS", EditorStyles.boldLabel);

                EMProperty products = IAPProperties.products;

                if (products.property.arraySize > 0)
                {
                    EditorGUI.indentLevel++;
                    isIAPProductsFoldout = EditorGUILayout.Foldout(isIAPProductsFoldout, products.property.arraySize + " " + products.content.text);
                    EditorGUI.indentLevel--;

                    if (isIAPProductsFoldout)
                    {
                        // Draw the array of IAP products.
                        DrawArrayProperty(products.property, DrawIAPProduct);

                        // Detect duplicate product names.
                        string duplicateName = EM_EditorUtil.FindDuplicateFieldInArrayProperty(products.property, IAPProduct_NameProperty);
                        if (!string.IsNullOrEmpty(duplicateName))
                        {
                            EditorGUILayout.Space();
                            EditorGUILayout.HelpBox("Found duplicate name of \"" + duplicateName + "\".", MessageType.Warning);
                        }
                    }
                }
                else
                {
                    EditorGUILayout.HelpBox("No products added.", MessageType.None);
                }

                EditorGUILayout.Space();
                if (GUILayout.Button("Add New Product", GUILayout.Height(EM_GUIStyleManager.buttonHeight)))
                {
                    // Add new IAP product.
                    AddNewProduct(products.property);

                    // Open the foldout if it's closed.
                    isIAPProductsFoldout = true;
                }

                // Constant generation.
                EditorGUILayout.Space();
                EditorGUILayout.LabelField("CONSTANTS GENERATION", EditorStyles.boldLabel);
                EditorGUILayout.HelpBox(IAPConstantGenerationIntro, MessageType.None);

                EditorGUILayout.Space();
                if (GUILayout.Button("Generate Constants Class", GUILayout.Height(EM_GUIStyleManager.buttonHeight)))
                {
                    GenerateIAPConstants();
                }
                #endif
            }

            EditorGUILayout.EndVertical();
        }
Ejemplo n.º 6
0
        string[] BuildListOfNotificationCategoryGroupsName(IDictionary <string, string> categoryGroupsDict)
        {
            if (categoryGroupsDict == null)
            {
                return new string[] { EM_Constants.NoneSymbol }
            }
            ;

            var list = new string[categoryGroupsDict.Count + 1];

            // Add "None" as first item.
            list[0] = EM_Constants.NoneSymbol;

            // Copy keys from the dict.
            categoryGroupsDict.Keys.CopyTo(list, 1);

            return(list);
        }

        string GetNotificationCategoryNameFromId(IDictionary <string, string> dict, string id)
        {
            string name = string.Empty;

            if (string.IsNullOrEmpty(id))
            {
                name = EM_Constants.NoneSymbol;
            }
            else if (dict != null)
            {
                name = EM_EditorUtil.GetKeyForValue(dict, id);
            }

            return(name);
        }

        // Generate a static class containing constants of category IDs.
        void GenerateNotificationConstants()
        {
            // First create a hashtable containing all the names to be stored as constants.
            SerializedProperty userCategoriesProp = NotificationProperties.userCategories.property;

            // First check if there're duplicate IDs.
            string duplicateID = EM_EditorUtil.FindDuplicateFieldInArrayProperty(userCategoriesProp, NotificationCategory_Id);

            if (!string.IsNullOrEmpty(duplicateID))
            {
                EM_EditorUtil.Alert("Error: Duplicate IDs", "Found duplicate category ID of \"" + duplicateID + "\".");
                return;
            }

            // Proceed with adding resource keys.
            Hashtable resourceKeys = new Hashtable();

            // Add the category IDs.
            for (int i = 0; i < userCategoriesProp.arraySize; i++)
            {
                SerializedProperty element = userCategoriesProp.GetArrayElementAtIndex(i);
                string             id      = element.FindPropertyRelative(NotificationCategory_Id).stringValue;

                // Ignore all items with an empty ID.
                if (!string.IsNullOrEmpty(id))
                {
                    string key = "UserCategory_" + id;
                    resourceKeys.Add(key, id);
                }
            }

            if (resourceKeys.Count > 0)
            {
                // Now build the class.
                EM_EditorUtil.GenerateConstantsClass(
                    EM_Constants.GeneratedFolder,
                    EM_Constants.RootNameSpace + "." + EM_Constants.NotificationsConstantsClassName,
                    resourceKeys,
                    true
                    );
            }
            else
            {
                EM_EditorUtil.Alert("Constants Class Generation", "No user category has been defined or category ID is missing.");
            }
        }

        //----------------------------------------------------------------
        // Importing Android Notification Res Folder
        //----------------------------------------------------------------
        void ImportAndroidNotificationResFolder()
        {
            string selectedFolder = EditorUtility.OpenFolderPanel(null, notificationSelectedAndroidResFolder, null);

            if (!string.IsNullOrEmpty(selectedFolder))
            {
                // Some folder was selected.
                notificationSelectedAndroidResFolder = selectedFolder;

                // Build Android library from the selected folder.
                if (EM_EditorUtil.DisplayDialog(
                        "Building Android Resources",
                        "Please make sure the selected folder is correct before proceeding.\n" + notificationSelectedAndroidResFolder,
                        "Go Ahead",
                        "Cancel"))
                {
                    // Prepare the lib config.
                    var config = new EM_AndroidLibBuilder.AndroidLibConfig();
                    config.packageName             = EM_Constants.AndroidNativeNotificationPackageName;
                    config.targetLibFolderName     = EM_Constants.NotificationAndroidResFolderName;
                    config.targetContentFolderName = "res";

                    EM_AndroidLibBuilder.BuildAndroidLibFromFolder(
                        notificationSelectedAndroidResFolder,
                        config,
                        OnAndroidNotificationResBuildProgress,
                        OnAndroidNotificationResBuildNewStep,
                        OnAndroidNotificationResBuildComplete
                        );
                }
            }
        }

        void OnAndroidNotificationResBuildProgress(float progress)
        {
            // Display progress bar.
            EditorUtility.DisplayProgressBar(
                "Generating Android Notification Resources",
                notificationCreateAndroidResCurrentStep,
                progress
                );
        }

        void OnAndroidNotificationResBuildNewStep(string step)
        {
            notificationCreateAndroidResCurrentStep = step;
        }

        void OnAndroidNotificationResBuildComplete()
        {
            EditorUtility.ClearProgressBar();
            EM_EditorUtil.Alert(
                "Android Resources Imported",
                "Android notification resources have been imported successfully!"
                );
        }
    }
        // Generate a static class containing constants of leaderboard and achievement names.
        void GenerateGameServiceConstants()
        {
            // First create a hashtable containing all the names to be stored as constants.
            SerializedProperty ldbProp = GameServiceProperties.leaderboards.property;
            SerializedProperty acmProp = GameServiceProperties.achievements.property;

            // First check if there're duplicate names.
            string duplicateLdbName = EM_EditorUtil.FindDuplicateFieldInArrayProperty(ldbProp, GameServiceItem_NameProperty);

            if (!string.IsNullOrEmpty(duplicateLdbName))
            {
                EM_EditorUtil.Alert("Error: Duplicate Names", "Found duplicate leaderboard name of \"" + duplicateLdbName + "\".");
                return;
            }

            string duplicateAcmName = EM_EditorUtil.FindDuplicateFieldInArrayProperty(acmProp, GameServiceItem_NameProperty);

            if (!string.IsNullOrEmpty(duplicateAcmName))
            {
                EM_EditorUtil.Alert("Error: Duplicate Names", "Found duplicate achievement name of \"" + duplicateAcmName + "\".");
                return;
            }

            // Proceed with adding resource keys.
            Hashtable resourceKeys = new Hashtable();

            // Add the leaderboard names.
            for (int i = 0; i < ldbProp.arraySize; i++)
            {
                SerializedProperty element = ldbProp.GetArrayElementAtIndex(i);
                string             name    = element.FindPropertyRelative(GameServiceItem_NameProperty).stringValue;

                // Ignore all items with an empty name.
                if (!string.IsNullOrEmpty(name))
                {
                    string key = "Leaderboard_" + name;
                    resourceKeys.Add(key, name);
                }
            }

            // Add the achievement names.
            for (int j = 0; j < acmProp.arraySize; j++)
            {
                SerializedProperty element = acmProp.GetArrayElementAtIndex(j);
                string             name    = element.FindPropertyRelative(GameServiceItem_NameProperty).stringValue;

                // Ignore all items with an empty name.
                if (!string.IsNullOrEmpty(name))
                {
                    string key = "Achievement_" + name;
                    resourceKeys.Add(key, name);
                }
            }

            if (resourceKeys.Count > 0)
            {
                // Now build the class.
                EM_EditorUtil.GenerateConstantsClass(
                    EM_Constants.GeneratedFolder,
                    EM_Constants.RootNameSpace + "." + EM_Constants.GameServicesConstantsClassName,
                    resourceKeys,
                    true
                    );
            }
            else
            {
                EM_EditorUtil.Alert("Constants Class Generation", "Please fill in required information for all leaderboards and achievements.");
            }
        }