The Seventh Anniversary and Hiatus

It’s been another productive year for Gigi Labs, which today celebrates its 7th anniversary! Here are some of the more interesting things I’ve written in the year since the Sixth Anniversary:

Other than that, I released early versions of Sirius Planner, my calendar-based task planner. Feel free to give it a try if you need something to organise your day-to-day tasks.

And now, enough about the past and let’s talk about the future.

Since COVID19 changed the world, I’ve had a lot of time to explore and write about technologies that were either new to me (like Azure) or that I had been meaning to revisit (like Unity3D).

However, at the same time, life circumstances have changed quite a bit, and my interest in free-time coding and writing has been waning. As a result, for the time being, I am retiring from tech blogging. It’s quite possible that I might write something interesting from time to time, but for now, let’s say I’m taking a break.

Thanks for your support for the past seven years (or more, if you’ve been following Programmer’s Ranch or any of my other, earlier sites). I’ve received a lot of positive feedback about my articles and I hope people will continue to find them useful for many years to come.

Enabling Touchpad Tap-To-Click in Kubuntu

Kubuntu, the KDE flavour of Ubuntu, seems to work very well out of the box when installed on a new machine, needing very little configuration. One instance where you need to take that extra step is to allow taps on a laptop touchpad to be interpreted as clicks. For some reason I can’t imagine, this is not enabled by default.

Note: I’m using Kubuntu 20.04 LTS with KDE Plasma 5.18.5.

To enable this, you need to go into System Settings -> Input Devices -> Touchpad. You can reach the Touchpad settings directly by searching for “touchpad” in the search box (top-left in System Settings).

In Touchpad settings, enable “Tap-to-click”.

Once in the Touchpad settings, all you need to do is enable the “Tap-to-click” option. Once this is enabled, there are additional settings you can customise, but there’s typically no need to change them.

Using Visual Studio Code with Unity3D on Linux

So you’ve set up Unity3D on Linux, but now you need a good text editor to write your scripts. In that case, you can consider using Visual Studio Code (VS Code for short), a cross-platform text editor from Microsoft. VS Code has become hugely popular for web development, but its versatility means that it can also be used for programming in languages such as C#, Python, Go, etc.

Note that I am using Linux Kubuntu 20.04 (LTS) and Unity3D 2020.3.15f2 (LTS).

Installing VS Code

The VS Code documentation explains how to set up VS Code on Linux. The easiest option is via the snap package manager as follows:

sudo snap install --classic code

Alternatively, you can download and install a .deb or .rpm package of you prefer. See the documentation for details.

Configuring the External Editor in Unity3D

Next, we’ll configure Unity3D to use VS Code as its external editor for scripts.

First, you’ll need to have the “Visual Studio Code Editor” package installed. This is set up for you when you create a new project, but you can double-check via the Window menu and then Package Manager:

Visual Studio Code Editor 1.2.3 is installed.

With that in place, go to the Edit menu and then Preferences… and switch to the External Tools tab. Click the dropdown next to the “External Script Editor” setting and then Browse… for the VS Code executable. If you don’t know where it is, use the following command in a terminal to locate it. In my case it’s at /snap/bin/code.

whereis code
To set VS Code as the Unity3D script editor, go to the Edit menu -> Preferences…, switch to the External Tools tab and then set the value of “External Script Editor” to the path to the VS Code executable.

Now, if you create a C# script in Unity3D and open it, it should open in VS Code.

Configuring VS Code for Unity3D

You can now write C# scripts for Unity3D in VS Code and you have syntax highlighting to help you. However, Intellisense — the helpful suggestions that pop up e.g. when you try to access an object’s properties — doesn’t work yet. You also don’t get any indication of C# syntax errors. Let’s fix this so that we can write Unity3D scripts in a comfortable environment.

First, install the .NET Core SDK on Linux by following the instructions in the relevant documentation.

Next, head to the Mono Download page, and follow the first set of instructions to add the Mono repository to your system. Then, for the second step, install mono-complete instead of mono-devel as shown below. (Note: don’t run the following command before first setting up the Mono repository. The version coming from the Ubuntu repositories doesn’t seem to play well with VS Code and Unity3D.)

sudo apt-get install mono-complete

Then, in VS Code, go to the Extensions tab on the left, search for “C#”, and install the first extension that comes up:

Install the C# extension for Visual Studio by Omnisharp.

Open up settings via File menu -> Preferences -> Settings (or Ctrl+, (control comma)) and search for “Omnisharp: Use Global Mono“, then set its value to “always”. Click “Restart Omnisharp” from the notification that appears at the bottom-right. You can also restart Omnisharp at any time by pressing Ctrl+Shift+P and selecting “OmniSharp: Restart OmniSharp”.

In Settings, set “Omnisharp: Use Global Mono” to “always” and then restart OmniSharp.

Still No Intellisense?

The above steps are usually enough to get Intellisense working, but as I’m writing this right now, it doesn’t seem to work. To fix this, I had to downgrade the C# extension in VS Code, as follows:

  1. Go into the Extensions tab in VS Code.
  2. Locate the C# extension by OmniSharp.
  3. Click the small arrow next to the “Uninstall” button.
  4. Select “Install Another Version…”
  5. In my case, the latest version (1.23.14) was released just 3 days ago. I went for an older version that had been around for a couple of months (1.23.12).
  6. Click the “Reload Required” button.
  7. Watch the Output and wait for OmniSharp to finish downloading and installing.

Testing Intellisense

VS Code should now provide Intellisense as you type, and you should also see syntax errors called out via both a squiggly red underline and in the Problems window below.

Intellisense and errors both work.

You should now be all set up. Happy game development!

Playing Painkiller on Linux

Painkiller is a little-known gem in the first-person shooter (FPS) genre. I’ve played through it many times, but my most recent playthrough had something different: I did it entirely on Linux (Kubuntu 20.04 LTS).

The view from the Old Monastery.

I recently wrote about how Warcraft 3 runs (almost) flawlessly on Linux. In this article I’ll give Painkiller a similar treatment.

Installing Painkiller

All along, I’ve been using the Painkiller Black Edition from GOG.com, which has a Windows-only installer of close to 4GB that you can download after purchasing the game.

Downloading the Painkiller installer from GOG.com.

Once downloaded, we can install the game by opening a terminal, navigating to the directory where it downloaded, and running the installer with WINE, as I’ve shown many times before in previous articles (note that that’s a single filename… the spaces should actually be underscores):

cd Downloads
wine setup_painkiller_black_1.64_lang_update_\(24538\).exe

This brings up the GOG installer for Painkiller, so you select the installation language, tick the box accepting the EULA, and optionally choose the folder to install to.

The GOG installer for Painkiller.

Click the Install button to begin the installation, and the installer runs into a snag towards the end:

This problem has happened to me with several GOG games before, and each time, I thought all was lost. So it was to my great surprise that, when I clicked the Launch button after closing the error, the game ran perfectly fine anyway.

I was able to play through the entire game without problems, so if you get this error while installing Painkiller (or another GOG game) with WINE, just ignore it and try playing anyway. Chances are it will be fine.

Playing the Game

You can run Painkiller by double-clicking the handy desktop icon it sets up for you. At that point, all that’s left is to enjoy the action and stunning visuals.

First Person Grid Movement with Unity3D

Before 3D took the gaming world by storm, many RPGs used a pseudo-3D engine in which you could move in discrete steps within a grid, with a first person view. Although this was popularised by “blobbers” such as Dungeon Master and Eye of the Beholder, it goes at least as far back as 1979 with Akalabeth.

Blobber: A slang term for party-based games with first-person view, such as Wizardry, Dungeon Master and Legend of Grimrock, where the entire party moves as one, as if it was an amorphous blob.”

The CRPG Book, page 522

Unity3D gives us everything we need to easily set up this first-person grid-based movement.

This article is based on Unity3D 2020.3.13f1 (LTS), and the source code is available in the Unity3dFirstPersonGridMovement folder of the Gigi Labs Bitbucket Repository.

Setting the Scene

First, create a new 3D project. Add a few Cubes to the scene so that we’ll have something to see as we move around. You can do this via GameObject menu -> 3D Object -> Cube, although after creating the first one, you can select it and press Ctrl+D to duplicate it. Set their X and Z positions to integer values, leaving their Y position set to zero. Since the Main Camera faces down the Z-axis, it’s also nice to not set X=0 for any cubes, so that we have a corridor that we can immediately walk down.

Added a few cubes to the scene.

Optionally, add a few different-coloured materials and apply them to the cubes. This will later help make the movement more obvious, rather than just having a long, solid white wall. Refer to “Simple Brick Wall with Unity3D” if you don’t know how to do this.

A bit of colour makes all the difference.

Finally, adjust the camera so that its Y-position is zero, aligning it with the cubes.

Implementing Movement

In my old article “Unity3D: Moving an Object with Keyboard Input“, I showed how Input.GetKeyDown() can be used to move an object when a key is pressed. We can use the same thing to move and rotate the camera.

Create a script called “Movement”, and drag it onto your Main Camera. Double-click the script to open it in an editor.

In the Update() method, add the following to enable the typical WASD keyboard movement (‘W’ to go forwards, ‘S’ to go backwards, ‘A’ to move left, and ‘D’ to move right, all while facing down the Z-axis):

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.W))
            this.transform.position += Vector3.forward;
        else if (Input.GetKeyDown(KeyCode.S))
            this.transform.position += Vector3.back;
        else if (Input.GetKeyDown(KeyCode.A))
            this.transform.position += Vector3.left;
        else if (Input.GetKeyDown(KeyCode.D))
            this.transform.position += Vector3.right;
    }

If you press Play, you can use the WASD keys to move around:

Moving forwards into the corridor.

Implementing Rotation

That was easy enough! The fact that the camera is conveniently aligned with the Z-axis allows us to use predefined vectors to move in specific directions. However, this is no longer the case once we allow rotation. Any movement must be done with respect to whatever direction the camera is currently facing.

Fortunately, we already know what direction the camera is facing. That’s its transform.rotation. We can change direction by multiplying it by a Quaternion, which we can conveniently create based on an angle in degrees using Quaternion.Euler(). Let’s see this in practice by allowing the ‘Q’ and ‘E’ keys to rotate the camera 90 degrees left and right, respectively:

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.W))
            this.transform.position += Vector3.forward;
        else if (Input.GetKeyDown(KeyCode.S))
            this.transform.position += Vector3.back;
        else if (Input.GetKeyDown(KeyCode.A))
            this.transform.position += Vector3.left;
        else if (Input.GetKeyDown(KeyCode.D))
            this.transform.position += Vector3.right;
        else if (Input.GetKeyDown(KeyCode.Q))
            this.transform.rotation *= Quaternion.Euler(0, -90, 0);
        else if (Input.GetKeyDown(KeyCode.E))
            this.transform.rotation *= Quaternion.Euler(0, 90, 0);
    }

The camera can now turn left and right:

I walked down the corridor and turned left.

However, this messes things up because the WASD keys still move with respect to the Z-axis, rather than in the direction that the camera is facing. We can easily fix this by multiplying the movement vectors by the camera’s rotation:

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.W))
            this.transform.position += this.transform.rotation * Vector3.forward;
        else if (Input.GetKeyDown(KeyCode.S))
            this.transform.position += this.transform.rotation * Vector3.back;
        else if (Input.GetKeyDown(KeyCode.A))
            this.transform.position += this.transform.rotation * Vector3.left;
        else if (Input.GetKeyDown(KeyCode.D))
            this.transform.position += this.transform.rotation * Vector3.right;
        else if (Input.GetKeyDown(KeyCode.Q))
            this.transform.rotation *= Quaternion.Euler(0, -90, 0);
        else if (Input.GetKeyDown(KeyCode.E))
            this.transform.rotation *= Quaternion.Euler(0, 90, 0);
    }

And this works really nicely.

I walked down the corridor, turned right, and moved a step backwards. I’m actually inside the red cube, because we have no collision detection!

Watch the demo video on YouTube to see this in action!

Wrapping Up

As we’ve seen, Unity3D makes it really easy to set up this simple game mechanic where you have a first person view, move in steps, and turn in 90-degree angles. It does help to be comfortable with vectors. If you’re not, check out my “A Concise Introduction to Vectors” [PDF] at Gigi’s Computer Corner.

If you’d like a little extra exercise, try to make the movement and rotations as smooth transitions. You can use Vector3.MoveTowards() and Quaternion.RotateTowards() for this. You’ll also need to change your camera’s near clipping plane to zero to avoid weirdness during rotations.

"You don't learn to walk by following rules. You learn by doing, and by falling over." — Richard Branson