Website may be up and down over next few months. I'm currently doing a complete overhaul of everything. Going back to simple individual .htm pages, new overall site theme, sanitizing and cleaning up html of all pages and blog posts, attempting to implement a new tooling and publishing system etc etc.

If you are submitting packages to the asset store and are including version changes that contain html links, remember to specify target="_blank" in your link attributes. Failure to do so can result in the asset store window being replaced by the web page that your link was pointing to. In some occasions it can also cause unity to crash.

Unity101Tip52


I have been thinking that because texture atlases are so common Unity should have some build in support for generating them. Turns out it already does! Check out the Texture2D.PackTextures method.


If your Unity code requires conditional compilation symbols to be present this bit of code may come in handy. After unity compiles your scripts it executes any classes that have the InitializeOnLoad attribute. You can call the SetupConditionalCompilation method provided in the code snippet below to ensure that the conditional compilation symbols persist in your unity project. If a symbol was not present and was added it will write out a notification in the unity console.

ConditionalStartup

[InitializeOnLoad]
public class GridMappingSetup
{
    static GridMappingSetup()
    {
        var types = new[] { BuildTargetGroup.Standalone, BuildTargetGroup.WebPlayer };
        var toInclude = new[] { "CBXControls", "GridMapping", "QuickTools", "ToolService", "TileMaterialCreation" };

        SetupConditionalCompilation(types, toInclude);
    }
}

public static void SetupConditionalCompilation(BuildTargetGroup[] platformTargets, string[] symbolsToInclude)
{
    foreach (var type in platformTargets)
    {
        var hasEntry = new bool[symbolsToInclude.Length];
        var conditionals = PlayerSettings.GetScriptingDefineSymbolsForGroup(type).Trim();
        var parts = conditionals.Split(';');
        var changed = false;

        foreach (var part in parts)
        {
            for (int i = 0; i < symbolsToInclude.Length; i++)
            {
                if (part.Trim() == symbolsToInclude[i].Trim())
                {
                    hasEntry[i] = true;
                    break;
                }
            }
        }

        for (int i = 0; i < hasEntry.Length; i++)
        {
            if (!hasEntry[i])
            {
                conditionals += (String.IsNullOrEmpty(conditionals) ? String.Empty : ";") + symbolsToInclude[i];
                changed = true;
            }
        }

        PlayerSettings.SetScriptingDefineSymbolsForGroup(type, conditionals);

        if (changed)
        {
            Debug.Log(String.Format("Updated player conditional compilation symbols for {0}: {1}", type, conditionals));
        }
    }
}

Here is a C# script for visualizing render bounds. Just attach it to a game object and it will draw the render bounds of the game object and all it’s children. Handy for debugging!

Render Bounds Script Preview

public class RendererBoundsGizmo : MonoBehaviour
{
    public bool ShowCenter;

    public Color Color = Color.white;

    public bool DrawCube = true;

    public bool DrawSphere = false;

    /// <summary>
    /// When the game object is selected this will draw the gizmos
    /// </summary>
    /// <remarks>Only called when in the Unity editor.</remarks>
    private void OnDrawGizmosSelected()
    {
        Gizmos.color = this.Color;

        // get renderer bonding box
        var bounds = new Bounds();
        var initBound = false;
        if (CBX.Utilities.Helpers.GetBoundWithChildren(this.transform, ref bounds, ref initBound))
        {
            if (this.DrawCube)
            {
                Gizmos.DrawWireCube(bounds.center, bounds.size);
            }
            if (this.DrawSphere)
            {
                Gizmos.DrawWireSphere(bounds.center, Mathf.Max(Mathf.Max(bounds.extents.x, bounds.extents.y), bounds.extents.z));
            }
        }

        if (this.ShowCenter)
        {
            Gizmos.DrawLine(new Vector3(bounds.min.x, bounds.center.y, bounds.center.z), new Vector3(bounds.max.x, bounds.center.y, bounds.center.z));
            Gizmos.DrawLine(new Vector3(bounds.center.x, bounds.min.y, bounds.center.z), new Vector3(bounds.center.x, bounds.max.y, bounds.center.z));
            Gizmos.DrawLine(new Vector3(bounds.center.x, bounds.center.y, bounds.min.z), new Vector3(bounds.center.x, bounds.center.y, bounds.max.z));
        }

        Handles.BeginGUI();
        var view = SceneView.currentDrawingSceneView;
        var pos = view.camera.WorldToScreenPoint(bounds.center);
        var size = GUI.skin.label.CalcSize(new GUIContent(bounds.ToString()));
        GUI.Label(new Rect(pos.x - (size.x / 2), -pos.y + view.position.height + 4, size.x, size.y), bounds.ToString());
        Handles.EndGUI();
    }
}

And also the code for the GetBoundsWithChildren method.

/// <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 bound = new Bounds();
    var didOne = false;

    // get 'this' bound
    if (transform.gameObject.renderer != null)
    {
        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;
}

Big

I have released the v1.2 update to my CBX.GridMapping project.

Unity Asset Store: Asset store
Unty Forums: Fourm thread

Version Notes

Items starting with "Completed" are items that have been fully implemented as they were intended
Items starting with "Partial" are items that have only been partially implemented.

v1.2

  • Completed - CBXEditorHelpers.toolBarButtonSize need to be replaced with a setting in xml file
  • Completed - Need ability to specify in settings weather user need to hold alt ctrl or shift to draw and erase
  • Completed - For included prefabs you should have mesh generation ones as well as actual prefab *.fbx ones so the user does not wish to take advantage of mesh generation they can have option to use mesh based prefabs.
  • Completed - Should use multiple prefab list files so they can be bundled together and included with prefabs for distribution as a *.unitypackage
  • Completed - Recently used prefab list and recently used materials list should not show the name but rather just 'Select'
  • Completed - Recently used prefabs and material lists need a setting to say weather or not to show the object selection field or weather to just show a button.
  • Completed - Settings for setting the max number of items that a recently used lists can contain.
  • Completed - Need to localize all strings for grid mapping using a localization system
  • Completed - Have settings to specify the max height of the list of recent materials or prefabs
  • Completed - Categorized quick prefab selection
  • Completed - Additional prefab shapes for 2D and 3D
  • Partial - Ability to edit along different axis. Currently it only supports X/Z axis editing with layers extending into the Y plane. Partial support by granting the ability to rotate the map object and still draw
  • Completed - Have option to show recent material & prefab lists displayed as grid of buttons
  • Completed - GridMapping DrawMapInfo method uses a cloned skin from a label and makes it white for highlighting sections. You should not do this because it could cause issues if the skin is different.
  • Completed - Auto scale does not take into account rotation while drawing so a tall obj drawn with no rotation works fine but a tall object draw with 90 rotation along x causes it not to scale properly IE it does not fit within the cell dimensions. This has been fixed
  • Partial - When the map is rotated and user draws the prefabs are not rotated with the map and as such are not drawn in there proper location. This was partially fixed. Prefabs are placed where they should be but positioning the mouse over a grid cell on a map that is rotated is not exact and precice like it is when the map has no rotation. This gets exaderated when the active layer is beyond 3 layers deep. It works but it is not as acurate as I would like it to be.
  • Completed - Full setting customizability for changing grid & guideline colors etc
  • Completed - Tile material creation window needs setting in settings dialog for 'As list' check box as well as default output path etc
  • Completed - Tile material createion should have ability to flip along horiz and vert

v1.0 - v1.1

  • Initial release

Recently I needed to force the inspector to redraw itself from a piece of code outside of a Editor class. The solution I came up with was to call SetDirty on all selected objects of the type I was interested in. The inspector then picked up on the change and refreshed itself immediately. Although this code works under Unity 4 it is a hack and there may be a better way to force the inspector to redraw itself.

var objects = Selection.GetFiltered(typeof(GridMap), SelectionMode.TopLevel);
foreach (var o in objects)
{
    EditorUtility.SetDirty(o);
}

Did you know your editor scripts can acquire a preview texture for an asset? Check out the members of the AssetPreview class.


if you have MonoBehavior code that you want to run while in the unity editor you can use the ExecuteInEditMode attribute and your mono behavior will run as though it was in play mode.


SEO's are the devils play things

Published 12/21/2012 by createdbyx in News
Tags: ,

Recently I sent my brother (who dabbles in SEO) a email about some numbers I was thinking about relating to my CBX.GridMapping project for Unity. I was trying to come up with very small very reasonable numbers that I thought would be possible to achieve. Below is a copy of how the conversation went.

I sent my brother this first email ...

The goal: To sell 2500 copies of CBX.GridMapping tools at $10 over the course of 5 years earning me an average $291 month in income.

http://unity3d.com/company/public-relations/

1,000,000+ registered users as of april 2012
0.25% of the 1,000,000 users = 2500 users
2500 users times $10 = $25,000 in gross sales earnings
minus unity's take of 30% = $7,500 asset stores take
$25,000 - $7,500 = $17,500 in net earings

$17,500 by 60 months (5 year time span) = $291 a month

Given unity's rate growth and expansion into new markets this seems like an achieveable goal. The question is wether 0.25% of the registered
unity users are interested in my tool and if they are willing to spend $10 for it. $10 price point makes it the cheapest on the asset store
for paid tile/grid mapping tools. Other similar tools cost $25 to $50 & CBX.GridMapping will eventually contain a similar feature set. 

==== He responded with this email ... ====

You made a common mistake, based upon your calculations your assuming one million people are going to view your product (or 100% of unity members) and 0.25% or 25,000 of them are going to buy your product, you can aim for a 1% conversion rate on sales but if unity only has one million registered members your looking at the following calculation.

1,000,000 unity members
2% or 20,000 members see your product
1% or 200 members purchase your product
$10 x 200 = $2,000 sales
$2,000 - 30% unity take = $600
$600 unity take - $2,000 in sales = $1,400

If this is a five year goal your looking at $23.33/month over five years not $291/mo

Now a 2% view-count on your product with no advertising may even be a touch high, however with some advertising and promotion over the course of a few month's you can increase the view-count to more than 2%, in order to reach your goal at a 1% conversion rate you'll need a view-count of at least 20 - 25 percent.

But even more you have to take into account what is the approx unity population buying this specific type of product. For example if unity has 1 million registered shoppers and only 20,000 of them shop for this specific product your sales numbers will drop even more, even my calculations above leading to the $23.33/mo figure is assuming all 1 million members look at or purchase this type of product and that simple is not true.

This is probably why the others price there products at a higher price point because they know only a fraction of the total unity members are going to buy it.

So bottom line you need to think about it slightly differently, basically the break-down is this (For selling any product)

#1 - Determine size of market (Unity = 1,000,000 members)
#2 - Estimate what size of that market are interested in your product (In your case un-known but a slightly accurate estimation can be derived if the asset store show's how many sales a particular product has or a particular user has)
#3 - Of the estimated size of the market interested in your product, determine what percentage you can get to view your product (Click-Through-Rate [http://en.wikipedia.org/wiki/Click-through_rate] can be increased via advertising, ect.. no advertising means little visability)
#4 - Of the people who see your product how many will buy your product (Conversion Rate [http://en.wikipedia.org/wiki/Conversion_rate] - For amazing product can be as high as 5 or 10% for average product it sits around 2% for product from unknown brand sit's more around 1%)
#5 - Now that you know the percentage of users who buy your product after seeing it (#4 conversion rate), you can calculate how many estimated sales your expected to achieve.

Remember those five points and apply them to every product you'll ever sell in order to get an accurate number on the income you can achieve, the more information the more accurate your numbers become.

==== And my response to that was ... ====

your math is depressing    lulz :p


The DrawGizmo attribute allows you to setup your gizmo drawing code some place other then with your MonoBehavior class. The code below shows an example of the DrawGizmo attribute used on a method within a Editor class.

    /// <summary>
    /// Provides a editor for the <see cref="TileMap"/> component
    /// </summary>
    [CustomEditor(typeof(TileMap))]
    public class TileMapEditor : Editor
    {
        /// The RenderMapGizmo method will be called if the map is selected. 
        [DrawGizmo(GizmoType.Selected | GizmoType.Active)]
        static void RenderMapGizmo(TileMap map, GizmoType gizmoType)
        {
            // store map width, height and position
            var mapWidth = map.Columns * map.CellWidth;
            var mapHeight = map.Rows * map.CellHeight;
            var position = map.transform.position;
            var activelayerHeight = map.ActiveLayer * map.Depth;

            if (map.drawGridLines)
            {
                // draw layer border
                Gizmos.color = Color.white;
                Gizmos.DrawLine(
                    position + new Vector3(0, activelayerHeight, 0), position + new Vector3(mapWidth, activelayerHeight, 0));
                Gizmos.DrawLine(
                    position + new Vector3(0, activelayerHeight, 0), position + new Vector3(0, activelayerHeight, mapHeight));
                Gizmos.DrawLine(
                    position + new Vector3(mapWidth, activelayerHeight, 0),
                    position + new Vector3(mapWidth, activelayerHeight, mapHeight));
                Gizmos.DrawLine(
                    position + new Vector3(0, activelayerHeight, mapHeight),
                    position + new Vector3(mapWidth, activelayerHeight, mapHeight));
 
                // more draw logic here
        }
    }

The alternative is that you can have a method in your MonoBehavior called OnDrawGizmosSelected

    /// <summary>
    /// Provides a component for tile mapping.
    /// </summary>
    public class TileMap : MonoBehaviour
    {
 
        /// <summary>
        /// When the game object is selected this will draw the gizmos
        /// </summary>
        /// <remarks>Only called when in the Unity editor.</remarks>
        private void OnDrawGizmosSelected()
        {
            // gizmo draw code goes here            
        }
    }

… But since gizmo drawing logic is typically for use within the unity editor the DrawGizmo attribute allows you to place the draw logic in a more appropriate location.


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