Animation Update

It occurs to me that I’ve been posting updates everywhere else on the Internet (Facebook, Twitter, Google+) but my own website. And in those places, it’s been 140-characters-or-less blurbs that don’t explain much.

We’ve made a lot of progress, actually.

  • Created an art style that combines 3D models with 2D animation.
  • Bettina made a full rig for one of the characters, Spogey. (Regular podcast listeners will remember Spogey.)
  • This character’s movements can be controlled and recorded inside of a machinema engine. (Unity3D with a bunch of custom code I wrote.)
  • Wrote and recorded a script for a 4-minute short animation.
  • Started character design for the next character, Mr. Stomps.

We’ll continue animating and coding til its done. Some new people are joining the project soon, to be announced.

What? You don’t care about all that, and just want to see videos? Okay, that’s fine…

-Erik

Joystick-controlled menus in Unity.

Here is some code I wrote to control menus in Unity using a game controller (joystick). It is very loosely based off Alex Hackl’s JoystickButtonMenu code. (Thanks, Alex!) I noticed a few things I wanted to improve upon for my own use. And now I’m following the open source ethos and dropping it out on the Internet.

Differences from Alex’s JoystickButtonMenu include:

  • Rect calculation to display menu is automatic.
  • Constructor is simplified–just pass in option names.
  • Back button support.
  • Can change menu options after menu created.
  • The input freeze timing delay is included in the class instead of required by calling code.

To initialize a menu…

Menu myMenu = new Menu(new string[] {"One", "Two", "Three"});

In an OnGUI() method, call…

myMenu.Display();

In an Update() method, call…

myMenu.CheckForSelection();

Actually, you will want to see what the selection is and do something, so…

if (myMenu.CheckForSelection()) {
    switch (myMenu.Selection()) {
        case "one":
            Debug.Log("One!");
            break;

        case "two":
            Debug.Log("Two!");
            break;

        case "three":
            Debug.Log("Three!");
            break;
    }
}

Typically, after a user selects a menu option, you would hide the menu. Calling code should track that “is menu visible” state itself and call Display() and CheckForSelection() for a menu only if the intent is to show that menu. Example of everything put together…

using UnityEngine;
using StickMenu;

public class MenuExample : MonoBehaviour {
    Menu menu = new Menu(new string[] {"Your Only Option"});

    void Update() {
        if (menu != null) {
            if (menu.CheckForSelection()) {
                Debug.Log("You picked " + menu.Selection() + "--great choice!");
                menu = null;
            }
        }
    }

    void OnGUI() {
        if (menu != null) {
            menu.Display();
        }
    }
}

And here is the full source for the Menu class.

Menu.cs

/*
 I had Alex Hackl's menu code ( http://wiki.unity3d.com/index.php?title=JoystickButtonMenu ) 
 up as a reference when writing this. But the code in this file is a complete rewrite, 
 so I'll release this code as PUBLIC DOMAIN instead of CC license. Much thanks to Alex 
 for posting his code which made mine much easier to write.

 Enjoy! You can contact me, Erik Hermansen, at info@seespacelabs.com if you like.
*/

using UnityEngine;

namespace StickMenu
{
    public class Menu {
        //Change these to match what you've defined in InputManager.
        private const string SELECT_AXIS = "Vertical";
        private const string SELECT_BUTTON = "Fire1";
        private const string BACK_BUTTON = "Fire2";

        //Constants for drawing menu options.
        private const float RECT_CY = 30f;
        private const float RECT_CX = 220f;
        private const float TEXT_INDENT_CX = 50f;

        //Input freeze intervals to help the menu control work intuitively.
        private const float BUTTON_FREEZE_DELAY = .1f;
        private const float AXIS_FREEZE_DELAY = .2f;
        private float noInputUntil = -1f;

        //Each menu option button will be in one of these three states.
        private enum ButtonState {
            up,
            down,
            over
        }

        //Menu state data.
        private struct MenuOption {
            public string name;
            public Rect rect;
            public Rect textRect;
            public ButtonState state;
        }
        private MenuOption[] options;
        private int selectedNo = 0;

        //Pass in names to be displayed in menu options.
        public Menu (string[] optionNames) {
            SetOptions (optionNames);
        }

        //Can be called for initial setup or at any time after.
        public void SetOptions (string[] optionNames) {
            string oldSelectedName = "";
            if (this.options != null && this.options.Length > 0)
                oldSelectedName = this.options[this.selectedNo].name.ToLower();
            this.selectedNo = 0;
            this.options = new MenuOption[optionNames.Length];

            float rectX = Screen.width / 2 - RECT_CX / 2;
            float rectY = Screen.height / 2 - (this.options.Length * RECT_CY) / 2;

            int i = 0;
            foreach (string optionName in optionNames) {
                MenuOption mo = new MenuOption ();
                mo.name = optionName;
                if (optionName.ToLower() == oldSelectedName) //If changing options in a preexisting menu, preserve original selection.
                    this.selectedNo = i;
                mo.rect = new Rect (rectX, rectY, RECT_CX, RECT_CY);
                mo.textRect = new Rect (rectX + TEXT_INDENT_CX, rectY, RECT_CX - TEXT_INDENT_CX, RECT_CY);
                mo.state = ButtonState.up;
                options [i++] = mo;
                rectY += RECT_CY;
            }
            options [selectedNo].state = ButtonState.over;
        }

        //Must be called from OnGUI of a MonoBehavior class. Change this method 
        //if you want to draw your menu differently.
        public void Display () {
            Texture upTexture = (Texture)GUI.skin.button.normal.background;
            Texture overTexture = (Texture)GUI.skin.button.hover.background;
            Texture downTexture = (Texture)GUI.skin.button.active.background;

            foreach (MenuOption mo in options) {
                if (mo.state == ButtonState.down) {
                    GUI.DrawTexture (mo.rect, downTexture);
                    GUI.skin.label.normal.textColor = GUI.skin.button.active.textColor;
                } else if (mo.state == ButtonState.over) {
                    GUI.DrawTexture (mo.rect, overTexture);
                    GUI.skin.label.normal.textColor = GUI.skin.button.hover.textColor;
                } else {
                    GUI.DrawTexture (mo.rect, upTexture);
                    GUI.skin.label.normal.textColor = GUI.skin.button.normal.textColor;
                }
                GUI.Label (mo.textRect, mo.name);
            }
        }

        //Returns true if user has made a selection by clicking. Also handles update 
        //of menu state based on controller input. Call from Update() method of a 
        //MonoBehavior object.
        public bool CheckForSelection () {
            float now = Time.realtimeSinceStartup; //Using .realtime instead of .time so that menus are immune to pausing via Time.timeScale = 0.
            if (now < this.noInputUntil) return false; //Check for previously clicked option that was animated in down state. 
            if (this.options [this.selectedNo].state == ButtonState.down) { 
                this.options [this.selectedNo].state = ButtonState.over; return true; 
            } 

            //When user clicks option, it is shown temporarily in down state. 
            if (Input.GetButtonDown(SELECT_BUTTON)) { 
                this.options [this.selectedNo].state = ButtonState.down; 
                this.noInputUntil = now + BUTTON_FREEZE_DELAY; //Add the freeze so I can see button displayed in down state. 
                return false; 
            } 

            //When user clicks back button, select a menu option called "back" if 
            //present and show temporary down state. 
            if (Input.GetButtonDown (BACK_BUTTON)) { 
                //Check for back button in menu to jump to. 
                int backButtonNo = findBackButton (this.options); 
                if (backButtonNo == -1) return false; //No back button. 
                this.options [this.selectedNo].state = ButtonState.up; 
                this.selectedNo = backButtonNo; 
                this.options [this.selectedNo].state = ButtonState.down; 
                this.noInputUntil = now + BUTTON_FREEZE_DELAY; //Add the freeze so I can see button displayed in down state. 
                return false; 
            } 

            //Check for up/down menu movement. 
            float axisValue = Input.GetAxis (SELECT_AXIS); 
            if (axisValue > .1f) {
                this.options [this.selectedNo].state = ButtonState.up;
                if (++this.selectedNo == this.options.Length)
                    this.selectedNo = 0; //Loop selection to top.
                this.options [this.selectedNo].state = ButtonState.over;
                this.noInputUntil = now + AXIS_FREEZE_DELAY; //Add the freeze so menu traversal isn't too fast.
            } else if (axisValue < -.1f) {
                this.options [this.selectedNo].state = ButtonState.up;
                if (--this.selectedNo == -1)
                    this.selectedNo = this.options.Length - 1; //Loop selection to bottom.
                this.options [this.selectedNo].state = ButtonState.over;
                this.noInputUntil = now + AXIS_FREEZE_DELAY; //Add the freeze so menu traversal isn't too fast.
            }
            return false;
        }

        //Returns current selection of menu. In lower case for use in 
        //case-insensitive comparisons.
        public string Selection () {
            return options [this.selectedNo].name.ToLower ();
        }

        //Look for a button called "back".
        private static int findBackButton (MenuOption[] mos) {
            for (int i = 0; i < mos.Length; ++i) {
                if (mos [i].name.ToLower () == "back")
                    return i;
            }
            return -1; //No match found.
        }
    }
}

Machine Court animated.

Everything I do is complicated!

Everything I do is complicated!

I am working with Bettina Throckmorton to put together an animated version of Machine Court. Characters from the earlier seasons like Spogey and Mr. Stomps will return in walking, talking form. And instead of an audio podcast, we will release video.

It is early days, but already we’re busy with character designs and the creation of an animation engine that renders with a mixture of 2D and 3D art. The engine will let us create episodes quickly in a cheap but interesting style. I’m using my past experience as a game developer and artist to put this together, and relying on Bettina’s impressive animation and character design chops.

For A/V geeks, I’ve attached an image of the animation pipeline.

I know some listeners are going to be bummed that the podcast isn’t releasing new episodes. But remember… Machine Court isn’t gone, and nobody’s taking a break. I’m so damn hardcore about this. You don’t even know!

-Erik

4.16 “Person of Dumbitude”

person_of_dumbitude_0001_final

Our guest, Mitch Burrow, is not smart enough to use the Internet. Life is difficult for a man who refuses to take an intelligence upgrade like everybody else. Pornbots try to steal his kidneys. Scammers want to frame him for car accidents. And worst of all, he might not be smart enough to have decent sex with his girlfriend. A story about being left for dumb.

Music/Sfx: Machine Court Theme by Rich Vreeland. In A Vat by Erik Hermansen and Jon Sonnenberg. All other songs from royalty-free music providers. Sound effects provided from various authors under Creative Commons license (Freesound attribution page).

4.15 “Jimmy Shack”

jimmy_shack_0001_final

An important thought experiment: what if Jimmy Stewart ran a Radio Shack? Direct Internet shopping threatens to make the walk-in retail experience a thing of the past. Is Potter to blame? Or Uncle Billy?

Music/Sfx: Machine Court Theme by Rich Vreeland. In A Vat by Erik Hermansen and Jon Sonnenberg. All other songs from royalty-free music providers. Sound effects provided from various authors under Creative Commons license (Freesound attribution page).

4.14 “Your Facebooky Life”

your_facebooky_life_0004_final_perspective

Facebook automatically assembles nostalgic memories of you into a multimedia presentation that is sure to give you and your friends the warm fuzzies. Also, a lonely man desperately wants to be your Facebook friend.

Music/Sfx: Machine Court Theme by Rich Vreeland. In A Vat by Erik Hermansen and Jon Sonnenberg. All other songs from royalty-free music providers. Sound effects provided from various authors under Creative Commons license (Freesound attribution page).

4.13 “Death of Auto-Complete”

death_of_auto_complete_0001_final

We’ve all come to rely on auto-complete. And everybody assumes it will always be there. But what if, one day, auto-complete died? Imagine having to completely type every word of your search queries without Google’s help. And perhaps this reliance is more insidious. Does Google fill in our missing thoughts? Also in this episode: a surprising comeback from a 90′s search engine, and a pervy shout-out to the powermoms.

Music/Sfx: Machine Court Theme by Rich Vreeland. In A Vat by Erik Hermansen and Jon Sonnenberg. All other songs from royalty-free music providers. Sound effects provided from various authors under Creative Commons license (Freesound attribution page).

4.12 “Big Dumb Pipe”

big_dumb_pipe_0001_final

Mobile network CEOs get together in a smoke-filled backroom for an evening of collusion and unwanted nudity. Is there some way to convince the world that they really are special, and not just a big dumb pipe? Also Jimmy Stewart stops by to brag about his new smartphone and all the cool apps he’s installed on it.

Music/Sfx: Machine Court Theme by Rich Vreeland. In A Vat by Erik Hermansen and Jon Sonnenberg. All other songs from royalty-free music providers. Sound effects provided from various authors under Creative Commons license (Freesound attribution page).

4.11 “iPhone Mafia”

iphone_mafia_0001_final

Our guest, David Leon, is just a man who wants to be appreciated–or at least for people to stop yelling at him. After a catastrophic presentation at work, David’s iPhone begins talking to him. And so begins David’s descent into a Kafkaesque set of circumstances threatening his safety and sanity.

Music/Sfx: Machine Court Theme by Rich Vreeland. In A Vat by Erik Hermansen and Jon Sonnenberg. All other songs from royalty-free music providers. Sound effects provided from various authors under Creative Commons license (Freesound attribution page).

4.10 “Trevortown Parade”

This is a short bonus episode with a few odds and ends. Includes a newscast from Trevortown, Tina Zamm’s hit song Leather Couch Stanky Dog, and a visit from Spogey and Mr. Stomps.

Music/Sfx: Machine Court Theme by Rich Vreeland. In A Vat by Erik Hermansen and Jon Sonnenberg. All other songs from royalty-free music providers. Sound effects provided from various authors under Creative Commons license (Freesound attribution page).

1 2 3 21