Steve Exploring Stuff: 2011


Gotchas: Unity Plugins for Multiple Platforms

As mentioned in my last post, I was surprised by some of the platform compatibility issues I experienced with the CAINav code. In this post I'll cover some of the issues and how they can be resolved.

A quick side note before getting into the main topic: The first sample pack that includes source code has been released. It includes various demos and feature explorers that will help you get started using CAINav.

The Basics

I'm not going to cover the basics of Unity plugin development. That is covered well by the Unity documentation. There is nothing surprising on the C++ side. Just make sure you include the necessary conditional compile macros for platform specific code (e.g. "__declspec(dllexport)" for Windows) and don't use platform specific syntax (e.g. nullptr).

Special Cases for the iOS Platform

While you may be aware that Unity provides wide, but still limited support for the standard .NET libraries, you may not be aware that there are differences in the support provided for the different platforms. This is especially true for iOS.

Lets start with a simple plugin method that will work for the desktop and Android platforms.

[DllImport("cai-nav-rcn", EntryPoint = "dtnqFree")] 
public static extern void FreeEx(ref IntPtr query);

The DLLImport attribute indicates the name of the plugin (cai-nav-rcn) and remaps its name to a C# friendly version (dtnqFree to FreeEx). In this case the method frees a query object's unmanaged resources.

Issue #1: iOS does not support remapping the name of the extern method. All method names must match the C function name. So the the signature must be simplified to:

public static extern void dtnqFree(ref IntPtr query);

Issue #2: iOS requires the custom plugin name __Internal. This makes the iOS signature incompatible with the other platforms.

One easy way to handle this is using conditional compilation as follows:

internal struct InteropUtil 
    public const string PLATFORM_DLL = "__Internal";
    public const string PLATFORM_DLL = "cai-nav-rcn";

This allows standardized method signatures throughout the rest of the code. Our example is altered as follows:

public static extern void dtnqFree(ref IntPtr query);

Issue #3: With compile time differences between platforms, how do you deploy the C# code?

Due to the incompatible code, just as with the plugins, you an no longer use a single pre-compiled .NET DLL for all platforms. There may be other options, but the easiest is to drop the use of pre-compiled .NET DLL's and distribute the C# code directly to the Unity project. Unity will then decide which type of build to perform.

Since CAINav targets the Windows platform, I chose a different method. The main Unity package contains pre-compiled DLL's, but the source code is organized so the C# code can be easily dropped directly into a Unity project.

Oh No, What About Serialization

If you are using Unity serialization for everything, then there is no problem. But that may not be possible. Often a plugin object contains references to unmanaged memory. Also, Unity doesn't support serialization of custom structures or unsigned primitives such as uint, ushort, etc.

You may be tempted to implement custom serialization based on the ISerializable interface. But that isn't cross-platform compatible since your iOS platform uses a different run-time DLL than the rest of the platforms. So you can't for example, serialize a Navmesh object in a Windows project and share it with an iOS project.

Since CAINav supports standalone use in .NET applications, it supports two serialization methods. The standard ISerializable interface, plus byte array serialization. Byte arrays can be serialized using the standard .NET serialization process and shared between all platforms.

An example of this is the Navmesh class. The Navmesh.GetSerializedMesh() method returns byte[]. This byte array can be serialized using either Unity or .NET serialization. One of the overloads of Navmesh.Build() can be used to re-create the navigation mesh from a byte array. This is how the BakedNavmesh component handles the serialization issue.

I'm sure there are other gotcha's. But this post should help you avoid some of the headaches involved in cross-platform plugin development.


CAINav 0.3.0: Path Corridors and More

CAINav v0.3.0 has been released. It is a 'big little' update.  There aren't a lot of new features, but the underlying structure has changed a lot.  

The full change log can be found in both the distribution package and the SVN repository.  But there are several changes worth mentioning here.

Two Boring Items

The NavManager Unity component has been retired and replaced with the NavSource component.  This is worth mentioning here because the functionality of the two components appears to be the same.  And they are pretty much 99% the same.  The difference is that NavSource only provides navigation resources.  It does not handle the CrowdManger.Update() method.  So if you use it you'll need to make sure you add code somewhere to manage the crowd manager. 

The documentation for the last release had some big holes, especially for the crowd manager and introduction to navigation.  That has been mostly addressed.  There is even some navigation related getting started code to hold you over until the new sample pack is released.

The New PathCorridor

The biggest new feature is the addition of the PathCorridor class.  (And U3DPathCorridor Unity extension.)

The NavmeshQuery class contains almost all the pathfinding features needed to hand roll your own navigation solution, whether you just need a list of waypoints or you want to implement complex local movement and steering.  

If you want to manage steering for crowds of agents, then you can use to the CrowdManger.  The crowd manager is great, but it requires that you give it a lot of control and can be overkill for certain situations.

The path corridor fills in the middle ground.  It takes an initial path of polygons (a corridor), then helps you manage it without worrying about locomotion inaccuracies, floating point errors, a moving goal, or any of the other common pathfinding gotchas that make moving along the path difficult.  

Definitely take a look at this new class.  It can help you get rid of a lot of your path management code.

Now, Multi-Platform Friendly

This is where the 'big' comes in, even though most of it is hidden.

CAINav is still supported only on Windows, and is likely to stay that way as long as I don't have a way to validate it on other platforms before release.  But my goal is to keep it friendly for use on the other Unity target platforms

That goal is now much closer to being  met due to a lot of work by Dan Treble down in Australia.  Dan, who is working on an game called Quick Quest, went through the pain and suffering of getting CAINav working with Unity on iPhone and Android.

I expected some minor adjustments to the C++ code.  The surprise was the C# code.  I'll cover that in detail in a separate post.  For now, suffice it to say that the C# syntax for Windows and iPhone plugins is not compatible.

You will have to build your own native libraries.  And for iPhone you will need to copy the C# code directly into your project.  But due to Dan's feedback the CAINav code base should now be useable on both Unity iPhone and Android. The documentation includes some tips.

In closing, here is a much nicer navigation mesh visualization than I can produce, courtesy of Dan:

Until next time, take care of the bobcats.


A Quick Death and Building a Sample Pack

I was just browsing the Unity roadmap for 2011.  Oh look!  Navigation mesh generation, pathfinding, and local steering is planned for version 3.5!  The fact they they are adding built-in pathfinding isn't a big surprise.  They've been advertising for AI programmers for a while now, and the first task for AI is almost always pathfinding.  What I'm surprised at is that we may get it by the end of the year.

So, CAINav may have a lifespan of only about 6 months.  At least in Unity.  Luckily, most of my reasons for creating CAINav still apply.  For example, it has been a great project for becoming comfortable with C++, .NET/C++ interop, and Unity plug-in development.

Don't worry, I'm not halting work on CAINav.  Even if it dies with Unity 3.5 I still need its functionality now.  And there is still a lot of good exploration and personal training benefit to continuing development.

On that front, I've been experimenting with CrowdManager. (Local steering.)  It is nice.  When I add animation and locomotion, the agents look quite natural.  Better locomotion handling, such as the ability to side-step and back away, rather than just turning, and it will look great.

The big thing to remember about CrowdManger is that it isn't a silver bullet.  It is one part of the navigation puzzle and has its limits.  Don't succumb to the temptation to just add agents, set targets, and expect it to do everything for you.

It won't do long distance planning.  You will need to implement that separately. In my prototype I plan the long distance path, perform a partial string-pull, then feed CrowdManager a target several waypoints out from the agent's current location.

You also have to build in some edge case handling, such as when slow moving agents meet head on at corners.  They can block each other.

And remember, CrowdManager isn't meant to be used with large crowds.  The goal is to support up to 20 agents in a moderately complex environment, and that seems to be working out.

If you want to see CrowdManger in action, it is available in the new SamplePack.  This is how I'll release most demo's, tutorials, and samples in the future; packaged together in one download.

The demo allows you to add up to 25 agents to the scene and watch their behavior as they wander around at different speeds.  Clicking on an agent will toggle it's debug visualizations.  So you can see the world as CrowdManger see's it.

Still no sample code I'm afraid.  Beside the fact that I am still learning the best ways of using CAINav, there are some licensing and format issues I need to figure out for the models, animations, and support code.

Enjoy the new demo, and stay away from Arizona.



Kicked out the Door: Recast Navigation for .NET and Unity Pro

CAINav is out in the wild.  I kicked it out the door yesterday.

As mentioned in my last post, CAINav gives .NET and Unity Pro users access to almost all of Recast Navigation's features.

It's always fun trying to decide when the first version of new project is ready for release.  Which features to includes is not too difficult.  It just needs to do something useful and do it well.  In CAINav's case, I just had to decide which of Recast Navigation's features to include.  Documentation is another matter.  A project's code and API may be great, but without good documentation its usefulness will be limited.  Especially when the project offers a wide range of complex features.

This release includes API documentation for all features I understand, which is luckily almost all of the mesh generation and pathfinding features. The local steering features are another matter. (Anyone know what 'weightToi' or 'adaptiveDivisions' means?  Me neither. More research needed.) 

Also included is a decent overview of CAINav's structure, some 'getting started' documentation, and a feature explorer application for the pathfinding component.  What I gave up on including in this release is a proper Unity sample/tutorial project.  That's a bummer.  But I can add that over the next month or two.

There are three main reasons CAINav remains in an alpha state:

The local steering features, and some minor features in the other components, need more rigorous automated testing.  Some of these features only have smoke/sanity tests.

I need to add more optimized features.  Low level access is great and necessary.  But there are more efficient ways of marshalling data between the managed code and the native Recast Navigation code.

The Unity Editor ease-of-use components need more work.  I'm not sure how I'm going to implement them all yet, but I'd like to support the more advanced features such as off-mesh connections, areas, and per polygon flags.

Anyway...enjoy, report bugs, ask questions, and don't blame the martians.


Sneak Peak: Recast Navigation for Unity

Things change, especially when exploring. For example, the benefits of maintaining and upgrading my navigation prototype no longer outweighs the costs. But I still need an good pathfinding solution for Unity. So after looking longingly at its various features, I'm switching over to a Unity plug-in based on Recast Navigation.

And my completely free to use projects continue to fade away.

That's the bad news. The good news is that I'm very close to an alpha release of the new navigation project. It exposes almost all of Recast Navigation's features, and can be used with both Unity Pro and .NET. I have another week or two of cleanup. API standardization, documentation, sample projects, etc. But primary coding is complete.

To give an idea of what's coming I've posted a sneak peak over on the project home site. It's a nice easy to use feature explorer for the NavmeshQuery class.

Enjoy, and ignore all anti-virus warnings.