Sunday, January 20, 2013

Unity Thoughts & StartCoroutine in a non-MonoBehaviour [Unity][C#]

In revisiting Unity recently I was reminded of a few things that I hit in my first experience with Unity.

  1. Do not make every class a MonoBehaviour. Static classes with initializers or Singletons are also perfectly reasonable too! Just ask yourself if the given class needs an update every frame.
  2. GetComponent is not a quick lookup. Try to limit its use. (Script Optimizations)
  3. If you do have non-UI MonoBehaviour objects (and you probably do) useGUILayout should be false. Learn about it.
  4. If you make a class a MonoBehaviour solely to be capable of running Coroutines consider this alternative: Have a centralized MonoBehaviour be responsible for starting Coroutines in your non-MonoBehaviours.
Coroutines can be used in non-MonoBehaviour objects if you have a central MonoBehaviour object that has a method for starting Coroutines with the desired delegate as a parameter. This MonoBehaviour should be an instance that any object can easily access to run Coroutines. The code below would be in the MonoBehaviour and StartCoroutineDelegate would be called from the non-MonoBehaviour object.

public delegate IEnumerator CoroutineMethod();

IEnumerator RunCoroutine(
CoroutineMethod coroutineMethod)

public void StartCoroutineDelegate(CoroutineMethod coroutineMethod)
RunCoroutine", coroutineMethod);

Be sure to read about StartCoroutine and StopCoroutine to learn more!

Monday, January 3, 2011

Useful System Information (.NET C#)

System.Windows.Forms.SystemInformation is a very useful source of information to help you access various operating system level settings. My C# text editor required me to figure out the vertical size of the scroll arrow in a scrollable control. This information was readily available in the SystemInformation class.


Wednesday, December 1, 2010

Changing the opacity of an Image (.NET C#)

Setting up the opacity of an Image is very useful and reasonably easy to to. In my Card Maker application I needed to load a bitmap, set the opacity, and make sure the image file was not locked(!).

The critical piece is the configuration of the Matrix33 value. This configures the alpha component of the matrix. (a value from 0 to 1 in my sample)

It is important to call Dispose on loaded images files that are no longer needed. This is critical to avoid locking the file (and angering you and your application users).

ColorMatrix zColor = new ColorMatrix();
zColor.Matrix33 = (float)opacity / 255.0f;
ImageAttributes zAttrib = new ImageAttributes();
Bitmap zSourceImage = new Bitmap(sFile);
Bitmap zBitmap = new Bitmap(zSourceImage.Width, zSourceImage.Height); // target image
Graphics zGraphics = Graphics.FromImage(zBitmap);
// draw the source image into the destination with the desired opacity
zGraphics.DrawImage(zSourceImage, new Rectangle(0, 0, zBitmap.Width, zBitmap.Height), 
0, 0, zBitmap.Width, zBitmap.Height, GraphicsUnit.Pixel, zAttrib);

Monday, November 29, 2010

Named Pipes (.NET C#)

Named pipes can be very useful for a variety of functions. I found that a named pipe was the right solution for passing the command line arguments to an already running application (avoiding reloading the application entirely). NamedPipeServerStream and NamedPipeClientStream are the basic classes necessary to create a byte stream pipe between various applications.

A named pipe is a single connection utility. If you make a connection and disconnect you will need to create a new one. You have a few options on how to handle multiple connections with multiple threads. I simply setup a while loop that continued to recreate the named pipe as connections were made and closed. I am sure I still have a lot more to learn about using pipes.

// Server Side
NamedPipeServerStream zPipe = new NamedPipeServerStream(PIPE_NAME, PipeDirection.In, 1, PipeTransmissionMode.Message);
StringBuilder zBuilder = new StringBuilder();
    int nLength = zPipe.Read(arrayData, 0, arrayData.Length);
zBuilder.Append(Encoding.UTF8.GetString(arrayData, 0, nLength));
} while (!zPipe.IsMessageComplete);
// Client Side
NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", PIPE_NAME, PipeDirection.Out);
catch (Exception) {return;}
if (pipeClient.IsConnected && pipeClient.CanWrite)
    byte[] arrayData = Encoding.UTF8.GetBytes(sMessage);
pipeClient.Write(arrayData, 0, arrayData.Length);

Server Pipe
Client Pipe

My previous experience with byte reading and writing to Sockets helped me understand the basics of a pipe. If you have written to streams of any sort you should be able to figure out pipes.

Tuesday, November 16, 2010

ScrollToControl (.NET C#)

If you are using a Panel you may notice that it jumps around when you restore focus. It is actually jumping to the xy position of the current control within the Panel. This is due to implementation calling ScrollToControl. For example I have an application that uses a Panel as a convenient alternative to creating a ScrollableControl. My control is the only one in the Panel and I wanted to take advantage of the background image along with the auto-scroll functionality. My Panel is a specialized derivative of Panel so I went ahead and created the override below:

   1:  protected override Point ScrollToControl(Control activeControl)
   2:  {
   3:      if (m_bDisableScrollToControl)
   4:      {
   5:      return AutoScrollPosition;
   6:      }
   7:      return base.ScrollToControl(activeControl);
   8:  }

Basically I only allow the ScrollToControl if my custom boolean is enabled. In the case of a single control it will likely never be enabled. Instead I just leave the position as-is based on the AutoScrollPosition.

Friday, November 5, 2010

Saving & Loading a Form's state (.NET C#)

Saving and loading the state of a window is a very much appreciated (and often overlooked) feature of an application. There is a slight trick to it though. It is not apparent unless you dig a bit. Based on the state of the Form the Location and Size are not what you expect. If a window is anything but FormWindowState.Normal the Size and Location are not the values you want to save. Instead you need to store the RestoreBounds values of those fields. I have an example implementation below for storing the state of a Form to a string followed by the code to restore it.

   1:  public static string GetFormSettings(Form zForm)
   2:  {
   3:      if (FormWindowState.Normal == zForm.WindowState)
   4:      {
   5:          return zForm.Location.X + ";" + zForm.Location.Y + ";" +
   6:              zForm.Size.Width + ";" + zForm.Size.Height + ";" +
   7:              (int)zForm.WindowState;
   8:      }
   9:      else
  10:      {
  11:          return zForm.RestoreBounds.Location.X + ";" + zForm.RestoreBounds.Location.Y + ";" +
  12:              zForm.RestoreBounds.Size.Width + ";" + zForm.RestoreBounds.Size.Height + ";" +
  13:              (int)zForm.WindowState;
  14:      }
  15:  }
  17:  public static void RestoreState(Form zForm, string sInput)
  18:  {
  19:      string[] arraySplit = sInput.Split(new char[] { ';' });
  20:      int[] nValues = new int[5];
  21:      if (5 == arraySplit.Length)
  22:      {
  23:          for (int nIdx = 0; nIdx < arraySplit.Length; nIdx++)
  24:          {
  25:              nValues[nIdx] = int.Parse(arraySplit[nIdx]);
  26:          }
  27:          zForm.Location = new Point(nValues[0], nValues[1]);
  28:          zForm.Size = new Size(nValues[2], nValues[3]);
  29:          zForm.WindowState = (FormWindowState)nValues[4];
  30:      }
  31:  }

MSDN Forms.RestoreBounds

Thursday, November 4, 2010

Dictionary vs. Enum.Parse (.NET C#)

I have often wondered if I should use Enum.Parse or a Dictionary of strings to translation a string to an enumeration value. I have always suspected the Dictionary look up would be faster than the Enum.Parse. I decided it was time to test things out. Here's the sample code:

   1:  enum Place
   2:  {
   3:      Home,
   4:      Work,
   5:      Neither,
   6:      End = Neither
   7:  }
   9:  static Dictionary<string, Place> dictionaryPlaces = null; 
  11:  static void Main(string[] args)
  12:  {
  13:      const string sHome = "Home";
  14:      const string sWork = "Work";
  15:      const string sNeither = "Neither";
  16:      const string sEnd = "End";
  18:      if (!Stopwatch.IsHighResolution) { throw new Exception("No high resolution timer! :("); }
  19:      Stopwatch zStopwatch = Stopwatch.StartNew();           
  20:      dictionaryPlaces = new Dictionary<string, Place>();
  21:      dictionaryPlaces.Add(sHome, Place.Home);
  22:      dictionaryPlaces.Add(sWork, Place.Work);
  23:      dictionaryPlaces.Add(sNeither, Place.Neither);
  24:      dictionaryPlaces.Add(sEnd, Place.End);
  25:      zStopwatch.Stop();
  26:      Console.WriteLine("Dictionary Creation: " + zStopwatch.ElapsedTicks);
  28:      for (int nIdx = 0; nIdx < 2; nIdx++)
  29:      {
  30:      Console.WriteLine(Environment.NewLine + "--Dictionary Lookup");
  31:      LookupEnumByDictionary(sHome);
  32:      LookupEnumByDictionary(sWork);
  33:      LookupEnumByDictionary(sNeither);
  34:      LookupEnumByDictionary(sEnd);
  36:      Console.WriteLine(Environment.NewLine + "--Enum Lookup");
  37:      LookupEnum(sHome);
  38:      LookupEnum(sWork);
  39:      LookupEnum(sNeither);
  40:      LookupEnum(sEnd);
  41:      }
  42:      Console.ReadLine();
  43:  }
  45:  static void LookupEnumByDictionary(string sValue)
  46:  {
  47:      Stopwatch zStopwatch = Stopwatch.StartNew();
  48:      Place ePlace = dictionaryPlaces[sValue];
  49:      zStopwatch.Stop();
  50:      Console.WriteLine(ePlace.ToString() + "(" + zStopwatch.ElapsedTicks + ")");
  51:  }
  53:  static void LookupEnum(string sValue)
  54:  {
  55:      Stopwatch zStopwatch = Stopwatch.StartNew();
  56:      Place ePlace = (Place)Enum.Parse(typeof(Place), sValue);
  57:      zStopwatch.Stop();
  58:      Console.WriteLine(ePlace.ToString() + "(" + zStopwatch.ElapsedTicks + ")");
  59:  }

Here's the output:

Dictionary Creation: 15315
--Dictionary Lookup
--Enum Lookup
--Dictionary Lookup
--Enum Lookup

The initial hits of the Dictionary construction and initial query are large but if you have a program performing a lot of string to enumeration values the Dictionary would result in the best performance over time. I added the second pass through the loop as I guessed it might further reveal the likely pattern.