Initial commit: Unity WordConnect project

This commit is contained in:
2025-08-01 19:12:05 +08:00
commit f14db75802
3503 changed files with 448337 additions and 0 deletions

View File

@ -0,0 +1,43 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System.IO;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace WordsToolkit.Scripts.Editor
{
[InitializeOnLoad]
public class Autorun
{
static Autorun()
{
EditorApplication.update += InitProject;
}
private static void InitProject()
{
EditorApplication.update -= InitProject;
if (EditorApplication.timeSinceStartup < 10 || !EditorPrefs.GetBool(Application.dataPath + "AlreadyOpened"))
{
if (SceneManager.GetActiveScene().name != "game" && Directory.Exists("Assets/WordConnectGameToolkit/Scenes"))
{
EditorSceneManager.OpenScene("Assets/WordConnectGameToolkit/Scenes/main.unity");
}
EditorPrefs.SetBool(Application.dataPath + "AlreadyOpened", true);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8c10bf1643144c3398f97f61968329b4
timeCreated: 1725765484

View File

@ -0,0 +1,24 @@
{
"name": "CandySmith.BlockPuzzle.Drawers",
"rootNamespace": "",
"references": [
"GUID:2bafac87e7f4b9b418d9448d219b01ab",
"GUID:d3bf71b33c0c04eb9bc1a8d6513d76bb",
"GUID:b0214a6008ed146ff8f122a6a9c2f6cc",
"GUID:00dd4a7ac8c24c898083910c81898ecc",
"GUID:ac6e78962cfc743b9a5fc5f5a808aa72",
"GUID:b25ad8286798741e3b2cc3883283e669",
"GUID:75bdbcf23199f4cfb86c610d1d946666"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c912612fdd262440c8671324bf615a5d
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 02cd75ced4a647a6b2dcece605b128db
timeCreated: 1725704655

View File

@ -0,0 +1,65 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEditor;
using UnityEngine.UIElements;
using WordsToolkit.Scripts.Attributes;
using WordsToolkit.Scripts.Gameplay;
using WordsToolkit.Scripts.Settings;
namespace WordsToolkit.Scripts.Editor.Drawers
{
// Custom attribute
// Drawer for the custom attribute
[CustomPropertyDrawer(typeof(IconPreviewAttribute))]
public class IconDrawer : PropertyDrawer
{
private Label m_Icon;
private ScriptableData m_IconScriptable;
private SerializedProperty m_property;
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
m_property = property;
m_Icon = new Label();
m_Icon.style.width = 200;
m_Icon.style.height = 200;
// get parent of the property
m_IconScriptable = property.serializedObject.targetObject as ScriptableData;
if (m_IconScriptable != null)
{
m_IconScriptable.OnChange += UpdatePreview;
}
UpdatePreview();
return m_Icon;
}
private void UpdatePreview()
{
EditorApplication.delayCall += () =>
{
var itemTemplate = m_IconScriptable as ColorsTile;
if (itemTemplate != null && itemTemplate.HasCustomPrefab())
{
// m_Icon.style.backgroundImage = EditorUtils.GetPrefabPreview(itemTemplate.customItemPrefab.gameObject);
}
else
{
m_Icon.style.backgroundImage = EditorUtils.GetCanvasPreviewVisualElement(m_IconScriptable.prefab, obj => obj.FillIcon(m_IconScriptable));
}
};
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 170ab4d5b40704f788163abb29cc0bd8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,30 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEditor;
using UnityEngine.UIElements;
using WordsToolkit.Scripts.Data;
namespace WordsToolkit.Scripts.Editor.Drawers
{
[CustomPropertyDrawer(typeof(ResourceValue))]
public class ResourceValueDrawer : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
var root = new VisualElement();
root.Add(new Label("Resource Value: " + ((ResourceObject)property.serializedObject.targetObject).LoadResource()));
return root;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cf9081abd17a4ace8aaf39202c8b18da
timeCreated: 1712217714

View File

@ -0,0 +1,175 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
using WordsToolkit.Scripts.Utils;
namespace WordsToolkit.Scripts.Editor.Drawers
{
[CustomPropertyDrawer(typeof(SerializableDictionary<,>))]
public class SerializableDictionaryDrawer : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
var container = new VisualElement();
var foldout = new Foldout();
foldout.text = property.displayName;
container.Add(foldout);
var listView = new ListView();
foldout.Add(listView);
var dictionaryType = fieldInfo.FieldType;
var keyType = dictionaryType.GetGenericArguments()[0];
listView.makeItem = () => new PackElementDictionaryItemElement(keyType);
listView.bindItem = (element, index) =>
{
var itemElement = (PackElementDictionaryItemElement)element;
var keysProp = property.FindPropertyRelative("keys");
var valuesProp = property.FindPropertyRelative("values");
itemElement.BindProperties(keysProp.GetArrayElementAtIndex(index), valuesProp.GetArrayElementAtIndex(index), property, index);
};
listView.showAddRemoveFooter = true;
listView.showFoldoutHeader = false;
listView.showBorder = true;
listView.showBoundCollectionSize = false;
listView.reorderable = true;
void RefreshList()
{
var keysProp = property.FindPropertyRelative("keys");
listView.itemsSource = new List<int>(Enumerable.Range(0, keysProp.arraySize));
listView.RefreshItems();
}
RefreshList();
listView.itemsAdded += indexes =>
{
var keysProp = property.FindPropertyRelative("keys");
var valuesProp = property.FindPropertyRelative("values");
property.serializedObject.Update();
foreach (var index in indexes)
{
keysProp.InsertArrayElementAtIndex(index);
valuesProp.InsertArrayElementAtIndex(index);
var keyProp = keysProp.GetArrayElementAtIndex(index);
keyProp.objectReferenceValue = null;
var valueProp = valuesProp.GetArrayElementAtIndex(index);
// Reset the value based on its type
ResetValue(valueProp);
}
property.serializedObject.ApplyModifiedProperties();
RefreshList();
};
listView.itemsRemoved += indexes =>
{
var keysProp = property.FindPropertyRelative("keys");
var valuesProp = property.FindPropertyRelative("values");
property.serializedObject.Update();
foreach (var index in indexes.OrderByDescending(i => i))
{
keysProp.DeleteArrayElementAtIndex(index);
valuesProp.DeleteArrayElementAtIndex(index);
}
property.serializedObject.ApplyModifiedProperties();
RefreshList();
};
return container;
}
private void ResetValue(SerializedProperty valueProp)
{
switch (valueProp.propertyType)
{
case SerializedPropertyType.Integer:
valueProp.intValue = 0;
break;
case SerializedPropertyType.Boolean:
valueProp.boolValue = false;
break;
case SerializedPropertyType.Float:
valueProp.floatValue = 0f;
break;
case SerializedPropertyType.String:
valueProp.stringValue = string.Empty;
break;
case SerializedPropertyType.ObjectReference:
valueProp.objectReferenceValue = null;
break;
// Add more cases as needed for other types
}
}
}
public class PackElementDictionaryItemElement : VisualElement
{
private readonly ObjectField keyField;
private readonly PropertyField valueField;
public PackElementDictionaryItemElement(Type keyType)
{
style.flexDirection = FlexDirection.Row;
style.flexGrow = 1;
style.width = Length.Percent(100);
keyField = new ObjectField
{
style =
{
flexGrow = 1,
flexBasis = 0,
marginRight = 5,
marginLeft = 5
}
};
keyField.objectType = keyType;
Add(keyField);
valueField = new PropertyField
{
style =
{
flexGrow = 1,
flexBasis = 0,
marginRight = 5
}
};
Add(valueField);
}
public void BindProperties(SerializedProperty keyProp, SerializedProperty valueProp, SerializedProperty parentProp, int index)
{
keyField.BindProperty(keyProp);
valueField.BindProperty(valueProp);
keyField.label = string.Empty;
valueField.label = string.Empty;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 79fbdca59d1e412280be81efe1abef0b
timeCreated: 1723101447

View File

@ -0,0 +1,51 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System.Collections.Generic;
namespace WordsToolkit.Scripts.Editor.Drawers
{
using UnityEditor;
using UnityEngine.UIElements;
using UnityEditor.UIElements;
[CustomPropertyDrawer(typeof(TagFieldUIAttribute))]
public class TagFieldUIPropertyDrawer : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
var container = new VisualElement();
if (property.propertyType == SerializedPropertyType.String)
{
var dropdown = new DropdownField(property.displayName);
dropdown.choices = new List<string>(UnityEditorInternal.InternalEditorUtility.tags);
dropdown.value = property.stringValue;
dropdown.RegisterValueChangedCallback(evt =>
{
property.stringValue = evt.newValue;
property.serializedObject.ApplyModifiedProperties();
});
container.Add(dropdown);
}
else
{
var propertyField = new PropertyField(property);
container.Add(propertyField);
}
return container;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6919d8875d47445b8a6d10d54e7cd8b2
timeCreated: 1747970167

View File

@ -0,0 +1,166 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using WordsToolkit.Scripts.Gameplay.Managers;
using WordsToolkit.Scripts.System;
namespace WordsToolkit.Scripts.Editor
{
public static class EditorMenu
{
public static string WordConnect = "WordConnect";
private static string WordConnectPath = "Assets/WordConnectGameToolkit";
[MenuItem( nameof(WordConnect) + "/Settings/Shop settings")]
public static void IAPProducts()
{
Selection.activeObject = AssetDatabase.LoadMainAssetAtPath(WordConnectPath + "/Resources/Settings/CoinsShopSettings.asset");
}
[MenuItem( nameof(WordConnect) + "/Settings/Ads settings")]
public static void AdsSettings()
{
Selection.activeObject = AssetDatabase.LoadMainAssetAtPath(WordConnectPath + "/Resources/Settings/AdsSettings.asset");
}
//DailyBonusSettings
[MenuItem( nameof(WordConnect) + "/Settings/Daily bonus settings")]
public static void DailyBonusSettings()
{
Selection.activeObject = AssetDatabase.LoadMainAssetAtPath(WordConnectPath + "/Resources/Settings/DailyBonusSettings.asset");
}
//GameSettings
[MenuItem( nameof(WordConnect) + "/Settings/Game settings")]
public static void GameSettings()
{
Selection.activeObject = AssetDatabase.LoadMainAssetAtPath(WordConnectPath + "/Resources/Settings/GameSettings.asset");
}
//SpinSettings
[MenuItem( nameof(WordConnect) + "/Settings/Spin settings")]
public static void SpinSettings()
{
Selection.activeObject = AssetDatabase.LoadMainAssetAtPath(WordConnectPath + "/Resources/Settings/SpinSettings.asset");
}
//DebugSettings
[MenuItem( nameof(WordConnect) + "/Settings/Debug settings")]
public static void DebugSettings()
{
Selection.activeObject = AssetDatabase.LoadMainAssetAtPath(WordConnectPath + "/Resources/Settings/DebugSettings.asset");
}
[MenuItem( nameof(WordConnect) + "/Settings/Crossword config")]
public static void CrosswordConfig()
{
Selection.activeObject = AssetDatabase.LoadMainAssetAtPath(WordConnectPath + "/Resources/Settings/CrosswordConfig.asset");
}
[MenuItem( nameof(WordConnect) + "/Settings/Tutorial settings")]
public static void TutorialSettings()
{
Selection.activeObject = AssetDatabase.LoadMainAssetAtPath(WordConnectPath + "/Resources/Settings/TutorialSettings.asset");
}
[MenuItem( nameof(WordConnect) + "/Settings/Language configuration")]
public static void LanguageConfiguration()
{
Selection.activeObject = AssetDatabase.LoadMainAssetAtPath(WordConnectPath + "/Resources/Settings/LanguageConfiguration.asset");
}
[MenuItem( nameof(WordConnect) + "/Settings/Gift settings")]
public static void GiftSettings()
{
Selection.activeObject = AssetDatabase.LoadMainAssetAtPath(WordConnectPath + "/Resources/Settings/GiftSettings.asset");
}
[MenuItem( nameof(WordConnect) + "/Scenes/Main scene &1", priority = 0)]
public static void MainScene()
{
EditorSceneManager.OpenScene(WordConnectPath + "/Scenes/main.unity");
var stateManager = Object.FindObjectOfType<StateManager>();
if (stateManager != null)
{
stateManager.CurrentState = EScreenStates.MainMenu;
}
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
}
[MenuItem( nameof(WordConnect) + "/Scenes/Game scene &2")]
public static void GameScene()
{
var stateManager = Object.FindObjectOfType<StateManager>();
stateManager.CurrentState = EScreenStates.Game;
EditorSceneManager.MarkSceneDirty(EditorSceneManager.GetActiveScene());
}
[MenuItem( nameof(WordConnect) + "/Editor/Tile editor", priority = 1)]
public static void ColorEditor()
{
string folderPath = WordConnectPath + "/Resources/ColorsTile";
// Get all tile assets in the folder
string[] guids = AssetDatabase.FindAssets("t:Object", new[] { folderPath });
if (guids.Length == 0)
{
Debug.LogWarning($"No tile assets found in: {folderPath}");
return;
}
// Select a random tile asset
string randomGuid = guids[Random.Range(0, guids.Length)];
string assetPath = AssetDatabase.GUIDToAssetPath(randomGuid);
var tileAsset = AssetDatabase.LoadAssetAtPath<Object>(assetPath);
// Select and ping the tile asset in the Project window
Selection.activeObject = tileAsset;
EditorGUIUtility.PingObject(tileAsset);
}
[MenuItem( nameof(WordConnect) + "/Documentation/Main", priority = 2)]
public static void MainDoc()
{
Application.OpenURL("https://candy-smith.gitbook.io/main");
}
[MenuItem( nameof(WordConnect) + "/Documentation/ADS/Setup ads")]
public static void UnityadsDoc()
{
Application.OpenURL("https://candy-smith.gitbook.io/bubble-shooter-toolkit/tutorials/ads-setup/");
}
[MenuItem( nameof(WordConnect) + "/Documentation/Unity IAP (in-apps)")]
public static void Inapp()
{
Application.OpenURL("https://candy-smith.gitbook.io/main/block-puzzle-game-toolkit/setting-up-in-app-purchase-products");
}
[MenuItem( nameof(WordConnect) + "/NLP/Training Language Model")]
public static void TrainingModel()
{
Application.OpenURL("https://colab.research.google.com/drive/199zNcB3FPfnrD6E7OiwmwCcf27jMnY1b?usp=sharing");
}
[MenuItem( nameof(WordConnect) + "/Reset PlayerPrefs &e")]
private static void ResetPlayerPrefs()
{
GameDataManager.ClearALlData();
PlayerPrefs.DeleteKey("GameState");
Debug.Log("PlayerPrefs are reset");
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c84cc4add82c408ab2fbbd7d25c01ba6
timeCreated: 1725699029

View File

@ -0,0 +1,255 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.UIElements;
using WordsToolkit.Scripts.Gameplay;
using WordsToolkit.Scripts.System;
using WordsToolkit.Scripts.Utils;
using Object = UnityEngine.Object;
namespace WordsToolkit.Scripts.Editor
{
public static class EditorUtils
{
public static Texture2D GetPrefabPreview(GameObject prefab)
{
var previewRender = new PreviewRenderUtility();
previewRender.camera.backgroundColor = Color.black;
previewRender.camera.clearFlags = CameraClearFlags.SolidColor;
previewRender.camera.cameraType = CameraType.Game;
previewRender.camera.farClipPlane = 1000f;
previewRender.camera.nearClipPlane = 0.1f;
var obj = previewRender.InstantiatePrefabInScene(prefab);
var rect = obj.GetComponent<RectTransform>().rect;
previewRender.BeginStaticPreview(new Rect(0.0f, 0.0f, rect.width*1.5f, rect.height*1.5f));
SetupPreviewCanvas(obj, previewRender.camera);
previewRender.Render();
var texture = previewRender.EndStaticPreview();
previewRender.camera.targetTexture = null;
previewRender.Cleanup();
return texture;
}
private static void SetupPreviewCanvas(GameObject obj, Camera camera)
{
var canvasInstance = obj.AddComponent<Canvas>();
canvasInstance.renderMode = RenderMode.ScreenSpaceCamera;
canvasInstance.worldCamera = camera;
var canvasScaler = obj.AddComponent<CanvasScaler>();
canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ConstantPixelSize;
canvasScaler.referenceResolution = new Vector2(1920, 1080);
var scaleFactorX = Screen.width / canvasScaler.referenceResolution.x;
var scaleFactorY = Screen.height / canvasScaler.referenceResolution.y;
canvasScaler.scaleFactor = Mathf.Min(scaleFactorX, scaleFactorY) * 7;
}
public static SerializedProperty GetPropertyFromValue(Object targetObject)
{
var serializedObject = new SerializedObject(targetObject);
var property = serializedObject.GetIterator();
// Go through each property in the object
while (property.Next(true))
{
// Skip properties with child properties (e.g., arrays, structs)
if (property.hasVisibleChildren)
{
continue;
}
// Check if the property value matches the desired field value
// if (fieldValue.Equals(GetFieldValue(targetObject, property.name)))
{
// Create a copy of the property
var copiedProperty = property.Copy();
// Make sure the serializedObject is up to date
copiedProperty.serializedObject.Update();
// Apply the modified properties
copiedProperty.serializedObject.ApplyModifiedProperties();
return copiedProperty;
}
}
return null; // Field value not found
}
private static object GetFieldValue(Object targetObject, string fieldName)
{
var field = targetObject.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null)
{
return field.GetValue(targetObject);
}
Debug.LogError($"Field {fieldName} not found in object {targetObject.GetType().Name}");
return null;
}
public static VisualElement GetObjectFields(SerializedObject serializedObject, Action<SerializedProperty> onChange = null)
{
var visualElement = new VisualElement();
// Iterate through the fields of the Icon class
var iterator = serializedObject.GetIterator();
var enterChildren = true;
while (iterator.NextVisible(enterChildren))
{
// Exclude the "m_Script" field
if (iterator.name == "m_Script")
{
continue;
}
// Create a PropertyField for each field
var propertyField = new PropertyField(iterator.Copy());
propertyField.Bind(serializedObject);
propertyField.style.flexShrink = 0;
propertyField.style.flexGrow = 0;
propertyField.style.width = 400;
propertyField.RegisterValueChangeCallback(evt => { onChange?.Invoke(evt.changedProperty); });
visualElement.Add(propertyField);
enterChildren = false;
}
return visualElement;
}
public static VisualElement GetPropertyFields(SerializedProperty property, bool children, Action<SerializedProperty> onChange = null)
{
var visualElement = new VisualElement();
var methods = TypeCache.GetMethodsWithAttribute<CustomSerializeTypePropertyAttribute>();
foreach (var m in methods)
{
foreach (var customAttributeData in m.CustomAttributes)
{
foreach (var customAttributeTypedArgument in customAttributeData.ConstructorArguments)
{
if (property.managedReferenceValue != null && (Type)customAttributeTypedArgument.Value != property.managedReferenceValue.GetType())
{
continue;
}
if (m.IsStatic)
{
return m.Invoke(null, new object[] { property }) as VisualElement;
}
var instance = Activator.CreateInstance(m.DeclaringType);
return m.Invoke(instance, new object[] { property }) as VisualElement;
}
}
}
// Iterate through the fields of the Icon class
var iterator = property.Copy();
while (iterator.NextVisible(children))
{
// Exclude the "m_Script" field
if (iterator.name == "m_Script")
{
continue;
}
if (iterator.depth == property.depth + 1)
{
// Create a PropertyField for each field
var propertyField = new PropertyField(iterator.Copy());
propertyField.Bind(property.serializedObject);
propertyField.style.flexShrink = 0;
propertyField.style.flexGrow = 0;
propertyField.style.width = 400;
propertyField.RegisterValueChangeCallback(evt => { onChange?.Invoke(evt.changedProperty); });
visualElement.Add(propertyField);
}
children = false;
}
return visualElement;
}
public static DropdownField GetTypesDropdown(SerializedProperty property)
{
var fieldInfo = TypeCache.GetFieldsWithAttribute<SerializeTypePropertyAttribute>()[0];
var typesDerivedFrom = TypeCache.GetTypesDerivedFrom(fieldInfo.FieldType);
var typeNames = typesDerivedFrom.Select(t => t.Name).ToList();
var typeNamesFormatted = typesDerivedFrom.Select(t => t.Name.CamelCaseSplit()).ToList();
var parentTypeName = fieldInfo.Name.CamelCaseSplit();
// get the index of the current type
var currentIndex = Mathf.Max(typeNames.IndexOf(property.managedReferenceValue?.GetType().Name), 0);
var dropdown = new DropdownField(parentTypeName, typeNamesFormatted, typeNamesFormatted[currentIndex]);
dropdown.RegisterCallback<ChangeEvent<string>>(evt =>
{
var selectedTypeIndex = typeNamesFormatted.IndexOf(evt.newValue);
if (selectedTypeIndex >= 0 && selectedTypeIndex < typesDerivedFrom.Count)
{
property.managedReferenceValue = Activator.CreateInstance(typesDerivedFrom[selectedTypeIndex]);
property.serializedObject.ApplyModifiedProperties();
}
else
{
Debug.LogError($"Selected type index {selectedTypeIndex} is out of range.");
}
});
return dropdown;
}
public static Vector2 GetAbsolutePosition(List<VisualElement> elements, VisualElement parent)
{
var position = Vector2.zero;
foreach (var element in elements)
{
position += element.LocalToWorld(element.layout.position);
}
return parent.WorldToLocal(position / elements.Count);
}
public static Texture2D GetCanvasPreviewVisualElement<T>(T prefab, Action<T> action) where T : FillAndPreview
{
var previewRender = new PreviewRenderUtility();
previewRender.camera.backgroundColor = Color.black;
previewRender.camera.clearFlags = CameraClearFlags.SolidColor;
previewRender.camera.cameraType = CameraType.Game;
previewRender.camera.farClipPlane = 1000f;
previewRender.camera.nearClipPlane = 0.1f;
var obj = previewRender.InstantiatePrefabInScene(prefab.gameObject);
action.Invoke(obj.GetComponent<T>());
var rect = obj.GetComponent<RectTransform>().rect;
previewRender.BeginStaticPreview(new Rect(0.0f, 0.0f, rect.width, rect.height));
SetupPreviewCanvas(obj, previewRender.camera);
previewRender.Render();
var texture = previewRender.EndStaticPreview();
previewRender.camera.targetTexture = null;
previewRender.Cleanup();
return texture;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 21dbac26e1b24752a2671933b2f10f85
timeCreated: 1727422844

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: af35eb4a48054f31955ec1854a3992f8
timeCreated: 1725694446

View File

@ -0,0 +1,86 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System.Reflection;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
using WordsToolkit.Scripts.GUI.Buttons;
namespace WordsToolkit.Scripts.Editor.GUI
{
[CustomEditor(typeof(CustomButton), true)]
internal class CustomButtonDrawer : UnityEditor.Editor
{
private CustomButtonEditor customButtonEditor;
private void OnEnable()
{
customButtonEditor = new CustomButtonEditor();
}
public override VisualElement CreateInspectorGUI()
{
var root = new VisualElement();
root.Add(new PropertyField(serializedObject.FindProperty("noSound")));
root.Add(new PropertyField(serializedObject.FindProperty("overrideClickSound")));
root.Add(new PropertyField(serializedObject.FindProperty("overrideAnimatorController")));
var foldout = new Foldout { text = "Custom Button Settings", value = false };
foldout.Add(customButtonEditor.CreateInspectorGUI(serializedObject));
root.Add(foldout);
// Draw default inspector for inherited fields
var iterator = serializedObject.GetIterator();
iterator.NextVisible(true); // Skip Script field
var customButtonType = typeof(CustomButton);
while (iterator.NextVisible(false))
{
var field = iterator.serializedObject.targetObject.GetType().GetField(iterator.name);
if (field != null)
{
// Show fields from parent classes AND child classes, but not from CustomButton itself
if (field.DeclaringType != customButtonType)
{
root.Add(new PropertyField(serializedObject.FindProperty(iterator.name)));
}
}
}
// Draw fields from derived class if any
var targetType = serializedObject.targetObject.GetType();
if (targetType != customButtonType)
{
var derivedFields = targetType.GetFields(BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.DeclaredOnly);
foreach (var field in derivedFields)
{
if (field.IsPrivate && !field.IsDefined(typeof(SerializeField), false))
continue;
var property = serializedObject.FindProperty(field.Name);
if (property != null)
{
root.Add(new PropertyField(property));
}
}
}
return root;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f5ef7a07874d4973a9672a27bb8b024e
timeCreated: 1748678361

View File

@ -0,0 +1,156 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.UI;
using UnityEngine.UIElements;
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.Popups.Reward;
namespace WordsToolkit.Scripts.Editor.GUI
{
public class CustomButtonEditor
{
public VisualElement CreateInspectorGUI(SerializedObject serializedObject)
{
var root = new VisualElement();
root.Add(new VisualElement { name = "space", style = { height = 10 } });
// Add the Interactable field
root.Add(GetBindPropertyField(serializedObject, "m_Interactable", "Interactable"));
// Add the Transition field
var transition = GetPropertyField(serializedObject, "m_Transition", "Transition");
transition.Bind(serializedObject);
root.Add(transition);
var onClickProperty = GetBindPropertyField(serializedObject, "m_OnClick", "On Click");
var spriteProperty = GetPropertyField(serializedObject, "m_SpriteState", "Sprites");
var colorProperty = GetPropertyField(serializedObject, "m_Colors", "Colors");
var animations = GetPropertyField(serializedObject, "m_AnimationTriggers", "Animations");
transition.RegisterValueChangeCallback(evt =>
{
Transition(serializedObject, root, spriteProperty, animations, colorProperty);
root.Remove(onClickProperty);
root.Add(onClickProperty);
});
Transition(serializedObject, root, spriteProperty, animations, colorProperty);
root.Add(onClickProperty);
colorProperty.Bind(serializedObject);
spriteProperty.Bind(serializedObject);
animations.Bind(serializedObject);
return root;
}
private static void Transition(SerializedObject serializedObject, VisualElement root, PropertyField spriteProperty, PropertyField animations, PropertyField colorProperty)
{
var transitionProperty = serializedObject.FindProperty("m_Transition");
// Add transition details based on the selected type
if (transitionProperty.enumValueIndex == (int)Selectable.Transition.ColorTint)
{
if (root.Contains(spriteProperty))
{
root.Remove(spriteProperty);
}
if (root.Contains(animations))
{
root.Remove(animations);
}
root.Add(colorProperty);
}
else if (transitionProperty.enumValueIndex == (int)Selectable.Transition.SpriteSwap)
{
if (root.Contains(colorProperty))
{
root.Remove(colorProperty);
}
if (root.Contains(animations))
{
root.Remove(animations);
}
root.Add(spriteProperty);
}
else if (transitionProperty.enumValueIndex == (int)Selectable.Transition.Animation)
{
if (root.Contains(colorProperty))
{
root.Remove(colorProperty);
}
if (root.Contains(spriteProperty))
{
root.Remove(spriteProperty);
}
root.Add(animations);
}
else if (transitionProperty.enumValueIndex == (int)Selectable.Transition.None)
{
if (root.Contains(colorProperty))
{
root.Remove(colorProperty);
}
if (root.Contains(spriteProperty))
{
root.Remove(spriteProperty);
}
if (root.Contains(animations))
{
root.Remove(animations);
}
}
}
private VisualElement GetBindPropertyField(SerializedObject serializedObject, string propertyName, string label)
{
var propertyField = GetPropertyField(serializedObject, propertyName, label);
propertyField.Bind(serializedObject);
return propertyField;
}
private PropertyField GetPropertyField(SerializedObject serializedObject, string propertyName, string label)
{
return new PropertyField(serializedObject.FindProperty(propertyName), label);
}
}
[CustomEditor(typeof(RewardedButton))]
internal class RewardedButtonEditor : UnityEditor.Editor
{
private CustomButtonEditor customButtonEditor;
private void OnEnable()
{
customButtonEditor = new CustomButtonEditor();
}
public override VisualElement CreateInspectorGUI()
{
var root = new VisualElement();
// Add a property field for placement
var placementField = new PropertyField(serializedObject.FindProperty("placement"), "Placement");
root.Add(placementField);
root.Add(customButtonEditor.CreateInspectorGUI(serializedObject));
return root;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e393e17f265f4314be795dcbc434ac2a
timeCreated: 1709550126

View File

@ -0,0 +1,132 @@
// // ©2015 - 2025 Candy Smith
// // All rights reserved
// // Redistribution of this software is strictly not allowed.
// // Copy of this software can be obtained from unity asset store only.
// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// // FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
// // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// // THE SOFTWARE.
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
using WordsToolkit.Scripts.Utils;
namespace WordsToolkit.Scripts.Editor
{
public class PostImporting : AssetPostprocessor
{
private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
CheckDefines("Assets/GoogleMobileAds", "ADMOB");
CheckDefines("Assets/FacebookSDK", "FACEBOOK");
CheckDefines("Assets/PlayFabSDK", "PLAYFAB");
CheckDefines("Assets/GameSparks", "GAMESPARKS");
CheckDefines("Assets/Appodeal", "APPODEAL");
}
private static void CheckDefines(string path, string symbols)
{
if (Directory.Exists(path))
{
DefineSymbolsUtils.AddSymbol(symbols);
}
else
{
DefineSymbolsUtils.DeleteSymbol(symbols);
}
}
public static void CheckIronsourceFolder()
{
var str = "Assets/LevelPlay";
if (Directory.Exists(str))
{
var asmdefPath = Path.Combine(str, "IronsourceAssembly.asmdef");
if (!File.Exists(asmdefPath))
{
CreateAsmdefIronSource(asmdefPath);
}
// get GUID of the IronsourceAssembly.asmdef
var guid = AssetDatabase.AssetPathToGUID(asmdefPath);
// assign asmdef to the Scripts/Ads/CandySmith.Ads.asmdef
var adsAsmdefPath = Path.Combine("Assets/WordConnectGameToolkit/Scripts/Ads", "CandySmith.Ads.asmdef");
if (File.Exists(adsAsmdefPath))
{
var asmdef = JsonUtility.FromJson<AssemblyDefinition>(File.ReadAllText(adsAsmdefPath));
// check references and add IronsourceAssembly if not exists
if (asmdef.references == null)
{
asmdef.references = new[] { "IronsourceAssembly" };
}
else
{
if (Array.IndexOf(asmdef.references, "IronsourceAssembly") == -1 && Array.IndexOf(asmdef.references, "GUID:" + guid) == -1)
{
Array.Resize(ref asmdef.references, asmdef.references.Length + 1);
asmdef.references[asmdef.references.Length - 1] = "IronsourceAssembly";
}
}
File.WriteAllText(adsAsmdefPath, JsonUtility.ToJson(asmdef, true));
AssetDatabase.Refresh();
}
}
}
private static void CreateAsmdefIronSource(string path)
{
var assemblyDefinition = new AssemblyDefinition
{
name = "IronsourceAssembly",
references = new string[0],
includePlatforms = new string[0],
excludePlatforms = new string[0],
allowUnsafeCode = false,
overrideReferences = false,
precompiledReferences = new string[0],
autoReferenced = true,
defineConstraints = new string[0],
versionDefines = new VersionDefine[]
{
new()
{
name = "com.unity.services.levelplay",
define = "IRONSOURCE"
}
}
};
File.WriteAllText(path, JsonUtility.ToJson(assemblyDefinition, true));
AssetDatabase.Refresh();
}
}
[Serializable]
public class AssemblyDefinition
{
public string name;
public string[] references;
public string[] includePlatforms;
public string[] excludePlatforms;
public bool allowUnsafeCode;
public bool overrideReferences;
public string[] precompiledReferences;
public bool autoReferenced;
public string[] defineConstraints;
public VersionDefine[] versionDefines;
}
[Serializable]
public class VersionDefine
{
public string name;
public string expression;
public string define;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c79109d6925a4967a20c2e24d466dcc7
timeCreated: 1725699029