GODOT: Learning from the Tutorials

Stuff that I learned from the Godot Step-by-Step starter tutorials. I’m hoping I can shorten someone else’s journey (plus this is how I document stuff).

A Godot Game is a tree of Scenes (*.tscn files from the project explorer) and Scenes are made up of Nodes (the primitive types in a Godot game).

In the 2D starter tutorial we made four scenes:

  • Main.tscn
  • Player.tscn
  • Mob.tscn
  • HUD.tscn

You can see from the list of scenes (below) that the logical grouping of a Main controller scene – a Player and a Mob (Bad Guys) scene and the HUD scene is split out. The Main scene is the starting point for a Godot game. Scenes can be instantiated (many times as objects within another Scene – and edited independently – or all objects can be changed globally by editing the *.tscn) .

As you can see above – each Scene had a related Godot Script file (*.gd) that holds the code for that Scene (more on that later).

Scenes are your basic building block and design element. For each component in the game – make a Scene.

Scenes are a tree of Nodes. For example, the Scene for the Player uses the following Nodes: a CharacterBody2D node, a Sprite2D node, a Camera2D node, and a CollisionShape2D node.

Nodes have Properties and can receive callbacks to update every frame. They can be extended with new properties (and functions). They can be nested under another node as a child (the tree structure).

Properties can be edited in the Inspector Window to the Right of the Godot Editor. The Inspector displays a node’s properties in “Title Case” which are available to GDScript code (As lowercase – with words separated by an underscore – hover over Properties in the editor to check them).

Example: a script on a 2D Sprite Character that starts with:

extends Sprite2D

Which grants access to all the inherited properties of a 2D Sprite visible in the Inspector – which means you can write:

var texture = load("res://icon.svg")
$Sprite2D.texture = texture

Doing Stuff

Game Window Size Settings

Click on Project  -> Project Settings to open the project settings window and in the left column, open the Display Window tab. There, set “Viewport Width” to 480 and “Viewport Height” to 720.

Player Input

There are two main ways to process input :

  1. The built-in input callbacks, mainly _unhandled_input(). Like _process(), is a built-in virtual function that is called every time there is a keypress. Use if if you need to react to events that don’t happen every frame (like hitting Space to jump). To learn more about input callbacks, see Using InputEvent.
  2. The Input singleton. A singleton is a globally accessible object. Godot provides access to several in scripts. It’s the right tool to check for input every frame.

We use the Input singleton here as we need to know if the player wants to turn or move every frame.

Managing input is usually complex, no matter the OS or platform. To ease this a little, a special built-in type is provided, InputEvent. This datatype can be configured to contain several types of input events. Input events travel through the engine and can be received in multiple locations, depending on the purpose.

Here is a quick example, closing your game if the escape key is hit:

func _unhandled_input(event):
    if event is InputEventKey:
        if event.pressed and event.keycode == KEY_ESCAPE:
            get_tree().quit()

However, it is cleaner and more flexible to use the provided InputMap feature, which allows you to define input actions and assign them different keys. This way, you can define multiple keys for the same action (e.g. the keyboard escape key and the start button on a gamepad). You can then more easily change this mapping in the project settings without updating your code, and even build a key mapping feature on top of it to allow your game to change the key mapping at runtime!

You can set up your InputMap under Project > Project Settings > Input Map and then use those actions like this:

func _process(delta):
    if Input.is_action_pressed("ui_right"):
        # Move right.

Animations

Adding animations to 2D Animation Node is a little non-obvious.

To create one, find the Sprite Frames property under the Animation tab in the Inspector and click “[empty]” -> “New SpriteFrames”. Click again to open the “SpriteFrames” panel: (Note that there are two tabs sitting next to each other there in the panel – both called Sprite Frames – which is kind of like an expanded info box for the first Sprite Frames tab – doesn’t look obvious but it is when you know it’s there).

On the left is a list of animations. Click the “default” one and rename it to “up”.

The Animation Sprite Pane is called “Sprite Frames” down the bottom (in blue) – (it’s easy to lose it if you click on something else and then go looking for it in the obvious Animation footer): Add sprites by clicking on the little “folder” icon (next to the play/pause strip).

Anti-Aliasing

I was a bit disappointed with how scenes looked when I started playing with my own demo scenes but once I found the Anti-Aliasing settings I got more comfortable. Under Project -> Project Settings.

The difference between this:

and this:

GDScript

GDScript is an indent-based language – which is really annoying. I make tabs and spaces by muscle memory all the time when coding and it bites me in the ass. Cutting and Pasting code is also fraught with danger. But annoyances aside I do really like it as a scripting language – it’s simple, well designed (in that interfaces are consistent and logical) and does what you want with a minimum of fuss.

The only other gripe is that there are a lot of setting strings for references in the editor so you need to be careful when using them in code that you spell them correctly and are aware that they are case sensitive. For example you may have set an animation called “funnyWalk1” in the editor so when you code it in GDScript you need to use something like below:

if velocity.x != 0:
    $AnimatedSprite2D.animation = "funnyWalk1"

It would be nice if there was some kind of style checker or linter that could roll over your code for strings and do a fuzzy match against the instances in the editor and tell you when you used FunnyWalk1 or funnywalk1 or funnywolk1 or funmyWalk1 etc.

Inspector Access

Using the export keyword on the first variable speed allows us to set its value in the Inspector

@export var speed = 400 # How fast the player will move (pixels/sec).

Functions

func _process(delta):
    rotation += angular_speed * delta

Here, rotation is a property inherited from the class Node2D, which Sprite2D extends. It controls the rotation of our node and works with radians.

The func keyword defines a new function. After it, we have to write the function’s name and arguments it takes in parentheses. A colon ends the definition, and the indented blocks that follow are the function’s content or instructions.

Note: Notice how _process(), like _init(), starts with a leading underscore. These are Godot’s built-in functions you can override to communicate with the engine.

_process() is called every frame (it’s the equivalent of Update() in Unity).

I have my Snippets of GD Script here: https://github.com/zuluonezero/GodotSnippets

Differences Between Godot 3 and 4.

Spacial renamed to Node3d in v4.

Godot 4.0: nonexistent function “instance” in base PackedScene Instance is Godot 3, on 4 is instantiate()

PathFollow, offset (the path length moved from the beginning in distance units). PathFollow2D, you’ll see that it no longer has offset as an attribute – the new name is “progress”.

V3 has linear_interpolation in V4 it has been replaced with lerp.

Preparing 2D Art for Animation

I’ve been doing some work on the 2D side of things in preparation for another game.

This has been the general workflow.

1. Make the assets in Clip Studio.

2. Pack the sprites with Free-Tex-Packer

3. Import the art into Blender, make a mesh for each sprite and UV map it.

4. Add the Armature bones.

5. Weight Paint

6. Animate.

7. Export from Blender as an *.fbx with the animations baked.

8. Import into Unity

9. Add new Materials and import the UV images into Unity.

10. Add the *.fbx imported asset into a scene.

11. Add an Animator Component and drag the animations from the prefab into it.

12. Set up Triggers and connections for the animations.

It’s a lot of work. Especially if you make a custom mesh for each piece of art. But I did all this as I really like animating in Blender (especially now that the Pose Library is functional and part of the Asset Browser). But to tell the truth I think I got better results using the Spline system in Unity 3D with much less work. There are trade offs and I’ll go through them below after more exposition on the workflow.

In this post I’ll go through the asset creation process in Clip Studio.

Making the Asset

The 2D game has a bunch of cute animals so I dug deep into the Disney Sketchbook by Ken Shue and pulled out Bambi for inspiration.

An early Disney sketch

Using this as a rough guide I drafted a few basic shapes for a “Deer” character which looked like this:

Rough Sketch for the 2D Asset

I started using Clip Studio last year in place of the Gimp. I’ve tried all sorts of painting programs and would choose Gimp over most of them (I will not spring for a paid version of Photoshop – it’s extortion!) but Clip Studio won me over with it’s brushes. It’s not expensive by comparison and I really like how it fits into the specific things I want out of an art program. I’ll often go back to Gimp for projects that require a lot of filters and image manipulation but for straight drawing on the PC Clip Studio is a good fit for me. I like how you can make custom tools that mimic your real life counterparts for a pencil or brush and find this program better at it than most (though Adobe Sketchbook runs a close second).

To start with I create a set of layers for the Inking of the artwork. One for each moving element in the final asset.

There is a pretty simple formula for this where each limb or piece gets a layer. But you have to have a general idea of what you are going to need in the final asset and what animation is required. There is no point doing a separate component if it’s not going to move or be seen in the final product. Trouble is a lot of this work is iterative and often you find that you have to go back and change something when it doesn’t look right. There needs to be an awareness of where pieces overlap and what lines are going to be warped by the armature bending or where a line needs to be extended behind a piece that might move and reveal where it ends by another layer.

The Inking Layers
This is how the inking layers sit on top of each other that shows where lines overlap or extend.

It’s really easy to see on the body and legs but even here on the pieces surrounding the head the lines that make up the ears and hair and neck all have to move independently but still look connected.

Once I’m done with the inking stage I add more layers for color. At this point the whole file gets saved as export copy and the layers are merged into one for each piece again and numbered in the order in which they will sit on the animation cell. I keep the older copy with the separate layers for everything and all the drafts so I can go back to it if I have to change something.
This is the whole asset complete and ready for export. Each layer is exported individually as a *.png. The file size of each one is 1024 x 1024 pixels with 600 dpi and a transparent background.
The *.png files are imported into the Texture Packer to minimize the material size in the final project. Each of these elements get’s UV mapped to a mesh in Blender but more of that later in the next post.

Start to finish this took a couple of days elapsed time as there is a lot of noodling about with formats, designs and what-not.

Next up I’ll go into the Blender workflow and preparing the art for animation with complex and simple meshes.

Unity Message Bus

How to make use of a message bus in Unity. This is a good solution to decouple components and logically organise how your game runs. Instead of doing all the dragging in the editor of game objects and components into scripts the message bus works a bit like a proxy for the communication between scripts. This is good (makes complex things easier to manage – ie. no noodling of dependencies everywhere – and no heavy FindObject calls – plus I get to make a funny bus pun.

Events

When using Events your Objects become Subscribers and Publishers of actions. When you subscribe (or listen) to a particular event channel you get notified when something changes. These notifications go out to every object that is listening on the bus and each object script can respond to the message in their own way.

You can make this as complex as you like but it gets harder the more complex the data is that you are passing around on the bus. This example uses just four components and our events are nice and simple:

  1. An enum that lists the Events on the Bus.
  2. The YellowBus class that handles subscribtions and publishing (by using a dictionary of events)
  3. A BigYellowBusController script that is attached to our bus game object and subscribes to all the “bus” events like starting, stopping at stops, taking on passengers, etc.
  4. A PassengerController that is attached to the Bus Rider game objects (the coloured circles) which subscribes and reacts to rider type events like calling the bus, getting on and off, and ringing the bell when you want to get off.

EventsOnTheBus

This is just a simple collection of named constants that are meaningful for our events.


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace AdvancedCoding.YellowBus
{
    public enum EventsOnTheBus
    {
        CALL_BUS, ALL_ABOARD, START_ENGINE, NEXT_STOP_PLEASE, STOP_ENGINE, EMERGENCY_STOP, END_O_THE_LINE
    }
}

YellowBus

This class sets up the dictionary of events matching the EventsOnTheBus to a UnityEvent call. (An even simpler example of an event call is in the docs: https://docs.unity3d.com/ScriptReference/Events.UnityEvent.html).

It exposes the Subscribe, Unsubscribe, and Publish methods.

Notice the using UnityEngine.Events directive at the top.


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

namespace AdvancedCoding.YellowBus
{
    public class YellowBus
    {
        private static readonly IDictionary<EventsOnTheBus, UnityEvent>

        Events = new Dictionary<EventsOnTheBus, UnityEvent>();

        public static void Subscribe(EventsOnTheBus eventType, UnityAction listener)
        {
            UnityEvent thisEvent;

            if (Events.TryGetValue(eventType, out thisEvent))
            {
                thisEvent.AddListener(listener);
            }
            else
            {
                thisEvent = new UnityEvent();
                thisEvent.AddListener(listener);
                Events.Add(eventType, thisEvent);
            }
        }

        public static void Unsubscribe(EventsOnTheBus type, UnityAction listener)
        {
            UnityEvent thisEvent;

            if (Events.TryGetValue(type, out thisEvent))
            {
                thisEvent.RemoveListener(listener);
            }
        }

        public static void Publish(EventsOnTheBus type)
        {
            UnityEvent thisEvent;

            if (Events.TryGetValue(type, out thisEvent))
            {
                thisEvent.Invoke();
            }
        }

    }
}

BigYellowBusController

The controller on the bus is a subscriber to the events that it needs to react to and defines private methods of handling those events when they are broadcast. You can have any number of subscribers listening in to your events. In this example we only got two: The YellowBus and the Rider.


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace AdvancedCoding.YellowBus
{
    public class BigYellowBusController : MonoBehaviour
    {
        private string _an_event;
        private bool _engine_is_running;
        private bool _rider_waiting;
        private bool _next_stop;
        public float speed = 3;

        void OnEnable()
        {
            YellowBus.Subscribe(EventsOnTheBus.START_ENGINE, StartBus);
            YellowBus.Subscribe(EventsOnTheBus.STOP_ENGINE, StopBus);
            YellowBus.Subscribe(EventsOnTheBus.EMERGENCY_STOP, Emergency);
            YellowBus.Subscribe(EventsOnTheBus.CALL_BUS, RiderWaiting);
            YellowBus.Subscribe(EventsOnTheBus.NEXT_STOP_PLEASE, NeedToGetOff);
        }

        void OnDisable()
        {
            YellowBus.Unsubscribe(EventsOnTheBus.START_ENGINE, StartBus);
            YellowBus.Unsubscribe(EventsOnTheBus.STOP_ENGINE, StopBus);
            YellowBus.Unsubscribe(EventsOnTheBus.EMERGENCY_STOP, Emergency);
            YellowBus.Subscribe(EventsOnTheBus.CALL_BUS, RiderWaiting);
            YellowBus.Subscribe(EventsOnTheBus.NEXT_STOP_PLEASE, NeedToGetOff);
        }

        private void StartBus()
        {
            _an_event = "Started!";
            _engine_is_running = true;

        }

        private void StopBus()
        {
            _an_event = "Stopped!";
            _engine_is_running = false;

        }

        private void Emergency()
        {
            _an_event = "Emergency Stop!";
            _engine_is_running = false;
        }

        private void RiderWaiting()
        {
            _rider_waiting = true;
            _an_event = "Rider waiting...";
        }

        private void NeedToGetOff()
        {
            _next_stop = true;
            _an_event = "Passenger needs to get off...";
        }


        private void OnGUI()
        {
            GUI.color = Color.green;
            GUI.Label(new Rect(300, 60, 200, 20), "BUS EVENT is...  " + _an_event);

            if (GUILayout.Button("Start Bus Up")) YellowBus.Publish(EventsOnTheBus.START_ENGINE);
            if (GUILayout.Button("Stop Bus")) YellowBus.Publish(EventsOnTheBus.STOP_ENGINE);
        }

        public void Update()
        {
            if (_engine_is_running)
            {
                transform.position = new Vector3(transform.position.x + speed * Time.deltaTime, transform.position.y, transform.position.z);
                if (transform.position.x > 18f)
                {
                    transform.position = new Vector3(-17f, 0f, 0f);
                }

                if (_rider_waiting)
                {
                    if (transform.position.x > 0 && transform.position.x < 1)
                    {
                        YellowBus.Publish(EventsOnTheBus.STOP_ENGINE);
                        YellowBus.Publish(EventsOnTheBus.ALL_ABOARD);
                        _rider_waiting = false;
                    }
                }

                if (_next_stop)
                {
                    if (transform.position.x > 0 && transform.position.x < 1)
                    {
                        YellowBus.Publish(EventsOnTheBus.STOP_ENGINE);
                        YellowBus.Publish(EventsOnTheBus.END_O_THE_LINE);
                        _next_stop = false;
                    }
                }
            }
        }
    }
}

RiderController

The Rider Controller does pretty much the same as the Big Yellow Bus Controller but only listens to those events that relate to the riders (some of which it shares with the Big Yellow Bus Controller). Now here is where it gets interesting… look at the NEXT_STOP_PLEASE parts. Start with the OnGui() method below on line 75 where a button press will publish the NEXT_STOP_PLEASE event. In the OnEnable() method our Subscription code calls the PressTheBell() private method (line 17) that let’s our game object handle the call and produce the text message. BUT the YellowBus Controller also subscribes to this event and handles it with the NeedToGetOff() method so that it knows it has to stop and let the passenger off (line 21, 59 and 95 in the BigYellowBusController script above). AAANNDDDD responds by triggering the STOP_ENGINE event and the END_O_THE_LINE event. See how they can chain together into larger behaviours and complex interactions between different game objects without actually entangling them at the script reference level.

Also be aware that on these scripts there is a subscription in OnEnable() and an Unsubscribe in OnDisable() (so we are not holding on to the listener when the game object disappears) the thing to note here is that we are listening to our own events. For example the RiderController triggers the CALL_BUS event and both our own script and the BigYellowBusController script react to it.


using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace AdvancedCoding.YellowBus
{
    public class RiderController : MonoBehaviour
    {

        private bool _lightMeGUIup;
        private string _message;
        private GameObject _bus;

        void OnEnable()
        {
            YellowBus.Subscribe(EventsOnTheBus.CALL_BUS, HopOnTheBus);
            YellowBus.Subscribe(EventsOnTheBus.NEXT_STOP_PLEASE, PressTheBell);
            YellowBus.Subscribe(EventsOnTheBus.ALL_ABOARD, JumpOn);
            YellowBus.Subscribe(EventsOnTheBus.END_O_THE_LINE, JumpOff);
        }

        void OnDisable()
        {
            YellowBus.Unsubscribe(EventsOnTheBus.CALL_BUS, HopOnTheBus);
            YellowBus.Unsubscribe(EventsOnTheBus.NEXT_STOP_PLEASE, PressTheBell);
            YellowBus.Unsubscribe(EventsOnTheBus.ALL_ABOARD, JumpOn);
            YellowBus.Unsubscribe(EventsOnTheBus.END_O_THE_LINE, JumpOff);

        }

        private void Start()
        {
            _bus = GameObject.Find("BigYellowBus");
        }

        private void PressTheBell()
        {
            _message = "I need to get off the bus !!";
            _lightMeGUIup = true;
        }


        private void HopOnTheBus()
        {
            _message = "I need to get on the bus please!!";
            _lightMeGUIup = true;
        }

        private void JumpOn()
        {
            _message = "Hi Driver!";
            transform.parent = _bus.transform;
            transform.position = new Vector3(0f, 0.2f, 0f);
        }

        private void JumpOff()
        {
            _message = "Thanks a lot!";
            transform.parent = null;
            transform.position = new Vector3(0f, -3f, 0f);
        }


        private void OnGUI()
        {


            if (GUI.Button(new Rect(160, 10, 100, 30), "Call Bus"))
            {
                YellowBus.Publish(EventsOnTheBus.CALL_BUS);
            }

            if (GUI.Button(new Rect(160, 50, 100, 30), "Bell Press"))
            {
                YellowBus.Publish(EventsOnTheBus.NEXT_STOP_PLEASE);
            }


            if (_lightMeGUIup)
            {
                GUI.color = Color.blue;
                GUI.Label(new Rect(300, 40, 200, 20), "..." + _message);
               StartCoroutine(DropGUI());
            }

        }

        private IEnumerator DropGUI()
        {
            yield return new WaitForSeconds(2f);
            _lightMeGUIup = false;
        }
    }
}

The other thing to note is that this message bus is like a party line – all messages are broadcast to anyone listening. All the passengers respond to the NEXT_STOP_PLEASE event as they all share the same code…but of course they could implement different controller scripts per player and all handle this event differently. It’s pretty powerful stuff.

Playtime in Bus Controller Land !

The code above was modified from an example in Game Development Patterns with Unity 2021: Explore practical game development using software design patterns and best practices in Unity and C#, 2nd Edition by David Baron – I recommend buying a copy it is the best book of this type that I have read – take yourself from a beginner to an intermediate Unity programmer. 🙂

Exporting Multiple Animations from Blender to Unity

This is one of those workflows that is always a bit fiddly to get right so I’ve documented how to do it here in case I forget! One of the downsides to being a solo developer is that your skillset is always being stretched by the available time so you can end up getting proficient in once aspect of game building and then by the time you get back to that phase you forget everything you’ve learned and all the tricks of efficiency and process. Also, in case someone else needs it.

This is what we are aiming for in Unity. An imported mesh with multiple animations being called independently.

Blender Workflow for Saving the Animations

Start with a new project. Select everything (the default cube and lamp) x –> delete.

In this case I’ve imported an existing fbx of a hand with supporting armature ready for animation. I won’t go over the modelling or rigging procedure there is plenty of help with that out there – but if you need it I would recommend the Riven Phoenix courses because they are so dense (these tutorials are no quick start or tricks videos but deep deep dives into the process and reasons behind it and how stuff works in Blender at a very technical level).

This is how I layout Blender for Animation with a dual screen front and right view with the Timeline below

Get your animation window set up and make sure the timeline is available at the bottom.

Making a Pose Library

In the Outliner select the Armature and make a Pose Library.
We can use this to set a few basic poses to make the animation process run a little easier.
The poses will be major key frames that we can interpolate between.

It’s not the best workflow but in the tech preview for upcoming Blender versions is an enhanced workflow for the animation process which looks really exciting – google it.

Make a Pose Library

Add the default pose as the first item.
Go to Pose Mode. Get the model into your default position and save this pose. (Important – this will be the pose that the model is exported as by default so try and make it your idle or standing pose).

Save several other poses (make sure you save all the bones you want the pose to effect – usually this is all the bones).
You can overwrite poses if you get it wrong.

Also, when a pose is added and a pose marker is created the whole keying set is used to determine which bones to key. But if any bones are selected, only keyframes for those bones are added, otherwise all bones in the keying set are keyed (this is why I usually have all the bones selected).

I’ve made several poses and saved them

It’s a good idea to set and select the poses a few times for each one to make sure you got it right. I’ve found that sometimes it’s a bit glitchy or I do something a little bit wrong and it doesn’t save properly (actually it’s probably not glitchy it’s probably just me).

That Book icon with the Question Mark is useful when you have all your poses completed. Pose libraries are saved to Actions. They are not generally used as actions, but can be converted to and from them. If you use this icon to “sanitize” the pose library it drops all the poses down to an Action with one pose per frame. So you can go into the NLA Editor window and select this track and sweep/scrub through them. Maybe this is useful as a clip in Unity if you want to split it up using the timing editor and make custom animations in Unity (never tried it).

Making the Animations

Go to Dope Sheet – and switch to the Action Editor View.

Action Editor


Make the animation (ie. start on the first frame – Assign the pose from the library – Shift + I save rotation and location. Go to last frame – assign the next pose – Shift + I and save again).

In the Timeline make sure you are on the beginning frame. Set the pose you want to move from (first keyframe) and save the required parameters.

Shift – I
Insert Location and Rotation
(make sure the Armature is Selected)

Start with the first pose
The Dope Sheet

Move to the next frame at a suitable scale and change the pose to your ending pose in the editor. Save the Location and Rotation parameters (if that’s all that’s changed).

Add the second pose
Saved Pose in the Dope Sheet

Pushing the Animation down the Action Stack

Once you are done hit the “Push Down” button. This is the magic button.

Magic Push Down Button

Next move over the the Nonlinear Animation Window.

The NLA Window

Your animations get made as Actions in the Non-Linear Action Editor Window: NlaTrack, NlaTrack.001, etc.

In the NLA Editor you can click the Star next to the NLA Track (rename them to make it friendlier) to scrub through the track. Make sure you got the right animation under the right name etc.

After hit Push Down after each animation is finished it appears as an NLA Track in the NLA Editor

I make a few more animations and hey presto. Each one of those NlaTracks is an animation that we can use in Unity. Also the PoseLib track is marked there with orange lines – one for each pose on a frame which is a good reference track if you need it.

The Animations Stacked up in the NLA ready for Export with the *.fbx

Export from Blender

These are the settings I use to export. It’s safer to manually select only the Armature and the Mesh here.

It’s useful to have Forward as -Z Forward for Unity.

Blender Export Settings

Import Into Unity

This is what it looks like when I import the .fbx into Unity.

The Animation Tab of the Asset (on import)

The animations come out as duplicates but you only need one set. Work out which one’s you want and delete the others using the minus button when you import. This bit can be a bit fiddly and sometimes I’ve had to do the process of exporting and importing a couple of times to get it to work. Sometimes what works is to drag and drop all your animations NLA Tracks into one track in the NLA Editor and select it with the star before exporting. Sometimes it works – sometimes not. Not sure why.

After that I drag the model into the scene and add an animation controller. Then you can just drag the animations from the imported model into the Animator window like below and set up transitions as you see fit. Below I’ve made them all come from an Any State and added some Triggers so I can play with them in the Window for Testing.

You can see the result of that testing in the .gif at the top of the article. (Apologies for the quality of that .gif it seems to have picked up some ghosting artifacts around the fingers – promise it looks awesome on the screen).

The Animator Controller

So there are a few limitations to this workflow that need to be mentioned. Some people like to save their whole .blend file into their Unity Assets so they can make updates on the fly while modelling etc. This won’t work with that set up. The animations need to be saved down to a *.fbx file so that Unity can find them when it’s imported as an asset. So if you like to have access to your .blend and use animations like this you need to export the *.fbx and import it again and have both .blend and .fbx in your asset folders which can be a bit confusing and messy and makes for a bigger project.

BYE

Blender: Simple Dishes Plates and Cups

One game currently in the pipe line is called The Gap. The game is about the gap between the private internal world of personal histories and how they feed and colour perceptions of the present. Esoteric I know right. But basically it’s an exploring game in an urban setting with lots of indoor areas that need indoor props. So I’ve been doing lots of modelling and researching new methods to make life easier when using Blender (which I love).

Part of this research is reading lots and lots of books and one of them was Blender for Animation and Film-Based Production by Michelangelo Manriqu. The book is great and I’d recommend it to anyone using Blender. One method that I had not seen in any other tutorials or books was this one for making complex cone or cylinder based meshes using Bezier circles and curves. Instead of starting with the basic forms and working complex changes on their geometry this method is quick and simple to make complex (and simple) shapes really fast.

It starts like this: Open a new Scene and add a Bezier Curve and a Bezier Circle:

This is the right view (num 3) with the Bezier Curve moved over a bit on the X axis and rotated 90 degrees on the Z. (R key, −90 , Enter).

A Bezier Circle (in orange) and the black line next to it is a Bezier Curve.

Select the Bezier Circle and Click on the little green Bezier Object button from the properties tab on the right.

Open the Geometry and Bevel sections and click on the Object button. Select your Bezier Curve as the Object.

This sets your Bezier Curve as the defining object for the edges and volume of the now filled Bezier Circle.

Set the Bezier Curve as the Object

Now Select the Bezier Curve and move to Edit mode. Subdivide it as much as you want and play with the shape and angle of the curve to get your Bezier Circle to form all sorts of complex shapes.

Once you are happy with what you have you can go back to Object mode and select the Bezier Circle and go to Object -> convert to mesh (or ALT+C Mesh from Curve/Meta/Surf/Text).

Here some examples I did for this post:

Plate
Platter with Bevels
Chalice!

I think the Chalice up there shows the power of this method for creating complex small objects very quickly and the relationship between the Bezier Curve and the resultant mesh.

The Gap will not be released for some time yet – years – it’s still early days but I’m really enjoying the process and it’s nice to be doing something personal and meaningful with game development.

Endless Elevator – Last Week of Beta Testing

Hi Harmony here,

I’d like to thank the wonderful Beta Testers who have signed up for our Open Beta of Endless Elevator over the last month. I was really surprised how many people responded to our call and very grateful to all those who provided feedback.

The Open Beta is still running for another week so there is still time to try the pre-release game: https://play.google.com/apps/testing/com.ZuluOneZero.EndlessElevator

Then head over to our Beta Landing Page if you want to give us some feedback.

https://zuluonezero.net/endless-elevator-beta/

Your feedback helps us make a better game – thank you!

One feature of our Beta Testing was opening multiple channels to provide users to get back to us. Not only did we use the Google Play Beta Feedback and Stars system but we also took feedback on email and through social media. One new thing we did was to incorporate a chat channel on our Beta Landing Page where users could provide anonymous feedback directly on to our web site. I feel that the anonymity of this format was a really important source of quality comments for us.

Our Chat Page

Happy Playing – Harmony Out.

Endless Elevator – Early Access Beta

Endless Elevator went into Beta Testing on May 27, 2020, 8:10 PM.

Finally we made it to public testing! After a few months of keeping myself quiet and a seeming eternity of bug fixes I think I got a minimum viable product fit for your eyes, ears, and phone-ready fingers.

Please head over to our Beta Test Page and try it out. I’d love to get some feedback from other enthusiasts and developers.

Get it Here!

Chat With Us!

We have an anonymous Chat Channel right there on the page that you can trash talk into so go for broke! (You are also allowed to be supportive and kind as well if that is your nature).

🙂

The Beta Page has more information about the game as well links to articles written during development that explore the mechanics of the game.

Unity: How to Create a Cut-Scene with Cinemachine and Unity Recorder

Hi Xander here,

For Endless Elevator we wanted to do an Introduction Scene for the game. The gameplay, as the name suggests, consists of climbing endless elevators and escalators. The player navigates floor after floor in the bad guys luxury hotel and tries to climb as high as possible while defeating the bad guys. It’s a 2.5D top down scroll mechanism and clipped into the limits of the building. Just dumping the player into the start of the game in the levels of the building felt a little weird as there was no context to where you were in the story. Hence the need for an opening shot to set the scene and to literally drop the player into the game.

Our hero flies into the enemies’ oasis headquarters in a helicopter and storms into the foyer of their luxury hotel. We mocked up a scene and a helicopter in Blender and imported the assets into the main foyer scene of the game. I hadn’t used Unity’s Cinemachine before but wanted to try it out. Previously, in other projects, we had captured gameplay for cut-scenes using external software and video editing suites which was OK but the experience with Cinemachine and Unity Recorder was way smoother. It was much easier to work with and much better quality avi files. Plus we didn’t have to do custom scripts for switching cameras and panning. It was so easy it kind of made me excited about movie making with Unity but you know I don’t need another distraction.

To start working with Cinemachine and Unity Recorder you can download them using the Package Manager. Unity Recorder has only recently been added (it’s still also on the Asset Store) so you need to enable the “Preview Packages” selection from the Advanced menu in the Package Manager.

Cinemachine in the Package Manager

Have a look at the Unity Manual and tutorials for more info about Cinemachine and Unity Recorder.

Below is a screen shot of my scene in Unity. You can see the main building in green and the surrounding buildings and water in the bad guys oasis HQ. The helicopter is just visible down where the camera sight lines join and on the left in the Hierarchy you can see my Timeline component and my two vcams (Virtual Cameras).

The Timeline is where all the magic happens and was very easy to set up.

First we did a few animations on the helicopter to fly it in to the building and make the rotor spin. Then we added an animation to move the character from the helicopter into the building (which looks terrible but remember this is a quick mock up)

The Helicopter Animation

We dragged this animation into a new Animation track on the Timeline object (right click and Add Animation Track). Then we created two Virtual Cameras in the scene. One Camera (vCam1) was set using the properties in the Inspector to automatically Loot At and Follow the helicopter. This means that where ever we flew the Helicopter the camera would follow it round from behind at a set distance and keep it in the frame automatically. This was really fun when we had it under manual control for testing and worked well when under the control of the Animator. We used a preset for a bit of camera jitter and shake to mimic a real camera man in a second helicopter.

The second Camera (vCam2) was stationary at the building site but set to Follow (ie. Look At) the Main Character. We timed the cut from one camera to the other so that once the helicopter landed it would pass control to the second camera and seamlessly start focusing on the Player. This was so easy it was ridiculous. The Camera objects were added to the Timeline and the split where we cut from one camera to the next is clearly visible in the screenshot below (two triangles). The first time I ran it and that view cut automatically from one vcam to the other gave me an enormous sense of satisfaction like I’d just been named a modern day Hitchcock.

The Timeline Editor Window

To record what we had done as an AVI we opened the Recorder Window:

Opening the Recorder Window.

We used the default settings and triggered the Start of the Recording with the start of the animation by having it in the Timeline. The Capture target was the Game View (you can also get the other elements of the Editor if you need it). The Output Resolution is interesting as you can use the Size of the Editor Game window on your screen or set it to standard default movie formats.

The Recorder Window

That’s about it. We hit Play in the Editor and the Timeline starts the Recording of the AVI and synchronises the Animations and the Camera movement automatically. Once we are done we are left with a good quality moving image of our game screen that we will use as the cut-scene to drop the player into the start of our game. Obviously what we got here is just a “screen test” but I was really happy with what we could achieve in just a few hours and with so little complexity.

Xander out…

Unity Audio vs Wwise

To start with I wanted to do a general investigation into Wwise the integrated audio package for Unity by AudioKinetic. When I started working through it I figured it would be more interesting to look at Wwise in comparison to Unity’s own audio API and mixer components which have been around since Unity 5.

To do that I’m going to compare a game in three different builds. Build one is it’s original state with simple scripts that run an AudioSource.Play() method. The Second build I will add another layer of complexity by using the Unity built in Mixer and see if there are any differences or advantages. Lastly I’ll redo the project with the Wwise API and investigate how that impacts build size and project complexity and weigh it up against the previous two builds. Mostly I’m looking for difference in performance between the three builds, build size and complexity, and weighing that up against ease of implementation and flexibility.

I refreshed an old project called “MusicVisualiser”that I started for my Five Games in Ten Weeks Challenge. The game is like a singing solar system. There is a bunch of “planets” in the night sky that play a set piece of music when clicked. It’s a really simple concept and project but I think it will work for this comparison as the parameters can be limited to just a few audio tracks but we can play with spacing and roll-off and other advanced audio features.

Let’s have a look at the game first.

These “planets” are simple native Unity sphere meshes with an Audio Source component and a particle system that’s triggered when it’s clicked. You can see in the Audio Source that we are not using a Mixer for Output and all the Audio sources compete for resources and play at their default volume and priority.

The PlayMe script just takes in the AudioSource and plays it:

   public AudioSource my_sound;
        
  if (Input.GetMouseButtonDown(0))
  {
      RaycastHit hitInfo;
      target = GetClickedObject(out hitInfo);
      if (target != null && target.name == my_name)
      {
          _mouseState = true;
          screenSpace = Camera.main.WorldToScreenPoint(target.transform.position);
          offset = target.transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenSpace.z));

          my_sound.Play();   // This is the Audio Component!
          var expl1 = GetComponent<ParticleSystem>();
          expl1.Play();
      }
  }

Pretty simple right. This is what the project looks like in the Profiler when it’s running and being actively engaged with. At that point we are looking at two Audio Sources are playing:

This is the build size from the Editor Log with our Audio Files broken out:

Build Report (Audio.Play)
Uncompressed usage by category:
Textures 0.0 kb 0.0%
Meshes 0.0 kb 0.0%
Animations 0.0 kb 0.0%
Sounds 547.5 kb 1.4%
Shaders 188.0 kb 0.5%
Other Assets 1.4 kb 0.0%
Levels 38.3 kb 0.1%
Scripts 941.9 kb 2.4%
Included DLLs 3.9 mb 10.2%
File headers 9.3 kb 0.0%
Complete size 38.6 mb 100.0%

Used Assets and files from the Resources folder, sorted by uncompressed size:
204.3 kb 0.5% Assets/SomethingLurks_AAS.wav
164.5 kb 0.4% Assets/Step2Down_AAS.wav
136.9 kb 0.3% Assets/Underwater_AAS.wav
41.8 kb 0.1% Assets/M1_M12_37_ThumPiano_Aflat1.wav

Unity Audio with Mixer

Now we add in the Mixer component to the project:

Then add a couple of Channels to the Mixer to split the audio between left and right. Then the Audio Sources are dropped into one or another of the Mixer Channels:

Adding the Mixer as the Output source

Next for bit more interest I added some effects in the Mixer. Here is where we see the advantages of using the Unity Mixer. Sounds can be manipulated in complex ways and the Audio Output chain be defined with presets and levels etc.

If we have a look at our Profiler while running with the new component we cannot really see any great differences. The ‘Others’ section of the CPU Usage is a bit higher and the Garbage Collector in the Memory is pumping regularly but the Audio Stats look pretty much unchanged:

Profiler Mixer

Mind you this is a fairly low utilising game so we might get wildly different stats if we were really putting the system under the pump but I’m not performance testing here just comparing run states between the two builds.

Next if we build the game and have a look at the Editor Log the only thing that’s changed here is that the “Other Assets” size is a KB higher (Complete size has not been changed):

Build Report (Mixer)
Uncompressed usage by category:
Textures 0.0 kb 0.0%
Meshes 0.0 kb 0.0%
Animations 0.0 kb 0.0%
Sounds 547.5 kb 1.4%
Shaders 188.0 kb 0.5%
Other Assets 2.3 kb 0.0%
Levels 38.3 kb 0.1%
Scripts 941.9 kb 2.4%
Included DLLs 3.9 mb 10.2%
File headers 9.3 kb 0.0%
Complete size 38.6 mb 100.0%

Unity with Wwise

Next we are going to add Wwise to the Project. This is the basic workflow. In the Wwise Launcher we register our project and on the first tab we are presented with three Hierarchies.

Project Audio Explorer in Wwise

The Master-Mixer Hierarchy – does what it says.
The Actor-Mixor Hierarchy – where most of your game audio develops (use the SoundSFX defaults).
Interactive Music Hierarchy – other stuff we won’t get into.

Events Tab

The next tab along is the events tab where you link your audio to game events. You can define your event here (use the default work unit).
Once you got the event there you can associate the event with the audio in the Action List.

SoundBank Tab – this is the bit that get’s imported into your project.

Next you generate a SoundBank with Wwise that includes your audio and the code for the API calls to trigger sounds. You export that SoundBank into your game engine and link up the calls in your code.

To Get Started with Wwise

To get started make an account with Audiokinetic and download the Wwise Launcher. The Integration package for Unity can be downloaded and installed directly from the WWise Launcher.

In the Wwise Launcher there is a WWISE tab that you can install and start the application from. Once you open it up you need to register your project within the launcher so Wwise can track you 🙂 ( click on the key icon next to your Wwise project and select ‘Register your Project to obtain a License’). Wise will run in Trial mode which restricts the SoundBank content to 200 media assets and cannot be used for Commercial purposes. Pricing for licensing is on their site but this is not a sales piece so if you want it you can look it up.

There are a bunch of plugins by Audiokinetic and their partners available and also Community offerings like AudioRain a dedicated rain synth with 60 procedurally generated presets for rain. What’s not to love about that!

There is a Wwise SDK for authoring your own plugins and a Wwise API which allows you to integrate into any engine, tool or application.

Audiokinetic do certifications that covers audio integration workflows,
mixing virtual soundscapes, working with sound triggering systems, and performance optimisation :
https://www.audiokinetic.com/learn/certifications/

Basically in Wwise you let the Launcher do all the setting up for you. You will install the Wwise binaries from here and manage your platform versions. Projects can be integrated here and if you don’t have the necessary plugins installed the Wwise Launcher will install them for you.

Integrating the MusicVisualiser project with Wwise.
This is how big the Wwise Integration packages and binaries are.
Applying…
Done!

That’s basically it for the set up of Wwise and Integration with your Project. Next up we will have a look at what this has done to the Unity Console.

Wwise in Unity

First thing we see is a bunch of errors that can be safely ignored. As we did not perform any configuration of our project in Wwise with audio files and events there was no SoundBank to generate yet.

Unity – Initial Errors can be ignored if you have not generated your SoundBank yet.

In the Unity Console we have a new tab in our editor. The Wwise Picker Tab contains all the elements of the Wwise project that have been imported with the project integration. There is also a Wwise Global Game Object in the Unity Hierarchy and all the Wwise folders in the Assets folder.

Unity Editor
The WwiseGlobal Game Object

Under the Component pull down there is a whole slew of Ak (AudioKinetic) options.

Wwise Components.
Wwise Configuration Settings.

I know there has been a lot of “show and tell” in this post but I’m going to keep going and show the process of importing the audio into the Wwise Project, creating Events, and Generating the SoundBank.

Working in Wwise

In the Wwise Project Explorer I right click on the Default Work Unit and import the audio files that were part of my project. (I’ve stripped the raw files out of my project for now and removed all the Mixer components and etc.).

Importing Audio Files into the Wwise Project.
This is what the files look like.
Right click on the file to create a new Event (which can be called in the Unity code).
Here is the event created for “Play”.
And all my “Play” events.

Finally a SoundBank is generated from which the Unity project can access the sound files through the AudioKinetic API.

Generating a SoundBank

Wwise Audio in Unity

When we go back to our Unity Editor and Refresh the Project and Generate SoudBanks we are presented with the following in the Wwise Picker. We can now access these files and and drag them on to our game objects directly. It’s that simple. Drag a sound from the Picker onto a Game Object and it automagically creates a component that is immediately accessible from within the editor.

The new audio imported into the Wwise Picker.

Below the Play_Underwater_AAS event and audio file has been added to the Sphere Game Object.

The Trigger, Actions, and Callbacks can all be configured and access through the API. In my case I easily integrated the functionality I wanted with only one line change to my attached PlayMe.cs script that we looked at above. So now instead of my audio coming from the AudioSource component referenced by my_sound the audio is played by the AKSoundEngine.PostEvent.

            //my_sound.Play();
            AkSoundEngine.PostEvent("Play_Underwater_AAS", this.gameObject);

Actually getting Wwise installed and set up and integrated with my Project was very very easy but not without bumps. It takes a very long time for packages to download and I had a bit of trouble upgrading my Wwise Launcher from an old version (it got stuck! and I had to remove it by hand and re-install). When I did have issues I got some very excellent help from AudioKinetic and after logging a case was emailed directly by a real person (which honestly was so surprising and wonderful to get that kind of support from a company when I’m on a trial license with no formal support agreement or rights).

So lets have a look at the differences in performance and package size. The first thing you notice with the Profiler below is that there is very little difference in performance but we can no longer see our audio stats as it’s been abstracted away from the Unity Engine. The Graph still shows the resources being used by Audio and the Total Audio CPU seems to be up to a third lower than the native Unity Audio statistics. It looks like it’s being clamped at just over 1.2. MB instead of regular peaks over 3 MB.

Profiler with Wwise Audio running.

The Build Report is only a couple of MB larger for the total project size:

Build Report
Uncompressed usage by category:
Textures 0.0 kb 0.0%
Meshes 0.0 kb 0.0%
Animations 0.0 kb 0.0%
Sounds 0.0 kb 0.0%
Shaders 188.0 kb 0.5%
Other Assets 7.3 kb 0.0%
Levels 38.5 kb 0.1%
Scripts 1.3 mb 3.1%
Included DLLs 3.9 mb 9.7%
File headers 13.2 kb 0.0%
Complete size 40.5 mb 100.0%

Basically a 2 MB difference! The Sounds have been extracted away as a file in the Build Report and we assume they are now part of “Other Assets” above.

I’m kinda blown away by how how little additional file size there is to the build considering the additional libraries code and available complexity that Wwise adds. There is literally a plethora of options and effects that we can play with in the Wwise package. It’s a bit like the excitement I got after the install of my first real Audio DAW. The scope is part boggling and part fantastical wonder at where we can go next. (Audio does get me unusually stimulated but that’s to be expected and tempered accordingly).

The questions I wanted to answer with this whole experiment was 1. Would including an audio middleware like Wwise make my Project more complex and difficult to manage? 2. Would the added Package make my build much larger? and 3. Would the performance of the Audio package be as good as the simple Unity Audio API? The answers are: No. No, and Yes. So I’m pretty happy with that and if the cost point of using the licensed version of Wwise is balanced out against the advantages of using it in the total cost of the Project then I would most definitely one hundred percent go for it.