The AGX Unity plugin is a set of wrapper, utility and rendering aid classes using the C# API of the AGX Dynamics physics engine. The AGXUnity plugin is a compiled, class library dll.
The AGXUnity plugin is preferably developed in Visual Studio with the latest version of Unity 3D (currently 5.4.1), 64-bit versions of AGX Dynamics and Unity 3D.
-
Download and install Unity 3D: https://unity3d.com/get-unity/download
-
Download and install Visual Studio Tools for Unity: https://visualstudiogallery.msdn.microsoft.com/8d26236e-4a64-4d64-8486-7df95156aba9
-
Follow the instructions how to enable debugging in Unity: https://msdn.microsoft.com/en-us/library/dn940025.aspx
-
Checkout AGXUnity
-
Open AGXUnity.sln in Visual Studio
-
Verify all references in the AgXUnity project (Browse for the correct dll files using the Reference Manager)
a. agxDotNet.dll: [AgX install path]\bin\x64\agxDotNet.dll
b. UnityEngine.dll: [Unity 3D install path]\Editor\Data\Managed\UnityEngine.dll
-
Build!
-
The output is AgXUnity.dll and AgXUnityEditor.dll in the output sub-directory
Required References in visual studio:
The next section describes how to install the folder hierarchy, resources and editor extensions using the pre-exported Unity packages.
After a successfull build of AGXUnity you should have two new dll-files: AgXUnity.dll and AgXUnityEditor.dll in your output directory.
-
Make sure AGX binaries and dependencies are in path when starting Unity, e.g., a. cmd -> setup_env.bat -> “<Unity 3D install path>\Editor\Unity.exe” or start the AGX Dynamics Commandline from the start menu.
-
Open or create a new Unity project. Start Unity3D with for example:
"c:\Program Files\Unity\Editor\Unity.exe"
-
The newly created project tab could look something like this:
-
From the AgXUnity\data directory – drag-drop “Data/PluginStructure.unitypackage” into the Assets folder. Make sure everything is marked and press “Import”:
-
Next copy AgXUnity.dll, agxDotNet.dll and optionally AgXUnity.pdb to the Assets/AgXUnity/Plugins folder.
Note: If the agxDotNet.dll is missing, you’ll get the following exceptions: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. Unhandled Exception: System.Reflection.ReflectionTypeLoadException: The classes in the module cannot be loaded.
It’s not enough to have the agxDotNet.dll file in path, it has to be in the Plugins folder as well.
Unity and Visual Studio Tools for Unity automatically creates “.meta” and debugging mdb files (if the pdb file is present).
-
Next you have to drop the files AgXUnityEditor.dll and AgXUnityEditor.pdb (for debugging) in the Editor/Plugins folder:
-
AgXUnity is now ready to use! The menu item AgXUnity contains the objects that can be created using the UI.
-
Optional Visual Studio post-build event. Right click AgXUnity project, select Properties -> Build Events -> “Post-build event command line:” -> “Edit Post-build …” and write: echo f | xcopy "$(TargetDir)$(TargetName).*" "\Assets\AgXUnity\Plugins" /Y
AgXUnity.dll and AgXUnity.pdb will be copied to the Unity project AgXUnity plugins folder each time you build the project/solution. A similar solution should be done for AgXUnityEditor.dll
It’s currently not possible to copy the agxDotNet.dll each time you build because the dll is not automatically unloaded by Unity.
Since we’re managing native objects and MonoBehaviour in general has a bit undeterministic behavior, all objects handling native objects should inherit from ScriptComponent rather than MonoBehaviour. There’re also native objects that depends on other native objects. One example of this is constraints. The native constraints has to have native instances to the rigid bodies when the constraints are instantiated. To enable this, ScriptComponent has two important methods. “Initialize” where all native objects should be instantiated and “GetInitialized” which guarantees that the returned component is initialized with a valid native instance.
public abstract class ScriptComponent : MonoBehaviour { ... ////// Initialize internal and/or native objects. /// /// true if successfully initialized protected virtual bool Initialize(); ... ////// Makes sure this component is returned fully initialized, if /// e.g., your component depends on native objects in this. /// /// Type of this component. /// This component fully initialized, or null if failed. public T GetInitialized() where T : ScriptComponent; }
Example usage – create a (native) lock joint between two game objects containing rigid body components:
protected override bool Initialize() { // GetInitializedComponent is an AgXUnity extension to UnityEngine.GameObject. // GetInitializedComponent == go.GetComponent().GetInitialized(). AgXUnity.RigidBody rb1 = m_gameObject1.GetInitializedComponent(); AgXUnity.RigidBody rb2 = m_gameObject2.GetInitializedComponent(); if ( rb1 == null || rb2 == null ) throw new NullReferenceException(); m_lock = new agx.LockJoint( rb1.Native, rb2.Native ); GetSimulation().add( m_lock ); return base.Initialize(); }
Each object is guaranteed to only receive one call to “Initialize”. The “Initialize” call is in general called during Unity “Start” phase. After the “Initialize” call the object receives a “property synchronization” update. Property synchronization When data is changed in the editor or when scenes are restored from file (e.g., loading a scene) it’s convenient to handle the data flow in properties rather than fields and update phases. Read more about this in Propagation of data with AgXUnityEditor.BaseEditor.
As briefly described in previous section, ScriptComponents receives property synchronization update after the object has been initialized. Property synchronization is using reflection to match a private, serialized field to a public property with a matching name.
// Private, serialized field. [SerializeField] private Vector3 m_myVectorValue = Vector3.zero; // Public property with matching name to m_myVectorValue. public Vector3 MyVectorValue { get { return m_myVectorValue; } set { m_myVectorValue = value; if ( something != null ) something.SetValue( m_myVectorValue ); } }
The name of the property has to be the name of the field but without "m_" and the first character capitalized. I.e., “MyVectorValue” is a match to “m_myVectorValue”. Whenever there’s a match, the property synchronization implementation performs obj.MyVectorValue = obj.m_myVectorValue, i.e., invoking the “set” method, passing the private field as value. The main benefit of this property synchronization is to write data to the native instances that were instantiated in the “Initialize” call. Consider the case where one presses “Play” in the editor or when a built application starts:
- Serialized data is restored from file (to the serialized private and public fields).
- ScriptComponent objects are initialized (“Initialize” is called) – native objects are created.
- Property synchronization.
The AGXUnity plugin is linked to the native AGX Dynamics physics engine – having the simulated objects in the native environment. When values/properties etc. are changed from within the editor or a script, the data has to be propagated to the native environment – when needed.
The native objects are in general instantiated when Unity performs the “Start” calls. This means that all data has to be stored in the managed environment and then written down to the native environment when the managed object receives the “Start”/initialize call. Since all data is present in the Unity managed environment the serialization is trivial (automatic).
The BaseEditor class is essential for the propagation of data from the managed to the native environment while using the editor. BaseEditor extends UnityEditor.Editor and it’s basically GUI code that you see in the “Inspector” tab in Unity:
Main features of the BaseEditor class is that it can:
- Visualize C# properties.
- Invoke “get” and “set” of C# properties when e.g., a value is changed.
- Invoke methods (when pressing a button).
- Handles custom attributes such as “This value must be larger than 0”.
- Like the default Unity editor class, visualize and change serializable fields.
Using properties instead of serializable fields makes it a lot easier to handle the data flow. Take for example the value of the mass of a rigid body.
// Public field, automatically serialized and // visible in the editor. public float Mass = 1.0f;
Having the mass as a field like this, we have to have at least one more line somewhere in the code where we assign it to the native rigid body instance. Using properties instead will in general result in more code but enables a solid workflow, minimizing the risk of forgetting to propagate the data and maximizing the understanding when and how the data flows.
// Tell Unity we want serialization of this private field. [SerializeField] private float m_mass = 1.0f; public float Mass { get { return m_mass; } set { if ( value <= 0.0f ) return; m_mass = value; // Synchronize to native instance if created. if ( m_native != null ) m_native.getMassProperties().setMass( m_mass ); } }
Since “m_mass” is private, it won’t be shown in the Inspector tab. Property “Mass” is public so it will be visualized and using BaseEditor the “set” method will be invoked when the value of the mass has been changed. If we have an instance of the native object, we can assign the new value directly.
For Unity to use a custom editor to render the GUI under the “Inspector” tab, the class implementing the “OnInspectorGUI” method has to carry the attribute “CustomEditor”. Consider the following, simple class that prints the input value to property “Test”:
public class TestComponent : MonoBehaviour { [SerializeField] private float m_test = 0.5f; public float Test { get { return m_test; } set { Debug.Log( value ); m_test = value; } } }
Note: The type constraint is BaseEditor is UnityEngine.Object
Assigning the script to a game object a text field labeled “Text” will appear with value “0.5”. Using the default editor, changing the value, won’t show the debug print.
To enable the BaseEditor functionality, add a new script in any folder named “Editor”, with the following class:
using UnityEditor; [CustomEditor( typeof( TestComponent ) ) ] class TestComponentEditor : AgXUnityEditor.BaseEditor { }
The TestComponent will be rendered the same in the Inspector tab, but when changing the value, the new value will be printed in the Console tab.