Showing posts with label Unity. Show all posts
Showing posts with label Unity. Show all posts

Wednesday, 28 July 2021

Unity editor scripting (part 4): basic scene management

If you have lot of assets in your project, and multiple scenes in particular, it can be hard to navigate between them.

I've encountered two basic problems while working in Unity:
1) doing something (or checking something) in multiple scenes
2) running a particular scene which is required to run for your game (for instance, it loads all assets), while working on another one.

This is where editor scripts come to the rescue :)

In the following code I'll show how to create a tool for iterating over enabled scenes (and logging their root count object as an example), and another - for simply running some chosen scene.

Create a file ScenesHelper.cs and put it in Assets/Editor folder of your project.

The code is simple:

using UnityEditor;
using System.Linq;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;

class ScenesHelper
{
    static EditorBuildSettingsScene[] GetEnabledScenes()
    {
        return  EditorBuildSettings.scenes
          .Where(s => s.enabled).ToArray();
    }

    static string[] GetEnabledSceneNames()
    {
        var scenes = GetEnabledScenes()
            .Select(s => s.path)
            .ToArray();

        return scenes;
    }
    
    [MenuItem("Tools/Iterate over scenes. %#p")]
    static void Iterate()
    {
        foreach (string s in GetEnabledSceneNames())
        {
            Scene scene = EditorSceneManager.OpenScene(s);
            Debug.Log("Root object count in scene" + 
                scene.name 
                + ": " + scene.rootCount);
        }
    }

    [MenuItem("Tools/Play particular scene. %#o")]
    static void PlayScene2()
    {
        EditorSceneManager.OpenScene(
            GetEnabledSceneNames()[2]);
        EditorApplication.isPlaying = true;
    }

}

The method GetEnabledScenes() returns all enabled scenes (by omitting the Where clause you can also include those not enabled), or to be precise - objects of class EditorBuildSettingsScene , which represents scenes "identifiers" in editor build settings. 
The method GetEnabledSceneNames() returns their paths/names (retrieved via Linq Select).

Then we have the method Iterate()  (available from the Tools menu or via Ctrl+Shift+P shortcut) which iterates over all the enabled scenes (using their names) and logs the root objects count for each of them, and  PlayScene2() (Tools menu or Ctrl+Shift+O) which makes the editor play the second scene in our list.

Please note opening a scene in  Iterate() uses Scene class object, returned by EditorSceneManager.OpenScene(s) - it's not the same as EditorBuildSettingsScene. The former represent Unity scenes with objects, which can be inspected or played, the latter is sort of id of a scene in editor build settings.

Monday, 19 July 2021

Unity editor scripting (part 3) - your own build (Android) tool

One of things I had scripted very early working as Unity developer, was building.

When doing a build "normally", you have to click through menu (File/Build Settings, or Ctrl+Shift+B), then choose name for the file (or use the old one, and accept the dialog asking whether to overwrite it), blah blah blah... 

But editor scripting allows us to avoid this pain: we can create a script, called from our custom menu (or via custom keyboard shortcut, like Ctrl+Shit+K, which I guess doesn't do anything interesting normally), which simply creates .aab or .apk file with, say, current date and time in name. No choosing name, no clicking through menus.

How to achieve such awesome result? Start with creating file Build.cs in your Assets/Editor folder and paste the following code there (should work for Unity 2020.3.10 and similar):



What's going on there? The GetEnabledScenes()  method simply returns all the enabled scenes in the current configuration. The build itself is taking place in PerformBuild() method. First (line 20) we get the scenes to be enabled in build. Then we get out desktop folder path (21). Then we create a string containing current time and date, so that every build could be easily identified and no two would overwrite (23), and then construct our file name with extension (24).

Then we create a BuildPlayerOptions object, containing all this information and specifying the target as Android.  And then, using EditorUserBuildSettings , we set up build features: enable Proguard, set the build to bundle (.aab, not .apk), and mark the build as release. Finally, the BuildPipeline.BuildPlayer(bpo);  runs the build itself.

In case you want .apk for debug, simply change the extension in line 26 to from .aab to .apk, then buildAppBundle in line 35 to false instead of true, and then  build type to AndroidBuildType.Release;  to AndroidBuildType.Debug in line 36.

Now having that scripted you can build your game using custom menu (without needing to specify file name everytime), or using keyboard schortcut Ctrl+Shift+K. But you can do one more thing - you can build your app without opening Unity. You can simply write a bash (Mac) or batch (Windows .bat) script to build the app for you, and then run it. It's helpful, if you build on different machine than you develop on.

For Windows, create a build.bat file with contents as follows (of course set appropriate the path to your Unity installation) and place it in parent folder of your project folder (not next to Assets, but next to folder containing Assets... I'll add a graph below ;D


Folder structure (it's really important)


So now when you run that script, it should build a nice .aab file on your Desktop :) If you want to use it that way (from the script, not via shortcut or menu), you should exit Unity before running it.

If you use Unity 2019 (not 2020), the script from the first listing may not work. But the problem is very simple: in Unity 2019.x you simply have to change the line 34 (the way minification is enabled has changed in the meantime):


Generally, if something does not work, doublecheck and triplecheck your file names and method names in both Unity and .bat scripts, also class and namespace and files placement. This whole system seems to be quite picky about it.

Side note: I've just realized a problem with using gists on blogger: you can't see the code in WYSIWYG mode... On the other hand, it's easier to write about code with line numbers ;) ... I'll have to think about it.

Tuesday, 29 June 2021

Unity hack - logging full object path instead of just its name

When debugging some Unity-related problem, there is often need of finding which game object in the scene does something. We can use transform.name (or gameObject.name), but there are often multiple objects in the scene with such name. The better solution would be to log full path in the scene graph (in a manner : objectsGrandParent/objectsParent/theObject). 

I don't know of any built-in solution for that, here is the helper class I've created for that purpose.

It takes transform as a parameter, of course we can call it with gameObject.transform if necessary.

using UnityEngine;

public class ScenePath
{
    public static string getPath(Transform transform)
    {
        string result = "";
        while (transform != null)
        {
            result = transform.name + 
                ((result!="")?("/" + result):"");
            transform = transform.parent;
        };

        return result;
    }
}

It can be used like this:

class MyBehaviour : MonoBehaviour
{
    void Awake()
    {
        Debug.Log(ScenePath.getPath(transform));
    }
}

As a result, if we add the MyBehaviour script to MyObject object in hierarchy like this:



... and run our scene, the logs will show up:




Tuesday, 22 June 2021

Unity editor scripting (part 2) - basic menus, basic object selection

As mentioned in part 1, Unity editor is very easily extensible by scripting.

It basically allows to do almost everything you could by clicking by a script. And programmers do not like clicking, if something can be automated.

In the following article, I'll show you some simple steps to achieve some simple results with Unity editor, beginning with the basics of creating an editor script, and focusing on selecting, inspecting and modifying objects in scene. The information below is, of course, available in the Unity documentation - my goal is to put it in one place, in a simple question-answer format.

In the future I'll provide more similar examples, maybe a bit more complex (so that one would show a few editor features/how-to's)


1) How to create a veery basic editor script, accessible from menu?

Create folder named Editor in Assets folder, create a file named SceneTools.cs and put this code inside:

using UnityEditor;
using UnityEngine;

public class SceneTools
{
    
    [MenuItem("Tools/Show some log.")]
    static void ShowLog()
    {
        Debug.Log("Showing this log");
    }

}

It's veery basic. It simply adds a menu item called "Do something" to Unity's Tools menu (and creates Tools menu itself, if it hasn't been added before). When clicked, it shows "Showing this log" text in the Console view.

Menu looks like this:


If we want to add some function to Unity's menu, we have to use the MenuItem attribute, and the method has to be static.

2) How to create not-so-basic script (maybe connected to scene somehow?)

Ok, let's extend our script. We can add another method below the first one. Remember to change the name in MenuItem annotation (the menu item name). You can add as many methods (and, therefore, items), as you like to your custom menu. Just remember to change the names (in both annotations and methods' names) This time it will log the selected object's name and print it to console (only if at least one object is selected).

[MenuItem("Tools/Show selected object's name.")]
static void ShowObjectsName()
{
    if (Selection.gameObjects.Length > 0)
        Debug.Log(Selection.activeGameObject.name);
}

And here is our result:



Without the check in if, the line below would throw NRE if no object was selected.

3) How to select the first root object in scene with particular name?

Another method (we select the first object named "Andrew"):

[MenuItem("Tools/Select object with name.")]
static void SelectObjectWithName()
{

    GameObject[] rootGOs = 
    SceneManager.GetActiveScene().GetRootGameObjects();
    
    foreach (GameObject go in rootGOs)
    {
        if (go.name == "Andrew")
        {
            Selection.activeObject = go;
            break;
        }
    }
}

If there is no such object, nothing happens.

For this one and another, update your "using" directives:

using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;


4) Ok, how to remove the first object in the scene with particular name?

Here you go (use cautiously!):

[MenuItem("Tools/Remove object with name...")]
static void RemoveObjectWithName()
{
    GameObject[] rootGOs = 
    SceneManager.GetActiveScene().GetRootGameObjects();

    GameObject objectToRemove = null;

    foreach (GameObject go in rootGOs)
    {
        if (go.name == "Andrew")
        {
            objectToRemove = go;
            break;
        }
    }
    
    if(objectToRemove)
        UnityEngine.Object.DestroyImmediate(objectToRemove);
}

If there is no such object in the scene, nothing happens.

To be continued. I think next time I'll prepare one more complex script, doing some made-up task, using the techniques mentioned above (and adding more).

Meta-blogger note for today: I have to find some way to add line-numbering to the embedded code here, and to make it wrap automatically.

Tuesday, 15 June 2021

Syntactic sugar in C# (part 1): Returning multiple values from a method in C# 7.0 (and Unity)

Here's some syntactic sugar I've discovered recently and I was quite impressed. Please note it requires C# 7.x (Unity 2019 and newer should be ok).

First, a little background:

A typical method in C# returns one value. Until recently, if you wanted to return two values, like an int and a float, you could either use out parameters, or define a helper struct. But there is other way!

Let's take a look on "old" methods first (if you want to see the new way, it' in 4-th point below).

The problem with out parameters is that returning value via them is a bit less readable than a normal return, and the problem with a helper struct is that is has to be defined. Let's see how it looks like, for reference; and then I'll show you a bit more elegant way.

Let's assume we have our method, getSomeData(), which we would like to return two values: integer age and floating point height.

The first 3 examples below are to show you some context, my "discovery" is in the 4-th point :)

1. out parameter version

The typical way, similar to references from C++: we can "return" our values by out parameters:

    private void getSomeData(out int age, out float height)
    {
        age = 100;
        height = 170.5f;
    }

    // Let's use it
    private void someMethod()
    {
        int age;
        float height;

        getSomeData(out age, out height); 

        Debug.Log(age); // 100
        Debug.Log(height); // 170.5
    }

Problem with this? Well, it doesn't quite look like returning a value. It looks like calling a method with parameters.

2. out+return version

We can return one value and get other one via out parameter:

    private int getSomeData(out float height)
    {
        int age = 100;
        height = 170.5f;
        return age;
    }

    // Let's use it
    private void someMethod()
    {
        int age;
        float height;

        age = getSomeData(out height);

        Console.WriteLine(age); // 100
        Console.WriteLine(height); // 170.5
    }

The problem with this version is that either of parameters is returned in a different way. If both are equally important, it looks strange (if not deceptive).

3. Helper struct

We can define a helper struct and return it:

    struct PersonalDetails
    {
        public int age;
        public float height;
    }

    private PersonalDetails getSomeData()
    {
        int ageToReturn = 100;
        float heightToReturn = 170.5f;

        return new PersonalDetails 
        { age = ageToReturn, height = heightToReturn };
    }

    // let's use it
    private void someMethod()
    {
        var myDate = getSomeData();

        Console.WriteLine(myDate.age);
        Console.WriteLine(myDate.height);
    }

This version is very clean and readable when we define and call out method, but...


... before that it requires us to create a whole new structure, just for returning two values at once.

4. Sugar in C#7!

So it seems... We can just return two values at once!

        private (int age, float height) getSomeData()
        {
            int ageToReturn = 100;
            float heightToReturn = 170.5f;

            return (ageToReturn, heightToReturn);
        }

        // now let's use it
        private void someMethod()
        {
            var myDate = getSomeData();

            Console.WriteLine(myDate.age);
            Console.WriteLine(myDate.height);
        }


Aaand here is our magic. In the method declaration we put (int age, float height) in the place we would normally place the returned type.

Then we can assign it to a variable, and use it - just like that! What's more, we can access the returned values after a dot, refering to them by names from the method declaration.


BTW if you wonder what type is myDate here, Console.WriteLine(myDate.GetType()), gives us:

    System.ValueTuple`2[System.Int32,System.Single]

So instead of 

    var myDate = getSomeData();

...we could write:

    (int age, float height) myDate = getSomeData();

Also nice and clean, isn't it?

Saturday, 12 June 2021

Unity editor scripting (part 1) - setting the Android keystore and password automatically

One of the interesting features of Unity game enigne is that it's quite easy to extend its capabilities via editor scripts.

Let's take a look at the first example: setting the Android keystore and passwords automatically. The code below has been used with Unity 2019.4.24f1 on Windows and MacOS. I cannot guarantee it works out-of-the-box on other versions, but the differences shouldn't be huge.

A commonly encountered problem when working with other people on an Android Unity game, is managing the signing credentials. 

As the keystore file and credentials are often outside the main repo, the signing setup is lost between pushed commits and developers' machines. Moreover: while the keystore file location and keyalias name are stored in Unity's famous ProjectSettings file (a.k.a. the Blob, really, there's everything there), and therefore on the repo; the passwords are not (which makes sense, as passwords should be secure and not stored in some random blob-like messy configuration files... Generally - they shouldn't be keeped on the repository at all).

Some solution to that could be an editor script, setting the credentials to ones given in a secure.properties file, which includes store file path, key alias path, store password and key password. Of course, if you want to keep the credentials outside the repo, remember to add both secure.properties and .keystore file to your .gitignore.


So, let's take a look:



And then we need to set up the secure.properties, as follows:
... and set up the folder structure, as follows:
 
YourProjectFolder/
├─ Assets/
│  ├─ Scripts/
│  │  ├─ Editor/
│  │  │  ├─ SetKeystore.cs
secure.properties
YourKeystore.keystore

Now how it works:
First of all, we need to find a way to run some code when the Unity is loaded. For that, we need the [InitializeOnLoad] directive added above the class name, and then the code we want to run in the static constructor. This way the code should be run when the Unity editor is open. The rest of the code (the DoSetKeystore() method) is pretty self-explanatory: it uses the PlayerSettings class to set up the appropriate credentials. From my experience, I'd say almost every thing you can do with Unity's GUI, you can also do with an editor script. And you should do it with a script.

The menuItem("Tools/Set keystore.")] attribute before a static method causes it to appear as a menu item ("Set keystore" command in the "Tools" menu) - such method can be called manually by choosing the option in the Unity's menu.
In our case: if you want to change the credentials and then restore the default ones (or if you run the editor without .properties and .keystore files set up properly), you can always set the credentials later using the menu item - without needing to restart the editor. If you don't want to have your Tools menu crowded, feel free to remome the attribute. But it's good to know such attribute exists (it's a handy way of calling various other editor scripts, and we all love editor scripts).

Please note the folder structure - the secure.properties file placed next to your project folder (the one containing Assets folder). As we can see, when opening the file in the Unity editor sciript, we refer to it's location relative to the Assets folder (not the script itself). The same applies to the .keystore file location in secure.properties - that's why it also has ".." there.

Some meta-information for myself (beginner in blogging):
  • For creating a Blogger-embeddable code snippet, it's easy to use Github's gists: https://gist.github.com/. It should be embeded using Blogger's HTML edit mode.
  • The folder structure ASCII graph can be easily generated with https://ascii-tree-generator.com/ tool, which is awesome. Such structure can be embedded using the Blogger's create mode. It'll provide useful when presenting more Unity editor scripts in the future...
  • As for now, I'm using Helvetica as the main blog font and Recursive as the font for inline code. I'm not sure if it's the most readable option. I'll experiment with this.
For part 2 of editor scripting tutorial, click here.

Python crash course part 10: inheritance and polymorphism

In the last part we've shown how to create and use a class in Python. Today we're going to talk about inheritance: wchich means cre...