Monday, May 23, 2011

Unity editor - 3D geometry intersecting gizmo axis




Experimenting with idea of adding simple 3d axis lines (intersecting with scene geometry, to better visualize object's whereabouts in scene) dynamically to origin of any GameObject currently selected. The way I imagine it, it will run automatically on Unity startup and work in the background, without need to have custom EditorWindow displayed or manually attach scripts to GameObjects. Only requirement would be to put plugin scripts or dll in Asset folder.

For now, it works in sync with editor's Pivot/Center and Local/Global settings, positioning itself in same point as default gizmo.

Unity Forum thread: Unity editor - 3D geometry intersecting gizmo axis

Tuesday, May 10, 2011

MonoDevelop file templates for Unity C# scripts

UPDATE(June 12, 2011): Added installation location description for MAC.

UPDATE(June 11, 2011): Added FAQ and one question/answer at the end of post.

UPDATE(May 11, 2011): Some last minute errors sneaked in template settings so MonoDevelop didn't load addin. Fixed version (0.81) is at new download link.

Description:
As the title says, here are some file templates for Unity, written in C#. It is initial version, I'm new to Unity and I didn't use all functions in practice, so this is some kind of... newbie template set :) If you have suggestions on how to improve them, or better yet you make some useful templates, feel free to share.

Installation:

Win XP
To install templates, extract package to "\MonoDevelop\Addins" folder and restart MonoDevelop (if it is already running).

MAC
To install templates, extract templates package and copy extracted folder to "Contents\MacOS\lib\monodevelop\AddIns" in MonoDevelop application package. Restart MonoDevelop (if it is already running).



MonoDevelop
To check if templates successfully loaded, go to New File dialog:

Here you should have few templates under QQ\Unity\C#:

If for some reason addin didn't successfully load on startup, go to Add-in Manager:

And try to Disable and Enable add-in, forcing it to reload:

Download:
Download here. Version 0.81

Links:
Reference on creating MonoDevelop file templates:
Monodevelop: Howto add a WinForm project and file template
Writing an Add-In - MonoDevelop
Extension Tree Reference - MonoDevelop
Forum:
Unity Forum thread

FAQ:

Q:
I used your package to develop my own template system, but, the package don't come in file menu, even when I disable/enable it. what's the problem?

A:
I made some tests and here are some conclusions.

On restart of MonoDevelop, if your .addin.xml has some errors or is not well formed xml file, MonoDevelop will not load it (if it has a cached working version of previous .addin.xml it will load that one).

Using enable/disable will not reload .addin.xml, only .xft.xml files. So if you make changes in .addin.xml and want to see them, you should restart MonoDevelop.

To know if changed .addin.xml has errors, you could change its 'name' and if you don't see in Add-in Manager a new name, but an old one, you know it didn't load because of errors, so previous cached file was loaded.

If some of .xtf.xml files didn't load, you can see in Message Log that there was a problem (Menu: View->Pads->Message Log). You will not see .addin.xml errors in Message Log.

If you can't find out what could be wrong with your .addin.xml you can contact me by email on my blog.

Tuesday, March 30, 2010

Insert, delete space at any place in file without making temporary file copy

Goal:
Implement methods that insert or delete space at any place in file (affecting file length), using technique that copies content within file so there is no need for temporary file.

Implementation idea:
When inserting space of N bytes:
    1. Extend file size by N bytes
    2. Copy/shift all data after insertion position, N bytes towards file's end
    When deleting space of N bytes:
        1. Copy/shift all data after deletion position + N bytes, N bytes towards file's beginning
        2. Shrink file size by N bytes

    Why?
    Framework and system file API offer functions to write new file or append to existing, there are no functions to insert new data (and not overwriting existing) in existing file at some arbitrary position e.g. beginning of file or middle of file. Also, there are no functions for removing data in similar way.

    Usual solutions that I've seen tend to (to simulate insert):
    1. make temporary file
    2. append data before insert position from original file
    3. append data to insert
    4. append data after insert position from original file
    5. delete original file
    6. rename temporary file to original file's name
    Similar technique is used to simulate deletion of part of file.

    Problem with that approach is:
    • choosing name for temporary file
    • need for disk space for original and temporary file
    • copying of original data positioned before insert/delete position (no need for that)
    Usage example:

    Legend:
    Unchanged
    Copied
    Filled/Inserted

    Notes:
     - Each step works on resulting data from previous step.
     - Inserted part can be also interpreted as unchanged data, but logically it is treated as uninitialized as it is expected that user will write new data after the part is inserted.


    Initial file stream (letter 'A' is at position 0)
    FileStream file = new FileStream("test.txt", FileMode.Create, FileAccess.ReadWrite);
    byte[] initial = Encoding.ASCII.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    file.Write(initial, 0, initial.Length);
    
    Result: ABCDEFGHIJKLMNOPQRSTUVWXYZ


    Insert (make room for) 4 bytes at position 7(letter 'H')
    Quazistax.QSFile.InsertFilePart(file, 7, 4);
    
    Result: ABCDEFGHIJKHIJKLMNOPQRSTUVWXYZ


    Fill 4 bytes at position 7 with '='
    Quazistax.QSFile.FillFilePart(file, 7, 4, (byte)'=');
    
    Result: ABCDEFG====HIJKLMNOPQRSTUVWXYZ


    Delete 7 bytes at position 16 (part 'MNOPQRS', letter 'M' is at position 16)
    Quazistax.QSFile.DeleteFilePart(file, 16, 7);
    
    Result: ABCDEFG====HIJKLTUVWXYZ


    Copy 8 bytes from position 5 (letter 'F') to position 14 (letter 'K')
    Quazistax.QSFile.CopyFilePart(file, 5, 14, 8);
    
    Result: ABCDEFG====HIJFG====HIZ

    Test:
     
    Source code:
    You can download the source here.

    Friday, March 26, 2010

    Managed way to show Drag&Drop feedback image while dragging

    Goal:
    Show translucent image under cursor while dragging data, inside own application or between applications, without using P/Invoke.

    Implementation idea:
    Using translucent top level Form that follows cursor while dragging

    Test results:
    Test is done only on Windows XP so i don't know if there is some unexpected different behavior on other platforms.

    Using image with specified transparent color
    [Cursor is in the middle, but isn't captured with Print Screen]

    Using dynamically generated image
    Features:
    • Show image with same level of translucency through pixels
    • Show image in shape other then rectangle by specifying color which is rendered transparent
    • Specify relative offset of cursor to image
    • Specify image opacity (greater opacity = lower translucency)
    Caveats:
    • Can not render images with different alpha per pixel.
    Source code:
    You can get the source here.

    Thursday, March 25, 2010

    Dynamic constructor invocation - based on Timwi's dynamic method invocation

    This is an addition to Timwi's excellent post: Dynamic method invocation without the TargetInvocationException wrapping.

    What I find it useful for:

        When you write a program that invokes methods through delegate's "Invoke" function, debugger does not break on place where that exception is thrown in invoked method, it breaks on "Invoke" function, with original exception wrapped as InnerException of TargetInvocationException.
        Timwi's approach solves that problem (debugger break position is in the invoked method, and there is no exception wrapping)

    What is changed:
    • made some modifications so it can be compiled for .Net 2.0
    • InvokeDirect renamed to Invoke
    • added Invoke overload that invokes constructor by ConstructorInfo
    • (details can be seen in source, search for "Quazistax" in comments)
    Basic idea behind implementation:
    • For each new constructor signature, dynamically create class with static method that returns object created using "new" on desired ConstructorInfo, and cache that method in dictionary with provided ConstructorInfo as key.
    e.g. for Form parameterless constructor we dynamically create something like this:
    public class FormConstructorGeneratedClass
    {
        public static Form Construct()
        {    return new Form();    }
    }
    
    Dynamically creating method that calls constructor with specified signature:
    private static MethodInfo createConstructorMethod(ConstructorInfo con, string sig)
    {
        var typeBuilder = modBuilder.DefineType("construct " + sig + ".GeneratedClass",
            TypeAttributes.Public,
            typeof(object));
    
        // Create a Construct method that creates new object trough given constructor
        var methodBuilder = typeBuilder.DefineMethod("Construct",
           MethodAttributes.Public | MethodAttributes.Static,
           con.DeclaringType, getParameterTypes(con));
    
        // Now generate IL which will do constructing.
        // Generated IL is equivalent to the following single line of C# code:
        // return new ConstructedType(parameters[0],  parameters[1], ...);
        // assuming that ConstructedType is type for which ConstructorInfo is given
        var il = methodBuilder.GetILGenerator();
        ParameterInfo[] parameters = con.GetParameters();
        for (int i = 0; i < parameters.Length; ++i)
            EmitLdarg(il, i);
    
        il.Emit(OpCodes.Newobj, con); //calling constructor
        il.Emit(OpCodes.Ret);
    
        return typeBuilder.CreateType().GetMethod("Construct");
    }
    
    • When invoking constructor, get from cache method created in step before and invoke it through Timwi's approach
    Source code:
    You can get the source here.