Showing posts with label C#. Show all posts
Showing posts with label C#. 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.

Sunday, 25 July 2021

Tray icon management in Windows Forms C# app

Today something different. The problem I’ll write about is: how to make a Windows Forms (C#) app minimize to tray when cliked on „X” and prevent it from displaying in the taskbar.

In other words – how to make a „typical tray app”.

To be honest – I was quite sure there would be some component (control) for it.

And there is (NotifyIcon) but… It’s not enough just to drag it to our app designer form in Visual Studio to make it work as we would like to.

Let’s begin with opening Visual Studio and creating a new Windows Forms App:

File -> New -> Project -> Windows Forms Apps -> Ok

First, wee drag NotifyIcon component from Toolbox to our form (it will appear below it, as it’s not visible in the form area, but outside it)



And here is the first strange behaviour: if we run (F5 or Ctrl+F5, but i’d suggest F5 as it allows it to kill the app if something goes wrong, and it can in this case) our app, the NotifyIcon component doesn’t seem to work. The tray icon doesn’t show up, and neither minimization nor clicking the „X” button causes our app to minimize to tray.

That’s because our tray icon is not set and we haven't implemented minimization by clicking "X". First the icon: in order to set it, we need the tray icon itself :)

We can just use Paint to create it – set the canvas size to 16x16 (this is the standard tray icon size in Windows), draw something beautiful and save it as .jpg file. Now rename it to something.ico – simply change the extension! The ico files are simply graphics files with a particular extension.

Now you can copy it to project – you can just drag it to project name in Solution Explorer view in VS.



It should show up below (myicon.ico) in the image above.

Now if you hit F5, the icon should show in the tray :)

The problem is it doesn’t do anything. The app still minimizes to the taskbar.

So let’s edit the code. Double click on the form project appearing in VS. The form code should appear, and it should be similar to this:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }
    }
}

 

First of all, let’s make our icon actually do something: it should show form if clicked twice, and hide it if clicked twice again. In order to do it, select our NotifyIcon component in the designer (Form1.cs [ Design] tab):


Now select the lightning icon in Properties windows (F4 if the window is not visible), click the empty area on the right of „MouseDoubleClick” and then double click on it again :)



 An empty method should appear in the editor:


private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
{

}

Let's implement it:


private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
{
    if (this.Visible == true)
    {
        this.Hide();
    }
    else
    {
        this.Show();
    }
}

Now when you run the app and double click on the tray icon, the window should disappear. Double click on the icon again, and the window should appear. Now close the window – let’s get back to the code: we’ll add a context (right click) menu to the tray icon.

The remaining problems are as follows: the application exits, when we click „X” in the top bar. This is correct behaviour for most apps, but those enabled on tray should keep working even if „X” is clicked (the typical way of closing them is to right click on the tray icon and choosing „Close” or something similar from the context menu). The second problem is: the application still appears on the „normal” taskbar, and we want it to be minimized only to tray.

To fix the first issue, modify the Form1 constructor in this way:  


public Form1()
{
    InitializeComponent();
    this.FormClosing += this.Form1_FormClosing;
}

 … and add the callback for FormClosing event below:


private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    if (e.CloseReason == CloseReason.UserClosing)
    {
        this.Hide();
        e.Cancel = true;
    }
}

If you want to run the app now, do it using F5 (not Ctrl+F5) – you’ll need to terminate it using „Stop Debugging” (red square button or Shift+F5 in Visual Studio). If you forget about it, you’ll have to close it via the task manager :)

Now let’s add a possibility of closing the app via the context menu for the tray icon. In order to do that, switch to the Form1.cs [Design] view and drag ContextMenuStrip component to your designer view (window).



Now click on the "Type Here” area, type "Exit” and double click on it. It should take you to the editor, with generated code stub:


private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{

}

We want to close app when choosing ‘Exit’ option, so implement like this:


private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
    Application.Exit();
}

Of course, you can add more items to the menu (and add whatever code you want to it).

The last thing to do is to attach this menu to our tray icon. In order to do that, choose notifyIcon1 from the area below the designer:



 ...and choose our ContextMenuStrip in the Properties window on the right:



When you run the app now and right-click on the tray icon, "Exit” option should appear (and should work).

To last thing to do is to prevent the app from showing on the "normal” taskbar. To do this, add one last line to Form1 constructor:


public Form1()
{
    InitializeComponent();
    this.FormClosing += this.Form1_FormClosing;
    ShowInTaskbar = false;
}

That's all for today - I think I was able to show you a simple stub of Windows app using tray in C#. If you have any questions about this - ask them in comments :)

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.

Monday, 21 June 2021

Syntactic sugar (and other nice features) in C# part 2


This time some simple things. Possibly obvious for programmers with some experience... But maybe not so obvious for everyone? Once again we're discussing C# 7.0 used with Unity.

1. You can inline an out variable declaration

The first one this time is a very little detail, which may seem obvious - but I wasn't aware it for quite some time. I think this may be quite new (since C# 7.x indeed?)

When using a method with an out param, you don't have to declare it before.

Which means you can write this:

string maybeNumber = "10";
int result;
int.TryParse(maybeNumber, out result);
DoSomethingWith(result);

as this:

string maybeNumber = "10";
int.TryParse(maybeNumber, out int result);
DoSomethingWith(result);

2. Long numbers made readable (works also in Python and Java)

Do you use big numbers in your code? You know, when something like long bigNumber1 = 2156454565; shows up, and you're not sure whether it's 215 millions 645 thousand and so on, or maybe 2 billions 154 millions or... Well, it's because you don't know it can be written like this:

long bigNumber2 = 2_156_454_565;

Works for other numeric types too:

double bigNumber1 = 100_123.125_324;

This way of declaring numeric literals works also in some other languages: I'm sure about Java and Python. It doesn't seem to work with C(++).

3. A short story about null values and question marks

There is something called conditional operator in most of modern programming languages. It's consists of an expression, followed by ? sign and then some value to be returned if expression is true; then : sign and a value to be returned if expression is false. Long story short:

int number = 42;
string nrDescription = (number==42)?"Ultimate answer":"An ordinary number";

It's pretty well known syntax. But in C#, there are more similar tools.

3.1 Null coalescing operator

One of them is a null coalescing operator, which may be handy when checking if something is null. Let's consider the following code (it does not make use of null coalescing operator yet, see below):

TimeZoneInfo tInfo = TimeZoneInfo.Local; // this can be null
          
TimeZoneInfo nonNullTInfo1 = tInfo != null ? tInfo : TimeZoneInfo.Utc;
Console.WriteLine(nonNullTInfo1.DisplayName);

It's simple: it shows the local time zone display name, and if it's not available (TimeZoneInfo.Local is null), it fallbacks to TimeZoneInfo.Utc. It uses the "traditional" conditional operator.
And here is our alternative, using the  null coalescing operator:

TimeZoneInfo tInfo = TimeZoneInfo.Local; // this can be null

TimeZoneInfo nonNullTInfo2 = tInfo ?? TimeZoneInfo.Utc;
Console.WriteLine(nonNullTInfo2.DisplayName);

A bit shorter, a bit more elegant. Can save quite a lot of typing in more complex cases.

3.2 Null conditional-operators ?. and ?[]

Let's consider following code:

class SomeClass
    {
        public void someMethod()
        {
            Console.WriteLine("Some method is working");
        }

        public TimeZoneInfo getTimeZone()
        {
            return TimeZoneInfo.Local;
        }
    };

    class Program
    {

        static void Main(string[] args)
        {
            SomeClass someObject = null;

            someObject.someMethod();

            TimeZoneInfo res = someObject.getTimeZone();

            Console.Write("Result:"+res.StandardName);
        }
    }

Obviously, this code will throw an exception. As we never initialize someObject, the call: someObject.someMethod(); will throw NullReferenceException. Of course, we can precede it with if(someObject!=null). But we can change the Main method to look like this:

static void Main(string[] args)
{
    SomeClass someObject = null;

    someObject?.someMethod();
    TimeZoneInfo res = someObject.getTimeZone();

    Console.Write("Result:"+res.StandardName);
}
What happens then? An NullReferenceException is thrown again, but it's the .getTimeZone() call that throws it! someObject?.someMethod(); is simply not called.

So let's add another ?, this time before .getTimeZone(); call:

static void Main(string[] args)
{
    SomeClass someObject = null;

    someObject?.someMethod();
    TimeZoneInfo res = someObject?.getTimeZone();

    Console.Write("Result:"+res.StandardName);
}

.someMethod();.is not called, and then .getTimeZone() is not called. What happend with res? Well, because the initializing expression can't be called, it... stays uninitialized: it's null.
Which means we get our NullReferenceException only in the call to Console.Write("Result:"+res.StandardName);. Which, by the way, can be avoided this way:

Console.Write("Result:"+res?.StandardName);

... resulting in writing to console simply Result: (the value returned by res.StandardName is simply null, which is printed as en empty char).

To sum up, the following code (consisting all the aforementioned features):

static void Main(string[] args)
{
    SomeClass someObject = null;

    someObject?.someMethod();
    TimeZoneInfo res = someObject?.getTimeZone()
    
    Console.Write("Result:"+res?.StandardName);
}

...is equivalent to the following ("old school") code:

class Program
{
    static void Main(string[] args)
    {
        SomeClass someObject = null;

        if(someObject!=null)
            someObject.someMethod();

        TimeZoneInfo res = 
        (someObject != null)?someObject.getTimeZone():null;
        
        Console.Write("Result:"+ ((res!=null)?res.StandardName:""));
    }
}

What about ?[] operator, mentioned in the midtitle? Well, it's similiar to ?. but involved using arrays. So it allows us to write:

int[] array = null;
Console.WriteLine(array?[2]);

instead of:

int[] array = null;
Console.WriteLine(array == null?"":array[2].ToString());

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?

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...