The code below allows you to copy a directory structure with or without sub folders including files. Also includes a callback that can be used to filter out files or folders and reports the copy progress.

/// <summary>
/// Copies a directory structure to the destination.
/// </summary>
/// <param name="source">The directory structure to be copied.</param>
/// <param name="destination">The destination where the directory structure will be copied to.</param>
/// <param name="copySubDirectories">true to copy all subdirectories.</param>
/// <param name="overwriteFiles">true if the destination files can be overwritten; otherwise, false.</param>
/// <param name="callback">Provides a callback function for reporting progress. </param>
/// <remarks><p>The callback invoked just before a file copy occurs providing a way of being notified.</p>
/// <p>The callback parameter order is source file, destination file, progress.</p>
/// <p>If the callback is specified it should return true to allow the file copy to occur.</p> 
/// <p>The progress parameter reports progress from 0 to 100. Values to the left of the decimal represent folder copy progress and values to the
/// right of the decimal from 0.000 to 0.99 represent the current file copy progress for the folder that is being copied.</p>
/// <p>To get the current file copy progress as a value from 0 to 100 use the formula fileProgress = progress - 100 * 100.</p></remarks>
public static void CopyDirectories(string source, string destination, bool copySubDirectories, bool overwriteFiles, Func<string, string, float, bool> callback)
{
    // ensure source folder exists
    if (!Directory.Exists(source))
    {
        throw new DirectoryNotFoundException("The path specified in source is invalid (for example, it is on an unmapped drive).");
    }

    // create destination folder
    Directory.CreateDirectory(destination);

    // get all files in source and copy them to destination folder
    var files = Directory.GetFiles(source);
    var progress = 0f; // used to report the progress from 0 to 100

    // set up action to copy files
    var fileProcessor = new Action<float, string[], string>((folderProgress, filesToCopy, folder) =>
        {
            // copy files
            for (var i = 0; i < filesToCopy.Length; i++)
            {
                // get file
                var file = filesToCopy[i];

                // set default result
                var result = true;

                // build destination filename
                var fileName = Path.GetFileName(file);
                if (fileName == null) // should never happen
                {
                    return;
                }

                fileName = Path.Combine(folder, fileName);

                // check if callback specified
                if (callback != null)
                {
                    // store result from callback
                    result = callback(file, fileName, progress);
                }

                // if result is true we are allowed to copy the file
                if (result)
                {
                    File.Copy(file, fileName, overwriteFiles);
                }

                // (folder progress * 100) + file progress
                progress = folderProgress + ((float)i / filesToCopy.Length);
            }
        });

    // copy initial files
    fileProcessor(0, files, destination);

    // check to copy sub directories
    if (!copySubDirectories)
    {
        return;
    }

    // get the folder tree for the source folder
    var folders = Directory.GetDirectories(source, "*.*", SearchOption.AllDirectories);

    // process each sub folder
    for (var index = 0; index < folders.Length; index++)
    {
        // get folder and increment index
        var folder = folders[index];

        // get files
        files = Directory.GetFiles(folder);

        // crop source root from destination and build destination folder path
        folder = folder.Remove(0, source.Length);
        folder = Path.Combine(destination, folder);

        // create destination folder
        Directory.CreateDirectory(folder);

        // process file copying
        fileProcessor((index / folders.Length) * 100, files, folder);
    }
}

Some .net/mono frameworks do not support the IsNullOrWhiteSpace method. The code below provides a simple replacement.

/// <summary>
/// Indicates whether a specified string is null, empty, or consists only of white-space characters.
/// </summary>
/// <param name="value">value: The string to test.</param>
/// <returns>true if the value parameter is null or System.String.Empty, or if value consists exclusively of white-space characters.</returns>
public static bool IsNullOrWhiteSpace(this string value)
{
    if (value == null)
    {
        return true;
    }

    var index = 0;
    while (index < value.Length)
    {
        if (char.IsWhiteSpace(value[index]))
        {
            index++;
        }
        else
        {
            return false;
        }
    }

    return true;
}

Some .net/mono frameworks do not support the 3 argument GetDirectories method. The code below provides a simple replacement.

/// <summary>
/// Builds an array of folders & sub folders.
/// </summary>
/// <param name="path">The path to search.</param>
/// <param name="pattern">The search string to match against the names of files in path. The parameter cannot end in two periods 
/// ("..") or contain two periods ("..") followed by System.IO.Path.DirectorySeparatorChar or System.IO.Path.AltDirectorySeparatorChar,
/// nor can it contain any of the characters in System.IO.Path.InvalidPathChars.
///</param>
/// <param name="searchOptions">One of the System.IO.SearchOption values that specifies whether the search operation should include 
/// all subdirectories or only the current directory.
///</param>
/// <returns>A String array of directories that match the search pattern.</returns>
public static string[] GetDirectories(string path, string pattern, SearchOption searchOptions)
{
    // check if searching for all directories
    if (searchOptions == SearchOption.AllDirectories)
    {
        // add start paths to list
        var list = Directory.GetDirectories(path, pattern);
        var index = 0;
        var count = list.Length;

        // process list and add folders to end of list
        while (index < count)
        {
            var directories = Directory.GetDirectories(list[index++], pattern);
            if (directories.Length > 0)
            {
                // check if we need more space to store the directories
                if (count + directories.Length > list.Length - 1)
                {
                    Array.Resize(ref list, list.Length + directories.Length + 1000);
                }

                // add directories to end of the list
                foreach (var directory in directories)
                {
                    list[count++] = directory;
                }
            }

            // trim unused index from end of the array
            if (list.Length > count)
            {
                Array.Resize(ref list, count);
            }
        }

        return list;
    }

    // just return initial list of folder with no sub folders
    return Directory.GetDirectories(path, pattern);
}

/// <summary>
/// Provides a game object that automatically self terminates.
/// </summary>
[ExecuteInEditMode]
public class SelfTerminatingObject : MonoBehaviour
{
    /// <summary>
    /// Holds a reference to a callback that will be called just before termination.
    /// </summary>
    public Action Callback;

    /// <summary>
    /// Holds the time delay in seconds before object self terminates.
    /// </summary>
    public float Delay;

    /// <summary>
    /// Holds a time value used to determine whether it's time to self terminate.
    /// </summary>
    private DateTime lastTime;

    /// <summary>
    /// Called by Unity to update the object.
    /// </summary>
    public void Update()
    {
        this.PerformCheck();
    }

    /// <summary>
    /// Called by Unity to draw the object's GUI.
    /// </summary>
    public void OnGui()
    {
        this.PerformCheck();
    }

    /// <summary>
    /// Determines whether it's time to terminate.
    /// </summary>
    private void PerformCheck()
    {
        // check if specified time span has elapsed
        if (DateTime.Now > this.lastTime + TimeSpan.FromSeconds(this.Delay))
        {
            // run callback is specified
            if (this.Callback != null)
            {
                this.Callback();
            }

            // destroy object
#if UNITY_EDITOR
        DestroyImmediate(this.gameObject, true);
#else
            Destroy(this.gameObject);
#endif
        }
    }

    /// <summary>
    /// Creates a self terminating game object.
    /// </summary>
    /// <param name="callback">The method to be called before the object terminates.</param>
    /// <returns>Returns a reference to the game object that will self terminate.</returns>
    public static GameObject CreateUpdateCallback(Action callback)
    {
        return CreateUpdateCallback(callback, 0);
    }

    /// <summary>
    /// Creates a self terminating game object.
    /// </summary>
    /// <param name="callback">The method to be called before the object terminates.</param>
    /// <param name="delay">Specifies a delay value in seconds before the object will self terminate.</param>
    /// <returns>Returns a reference to the game object that will self terminate.</returns>
    public static GameObject CreateUpdateCallback(Action callback, float delay)
    {
        var obj = new GameObject();
        var com = obj.AddComponent<SelfTerminatingObject>();
        com.lastTime = DateTime.Now;
        com.Delay = delay;
        com.Callback = callback;

        return obj;
    }
}

This code is designed to hook into unity’s EditorApplication.update delegate.

IRun.cs

namespace CBX.CoreProjectCode.Interfaces
{
    using CBX.CoreProjectCode.Models;

    /// <summary>
    /// Provides a interface for running a <see cref="CallbackModel{T}"/> type.
    /// </summary>
    public interface IRun
    {
        /// <summary>
        /// Runs the <see cref="CallbackModel{T}"/> type.
        /// </summary>
        void Run();                    
    }
}

CallbackModel.cs

namespace CBX.CoreProjectCode.Models
{
    using System;

    using CBX.CoreProjectCode.Interfaces;

    /// <summary>
    /// Provides a modal for callbacks.
    /// </summary>
    /// <typeparam name="T">The generic type used to represent the data type.</typeparam>
    public class CallbackModel<T> : IRun
    {
        /// <summary>
        /// A reference to a callback method.
        /// </summary>
        public Action<T> Callback;

        /// <summary>
        /// A reference to some data that will be passed to the callback method.
        /// </summary>
        public T Data;

        /// <summary>
        /// Implements <see cref="IRun.Run"/> to run the callback. 
        /// </summary>
        public void Run()
        {
            this.Callback(this.Data);
        }
    }
}

EditorCallbackService.cs

namespace CBX.CoreProjectCode.Services
{
    using System;
    using System.Collections.Generic;

    using CBX.CoreProjectCode.Interfaces;
    using CBX.CoreProjectCode.Models;

    /// <summary>
    /// The editor callback service.
    /// </summary>
    public class EditorCallbackService
    {
        /// <summary>
        /// Holds a singleton instance of the <see cref="EditorCallbackService"/> type.
        /// </summary>
        private static EditorCallbackService service;

        /// <summary>
        /// Holds a list of <see cref="CallbackModel{T}"/> types that implement <see cref="IRun"/>.
        /// </summary>
        private readonly Stack<IRun> callbacks;

        /// <summary>
        /// Initializes a new instance of the <see cref="EditorCallbackService"/> class.
        /// </summary>
        public EditorCallbackService()
        {
            this.callbacks = new Stack<IRun>();
        }

        /// <summary>
        /// Returns a singleton instance of the <see cref="EditorCallbackService"/> type.
        /// </summary>
        public static EditorCallbackService Instance
        {
            get
            {
                // if no service yet exists create one
                return service ?? (service = new EditorCallbackService());
            }
        }

        /// <summary>
        /// Runs any callbacks that have been registered.
        /// </summary>
        public void Run()
        {
            while (this.callbacks.Count > 0)
            {
                var cb = this.callbacks.Pop();
                cb.Run();
            }
        }

        /// <summary>
        /// Registers a <see cref="Action{T}"/> callback.
        /// </summary>
        /// <typeparam name="T">The type of data that the <see cref="callback"/> takes as a parameter.</typeparam>
        /// <param name="callback">A reference to a callback.</param>
        public void Register<T>(Action<T> callback)
        {
            this.Register(callback, default(T));
        }

        /// <summary>
        /// Registers a <see cref="Action{T}"/> callback.
        /// </summary>
        /// <typeparam name="T">
        /// The type of data that the <see cref="callback"/> takes as a parameter.
        /// </typeparam>
        /// <param name="callback">
        /// A reference to a callback.
        /// </param>
        /// <param name="data">
        /// The data that will be passed as a parameter when the <see cref="callback"/> is invoked.
        /// </param>
        public void Register<T>(Action<T> callback, T data)
        {
            if (callback == null)
            {
                throw new ArgumentNullException("callback");
            }

            var modal = new CallbackModel<T> { Callback = callback, Data = data };
            this.callbacks.Push(modal);
        }

        /// <summary>
        /// Registers a <see cref="Action"/> callback.
        /// </summary>
        /// <param name="callback">A reference to a callback.</param>
        public void Register(Action callback)
        {
            this.Register<object>(x => callback(), null);
        }
    }
}

Usage scenario …

    [InitializeOnLoad]
    public class EditorInitialization
    {
        /// <summary>
        /// Holds a value indicating whether the RunCallbacks method has been called at least once before.
        /// </summary>
        private static bool ranOnce;

        /// <summary>
        /// Initializes static members of the <see cref="EditorInitialization"/> class.
        /// </summary>
        static EditorInitialization()
        {
            EditorApplication.update += RunCallbacks;
        }

        private static void RunCallbacks()
        {
            if (!ranOnce)
            {
                // setup the settings system
                SetupSettings();

                // load localization strings
                LoadLocalizationData();

                ranOnce = true;
                return;
            }

            // invoke callbacks from editor callback service     
            EditorCallbackService.Instance.Run();
        }
    }

Source –> https://gist.github.com/chkn/4748252

using System;
using System.Linq;

public static class JSON 
{
    public static string Stringify (object obj)
    {
        string str;
        if (obj == null) return "null";
        if (obj is ValueType) return obj.ToString ().ToLowerInvariant ();
        if ((str = obj as string) != null) return System.Text.RegularExpressions.Regex.Escape (str);

        // assume it's a POCO
        return "{" + string.Join (",",
            from p in obj.GetType ().GetProperties ()
            let json = (JSONAttribute) p.GetCustomAttributes (typeof (JSONAttribute), true).FirstOrDefault ()
            where json != null
            select "\"" + (json.Key ?? p.Name) + "\":" + Stringify (p.GetValue (obj, null))
        ) + "}";
    }
}

public class JSONAttribute : Attribute
{
    public string Key { get; set; }
    public JSONAttribute () {}    
    public JSONAttribute (string key) { Key = key; }
}

        /// <summary>
        /// Determines if this rectangle intersects with rect.
        /// </summary>
        /// <param name="rect">
        /// The rectangle to test.
        /// </param>
        /// <returns>
        /// This method returns true if there is any intersection, otherwise false.
        /// </returns>
        public bool Intersects(RectangleF rect)
        {
            return !((this.X > rect.Right) || (this.Right < rect.Left) || (this.Y > rect.Bottom) || (this.Bottom < rect.Top));
        }

        /// <summary>
        /// Replaces this RectangleF with the intersection of itself and the specified RectangleF.
        /// </summary>
        /// <param name="rect">
        /// The RectangleF with which to intersect.
        /// </param>
        public void Intersect(RectangleF rect)
        {
            this.X = Math.Max(this.Left, rect.Left);
            this.Y = Math.Max(this.Top, rect.Top);
            this.Width = Math.Min(this.Right, rect.Right) - this.X;
            this.Height = Math.Min(this.Bottom, rect.Bottom) - this.Y;
        }

        /// <summary>
        /// Returns a third RectangleF structure that represents the intersection of two other RectangleF structures. 
        /// If there is no intersection, an empty RectangleF is returned.
        /// </summary>
        /// <param name="a">
        /// A rectangle to intersect.   
        /// </param>
        /// <param name="b">
        /// B rectangle to intersect.  
        /// </param>
        /// <returns>
        /// A RectangleF that represents the intersection of a and b.
        /// </returns>
        public static RectangleF Intersect(RectangleF a, RectangleF b)
        {
            float x = Math.Max((sbyte)a.X, (sbyte)b.X);
            float num2 = Math.Min(a.X + a.Width, b.X + b.Width);
            float y = Math.Max((sbyte)a.Y, (sbyte)b.Y);
            float num4 = Math.Min(a.Y + a.Height, b.Y + b.Height);
            if ((num2 >= x) && (num4 >= y))
            {
                return new RectangleF(x, y, num2 - x, num4 - y);
            }

            return Empty;
        }

I’ve recently come across what initially seems like a bug with the Unity 4 editor. it involves the use of the Resources.Load method from within a class marked InitializeOnLoad.

Take the fallowing example code …

namespace CBX.CoreProjectCode
{
    using UnityEditor;

    using UnityEngine;

    [InitializeOnLoad]
    public class EditorInitialization
    {
        static EditorInitialization()
        {
            LoadData(); // this will fail to see the test.txt file in the resource folder
        }     

        private static void LoadData()
        {
            // try to load data
            var data = Resources.Load("test", typeof(TextAsset)) as TextAsset;
            if (data == null)
            {
                Debug.LogWarning("No data found");
                return;
            }

            Debug.Log(data.text);
        }
    }
}

After Unity compiles the scripts it will invoke any static constructors of classes marked with InitializeOnLoad. But at this point I suspect that Unity has not yet identified assets you have within any Resources folders. When the class constructor calls the Resources.Load method it will fail to read the test.txt resource file and return a warning message out to the console.

What you could do at this point is right click the “Assets” folder in the project window and select “Reimport”. Unity will reimport the assets and again call classes marked with InitializeOnLoad. But this time Resources.load will successfully load the test.txt file resource and display it’s contents out to the Unity console window.

One work around to this is to hook into the EditorApplication.update callback and make a call to the LoadData method from there.  An example is provided below …

namespace CBX.CoreProjectCode
{
    using UnityEditor;

    using UnityEngine;

    [InitializeOnLoad]
    public class EditorInitialization
    {
        private static bool ranOnce;

        static EditorInitialization()
        {
            EditorApplication.update += RunCallbacks;
        }

        private static void RunCallbacks()
        {
            if (!ranOnce)
            {
                // this will see the test.txt file in the Resource folder and display
                // its contents
                LoadData();
                ranOnce = true;

                // return or don't it’s up to you
                return;
            }

            // do some work here
        }

        private static void LoadData()
        {
            var data = Resources.Load("test", typeof(TextAsset)) as TextAsset;
            if (data == null)
            {
                Debug.LogWarning("No data found");
                return;
            }

            Debug.Log(data.text);
        }
    }
}

Again I am unsure whether this is an actual bug or not but I have submitted a Unity bug report anyway just in case. Here is a link to the my bug report https://fogbugz.unity3d.com/default.asp?525005_eroka98ru2v4netm


/// <summary>
/// Blends two colors together.
/// </summary>
/// <param name="color">
/// The source color.
/// </param>
/// <param name="src">
/// The blend color.
/// </param>
/// <returns>
/// Returns the result as a <see cref="Color"/> type.
/// </returns>
/// <remarks>
/// From Wikipedia https://en.wikipedia.org/wiki/Alpha_compositing -> "Alpha blending" 
/// </remarks>
public static Color Blend(this Color color, Color src)
{
    float sr = src.R / 255f;
    float sg = src.G / 255f;
    float sb = src.B / 255f;
    float sa = src.A / 255f;
    float dr = color.R / 255f;
    float dg = color.G / 255f;
    float db = color.B / 255f;
    float da = color.A / 255f;

    float oa = sa + (da * (1 - sa));
    float r = ((sr * sa) + ((dr * da) * (1 - sa))) / oa;
    float g = ((sg * sa) + ((dg * da) * (1 - sa))) / oa;
    float b = ((sb * sa) + ((db * da) * (1 - sa))) / oa;
    float a = oa;

    return new Color((byte)(r * 255), (byte)(g * 255), (byte)(b * 255), (byte)(a * 255));
}

The code below allows you to scale a GameObject transform to a specific size along the x & z axis by taking into account the GameObjects renderer bounds if a renderer component is attached.

/// <summary>
/// Scales a transform to specific dimensions along the x & z axis.
/// </summary>
/// <param name="transform">
/// Reference to the transform to scale.
/// </param>
/// <param name="width">The width along the x axis that represents the target size.</param>
/// <param name="height">The height along the z axis that represents the target size.</param>
public static void ScaleTransform(Transform transform, float width, float height)
{
    // get bounds of the prefab
    var bounds = new Bounds();
    var encapsulate = false;
    if (!Utilities.Helpers.GetBoundWithChildren(transform, ref bounds, ref encapsulate))
    {
        return;
    }

    // get minimum size from the size dimensions
    var min = Mathf.Min(width, height);

    // get the maximum x or z size of the transform
    var max = Mathf.Max(bounds.size.x, bounds.size.z);

    // calculate the scale factor 
    var scaleFactor = min / max;

    // apply scaling to the transform
    transform.localScale *= scaleFactor;
}

/// <summary>
/// Gets the rendering bounds of the transform.
/// </summary>
/// <param name="transform">The game object to get the bounding box for.</param>
/// <param name="pBound">The bounding box reference that will </param>
/// <param name="encapsulate">Used to determine if the first bounding box to be 
/// calculated should be encapsulated into the <see cref="pBound"/> argument.</param>
/// <returns>Returns true if at least one bounding box was calculated.</returns>
public static bool GetBoundWithChildren(Transform transform, ref Bounds pBound, ref bool encapsulate)
{
    var didOne = false;

    // get 'this' bound
    if (transform.gameObject.renderer != null)
    {
        var bound = transform.gameObject.renderer.bounds;
        if (encapsulate)
        {
            pBound.Encapsulate(bound.min);
            pBound.Encapsulate(bound.max);
        }
        else
        {
            pBound.min = bound.min;
            pBound.max = bound.max;
            encapsulate = true;
        }

        didOne = true;
    }

    // union with bound(s) of any/all children
    foreach (Transform child in transform)
    {
        if (GetBoundWithChildren(child, ref pBound, ref encapsulate))
        {
            didOne = true;
        }
    }

    return didOne;
}

Created by: X

Just another personal website in this crazy online world

Name of author Dean Lunz (aka Created by: X)
Computer programming nerd, and tech geek.
About Me -- Resume