14 Feb

Hidden Soulmates: A Free Hidden Objects Minigame for Valentine’s Day

Hidden Soulmates is a hidden objects minigame you can play directly in your browser, completely free. In the game, you’ll be presented with a Valentine’s-themed scene filled with various images. Some of these images form pairs, and your goal is to find and click on those pairs to remove them from the scene. Completing Hidden Soulmates should only take a few minutes.

Play Hidden Soulmates now 🙂

Hidden Soulmates v1.0

Built with Unity 6Hidden Soulmates is designed to load fine in most browsers, though loading times may vary depending on your internet connection. It’s important that your browser is updated (it must support WebGL 2).

Hidden Soulmates screenshot

Creating this minigame was a fun break from the larger project we’re currently developing. We enjoyed every step of the process, from designing the Valentine’s scene to fine-tuning the gameplay with help from some friends.

Happy Valentine’s Day! May your day be filled with love, laughter, and maybe a little hidden object fun 🙂

10 Feb

Spooky Dwellers 1 in Czech

Hello! Spooky Dwellers 1 on Steam has just leveled up! The game now fully supports the Czech language, making it more accessible and enjoyable for our Czech-speaking players. Whether you’re a new adventurer or a returning player, you can now dive into the spooky world in English or Czech!

This localization update wouldn’t have been possible without the help of a dedicated player from the Steam community, who generously volunteered his time and expertise to translate the game from English into Czech, and we’re so grateful for his contribution. Thank you for helping us bring Spooky Dwellers 1 to even more players!

Our localization process also utilized the functions we previously introduced, which are designed to extract localization phrases directly from our Unity projects.

So, what are you waiting for? Immerse yourself in the eerie adventures that await: puzzles, secrets, or just enjoying the spooky Match 3’s levels! We hope you love playing Spooky Dwellers 1!

01 Feb

Extracting Localization Phrases from our Unity projects

In our Unity projects, we handle localization using the No Such Localization component, as detailed in a previous post. Specifically, we use the phrases version, where phrases act as keys to retrieve strings from a comprehensive table. Switching languages is as simple as changing the table for text (for images, we use a different method).

We maintain a large database of strings and extract a subset tailored to each game’s specific requirements. To do this, we compile a list of all the phrases used by the localization components within a given game. Our process is straightforward: we open each scene in the Unity project, retrieve all localization components, and store their phrases.

The following code demonstrates our approach. We’ve integrated this functionality into a Unity editor menu option, automating the entire process of retrieving phrases and writing them into a text file.

public class IKIGamesEditorTools
{
    private const string LocalizationMenuPath = "IKIGames/Localization/Extract All Phrases";
    private const string LocalizationFileName = "localization_keys.txt";

    [MenuItem(LocalizationMenuPath)]
    private static void ExtractAllLocalizationPhrasesInAllScenes()
    {
        HashSet<string> phrases = new HashSet<string>();
        List<string> scenePaths = GetScenePaths();

        foreach (var scenePath in scenePaths)
        {
            ExtractPhrasesFromScene(scenePath, phrases);
        }

        WritePhrases(phrases, LocalizationFileName);
    }

    private static List<string> GetScenePaths()
    {
        List<string> scenePaths = new List<string>();
        foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
        {
            scenePaths.Add(scene.path);
        }
        return scenePaths;
    }

    private static void ExtractPhrasesFromScene(string scenePath, HashSet<string> phrases)
    {
        UnityEditor.SceneManagement.EditorSceneManager.OpenScene(scenePath, UnityEditor.SceneManagement.OpenSceneMode.Single);
        ExtractPhrasesFromLocalizationComponentes(phrases);
        // No need to close the new scene since OpenSceneMode.Single opens it as the only active scene
        Debug.Log($"Processed: {scenePath}");
    }

    private static void ExtractPhrasesFromLocalizationComponentes(HashSet<string> phrases)
    {
        foreach (NoSuchStudio.Localization.Localizers.TMProTextLocalizer localizer in Resources.FindObjectsOfTypeAll<NoSuchStudio.Localization.Localizers.TMProTextLocalizer>())
        {
            if (!string.IsNullOrEmpty(localizer.phrase))
            {
                phrases.Add(localizer.phrase);
            }
        }
        foreach (NoSuchStudio.Localization.Localizers.TextLocalizer localizer in Resources.FindObjectsOfTypeAll<NoSuchStudio.Localization.Localizers.TextLocalizer>())
        {
            if (!string.IsNullOrEmpty(localizer.phrase))
            {
                phrases.Add(localizer.phrase);
            }
        }
    }

    private static void WritePhrases(HashSet<string> hashSet, string filename)
    {
        string filePath = Path.Combine(Application.dataPath, filename);
        try
        {
            using (StreamWriter writer = new StreamWriter(filePath))
            {
                foreach (string item in hashSet)
                {
                    writer.WriteLine(item);
                }
            }
            Debug.Log($"Phrases written successfully to: {filePath}");
        }
        catch (System.Exception ex)
        {
            Debug.LogError($"Failed to write phrases to file: {ex.Message}");
        }
    }
}