Monday, November 7, 2016

Unity: Shuriken particles do not randomize

There's an issue with some versions of Unity where particles that should render with some random parameters instead render exactly the same every time. For instance, when setting the size to "Random Between 2 Constants", you'll see that each particle does have a random size; however, when playing this particle multiple times, that size will be the same on each play.

Some later versions of Unity have fixed this; however, if you're seeing this problem but don't want to or can't upgrade for whatever reason, there is a manual solution: set the random seed the particle system uses before playing the particle. Add this line before manually playing your particle system:

particleSystem.randomSeed = (uint)Random.Range(0,9999999);

particleSystem is the name of variable representing your particle system.

Here is a full script that will find a ParticleSystem attached to the GameObject the script is attached to, set the seed, and play the particle. If you set the "destroyAfterPlay" bool to True, it will also destroy the GameObject after playing -- this is useful for instantiating lots of particles and being assured they'll be cleaned up properly.

using UnityEngine;

public class ParticleController : MonoBehaviour
{
    // Find, play, and destroy an attached particle system.
    private ParticleSystem particleSystem;
    private bool startDestructionProcess = false;
    public bool destroyAfterPlay = false;

    void Start ()
    {
        // Find the attached particle system.
        particleSystem = this.gameObject.GetComponent<ParticleSystem>();

        // If we find it, set the seed and play it.
        if (particleSystem)
        {
            particleSystem.randomSeed = (uint)Random.Range(0, 9999999);
            particleSystem.Play();

            // Flag that this particle can be destroyed after it stops playing.
            startDestructionProcess = true;
        }
    }
  
    void Update ()
    {
        if (startDestructionProcess && destroyAfterPlay)
        {
            if (!particleSystem.isPlaying || !particleSystem)
            {
                // The particle system is done playing and we've flagged we want it destroyed.
                Destroy(this.gameObject);
            }
        }
    }
}

Friday, October 9, 2015

Unity 3D: Compiled game freezes or crashes on exit

Sometimes -- and it seems to happen much more often when dealing with blocking resources (such as serial ports) or multiple threads -- your built Unity game will crash or hang when calling Application.Quit ().

First and foremost, check your logs and make sure you're not throwing an exception as the program exits. And of course check your code to make sure you haven't inadvertently made an infinite loop somewhere. Clean up any resources that may not be handled automatically -- for instance, close serial ports, kill threads, etc.

Assuming you've done all that and you're still freezing when the program exits, try replacing Application.Quit () with the following lines of code:

if (!Application.isEditor)
{
     System.Diagnostics.Process.GetCurrentProcess().Kill();
}

There seems to be the occasional bug that rears its head with Application.Quit (), so we're using a .NET command to kill the process directly. We are also checking to make sure that we're not in the Editor, because if you run this line of code from the Editor, it will close.

Tuesday, July 21, 2015

Unity 3D: Console displays wrong folder path for files

Sometimes, especially if you're bouncing projects between different users, your Console may show that your source files are in a different folder location than where they're actually stored.

For instance, let's say you have a null reference error in a "DoStuff ()" method. Normally, the second line on the console will show you the method name, and if you double-click the error, it will take you to that line of code. However, if you see a path to the source file and it's not valid (say, a path on someone else's computer), then it won't open MonoDevelop and you've got a problem.

There is, fortunately, a simple fix. Close the project, go to the project's directory, and find the "Library" folder. Delete everything inside of it, and open your project again. Unity will have to re-build the entire asset library, so it will take longer than normal to open, but once it does, your issue should be resolved. If it isn't, re-sync your MonoDevelop project ("Assets > Sync MonoDevelop Project").

If you have a team working on a project together, shipping the whole project back and forth is a bad way to work for a number of reasons. Look into version control!

Thursday, July 16, 2015

Unity 3D: "Screen position out of view frustum" error

"Screen position out of view frustum" is an error almost everyone will see at some point. This can be caused by a few different things, and fortunately is pretty easily fixed.

First off, there is a bug (at least as of Unity 4.5) that can cause this to happen randomly. The quickest way to find out if that's your issue is to close the "Scene" tab and reopen it from the "Window" menu. If there's nothing in your scene causing this issue, it should now go away.

If you're using an orthographic camera, don't set the orthographicSize to 0 or this will happen. Don't forget to check not only the editor, but any scripts attached to the camera -- any zooming scripts you have may be setting it 0 as well.

The last issue I've seen that causes this error is having a camera with a near clip plane set to 0. Again, don't just check the editor; make sure you look at any scripts manipulating the camera.

If anyone finds any other issues that can cause this error, let me know and I'll add it!


Wednesday, July 30, 2014

Unity 3D and Phidgets: Weather Underground app

Looking to have a little fun with my Phidgets displays, and provide some relatively useful functionality, I decided to make a little weather app. This app grabs weather data from Weather Underground at regular intervals for as many ZIP codes as you'd like, then displays the data on a Phidgets Text LCD device. It's not terribly complicated but it is a nice way to show the Text LCD functionality as well as using the .NET WebClient class in Unity and some simple XML parsing.

First things first: do your basic Phidgets setup in a Unity project. If you'd like to peruse the Phidgets Text LCD tutorial that I wrote a while back, that will give you a little background on the hardware commands, although we'll touch on that here as well.

Also, you'll need to set up a Weather Underground developer account to get access to your personalized API key.

So, you should at this point have a Weather Underground API key and a basic Unity project ready to go. We're going to keep this all very simple and you can expand on it as you like.

First, I'm going to set up a script to handle communication with my display. Create this script and drop it on to a GameObject in your scene (I called mine "DisplayController").

using Phidgets;
using Phidgets.Events;
using UnityEngine;
using System.Collections;

public class DisplayController : MonoBehaviour
{
    private TextLCD lcdDisplay= new TextLCD ();
    private bool lcdDisplayIsAttached = false;
  
    void Start ()
    {
        lcdDisplay.Attach += new AttachEventHandler (lcdControl_Attach);
        lcdDisplay.Detach += new DetachEventHandler (lcdControl_Detach);
        lcdDisplay.Error += new ErrorEventHandler (lcdControl_Error);
      
        lcdDisplay.open ();
    }
  
    void lcdControl_Attach(object sender, AttachEventArgs e)
    {
        Debug.Log ("Display attached.");
        lcdDisplay.open ();
        lcdDisplay.screens[0].Backlight = true;
        lcdDisplay.screens[1].Backlight = true;
        lcdDisplay.screens[0].ScreenSize = TextLCD.ScreenSizes._2x20;
        lcdDisplay.screens[1].ScreenSize = TextLCD.ScreenSizes._2x20;
        lcdDisplay.screens[0].initialize ();
        lcdDisplay.screens[1].initialize ();
        lcdDisplayIsAttached = true;
        WriteMessageToDisplay ("*** INITIALIZING ***", "***   PROGRAM    ***", false);
    }
  
    void lcdControl_Detach(object sender, DetachEventArgs e)
    {
        Debug.Log ("Detached.");
        lcdDisplayIsAttached = false;
    }
  
    void lcdControl_Error(object sender, ErrorEventArgs e)
    {
        Debug.Log ("Phidgets display error: " + e.ToString ());
    }

    public void WriteMessageToDisplay (string topLine, string bottomLine, bool centerText)
    {
        string topPadding = "";
        string bottomPadding = "";

        // Make sure strings are not too long.
        if (topLine.Length > 20)
        {
            topLine = topLine.Remove (19, topLine.Length - 20);
        }

        if (bottomLine.Length > 20)
        {
            bottomLine = bottomLine.Remove (19, bottomLine.Length - 20);
        }

        if (centerText)
        {
            if (topLine.Length <= 20)
            {
                int amountOfPaddingToAdd = (20 - topLine.Length) / 2;
                for (int i = 0; i < amountOfPaddingToAdd; i++)
                {
                    topPadding += " ";
                }
            }

            if (bottomLine.Length <= 20)
            {
                int amountOfPaddingToAdd = (20 - bottomLine.Length) / 2;
                for (int i = 0; i < amountOfPaddingToAdd; i++)
                {
                    bottomPadding += " ";
                }
            }
        }

        string formattedTopLine = topPadding + topLine;
        string formattedBottomLine = bottomPadding + bottomLine;

        if (lcdDisplayIsAttached)
        {
            lcdDisplay.screens[0].Backlight = true;
            lcdDisplay.screens[0].rows[0].DisplayString = formattedTopLine;
            lcdDisplay.screens[0].rows[1].DisplayString = formattedBottomLine;
            lcdDisplay.screens[1].Backlight = true;
            lcdDisplay.screens[1].rows[0].DisplayString = formattedTopLine;
            lcdDisplay.screens[1].rows[1].DisplayString = formattedBottomLine;
        }
    }
  
    void CloseDisplay ()
    {
        lcdDisplay.close ();
        lcdDisplay = null;
    }
  
    void OnApplicationQuit ()
    {
        CloseDisplay ();
    }
}


Let's go through this quickly; if you've followed the Unity Text LCD tutorial there isn't much new here.

We create a new Phidgets TextLCD () device to be able to communicate with the display, and a bool to keep track of when it is attached. We could instead check TextLCD.Attached before we try to communicate with it; this is just another way to go about it.

In Start (), we attach our event handlers and open the device for communication. In our event handlers, we're doing some simple initialization when it's attached (turning on the backlight, clearing the display, etc.), setting the attached status when it's detached, and logging errors as needed. Note that in lcdControl_Attach (), we set the screen size (number of lines by number of columns) -- this is critical. This size must be set before you send any text to the display. We are using a 2x20 display, so you'll need to change this if you have a different size.

WriteMessageToDisplay () is a public method we'll use from other methods to send text to the display. We've allowed for two lines, and a bool to decide if we want to center the text or not. We limit the text size to 20 characters, because if we go over, we'll throw an exception; again, change your text size to accomodate the size you need. If we do want the text centered, we look at the max text length minus the current length, and pad half that length to the beginning of the string. Then, as long as the display is attached, we set our backlight and shoot the text over to the display. Note also that we are talking to both displays on the controller; if you'd like to have identical output on two displays, this is already taken care of.

Finally, CloseDisplay () gets rid of the TextLCD object cleanly, and it gets called when the application is exited. If we don't do this (and, in my experience, even sometimes when we do), the application will hang when you exit.

So now we have a script that is capable of talking to the display. We need a script that can grab this weather data for us. Again, I made an object in my scene (this time called "WeatherController") and attached the following script to it.

using UnityEngine;
using System.Collections;
using System.Net;
using System.Xml;
using System.IO;

public class WeatherController : MonoBehaviour
{
    // Weather Underground API settings
    private const string WEATHER_UNDERGROUND_API_KEY = "abc123";

    public string[] ParseWUndergroundData (string zipCode)
    {
        // Form the proper URL.
        string inputXML = "http://api.wunderground.com/api/" + WEATHER_UNDERGROUND_API_KEY + "/conditions/q/" + zipCode + ".xml";

        string place = "";
        string weather = "";
        string temperature = "";
        string relativeHumidity = "";
        string windDirection = "";
        string windMPH = "";

        bool fullNameRetrieved = false;    // Need to use a flag. There are two "full" XML elements; we just want the first.

        WebClient webClient = new WebClient ();
        string weatherData = "";

        try
        {
            weatherData = webClient.DownloadString (inputXML);
        }
        catch
        {
            Debug.Log ("Could not receive XML document from Weather Underground.");
            return null;
        }

        using (XmlReader reader = XmlReader.Create (new StringReader (weatherData)))
        {
            while (reader.Read ())
            {
                switch (reader.NodeType)
                {
                case XmlNodeType.Element:
                    if (reader.Name.Equals ("full") && !fullNameRetrieved)
                    {
                        reader.Read ();
                        place = reader.Value;
                        fullNameRetrieved = true;
                    }
                    else if (reader.Name.Equals ("weather"))
                    {
                        reader.Read ();
                        weather = reader.Value;
                    }
                    else if (reader.Name.Equals ("temperature_string"))
                    {
                        reader.Read ();
                        temperature = reader.Value;
                    }
                    else if (reader.Name.Equals ("relative_humidity"))
                    {
                        reader.Read ();
                        relativeHumidity = reader.Value;
                    }
                    else if (reader.Name.Equals ("wind_dir"))
                    {
                        reader.Read ();
                        windDirection = reader.Value;
                    }
                    else if (reader.Name.Equals ("wind_mph"))
                    {
                        reader.Read ();
                        windMPH = reader.Value;
                    }

                    break;
                }
            }
        }

        string[] formattedData = { "Weather for " + zipCode, place, weather, temperature, "Rel. humidity: " + relativeHumidity, "Wind " + windDirection + " at " + windMPH + " MPH" };

        return formattedData;
    }
}


We'll go over this in a bit more detail, as we haven't touched the WebClient or XML parsing in any other tutorials.

First, we make a string constant to hold our API key. We're going to use this in the URL we build to process a request from the Weather Underground server.

We've got a string array method ParseWUndergroundData () that takes a ZIP code and returns certain weather data. There are lots of fields that it will return. If you'd like to just poke at the API and see everything you can get back, form a URL like this, substituting your own API key and ZIP code:

http://api.wunderground.com/api/API_KEY_GOES_HERE/conditions/q/60601.xml

If you plug in your own API key there, and pop that URL into a browser, you'll get an XML file with a bunch of weather info. I only parse a few fields for the sake of brevity; you can parse any others you want (I'll detail how below).

So, we form our URL using the API key and supplied ZIP code. We get a few variables ready to hold the data that we want. The fullNameRetrieved is a workaround to make our XML parsing similar, and again, there's a couple of ways we could have done this. The problem is that there are two "full" fields that get sent in the XML document. Rather than parsing multiple nodes, I just flag when the first one (the brief name) has been parsed, ignoring the second one. If you wanted, you could structure your XML parsing to be a bit more detailed on node and element names, but again, I wanted to illustrate with simplicity in mind.

Next, we instantiate a WebClient and a string to hold the XML data. You can check out the WebClient class in more detail here if you're interested, but the bottom line is it gets data from a URL. We put that data in a string -- in this case, it's the XML Weather Underground returned to us -- and now it's ready to parse.

We make a new XMLReader, which is used to read data in XML format. We need to give it XML data, and since we're holding that XML data in a string, we'll have to supply the string to the XMLReader in a StringReader. A StringReader is just like a TextReader, which reads text from a file, except it reads a string. We put this XMLReader in a using statement so that once it has finished processing, it will be taken care of by the garbage collector.

Next, we get the XMLReader reading the file. Any time we run across an XML element, we check the name. If it's the name of an element we want to parse, we assign its value to the appropriate variable and continue reading. You can see the element names that I'm grabbing. If you wanted to grab others, you would simply add a string of your choice, then create another "else if" statement with the element's name, and assign the variable properly. Once the reader has finished processing all elements, it will be disposed of (thanks to our using statement).

Finally, we create a new string array containing the data formatted how we want it. Each string in the array will be on a separate line. I've added a little formatting (such as prepending the ZIP code with "Weather for "). Again, however you want your data to display, go ahead and make changes accordingly. We return this string array and we're done here.

Now, we're going to need something to make requests to the web server and collect weather data, then send that data to the display at appropriate times. This is where my MasterController script comes into play. One final time, create an empty GameObject and assign this script to it.

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

public class MasterController : MonoBehaviour
{
    private const float TIME_BETWEEN_UPDATES = 3600f;
    private const float SCROLLING_TIME = 5f;

    private List<string> dataToScrollThrough = new List<string>();

    public WeatherController weatherController;
    public DisplayController displayController;

    private float updateTimer = 0;
    private float scrollingTime = 0;
    private int scrollingIndex = 0;
    private bool applicationPaused = false;

    public string[] weatherZipCodes = { "10001", "60601", "90010" };

    private void UpdateAllData ()
    {
        dataToScrollThrough.Clear ();

        foreach (string currentZipCode in weatherZipCodes)
        {
            foreach (string stringToAdd in weatherController.ParseWUndergroundData (currentZipCode))
            {
                dataToScrollThrough.Add (stringToAdd);
            }
        }
    }

    void Start ()
    {
        UpdateAllData ();
    }

    private void UpdateDataOnDisplay (int indexOfData)
    {
        displayController.WriteMessageToDisplay (dataToScrollThrough[indexOfData], dataToScrollThrough[scrollingIndex + 1], true);
    }

    void Update ()
    {
        if (!applicationPaused)
        {
            updateTimer += Time.deltaTime;

            if (updateTimer >= TIME_BETWEEN_UPDATES)
            {
                UpdateAllData ();
                updateTimer = 0;
            }

            scrollingTime += Time.deltaTime;

            if (scrollingTime >= SCROLLING_TIME)
            {
                scrollingTime = 0;

                if (scrollingIndex > dataToScrollThrough.Count - 1)
                {
                    scrollingIndex = 0;
                }

                UpdateDataOnDisplay (scrollingIndex);

                scrollingIndex += 2;
            }
        }

        if (Input.GetKeyDown (KeyCode.UpArrow))
        {
            scrollingTime = 0;
           
            if (scrollingIndex > dataToScrollThrough.Count - 1)
            {
                scrollingIndex = 0;
            }
           
            UpdateDataOnDisplay (scrollingIndex);
           
            scrollingIndex += 2;
        }

        if (Input.GetKeyDown (KeyCode.Space))
        {
            if (applicationPaused)
            {
                updateTimer = 0;
                scrollingTime = 0;
                applicationPaused = false;
            }
            else
            {
                applicationPaused = true;
            }
        }
    }
}


Personally, I like to create autonomous scripts with easy-to-use interfaces, then create a master object that talks to them. Here, we've got the weather and display controllers, which don't do anything on their own, and the master controller, which tells them what to do. This is nice because it keeps game logic in one place, and lets each script handle only what it needs to handle. There are multiple ways to handle these types of things, but I prefer this for simple programs.

First, we make a couple of const floats that represent times to update the weather and how quickly to scroll through the data on the display. Using these numbers, we're checking weather once per hour, and showing each two lines of weather data every five seconds. We make a generic list of strings to represent the full set of data that will end up getting sent to the displays.

We also make a couple of public declarations for the controllers that we're going to access (the weather and display controllers). We're doing this the simplest possible way here -- we declare an instance of each script as a public variable. In the Editor, we're going to drag the object that has that type of script right on to the appropriate field in the Inspector, so that our Master Controller looks like this:


When we declare a public instance of a script we wrote, we just assign that script to an object, then drag the object right on to our public field. Now, when we reference that script in our code, we are talking directly to the script on that object.

Keep in mind that we could have also looked for the appropriate objects in the scene programmatically (for instance, using GameObject.Find ()), but since these objects are static in the scene, this is easy. If you're going to try to talk to prefabs that you instantiate on-the-fly, you won't be able to do this.

Now, we've got some variables for features that we'll explain later, some timers to control when to get weather data and scrolling speed, and an array of ZIP codes we want to check weather data for.

UpdateAllData () clears the current set of weather data strings and gets new ones. For every ZIP code in weatherZipCodes[], we parse the data from Weather Underground. All of these strings get added to our generic list, which we'll scroll through on the display.

In Start (), we call UpdateAllData () to get the balling rolling.

UpdateDataOnDisplay sends two lines of data to the display from our generic list. It accepts the index of the line we want to start at, and displays that line of text plus the next, since we have a two-line display. You could easily modify this to show more lines, in the case that you have a 4-line display.

Update () is where we're doing the bulk of our processing, as is usually the case. First, we update the timer that determines when it's time to get new weather data, provided that the application isn't paused, which we'll deal with later. If the timer gets above our TIME_BETWEEN_UPDATES float, we'll reset the timer and get our new data.

We now do the same for scrolling text. If our timer gets above SCROLLING_TIME, we reset the timer and increment the line of data we want to display next. If we get to the end of the data, we're going to reset our index to 0. We call UpdateDataOnDisplay () with the index of the data we want, then we increment the index for next time -- remember, we're displaying two lines of data at a time, so we want to increment the index by two. So the first time we display data, we'll get lines 0 and 1, then 2 and 3, and so on, until the end of the list, at which point we'll start back over at 0.

Finally, we've got a couple of keyboard commands for usability's sake. Pressing the up arrow will move to the next two lines of data. Pressing the space bar will pause on the current lines of data, or if the application is already paused, it will un-pause it.

We omitted lots of good programming practices to create the simplest example we could. Here are just a handful of changes that could be made, some of which you'd certainly need to do if you were going to use something like this in a released product:

- Make a GUI to accept/parse ZIP codes, control update/scrolling speeds, etc.
- Add error handling (try/catches around hardware communication would be a good place to start)
- Get rid of all magic numbers, such as the max number of characters in WriteMessageToDisplay, and replace with consts or user-configurable values

If you do want to use this, you could also compile it and run it with the "-batchmode" command line switch. This would open the program without any graphical display, letting it just plug away in the background. One nice thing about that is you won't have a crash due to losing the graphics device if you lock your screen.

Thursday, July 24, 2014

Unity 3D and Phidgets: Text LCD displays

LCD displays have a wide variety of applications, from industrial to amusement and redemption games. Phidgets offers a very solid range of displays with plenty of options, and great controllers that can communicate with two at a time. Today, we'll tackle some of the ins and outs of working with them in Unity.

First, you'll want to check out our basic Phidgets setup tutorial to get the hardware talking in Unity. Once you have your DLL loaded and your basic script ready to go, plug in your display controller and move forward.

Like other Phidgets devices, the general pattern is this: you instantiate a new object relating to the type of hardware you want to talk to, you add the event handlers, then you communicate with them. In general, I like to instantiate the device immediately, add the event handlers and open the device in Start (), initialize the device in the attach method, and then communicate with the device in custom methods, making sure the device is attached, of course.

We're going to assume you have one Text LCD controller connected, which will allow you to talk to two displays. Here is the code:

using Phidgets;
using Phidgets.Events;
using UnityEngine;
using System.Collections;

public class PhidgetsController : MonoBehaviour
{
    private TextLCD textLCDController = new TextLCD ();


    private int arbitraryCounter = 0;

    void Start ()
    {
       
textLCDController.Attach += new AttachEventHandler (lcdControl_Attach);
       
textLCDController.Detach += new DetachEventHandler (lcdControl_Detach);
       
textLCDController.Error += new ErrorEventHandler (lcdControl_Error);
       
textLCDController.open ();
    }

    void lcdControl_Attach(object sender, AttachEventArgs e)
    {
        Debug.Log ("Attached text LCD controller. Serial #" +
textLCDController.SerialNumber.ToString () + ".");
       
textLCDController.open ();
       
textLCDController.screens[0].Backlight = true;
       
textLCDController.screens[1].Backlight = true;
       
textLCDController.screens[0].Brightness = 255;
       
textLCDController.screens[1].Brightness = 255;
       
textLCDController.screens[0].Contrast = 200;
       
textLCDController.screens[1].Contrast = 200;
       
textLCDController.screens[0].ScreenSize = TextLCD.ScreenSizes._2x20;
       
textLCDController.screens[1].ScreenSize = TextLCD.ScreenSizes._2x20;
       
textLCDController.screens[0].initialize ();
       
textLCDController.screens[1].initialize ();
       
textLCDController.screens[0].rows[0].DisplayString = "  DISPLAY ATTACHED";
       
textLCDController.screens[1].rows[0].DisplayString = "  DISPLAY ATTACHED";
    }
   
    void lcdControl_Detach(object sender, DetachEventArgs e)
    {
        Debug.Log ("Detached.");

        textLCDController.close ();
    }
   
    void lcdControl_Error(object sender, ErrorEventArgs e)
    {
        Debug.Log ("Phidgets display error: " + e.ToString ());
    }

    void Update ()
    {
        if (Input.GetKeyDown (KeyCode.Alpha0))
        {
            arbitraryCounter++;
            if (
textLCDController.Attached)
            { 

                textLCDController.screens[0].rows[0].DisplayString = "DISPLAY 0";
                textLCDController.screens[0].rows[1].DisplayString = "          " + arbitraryCounter.ToString ();
            }
        }
        if (Input.GetKeyDown (KeyCode.Alpha1))
        {
            arbitraryCounter++;
            if (
textLCDController.Attached)
            { 

                textLCDController.screens[1].rows[0].DisplayString = "DISPLAY 1";

                textLCDController.screens[1].rows[1].DisplayString = "          " + arbitraryCounter.ToString ();
            }
        }


    }
}


Let's go through a bit at a time and explain it all.

As per usual, we're including using statements at the top of our C# script for the "Phidgets" and "Phidgets.Events" namespaces. This allows us to talk to our Phidgets devices and use their event system.

We instantiate a TextLCD () object next. This is the object we'll use for communicating with the device. The "arbitraryCounter" int is just a number we'll increment and display on the screen so you can see that the output is working.

In Start (), we attach our event handlers. These events will fire when the device is attached or detached, or an error is thrown. We then open the device for communication.

The "lcdControl_Attach" method, which is fired when the device is attached, is where we can do our setup and initialization of the device. Keep in mind this will also fire if the devices are already connected when the program starts. First, we log that the device is attached along with its serial number. This is just for debugging, and having the serial number logged is nice if we end up working with multiple devices of the same type, which we touch on below. We open the device -- we opened it in Start (), but we can do it here as well so that if the device is detached and attached while the program is running, we can be sure that it's ready to talk to. We then run the initialization that we want -- here, I've turned the backlights on for both displays, set the brightness, contrast, and screen size, run the initialize () method (which clears all text from the display), and displayed "DISPLAY ATTACHED" on each display.

We should note a couple of things here. One potential gotcha is that you have to set the screen size before you send a string to be displayed. The screen size's default value is none, so if you send a string without setting the screen size, you'll get an exception. The screen that I have is a 2x20, meaning it has two rows, and each row has twenty characters. You'll want to make sure that you select the correct parameter for your display.

Also, notice how we reference each screen and each row. They are 0-based indices, meaning that the first screen/row is 0, and the next is 1. We always have to reference the screen we want to set for backlight, contrast, etc., and we have to reference the row number when we send a string to be displayed. Screens 0 and 1 are marked on the board itself. Rows are indexed from the top, so the top is 0, the next is 1, etc.

In "lcdControl_Detach", we just log that the device was detached, and close it for communication. When the device gets re-attached, the attach event will fire lcdControl_Attach (), where we open it again.

"lcdControl_Error" logs any errors that are thrown by the device.

In Update (), we just wrote a simple little keyboard-controlled test. If you press the 0 key at the top of your keyboard (not the number pad), you'll see the display number and our arbitrary number on screen 0; the 1 key sends the same data to screen 1. Each time you press either of those keys, the arbitrary number is incremented by one, so you'll get a new number each time you hit a button. This confirms that everything is still working.

Notice that we check textLCDController.Attached each time we send a command. This is because if we try to send a command to a device that is not attached, we'll get an exception. Again, pay attention to how we're referencing the screens and rows.

This is a simple test, largely taken from Phidgets' own test C# program, that will get you basic functionality on your LCD displays. While this program is only designed for one controller, one great thing about Phidgets is that you can reference different boards of the same type by their serial number. To do that, you'd just instantiate a TextLCD () object for each device you have, and instead of calling textLCDController.open () with no parameters, you pass it your serial as an int, like so:

textLCDController.open(123456);

This will open that specific controller board for communication. You can get a board's serial number programmatically (TextLCD.SerialNumber), and they are also printed right on the board. For usability, you'll want a place for end users or operators to enter the correct serials, or possibly even work up a calibration routine to assign them to the right spots. We've done all that before with great success, but it's outside of the scope of this article.

Tuesday, July 22, 2014

Unity 3D: Partial Classes with C#

Typically, Unity requires you to have one class per script, which can be a bit of a drag if you've got a larger, more complex class. Did you know that you can split that class up into multiple files using C# partial classes?

The method is simple. Let's say you create a C# script called "PlayerCharacter". You'll have a class generated for you automatically in the .cs file, with contents like so:

using UnityEngine;
using System.Collections;

public class PlayerCharacter : MonoBehaviour{

    // Use this for initialization
    void Start () {
  
    }
  
    // Update is called once per frame
    void Update () {
  
    }
}


We'd like to add a pretty complex bit of code just pertaining to armor. We'd like to keep it in this class, but we'd like to separate it from the rest of the code in this file. Well, first, add the "partial" keyword before "class" in your declaration:

public partial class PlayerCharacter : MonoBehaviour

Now, go ahead and create another C# script, and call it "PlayerCharacter.Armor". Open it up in MonoDevelop and you'll see something like this:

using UnityEngine;
using System.Collections;

public class PlayerCharacter.Armor : MonoBehaviour {

    // Use this for initialization
    void Start () {
   
    }
   
    // Update is called once per frame
    void Update () {
   
    }
}


We're going to get a parsing error, because we can't have a "." in our class name. Use the "partial" keyword again, and remove the ".Armor" portion of the class declaration, so we're using the exact same declaration we used in the other .cs file:

public partial class PlayerCharacter : MonoBehaviour

Here we have two files that represent different parts of the same class. Keep in mind one important thing: if you're inheriting from MonoBehaviour as we did above, you need to make sure that you don't have your built-in methods (such as "Start ()" and "Update ()") in multiple files. This is a single class spread out over multiple files, so you can't declare something multiple times. In the example above, we would simply remove "Start ()" and "Update ()" from one of the files and be good to go.

You can make as many files as you want, naming them with the convention "className.x". Anything that is declared in any of the files is available when working with any of the other files. For example, if I declare a private bool called "isWearingArmor" in the "PlayerCharacter.Armor" file, I can still access that bool from the "PlayerCharacter" file. It's all one big class, and is treated as such by the IDE and the compiler.

Of course, be wary: if you're making a class that is only convenient to use if you spread it out over a dozen files, you may be trying to jam too much into one class. We're completely disregarding class design considerations (and the associated holy wars) to show this technique. Ultimately, it's up to you to design your classes how you see fit.