Regarding Unity GameObjects( MonoBehaviours ).
If you want to allow multiple NewtonWorlds per scene you should probably make the NewtonWorld class likes this...
- Code: Select all
[AddComponentMenu("Newton Physics/Newton World")]
public class NewtonWorld : MonoBehaviour
{
//This function is always called before any Start functions and also just after a prefab is instantiated.
void Awake()
{
m_world = cpp.NewtonCreate();
}
//Start is called before the first frame update only if the script instance is enabled.
void Start()
{
}
//FixedUpdate is often called more frequently than Update. It can be called multiple times per frame if the frame rate is low
//and it may not be called between frames at all if the frame rate is high.
void OnFixedUpdate()
{
cpp.NewtonUpdate(m_world, Time.deltaTime);
}
//Update is called once per frame. It is the main workhorse function for frame updates.
void OnUpdate()
{
}
// LateUpdate is called once per frame, after Update has finished
void LateUpdate()
{
}
//This function is called after all frame updates for the last frame of the object’s existence
//(the object might be destroyed in response to Object.Destroy or at the closure of a scene). void OnDestroy()
{
cpp.NewtonDestroy(m_world);
}
internal SWIGTYPE_p_NewtonWorld m_world; //internal makes this reference available only to classes inside this assembly(dll).
}
It's not recommended to initializing anything in the constructor nor destroy it in the destructor.
The class may be created and destroyed several times by the Editor, due to serialisation and deserialisation etc.
One should use the events Awake(), Start(), OnApplicationQuit() & OnDestroy() for this instead.
Now, the problem as you mention is that if a NewtonBody is initiated( awake or start), the NewtonWorld must already be created.
By default it may not be so. The NewtonBody script may be called before the NewtonWorld script.
You can controll this by setting the Script Execution Order.
Giving the NewtonWorld script a start order of -500, we can guarantee that the NewtonWorld script will always be called before any NewtonBody script.
This is only a problem when loading a scene. During runtime we will know the world already exist.
So far it's good.
Now what happens when we quit the scene?
The same rules apply, Unity will normally call Destroy() in any given order, meaning NewtonWorld.Destroy() may be called before NewtonBody.Destroy().
In our case we specifically told the NewtonWorld to be called first so it will call NewtonWorld.Destroy before calling NewtonBody.Destroy.
Unity could have made this trivial by letting users define Script Execution Order on the methods as well. That way we could have defined NewtonWorld.Awake() to run first of all and NewtonWorld.Destroy() to run last.
But we can't, we can only define this on the whole Script Object, NewtonWorld. So the script execution order applies to all the methods inside the script.
So we have to work around this problem.
I used the Singleton "trick", that is, the NewtonWorld is kept alive all the time.
But that trick didn't really work. It only works when loading a new scene by keeping the NewtonWorld alive by using a function called "DontDestroyOnLoad". This makes the GameObject stay alive and move over to the next loaded scene.
But it still gets destroyed in random order when quitting the application.
That trick also prevents you from creating multiple worlds in the same scene.
I remember there was another "trick" to handle this... Can't remember it now though.
I will update this post when I find it again.
[EDIT]
Ah, found it.
But it still involves the Singleton approach.
Basically you do what I did in the first Plugin, but destroy the unmanaged object in the OnApplicationQuit event instead of in the OnDestroy event.
The OnApplicationQuit will trigger before destroy, so , if the unmanaged object in NewtonWorld is only destroyed in the OnDestroy() event, and all other objects in OnApplicationQuit, the NewtonWorld will always be the last object to be destroyed.
However, if you load a new scene, OnDestroy will be called for all objects but not OnApplicationQuit(since it's not quitting the applicaton). So therefor you also will need to check from within OnDestroy if it was called because the applicaton is quitting or loading a new scene.
But this means loading a new scene will call OnDestroy for the NewtonWorld.
Therefor you set DontDestroyOnLoad for the NewtonWorld, meaning it only will be Destroyed when you quit the game.
Sadly, this means you can only have one single NewtonWorld, not several.
FYI, PhysX only allow on world(scene) as well, maybe because of the same reason, or simply just to make it easier to use physics.
Multiple worlds would mean you would have to force the Unity User to choose which world each NewtonBody should belong to.