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,27 @@
// // ©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;
namespace WordsToolkit.Scripts.System
{
[AttributeUsage(AttributeTargets.Method)]
public class CustomSerializeTypePropertyAttribute : SerializeTypePropertyAttribute
{
public Type Description { get; }
public CustomSerializeTypePropertyAttribute(Type description)
{
Description = description;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e66ea3b1ddb14e0eadf46ab45a15b166
timeCreated: 1725704837

View File

@ -0,0 +1,36 @@
// // ©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;
namespace WordsToolkit.Scripts.System
{
public class Event<T>
{
private event Action<T> _event;
public void Subscribe(Action<T> subscriber)
{
_event += subscriber;
}
public void Unsubscribe(Action<T> subscriber)
{
_event -= subscriber;
}
public void Invoke(T arg)
{
_event?.Invoke(arg);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e4b49765164a41378a96bfa5418a423e
timeCreated: 1725698172

View File

@ -0,0 +1,89 @@
// // ©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 WordsToolkit.Scripts.Enums;
namespace WordsToolkit.Scripts.System
{
public static class EventManager
{
// A dictionary to hold all events
private static readonly Dictionary<EGameEvent, object> events = new();
public static Event<T> GetEvent<T>(EGameEvent eventName)
{
if (events.TryGetValue(eventName, out var e) && e is Event<T> typedEvent)
{
return typedEvent;
}
var newEvent = new Event<T>();
events[eventName] = newEvent;
return newEvent;
}
// no generic event
public static Event GetEvent(EGameEvent eventName)
{
if (events.TryGetValue(eventName, out var e) && e is Event typedEvent)
{
return typedEvent;
}
var newEvent = new Event();
events[eventName] = newEvent;
return newEvent;
}
public static Dictionary<EGameEvent, object> GetSubscribedEvents()
{
return events;
}
public static Action<EGameState> OnGameStateChanged;
private static EGameState gameStatus;
public static EGameState GameStatus
{
get => gameStatus;
set
{
if (gameStatus == value)
return;
gameStatus = value;
OnGameStateChanged?.Invoke(gameStatus);
}
}
}
public class Event
{
private event Action _event;
public void Subscribe(Action subscriber)
{
_event += subscriber;
}
public void Unsubscribe(Action subscriber)
{
_event -= subscriber;
}
public void Invoke()
{
_event?.Invoke();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8edb0b4c22b94346afdc11a697e6b4d2
timeCreated: 1725698126

View File

@ -0,0 +1,58 @@
// // ©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 UnityEngine;
namespace WordsToolkit.Scripts.System
{
public class FPSCounter : MonoBehaviour
{
private readonly float updateInterval = 0.5f;
private float accum;
private int frames;
private float timeLeft;
private float fps;
private readonly GUIStyle textStyle = new();
private void Start()
{
timeLeft = updateInterval;
// Set up the GUI style
textStyle.fontStyle = FontStyle.Bold;
textStyle.normal.textColor = Color.white;
textStyle.fontSize = 24; // Adjust this value to change the text size
}
private void Update()
{
timeLeft -= Time.deltaTime;
accum += Time.timeScale / Time.deltaTime;
frames++;
if (timeLeft <= 0f)
{
fps = accum / frames;
timeLeft = updateInterval;
accum = 0f;
frames = 0;
}
}
private void OnGUI()
{
// Display FPS in the top-left corner
UnityEngine.GUI.Label(new Rect(10, 10, 100, 30), "FPS: " + fps.ToString("F2"), textStyle);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 01dcce784a6940748924d2909c70ad22
timeCreated: 1726665435

View File

@ -0,0 +1,162 @@
// // ©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.Linq;
using UnityEditor;
using UnityEngine;
using WordsToolkit.Scripts.Data;
using WordsToolkit.Scripts.Enums;
using WordsToolkit.Scripts.Levels;
namespace WordsToolkit.Scripts.System
{
public static class GameDataManager
{
// Note: We're removing the static _level cache to avoid state issues
public static bool isTestPlay = false;
// Track if we're in test mode and which level is being tested
private static Level _testLevel;
public static void ClearPlayerProgress()
{
PlayerPrefs.DeleteKey("Level");
PlayerPrefs.Save();
}
public static void ClearALlData()
{
#if UNITY_EDITOR
// clear variables ResourceObject from Resources/Variables
var resourceObjects = Resources.LoadAll<ResourceObject>("Variables");
foreach (var resourceObject in resourceObjects)
{
resourceObject.Set(0);
}
AssetDatabase.SaveAssets();
PlayerPrefs.DeleteAll();
PlayerPrefs.DeleteAll();
PlayerPrefs.Save();
#endif
}
public static void UnlockLevel(int currentLevel)
{
isTestPlay = false;
// Store the level number in PlayerPrefs
PlayerPrefs.SetInt("Level", currentLevel);
PlayerPrefs.Save();
// No need to load and cache the level here
}
public static int GetLevelNum()
{
// Always get the current level from PlayerPrefs
return PlayerPrefs.GetInt("Level", 1);
}
public static Level GetLevel()
{
// For test play, return the test level if it exists
if (isTestPlay && _testLevel != null)
{
return _testLevel;
}
// Get current level number from PlayerPrefs
int currentLevelNum = GetLevelNum();
// Load the level directly from Resources
Level level = Resources.LoadAll<Level>("Levels").FirstOrDefault(i=>i.number == currentLevelNum);
// Debug level loading
if (level == null)
{
Debug.LogWarning($"Level 'Levels/level_{currentLevelNum}' not found");
}
// If level is null, LevelManager's fallback logic will handle finding a previous level
return level;
}
public static void SetLevel(Level level)
{
if (isTestPlay)
{
// In test mode, we store the level reference
_testLevel = level;
return;
}
// In normal mode, find the level number and store it in PlayerPrefs
if (level != null)
{
// Try to find the level's index in the Resources folder
Level[] allLevels = Resources.LoadAll<Level>("Levels").OrderBy(i => i.number).ToArray();
for (int i = 0; i < allLevels.Length; i++)
{
if (allLevels[i] == level)
{
// Found the level, store its index+1 as the level number
SetLevelNum(i + 1);
break;
}
}
}
}
public static EGameMode GetGameMode()
{
return (EGameMode)PlayerPrefs.GetInt("GameMode");
}
public static void SetGameMode(EGameMode gameMode)
{
PlayerPrefs.SetInt("GameMode", (int)gameMode);
PlayerPrefs.Save();
}
public static void SetAllLevelsCompleted()
{
var levels = Resources.LoadAll<Level>("Levels").Length;
PlayerPrefs.SetInt("Level", levels);
PlayerPrefs.Save();
}
internal static bool HasMoreLevels()
{
int currentLevel = GetLevelNum();
int totalLevels = Resources.LoadAll<Level>("Levels").Length;
return currentLevel < totalLevels;
}
public static void SetLevelNum(int stateCurrentLevel)
{
// Store the level in PlayerPrefs immediately
PlayerPrefs.SetInt("Level", stateCurrentLevel);
PlayerPrefs.Save();
// No need to clear cached level since we don't cache it anymore
}
public static void CleanupAfterTest()
{
isTestPlay = false;
_testLevel = null;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3ce0e28edfd14522abd162492d80e2a6
timeCreated: 1725699092

View File

@ -0,0 +1,292 @@
// // ©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.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using DG.Tweening;
using UnityEngine;
using VContainer;
using VContainer.Unity;
#if UNITY_2023_1_OR_NEWER
using Awaitable = UnityEngine.Awaitable;
#else
using Awaitable = System.Threading.Tasks.Task;
#endif
using WordsToolkit.Scripts.Audio;
using WordsToolkit.Scripts.Enums;
using WordsToolkit.Scripts.Gameplay.Managers;
using WordsToolkit.Scripts.GUI;
using WordsToolkit.Scripts.GUI.Buttons;
using WordsToolkit.Scripts.Popups;
using WordsToolkit.Scripts.Popups.Daily;
using WordsToolkit.Scripts.Services;
using WordsToolkit.Scripts.Services.IAP;
using WordsToolkit.Scripts.Settings;
using WordsToolkit.Scripts.Utils;
using ResourceManager = WordsToolkit.Scripts.Data.ResourceManager;
namespace WordsToolkit.Scripts.System
{
public class GameManager : IInitializable, IDisposable, IAsyncStartable
{
public Action<string> purchaseSucceded;
private (string id, ProductTypeWrapper.ProductType productType)[] products;
private int lastBackgroundIndex = -1;
private bool isTutorialMode;
private MainMenu mainMenu;
public Action<bool, List<string>> OnPurchasesRestored;
public ProductID noAdsProduct;
public string language = "en";
public int Score { get=> resourceManager.GetResource("Score").GetValue(); set => resourceManager.GetResource("Score").Set(value); }
private readonly StateManager stateManager;
private readonly SceneLoader sceneLoader;
private readonly MenuManager menuManager;
private readonly GameSettings gameSettings;
private readonly DailyBonusSettings dailyBonusSettings;
private readonly IIAPManager iapManager;
private readonly IInitializeGamingServices gamingServices;
private readonly ResourceManager resourceManager;
private readonly ILanguageService languageService;
public GameManager(
StateManager stateManager,
SceneLoader sceneLoader,
MenuManager menuManager,
GameSettings gameSettings,
DailyBonusSettings dailyBonusSettings,
IIAPManager iapManager,
IInitializeGamingServices gamingServices,
ResourceManager resourceManager,
ILanguageService languageService)
{
this.stateManager = stateManager;
this.sceneLoader = sceneLoader;
this.menuManager = menuManager;
this.gameSettings = gameSettings;
this.dailyBonusSettings = dailyBonusSettings;
this.iapManager = iapManager;
this.gamingServices = gamingServices;
this.resourceManager = resourceManager;
this.languageService = languageService;
}
public void Initialize()
{
mainMenu = menuManager.GetMainMenu();
if (mainMenu != null)
{
CustomButton.BlockInput(CheckDailyBonusConditions());
mainMenu.OnAnimationEnded += OnMainMenuAnimationEnded;
}
else
{
CustomButton.BlockInput(false);
}
// Get current language from LanguageService (already initialized as EntryPoint)
var langName = languageService.GetCurrentLanguageCode();
language = langName;
EventManager.GetEvent<string>(EGameEvent.LanguageChanged).Subscribe(LanguageChanged);
iapManager.SubscribeToPurchaseEvent(PurchaseSucceeded);
stateManager.OnStateChanged.AddListener((state) => {
if (state != EScreenStates.MainMenu)
CustomButton.BlockInput(false);
});
if (!IsTutorialShown() && !GameDataManager.isTestPlay)
{
SetTutorialMode(true);
}
}
private void LanguageChanged(string obj)
{
language = obj;
}
public void Dispose()
{
EventManager.GetEvent<string>(EGameEvent.LanguageChanged).Unsubscribe(LanguageChanged);
iapManager.UnsubscribeFromPurchaseEvent(PurchaseSucceeded);
if (mainMenu != null)
{
mainMenu.OnAnimationEnded -= OnMainMenuAnimationEnded;
}
if (GameDataManager.isTestPlay)
{
GameDataManager.CleanupAfterTest();
}
}
private bool IsTutorialShown()
{
return PlayerPrefs.GetInt("tutorial", 0) == 1;
}
public void SetTutorialCompleted()
{
PlayerPrefs.SetInt("tutorial", 1);
PlayerPrefs.Save();
}
async Awaitable IAsyncStartable.StartAsync(CancellationToken cancellation)
{
Application.targetFrameRate = 60;
DOTween.SetTweensCapacity(1250, 512);
if (gameSettings.enableInApps) {
products = Resources.LoadAll<ProductID>("ProductIDs")
.Select(p => (p.ID, p.productType))
.ToArray();
// Initialize gaming services
await gamingServices.Initialize(
OnInitializeSuccess,
OnInitializeError
);
// Initialize IAP directly if InitializeGamingServices is not used
await iapManager.InitializePurchasing(products);
}
if (GameDataManager.isTestPlay)
{
GameDataManager.SetLevel(GameDataManager.GetLevel());
}
}
private void OnInitializeSuccess()
{
Debug.Log("Gaming services initialized successfully");
}
private void OnInitializeError(string errorMessage)
{
Debug.LogError($"Failed to initialize gaming services: {errorMessage}");
}
private void HandleDailyBonus()
{
if (stateManager.CurrentState != EScreenStates.MainMenu || !dailyBonusSettings.dailyBonusEnabled || !gameSettings.enableInApps || !CheckDailyBonusConditions())
{
CustomButton.BlockInput(false);
return;
}
menuManager.ShowPopup<DailyBonus>();
DOVirtual.DelayedCall(0.5f, () => { CustomButton.BlockInput(false); });
}
private bool CheckDailyBonusConditions()
{
var today = DateTime.Today;
var lastRewardDate = DateTime.Parse(PlayerPrefs.GetString("DailyBonusDay", today.Subtract(TimeSpan.FromDays(1)).ToString(CultureInfo.CurrentCulture)));
return today.Date > lastRewardDate.Date && dailyBonusSettings.dailyBonusEnabled;
}
public void RestartLevel()
{
DOTween.KillAll();
menuManager.CloseAllPopups();
EventManager.GetEvent(EGameEvent.RestartLevel).Invoke();
}
public void RemoveAds()
{
if (gameSettings.enableAds) {
menuManager.ShowPopup<NoAds>();
}
}
public void MainMenu()
{
DOTween.KillAll();
sceneLoader.GoMain();
}
public void OpenGame()
{
sceneLoader.StartGameScene();
}
public void PurchaseSucceeded(string id)
{
EventManager.GetEvent<string>(EGameEvent.PurchaseSucceeded).Invoke(id);
}
public void SetGameMode(EGameMode gameMode)
{
GameDataManager.SetGameMode(gameMode);
}
private EGameMode GetGameMode()
{
return GameDataManager.GetGameMode();
}
public int GetLastBackgroundIndex()
{
return lastBackgroundIndex;
}
public void SetLastBackgroundIndex(int index)
{
lastBackgroundIndex = index;
}
public void NextLevel()
{
// Get current level and increment it
int currentLevel = GameDataManager.GetLevelNum();
GameDataManager.SetLevelNum(currentLevel + 1);
OpenGame();
RestartLevel();
}
public void SetTutorialMode(bool tutorial)
{
Debug.Log("Tutorial mode set to " + tutorial);
isTutorialMode = tutorial;
}
public bool IsTutorialMode()
{
return isTutorialMode;
}
private void OnMainMenuAnimationEnded()
{
HandleDailyBonus();
}
internal void RestorePurchases(Action<bool, List<string>> OnPurchasesRestored)
{
if (!gameSettings.enableInApps) return;
this.OnPurchasesRestored = OnPurchasesRestored;
iapManager.RestorePurchases(OnPurchasesRestored);
}
public bool IsPurchased(string id)
{
if (!gameSettings.enableInApps) return false;
return iapManager.IsProductPurchased(id);
}
}
}

View File

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

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f4fd5e5bff174179b5a5cd2603914f8a
timeCreated: 1729849601

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;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Android;
namespace WordsToolkit.Scripts.System.Haptic
{
public class HapticFeedback : MonoBehaviour
{
public enum HapticForce
{
Light,
Medium,
Heavy
}
private const string VibrationPrefKey = "VibrationLevel";
#if UNITY_IOS
[DllImport("__Internal")]
private static extern void _TriggerHapticFeedback(int force);
#endif
private static bool IsSystemSupported()
{
#if UNITY_IOS
return SystemInfo.supportsVibration;
#elif UNITY_ANDROID
return SystemInfo.supportsVibration && Permission.HasUserAuthorizedPermission("android.permission.VIBRATE");
#else
return false;
#endif
}
private static bool TryHapticFeedback(HapticForce force)
{
if (!IsSystemSupported())
return false;
try
{
#if UNITY_IOS
_TriggerHapticFeedback((int)force);
#elif UNITY_ANDROID
long[] pattern = force switch
{
HapticForce.Light => new long[] { 0, 50 },
HapticForce.Medium => new long[] { 0, 100 },
HapticForce.Heavy => new long[] { 0, 200 },
_ => new long[] { 0, 50 }
};
Vibration.Vibrate(pattern, -1);
#endif
return true;
}
catch (Exception e)
{
return false;
}
}
public static void TriggerHapticFeedback(HapticForce force)
{
if (!IsVibrationEnabled())
return;
}
private static bool IsVibrationEnabled()
{
return PlayerPrefs.GetFloat(VibrationPrefKey, 1) > 0;
}
}
}

View File

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

View File

@ -0,0 +1,36 @@
// // ©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 UnityEngine;
namespace WordsToolkit.Scripts.System.Haptic
{
public static class Vibration
{
public static void Vibrate(long[] pattern, int repeat)
{
if (Application.platform == RuntimePlatform.Android)
{
using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
using (var currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
{
using (var vibrator = currentActivity.Call<AndroidJavaObject>("getSystemService", "vibrator"))
{
vibrator.Call("vibrate", pattern, repeat);
}
}
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 29ec5197bae34b0d9bd40553ecd33494
timeCreated: 1729849644

View File

@ -0,0 +1,20 @@
// // ©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 UnityEngine;
namespace WordsToolkit.Scripts.System
{
public class SerializeTypePropertyAttribute : PropertyAttribute
{
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0e24f7ee9a9a49efaee3ca50d3871157
timeCreated: 1725704706

View File

@ -0,0 +1,47 @@
// // ©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 UnityEngine;
namespace WordsToolkit.Scripts.System
{
public class SingletonBehaviour<T> : MonoBehaviour where T : SingletonBehaviour<T>
{
private static T _instance;
public static T instance
{
get
{
if (_instance == null)
{
_instance = FindObjectOfType<T>();
}
return _instance;
}
private set => _instance = value;
}
public virtual void Awake()
{
if (instance != null && instance != this)
{
Destroy(gameObject);
}
else
{
instance = (T)this;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 72690b591b7e4edc8d4887649133671b
timeCreated: 1725684407

View File

@ -0,0 +1,34 @@
// // ©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 UnityEngine;
namespace WordsToolkit.Scripts.System
{
public abstract class SingletonScriptableSettings<T> : ScriptableObject where T : ScriptableObject
{
private static T _instance;
public static T instance
{
get
{
if (_instance == null)
{
_instance = Resources.Load<T>("Settings/" + typeof(T).Name);
}
return _instance;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9dea19f994814522b6c4a6a6cf51c021
timeCreated: 1725684407