diff options
Diffstat (limited to 'Runtime/Misc/GameObjectUtility.cpp')
-rw-r--r-- | Runtime/Misc/GameObjectUtility.cpp | 1276 |
1 files changed, 1276 insertions, 0 deletions
diff --git a/Runtime/Misc/GameObjectUtility.cpp b/Runtime/Misc/GameObjectUtility.cpp new file mode 100644 index 0000000..b2ee5b4 --- /dev/null +++ b/Runtime/Misc/GameObjectUtility.cpp @@ -0,0 +1,1276 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" +#include "ComponentRequirement.h" +#include "GameObjectUtility.h" +#include "Runtime/BaseClasses/GameObject.h" +#include "BatchDeleteObjects.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/Mono/MonoBehaviour.h" +#include "Runtime/Mono/MonoScript.h" +#include "Runtime/Mono/MonoScriptCache.h" +#include "Runtime/Mono/MonoManager.h" +#include "Runtime/Utilities/Word.h" +#include "Runtime/BaseClasses/IsPlaying.h" +#include "Runtime/Utilities/dynamic_bitset.h" +#include "Runtime/Serialize/TransferUtility.h" +#include "Runtime/Serialize/IterateTypeTree.h" +#include "Runtime/Serialize/PersistentManager.h" +#include "BuildSettings.h" +#include "Runtime/Camera/Camera.h" //// @TODO: Only used by FindMainCamera, should be moved to a different file? +#include "Runtime/BaseClasses/Tags.h" +#include "Player.h" +#include "Runtime/Profiler/Profiler.h" +#include "Runtime/Misc/ResourceManager.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h" +#include "Runtime/Scripting/Backend/ScriptingBackendApi.h" + +#if UNITY_EDITOR +#include "Editor/Src/Prefabs/Prefab.h" +#endif + +PROFILER_INFORMATION(gDestroyProfile, "Destroy", kProfilerOther) + +using namespace std; +const char* kUnityEngine = "UnityEngine"; + +#if UNITY_EDITOR + +static std::vector<AddComponentCallbackFunction*> gGOAddComponentCallbacks; + +void RegisterAddComponentCallback (AddComponentCallbackFunction* callback) +{ + gGOAddComponentCallbacks.push_back(callback); +} + +static void InvokeAddComponentCallback (Unity::Component& com) +{ + for (int i=0;i<gGOAddComponentCallbacks.size();i++) + gGOAddComponentCallbacks[i] (com); +} +#else +inline void InvokeAddComponentCallback (Unity::Component& com) { } +#endif + + + +bool IsComponentSubclassOfMonoClass (Unity::Component& com, MonoClass* requiredClass); +bool IsComponentSubclassOfMonoClass (Unity::Component& com, MonoClass* requiredClass) +{ +#if ENABLE_SCRIPTING + int curComponentClassID = com.GetClassID(); + + ScriptingClassPtr curKlass = GetMonoManager().ClassIDToScriptingClass (curComponentClassID); + + MonoBehaviour* monoBehaviour = dynamic_pptr_cast<MonoBehaviour*> (&com); + if (monoBehaviour) + curKlass = monoBehaviour->GetClass(); + + // Check classID against behaviour + if (curKlass && requiredClass) + { + if (requiredClass == curKlass || scripting_class_is_subclass_of (curKlass, requiredClass)) + return true; + } +#endif + return false; +} + +Unity::Component* AddComponentUnchecked (GameObject& go, int classID, MonoScriptPtr script, string* error) +{ + // Produce object + Unity::Component* o = static_cast<Unity::Component*>(Object::Produce (classID)); + if (o == NULL) + { + if (error) + *error = Format ("Can't add component because the component '%s' can't be produced.", Object::ClassIDToString (classID).c_str()); + return NULL; + } + + AssertIf (!o->IsDerivedFrom (ClassID (Component))); + + o->Reset (); + go.AddComponentInternal (static_cast<Unity::Component*> (o)); + + MonoBehaviour* monoBehaviour = dynamic_pptr_cast<MonoBehaviour*> (o); + if (monoBehaviour) + { +#if ENABLE_SCRIPTING + int instanceID = o->GetInstanceID(); + monoBehaviour->SetScript (script); + // Check if the object has destroyed itself in Awake. + if (!PPtr<Object> (instanceID).IsValid()) + return NULL; +#else + AssertIf( script == 0 && "Mono's disabled" ); +#endif + +#if UNITY_EDITOR + if (!IsInsidePlayerLoop()) + ApplyDefaultReferences(*monoBehaviour, script->GetDefaultReferences()); +#endif + } + + o->Reset(); + o->SmartReset(); + o->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad); + o->SetDirty (); + + InvokeAddComponentCallback (*o); + + return static_cast<Unity::Component*> (o); +} + +bool CheckForAbstractClass(GameObject& go, int classID, string* error) +{ + if (Object::ClassIDToRTTI(classID)->isAbstract) + { + // list all derived components + string objectList; + vector<SInt32> derivedObjects; + Object::FindAllDerivedClasses (classID, &derivedObjects); + for (vector<SInt32>::const_iterator it=derivedObjects.begin();it!=derivedObjects.end();++it) + { + SInt32 id = (*it); + objectList += Format("'%s'", Object::ClassIDToString (id).c_str()); + if (it != derivedObjects.end()-1) + objectList += " or "; + } + *error = Format ("Adding component failed. Add required component of type %s to the game object '%s' first.", objectList.c_str(), go.GetName ()); + return false; + } + return true; +} + +static void AddRequiredScriptComponents (GameObject& go, MonoScript* script, std::set<ScriptingClassPtr> &processed) +{ +#if ENABLE_MONO || UNITY_WINRT + ScriptingArrayPtr array = RequiredComponentsOf(script->GetClass()); + + if (array) + { + for (int j = 0; j < GetScriptingArraySize(array); j++) + { + ScriptingObjectPtr requiredClassObject = Scripting::GetScriptingArrayElementNoRef<ScriptingObjectPtr>(array, j); + if (requiredClassObject == SCRIPTING_NULL) + continue; + + ScriptingClassPtr requiredClass = GetScriptingTypeRegistry().GetType (requiredClassObject); + + if (requiredClass != NULL && processed.find(requiredClass) != processed.end()) + continue; + + // Is that script/component already added to the game obejct? + bool needToAdd = true; + for (int c=0;c<go.GetComponentCount();c++) + { + if (IsComponentSubclassOfMonoClass (go.GetComponentAtIndex(c), requiredClass)) + { + needToAdd = false; + break; + } + } + + if (needToAdd) + { + int clsID = -1; + MonoScript* requiredScript = NULL; + if (StrICmp (scripting_class_get_namespace (requiredClass), kUnityEngine) == 0) + clsID = Object::StringToClassID (scripting_class_get_name (requiredClass)); + + if (clsID == -1 || !Object::IsDerivedFromClassID (clsID, ClassID (Component))) + { + // We can't find the script. + // Not very good but we will just let it slip through + requiredScript = GetMonoScriptManager().FindRuntimeScript(requiredClass); + if (requiredScript == NULL ) + continue; + clsID = ClassID (MonoBehaviour); + } + + string internalError; + if (!CheckForAbstractClass(go, clsID, &internalError) || AddComponentInternal (go, clsID, requiredScript, processed, &internalError) == NULL) + ErrorString (internalError); + } + } + } +#endif +} + + +static bool ValidateScriptComponent (MonoScript* script, std::string* error) +{ + // Check if the script is instantiatable + if (script == NULL) + { + if (error) + *error = Format ("Can't add script behaviour because the script couldn't be found."); + return false; + } + + MonoScriptType type = (MonoScriptType)script->GetScriptType(); + + if (type == kScriptTypeMonoBehaviourDerived) + return true; + + + // Check if the script is instantiatable + if (type == kScriptTypeClassNotFound) + { + if (error) + { +#if UNITY_EDITOR + if (GetMonoManager().HasCompileErrors()) + *error = Format("Can't add script behaviour %s. You need to fix all compile errors in all scripts first!", script->GetName()); + else +#endif + *error = Format("Can't add script behaviour %s. The scripts file name does not match the name of the class defined in the script!", script->GetName()); + } + + return false; + } + // Check if the script is instantiatable + else + { + if (error) + { + if (script->IsEditorScript()) + *error = Format ("Can't add script behaviour %s because it is an editor script. To attach a script it needs to be outside the 'Editor' folder.", script->GetName()); + else if (type == kScriptTypeNotInitialized) + *error = Format("Script %s has not finished compilation yet. Please wait until compilation of the script has finished and try again.", script->GetName()); + else if (type == kScriptTypeClassIsAbstract) + *error = Format("Can't add script behaviour %s. The script class can't be abstract!", script->GetName()); + else if (type == kScriptTypeClassIsInterface) + *error = Format("Can't add script behaviour %s. The script can't be an interface!", script->GetName()); + else if (type == kScriptTypeClassIsGeneric) + *error = Format("Can't add script behaviour %s. Generic MonoBehaviours are not supported!", script->GetName()); + else + *error = Format("Can't add script behaviour %s. The script needs to derive from MonoBehaviour!", script->GetName()); + } + return false; + } +} + +bool CanAddComponent (Unity::GameObject& go, int classID) +{ + if (go.CountDerivedComponents (classID) && !DoesComponentAllowMultipleInclusion (classID)) + return false; + if (go.HasConflictingComponents(classID)) + return false; + return true; +} + +Unity::Component* AddComponent (GameObject& go, int classID, MonoScriptPtr script, string* error) +{ + std::set<ScriptingClassPtr> processed; + return AddComponentInternal (go, classID, script, processed, error); +} + +Unity::Component* AddComponentInternal (GameObject& go, int classID, MonoScriptPtr script, std::set<ScriptingClassPtr> &processed, std::string* error) +{ + // Are we actually derived from go component? + if (!Object::IsDerivedFromClassID (classID, ClassID (Component))) + { + if (error) + *error = Format ("Can't add component because '%s' is not derived from Component.", Object::ClassIDToString (classID).c_str ()); + return NULL; + } + + // We can't add a component if it has conflicting components. This is included when checking if we can add a component but we want to give a specific reason here i.e. a component conflict. + Unity::Component* conflictingComponent = go.FindConflictingComponentPtr (classID); + if (conflictingComponent) + { + if (error) + *error = Format ("Can't add component '%s' to %s because it conflicts with the existing '%s' derived component!", + Object::ClassIDToString (classID).c_str (), go.GetName (), Object::ClassIDToString (conflictingComponent->GetClassID ()).c_str ()); + return NULL; + } + + // We can't add a component if we are already inserted and the component doesn't allow multiple insertion! + if (!CanAddComponent (go, classID)) + { + if (error) + *error = Format ("Can't add component '%s' to %s because such a component is already added to the game object!", Object::ClassIDToString (classID).c_str (), go.GetName ()); + return NULL; + } + + // Don't allow adding a component to an imported model + // @TODO: go.TestHideFlag(Object::kNotEditable) && go.IsPersistent() + // -> Is weird. We should cleanup the hide flags to be sensible design + if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_5_a1) && go.TestHideFlag(Object::kNotEditable) && go.IsPersistent()) + { + if (error) + *error = Format ("Can't add component '%s' to %s because the game object is a generated prefab and can only be modified through an AssetPostprocessor.", Object::ClassIDToString (classID).c_str (), go.GetName ()); + return NULL; + } + + // Find all required components and add them before! + const vector_set<int>& requiredCom = FindRequiredComponentsForComponent (classID); + vector_set<int>::const_iterator i; + for (i=requiredCom.begin ();i!=requiredCom.end ();i++) + { + if (go.CountDerivedComponents (*i)) + continue; + + //TODO: check + string internalError; + if (!CheckForAbstractClass(go, *i, &internalError) || AddComponentInternal (go, *i, NULL, processed, &internalError) == NULL) + { + ErrorString (internalError); + return NULL; + } + } + + // Find all required components for a script and add them before! + if (classID == ClassID (MonoBehaviour)) + { + #if ENABLE_SCRIPTING + + if (!ValidateScriptComponent (script, error)) + return NULL; + + Assert(script->GetClass () != SCRIPTING_NULL); + + processed.insert(script->GetClass()); + AddRequiredScriptComponents (go, script, processed); + + #else + if( error ) + *error = Format ("Can't add script behaviour because the Mono's disabled."); + return NULL; + #endif // ENABLE_SCRIPTING + } + + // this means user is trying to add abstract class directly (AddComponent("Collider")) + if (Object::ClassIDToRTTI(classID)->isAbstract) + { + *error = Format ("Cannot add component of type '%s' because it is abstract. Add component of type that is derived from '%s' instead.", Object::ClassIDToString (classID).c_str (), Object::ClassIDToString (classID).c_str ()); + return NULL; + } + + return AddComponentUnchecked (go, classID, script, error); +} + + + +Unity::Component* AddComponent (GameObject& go, const char* name, string* error) +{ + if (BeginsWith(name, "UnityEngine.")) + name += strlen("UnityEngine."); + + int classID = Object::StringToClassID (name); + if (classID != -1 && Object::IsDerivedFromClassID(classID, ClassID(Component))) + { + return AddComponent (go, classID, NULL, error); + } +#if ENABLE_SCRIPTING + else + { + MonoScriptPtr script = GetMonoScriptManager().FindRuntimeScript (name); + + if (script) + return AddComponent (go, ClassID (MonoBehaviour), script, error); + + if (error) + { + if (classID == -1) + *error = Format ("Can't add component because class '%s' doesn't exist!", name); + else + *error = Format("Can't add component because '%s' is not derived from Component.", name); + } + + return NULL; + } +#else + return NULL; +#endif +} + +// varargs can only be passed around by passing the va_list, so caller is responsible for calling va_start/va_end +void AddComponentsFromVAList (GameObject& go, const char* componentName, va_list componentList) +{ + if (componentName == NULL) + return; + + string error; + if (AddComponent (go, componentName, &error) == NULL) + ErrorString (error); + + while (true) + { + const char* cur = va_arg (componentList, const char*); + if (cur == NULL) + break; + if (AddComponent (go, cur, &error) == NULL) + ErrorString (error); + } +} + +void AddComponents (GameObject& go, const char* componentName, ...) +{ + va_list ap; + va_start (ap, componentName); + AddComponentsFromVAList (go, componentName, ap); + va_end (ap); +} + +void ActivateGameObject (GameObject& go, const string& name) +{ + go.Reset (); + go.SetName (name.c_str ()); + go.AwakeFromLoad (kInstantiateOrCreateFromCodeAwakeFromLoad); + go.Activate (); +} + +void SetNameAndResetGameObject (GameObject& go, const string& name) +{ + go.Reset (); + go.SetName (name.c_str ()); + go.AwakeFromLoad (kInstantiateOrCreateFromCodeAwakeFromLoad); +} + +GameObject& CreateGameObject (const string& name, const char* componentName, ...) +{ + // Create game object with name! + GameObject &go = *NEW_OBJECT (GameObject); + + ActivateGameObject (go, name); + + // Add components with class names! + va_list ap; + va_start (ap, componentName); + AddComponentsFromVAList (go, componentName, ap); + va_end (ap); + + return go; +} + + +Unity::GameObject& CreateGameObjectWithVAList (const std::string& name, const char* componentName, va_list list) +{ + va_list componentList; + va_copy (componentList, list); + GameObject &go = *NEW_OBJECT (GameObject); + + ActivateGameObject (go, name); + AddComponentsFromVAList (go, componentName, componentList); + va_end (componentList); + + return go; +} + +GameObject& CreateGameObjectWithHideFlags (const string& name, bool isActive, int flags, const char* componentName, ...) +{ + // Create game object with name! + GameObject &go = *NEW_OBJECT (GameObject); + + // HideFlags need to be set before object activation because of a bug where + // Unity will not immediately update visible root game object list when + // changing hide flags after creation Case: 382530 + go.SetHideFlags (flags); + + if (isActive) + ActivateGameObject (go, name); + else + SetNameAndResetGameObject (go, name); + + // Add components with class names! + va_list ap; + va_start (ap, componentName); + AddComponentsFromVAList(go, componentName, ap); + va_end (ap); + + return go; +} + +inline string GetComponentOrScriptName (Unity::Component& com) +{ +#if ENABLE_SCRIPTING + MonoBehaviour* monoBehaviour = dynamic_pptr_cast<MonoBehaviour*> (&com); + if (monoBehaviour) + { + MonoScript* script = monoBehaviour->GetScript(); + if (script) + return Append (script->GetName(), " (Script)"); + } +#endif + return com.GetClassName (); +} + +bool CanRemoveComponent(Unity::Component& component, std::string* error) +{ + return CanReplaceComponent(component, -1, error); +} + +bool CanReplaceComponent(Unity::Component& component, int replacementClassID, std::string* error) +{ + GameObject* go = component.GetGameObjectPtr (); + if (go == NULL) + return false; + + int componentIndex = go->GetComponentIndex(&component); + if (componentIndex == -1) + return false; + + // Starting with Unity 3.2, a transform component has to be attached to every game object! + if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1)) + { + if (component.GetClassID () == ClassID (Transform)) + { + if (error) + { + const char* goName = go->GetName (); + const char* message = "Can't destroy Transform component of '%s'. " + "If you want to destroy the game object, please call 'Destroy' on the game object instead. " + "Destroying the transform component is not allowed."; + + *error = Format (message, goName); + } + + return false; + } + } + + int componentClassID = component.GetClassID (); + ScriptingClassPtr removeKlass = SCRIPTING_NULL; + MonoBehaviour* removeMonoBehaviour = dynamic_pptr_cast<MonoBehaviour*> (&component); + if (removeMonoBehaviour) + removeKlass = removeMonoBehaviour->GetClass(); + + int countSameComponentType = 0; + bool mayRemove = true; + for (int i=0;i<go->GetComponentCount ();i++) + { + int curClassID = go->GetComponentClassIDAtIndex (i); + const vector_set<int>& requiredCom = FindRequiredComponentsForComponent (go->GetComponentClassIDAtIndex (i)); + + // if the component we are releasing is needed by another component + if (requiredCom.count (componentClassID)) + { + if (error) + { + if (!mayRemove) + *error += ", "; + *error += Object::ClassIDToString (curClassID); + mayRemove = false; + } + } + + // Check component requirement for scripts. + // - Fetch all component requirement attributes + // - Check if the class we remove is derived from the requirement + if (curClassID == ClassID (MonoBehaviour)) + { + MonoBehaviour* behaviour = (MonoBehaviour*)&go->GetComponentAtIndex(i); + ScriptingClassPtr scriptClass = behaviour->GetClass(); + if (scriptClass) + { +#if ENABLE_MONO + + MonoArray* array = RequiredComponentsOf(behaviour); + + if (array) + { + for (int j=0;j<mono_array_length(array);j++) + { + MonoObject* requiredClassObject = GetMonoArrayElement<MonoObject*>(array, j); + if (requiredClassObject == NULL) + continue; + MonoClass* requiredClass = GetScriptingTypeRegistry().GetType(requiredClassObject); + if (IsComponentSubclassOfMonoClass (component, requiredClass)) + { + // Find if the new replacement meets the requirements + bool replacementMeetRequirements = false; + if (replacementClassID != -1) + { + MonoClass* replacementClass = GetMonoManager().ClassIDToScriptingClass(replacementClassID); + // This check doesn't work with MonoBehaviour type system. + // But currently component replacement only happens for Collider family. + // Assert it is a subclass of Collider + Assert(scripting_class_is_subclass_of(replacementClass, GetMonoManager().ClassIDToScriptingClass(ClassID(Collider)))); + if (replacementClass == requiredClass + || scripting_class_is_subclass_of(replacementClass, requiredClass)) + { + replacementMeetRequirements = true; + } + } + + // Find if other components of the same GO meets the requirements + bool otherComponentMeetRequirements = false; + for (int k = 0; k < go->GetComponentCount(); ++k) + { + Unity::Component& otherComponent = go->GetComponentAtIndex(k); + if (&otherComponent == &component // not the component being removed... + || &otherComponent == &go->GetComponentAtIndex(i)) // not the component asking requirements... + { + continue; + } + if (IsComponentSubclassOfMonoClass(otherComponent, requiredClass)) + { + otherComponentMeetRequirements = true; + break; + } + } + + if (!replacementMeetRequirements && !otherComponentMeetRequirements) + { + if (error) + { + if (!mayRemove) + *error += ", "; + *error += mono_class_get_name(scriptClass); + *error += " (Script)"; + mayRemove = false; + } + } + } + } + } +#endif // ENABLE_MONO + + if (removeKlass == scriptClass) + countSameComponentType ++; + } + } + else + { + if (curClassID == componentClassID) + countSameComponentType ++; + } + } + + if (mayRemove || countSameComponentType > 1) + { + if (error) + *error = ""; + return true; + } + else + { + if (error) + *error = Format ("Can't remove %s because %s depends on it", GetComponentOrScriptName (component).c_str (), error->c_str ()); + return false; + } +} + +#if UNITY_EDITOR + +int GetMonoBehaviourEngineTypeTreeVariableCount () +{ + static int gCount = -1; + if (gCount == -1) + { + MonoBehaviour* temp = NEW_OBJECT(MonoBehaviour); + temp->HackSetResetWasCalled(); + temp->HackSetAwakeWasCalled(); + + TypeTree tree; + if (temp) + { + GenerateTypeTree(*temp, &tree); + DestroySingleObject(temp); + } + gCount = CountTypeTreeVariables(tree); + } + + return gCount; +} + +struct DisableMonoBehaviourPPtrSerialize +{ + dynamic_bitset override; + bool operator () (const TypeTree& typeTree, dynamic_array<UInt8>& data, int bytePosition) + { + if (IsTypeTreePPtr (typeTree)) + override[typeTree.m_Index] = false; + else if (IsTypeTreePPtrArray (typeTree)) + override[typeTree.m_Father->m_Index] = false; + else if (typeTree.m_Index < GetMonoBehaviourEngineTypeTreeVariableCount()) + override[typeTree.m_Index] = false; + + return true; + } +}; + +// Resets all non-pptr values. +// PPtr values that are already set to a value stay, otherwise they are set to the value of the default properties +// Does not call AwakeFromLoad so you need to do that yourself +static void ResetMonoBehaviourToScriptDefaults (MonoBehaviour& behaviour) +{ + MonoScript* script = behaviour.GetScript(); + if (script == NULL) + return; + + if (script->GetScriptType() != kScriptTypeMonoBehaviourDerived && script->GetScriptType() != kScriptTypeScriptableObjectDerived && script->GetScriptType() != kScriptTypeEditorScriptableObjectDerived) + return; + + // When there is a script class available + if (script->GetClass()) + { + // Create a clone and write out the pristine state + MonoBehaviour* clone = NEW_OBJECT (MonoBehaviour); + clone->HackSetResetWasCalled(); + clone->HackSetAwakeWasCalled(); + + clone->SetScript(behaviour.GetScript()); + dynamic_array<UInt8> data(kMemTempAlloc); + TypeTree typeTree; + WriteObjectToVector (*clone, &data, kSerializeForPrefabSystem); + GenerateTypeTree (*clone, &typeTree, kSerializeForPrefabSystem); + + DestroySingleObject (clone); + + // Read back replacing everything except pptrs + ReadObjectFromVector (&behaviour, data, typeTree, kSerializeForPrefabSystem); + } + + ApplyDefaultReferences(behaviour, script->GetDefaultReferences()); +} + +#endif + + +void UnloadGameObjectAndComponents (GameObject& go) +{ + LockObjectCreation(); + Assert(go.IsPersistent()); + Assert(!go.IsActive()); + + for (int i=0;i<go.GetComponentCount();i++) + { + if (go.GetComponentAtIndexIsLoaded(i)) + { + Unity::Component& com = go.GetComponentAtIndex(i); + delete_object_internal (&com); + } + } + + delete_object_internal (&go); + UnlockObjectCreation(); +} + +bool UnloadGameObjectHierarchy (GameObject& go) +{ + LockObjectCreation(); + Assert(!go.IsActive()); + Transform* transform = go.QueryComponent(Transform); + // AssertIf(transform && !transform->IsPersistent()); + for (int i=0;i<transform->GetChildrenCount();i++) + { + Transform& child = transform->GetChild(i); + UnloadGameObjectHierarchy (child.GetGameObject()); + } + + ///@TODO: This should probably be removed and fixed in the specific components that are misbehaving. + + // Run this once to make sure all components are actually loaded. + // Otherwise, components of incompletely loaded GameObjects may be loaded in the process, + // and when the reference other, previous Components of the same GO, those will be loaded as well + // even though they should be unloaded already, causing crashes later on. + for (int i=0;i<go.GetComponentCount();i++) + go.GetComponentAtIndex(i); + + for (int i=0;i<go.GetComponentCount();i++) + { + Unity::Component& com = go.GetComponentAtIndex(i); + delete_object_internal (&com); + } + + delete_object_internal (&go); + + UnlockObjectCreation(); + return true; +} + +void SendMessageToEveryone(MessageIdentifier message, MessageData msgData) +{ + // First, collect all GameObjects + vector<GameObject*> gameObjects; + Object::FindObjectsOfType (&gameObjects); + + // Next, keep a list of all instance IDs, since it is possible for any GameObject + // to be destroyed when a message is handled. + set<SInt32> ids; + for (int i=0;i<gameObjects.size ();i++) + { + + GameObject* go = gameObjects[i]; + if (go->IsActive ()) + ids.insert(go->GetInstanceID()); + } + + // Send all active gameobjects a message + for (set<SInt32>::iterator i=ids.begin ();i != ids.end ();i++) + { + GameObject* go = static_cast<GameObject*>(Object::IDToPointer(*i)); + if (go && go->IsActive()) + go->SendMessageAny (message, msgData); + } +} + +GameObject* FindGameObjectWithTag (UInt32 tag) +{ + GameObjectList& tagged = GetGameObjectManager().m_TaggedNodes; + for (GameObjectList::iterator i=tagged.begin();i != tagged.end();i++) + { + GameObject& go = **i; + Assert(go.IsActive() && go.GetTag() != 0); + if (go.GetTag() == tag) + return & go; + } + return NULL; +} + +void FindGameObjectsWithTag (UInt32 tag, std::vector<Unity::GameObject*>& gos) +{ + GameObjectList& tagged = GetGameObjectManager().m_TaggedNodes; + for (GameObjectList::iterator i=tagged.begin();i != tagged.end();i++) + { + GameObject& go = **i; + Assert(go.IsActive() && go.GetTag() != 0); + + if (go.GetTag() == tag) + gos.push_back(&go); + } +} + +Camera* FindMainCamera () +{ + std::vector<GameObject*> gos; + FindGameObjectsWithTag(kMainCameraTag, gos); + for (int i=0;i<gos.size();i++) + { + GameObject* go = gos[i]; + Camera* cam = go->QueryComponent(Camera); + if (cam != NULL && cam->GetEnabled()) + return cam; + } + return NULL; +} + +void SmartResetObject (Object& object) +{ + MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*> (&object); + if (behaviour) + { + #if UNITY_EDITOR + if (!IsWorldPlaying()) + ResetMonoBehaviourToScriptDefaults(*behaviour); + #endif + + behaviour->Reset(); + behaviour->SmartReset(); + behaviour->AwakeFromLoad(kDefaultAwakeFromLoad); + behaviour->SetDirty(); + } + else + { + object.Reset(); + object.SmartReset(); + object.AwakeFromLoad(kDefaultAwakeFromLoad); + object.SetDirty(); + } +} + +Unity::Component* GetComponentWithScript (GameObject& go, int classID, MonoScriptPtr script) +{ + + if (classID != ClassID(MonoBehaviour)) + return go.QueryComponentT<Unity::Component>(classID); +#if ENABLE_SCRIPTING + if (script == NULL) + return NULL; + + ScriptingClassPtr compareKlass = script->GetClass(); + if (compareKlass == NULL) + return NULL; + + int count = go.GetComponentCount (); + for (int i=0;i<count;i++) + { + // We are looking only for MonoBehaviours + int clsID = go.GetComponentClassIDAtIndex (i); + if (!Object::IsDerivedFromClassID (clsID, ClassID (MonoBehaviour))) + continue; + + MonoBehaviour& behaviour = static_cast<MonoBehaviour&> (go.GetComponentAtIndex (i)); + ScriptingObjectPtr object = behaviour.GetInstance (); + if (object) + { + ScriptingClassPtr klass = scripting_object_get_class(object, GetScriptingTypeRegistry()); + if (scripting_class_is_subclass_of (klass, compareKlass)) + return &behaviour; + } + } +#endif + return NULL; +} + +void GetComponentsInChildren (const GameObject& gameObject, bool includeInactive, int classID, dynamic_array<Unity::Component*>& outComponents) +{ + // Find components on this game object + if (includeInactive || gameObject.IsActive()) + { + for (int i=0;i<gameObject.GetComponentCount();i++) + { + if (Object::IsDerivedFromClassID(gameObject.GetComponentClassIDAtIndex(i), classID)) + outComponents.push_back(&gameObject.GetComponentAtIndex(i)); + } + } + + // Recurse children + Transform* transform = gameObject.QueryComponent(Transform); + if (transform != NULL) + { + for (Transform::iterator i=transform->begin();i != transform->end();++i) + { + GameObject& child = (**i).GetGameObject (); + GetComponentsInChildren(child, includeInactive, classID, outComponents); + } + } +} + +static void AddToBatchDeleteAndMakeUnpersistent (Object& object, BatchDelete& batchDelete) +{ + if (object.IsPersistent()) + GetPersistentManager ().MakeObjectUnpersistent (object.GetInstanceID (), kDestroyFromFile); + + batchDelete.objects[batchDelete.objectCount++] = &object; +} + +static void DestroyGameObjectRecursive (GameObject& gameObject, BatchDelete& batchDelete) +{ + Assert(!gameObject.IsActive()); + Assert(gameObject.IsDestroying()); + + Transform* transform = gameObject.QueryComponent (Transform); + if (transform) + { + for (Transform::iterator i=transform->begin();i!=transform->end();i++) + { + Transform& transform = **i; + DestroyGameObjectRecursive(*transform.GetGameObjectPtr(), batchDelete); + } + } + + if (gameObject.IsActivating()) + { + if (transform) + transform->RemoveFromParent(); + ErrorStringObject("Cannot destroy GameObject while it is being activated or deactivated.", &gameObject); + return; + } + + for (int i=0;i<gameObject.GetComponentCount();i++) + { + Unity::Component& com = gameObject.GetComponentAtIndex(i); + AddToBatchDeleteAndMakeUnpersistent (com, batchDelete); + } + + AddToBatchDeleteAndMakeUnpersistent (gameObject, batchDelete); +} + +static void PreDestroyRecursive (GameObject& gameObject, size_t* destroyedObjectCount) +{ + if (gameObject.IsActivating()) + { + ErrorStringObject("Cannot destroy GameObject while it is being activated or deactivated.", &gameObject); + return; + } + + + // the callback is only called if the GameObject is + // really destroyed (not only removed from memory) + GameObject::InvokeDestroyedCallback(&gameObject); + + if (!IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1)) + gameObject.Deactivate(kWillDestroyGameObjectDeactivate); + + gameObject.WillDestroyGameObject(); + *destroyedObjectCount += 1 + gameObject.GetComponentCount(); + + Transform* transform = gameObject.QueryComponent (Transform); + if (transform) + { + for (Transform::iterator i=transform->begin();i!=transform->end();i++) + { + Transform& child = **i; + PreDestroyRecursive(child.GetGameObject(), destroyedObjectCount); + } + } +} + +#if UNITY_EDITOR +static void DisconnectPrefabInstanceRecursive (GameObject& gameObject) +{ + PrefabDestroyObjectCallback(gameObject); + + Transform* transform = gameObject.QueryComponent (Transform); + if (transform == NULL) + return; + + for (Transform::iterator i=transform->begin();i!=transform->end();i++) + { + Transform& child = **i; + DisconnectPrefabInstanceRecursive(child.GetGameObject()); + } +} +#endif + +void DestroyTransformComponentAndChildHierarchy (Transform& transform) +{ + size_t objectCount = 0; + for (Transform::iterator i=transform.begin();i!=transform.end();i++) + { + Transform& child = **i; + child.GetGameObject().Deactivate(kWillDestroyGameObjectDeactivate); + PreDestroyRecursive(child.GetGameObject(), &objectCount); + } + + // Remove transform from transform hierarchy + transform.RemoveFromParent(); + + BatchDelete batchDelete = CreateBatchDelete (objectCount); + + for (Transform::iterator i=transform.begin();i!=transform.end();i++) + { + Transform& child = **i; + DestroyGameObjectRecursive(child.GetGameObject(), batchDelete); + } + + CommitBatchDelete (batchDelete); +} + +void DestroyGameObjectHierarchy (GameObject& gameObject) +{ + if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1)) + { + // Deactivate and mark is being destroyed recursively + // Send all necessary callbacks etc. + gameObject.Deactivate(kWillDestroyGameObjectDeactivate); + } + + size_t objectCount = 0; + PreDestroyRecursive(gameObject, &objectCount); + + // Remove transform from transform hierarchy + Transform* transform = gameObject.QueryComponent(Transform); + if (transform) + transform->RemoveFromParent(); + + BatchDelete batchDelete = CreateBatchDelete (objectCount); + + // Destroy the objects (There should be no callbacks happening at this stage anymore) + DestroyGameObjectRecursive(gameObject, batchDelete); + + CommitBatchDelete (batchDelete); +} + +void DestroyObjectHighLevel (Object* object, bool forceDestroy) +{ + PROFILER_AUTO (gDestroyProfile, NULL) + + if (object) + { + if (object->IsDerivedFrom (ClassID (Component))) + { + Unity::Component& component = *static_cast<Unity::Component*> (object); + MonoBehaviour* monoBehaviour = dynamic_pptr_cast<MonoBehaviour*> (object); + + // MonoBehaviour needs per + if (monoBehaviour && monoBehaviour->IsDestroying()) + { + ErrorString("Destroying object multiple times. Don't use DestroyImmediate on the same object in OnDisable or OnDestroy."); + return; + } + + + GameObject* gameObject = component.GetGameObjectPtr(); + if (gameObject) + { + if (GetDisableImmediateDestruction()) + { + ErrorStringObject ("Destroying components immediately is not permitted during physics trigger/contact, animation event callbacks or OnValidate. You must use Destroy instead.", object); + return; + } + + if (gameObject->IsDestroying()) + { + ErrorString("Destroying object multiple times. Don't use DestroyImmediate on the same object in OnDisable or OnDestroy."); + return; + } + + + if (gameObject->IsActivating()) + { + ErrorStringObject("Cannot destroy Component while GameObject is being activated or deactivated.", gameObject); + return; + } + + string error; + if (!forceDestroy && !CanRemoveComponent(component, &error)) + { + ErrorStringObject (error, &component); + return; + } + + PPtr<Unity::Component> componentPPtr = &component; + + if (gameObject->IsActive ()) + { + component.Deactivate (kWillDestroySingleComponentDeactivate); + + // The game object might get destroyed during the OnDisable / OnDestroy callbacks, so don't rely on it. + if ((Unity::Component*)componentPPtr != &component) + return; + } + + // Deleting a transform component is a special case, we have to destroy + // the entire child transform hierarchy. + // This is necessary to keep behaviour consistent for pre 3.2 content + // Starting with 3.2 we prevent this in CanRemoveComponent + if (!IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1) && component.GetClassID() == ClassID (Transform)) + { + DestroyTransformComponentAndChildHierarchy (static_cast<Transform&> (component)); + + if ((Unity::Component*)componentPPtr != &component) + return; + } + + component.WillDestroyComponent(); + + // The game object might get destroyed during the OnDisable / OnDestroy callbacks, so don't rely on it. + if ((Unity::Component*)componentPPtr != &component) + return; + + int componentIndex = gameObject->GetComponentIndex(&component); + if (componentIndex != -1) + component.GetGameObject().RemoveComponentAtIndex (componentIndex); + else + { + ErrorString("Component Removing internal failure"); + } + } + else + { + component.WillDestroyComponent(); + } + + #if UNITY_EDITOR + PrefabDestroyObjectCallback(component); + #endif + + DestroySingleObject (&component); + } + else if (object->IsDerivedFrom (ClassID (GameObject))) + { + if (GetDisableImmediateDestruction()) + { + ErrorStringObject ("Destroying GameObjects immediately is not permitted during physics trigger/contact, animation event callbacks or OnValidate. You must use Destroy instead.", object); + return; + } + + GameObject& gameObject = *static_cast<GameObject*>(object); + if (gameObject.IsDestroying()) + { + ErrorString("Destroying object multiple times. Don't use DestroyImmediate on the same object in OnDisable or OnDestroy."); + return; + } + + if (gameObject.IsActivating()) + { + ErrorStringObject("Cannot destroy GameObject while it is being activated or deactivated.", &gameObject); + return; + } + + Transform* parent = gameObject.QueryComponent(Transform); + if (parent) + { + parent = parent->GetParent(); + if (parent && parent->GetGameObject().IsActivating()) + { + ErrorStringObject("Cannot destroy GameObject while it is being activated or deactivated.", &gameObject); + return; + } + } + #if UNITY_EDITOR + DisconnectPrefabInstanceRecursive (gameObject); + #endif + + DestroyGameObjectHierarchy(gameObject); + } + else if (object->IsDerivedFrom (ClassID(AssetBundle))) + { + ErrorStringObject ("Destroying AssetBundle directly is not permitted.\nUse AssetBundle.UnloadBundle to destroy an asset bundle.", object); + return; + } + else + { + DestroySingleObject(object); + } + } +} + +std::string UnityObjectToString (Object *object) +{ + #if ENABLE_SCRIPTING + std::string type; + + if (object == NULL) return "null"; + + if (object->GetClassID() == ClassID(MonoBehaviour)) + type = dynamic_pptr_cast<MonoBehaviour*>(object)->GetScriptFullClassName(); + else + type = "UnityEngine."+object->GetClassName(); + + if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_2_a1)) + return Format("%s (%s)", object->GetName(), type.c_str()); + else + return type; + #else + return "UnityObject"; + #endif +} + +Unity::Component* FindAncestorComponentExactTypeImpl (Unity::GameObject& gameObject, int classId) +{ + Transform* parent = gameObject.QueryComponent (Transform); + while (parent != NULL) + { + Unity::Component* component = parent->GetGameObject().QueryComponentExactTypeImplementation (classId); + if (component != NULL) + return component; + + parent = parent->GetParent (); + } + return NULL; +} + +Unity::Component* FindAncestorComponentImpl (Unity::GameObject& gameObject, int classId) +{ + Transform* parent = gameObject.QueryComponent (Transform); + while (parent != NULL) + { + Unity::Component* component = parent->GetGameObject().QueryComponentImplementation (classId); + if (component != NULL) + return component; + + parent = parent->GetParent (); + } + return NULL; +} + +#if ENABLE_SCRIPTING +int ExtractTagThrowing(ICallString& name) +{ + string tagString = name; + int tag = StringToTag (tagString); + if (tag != kUndefinedTag) + return tag; + else + { + Scripting::RaiseMonoException ("Tag: %s is not defined!", tagString.c_str()); + return tag; + } +} +#endif |