diff options
Diffstat (limited to 'Runtime/GameCode')
-rw-r--r-- | Runtime/GameCode/Behaviour.cpp | 259 | ||||
-rw-r--r-- | Runtime/GameCode/Behaviour.h | 87 | ||||
-rw-r--r-- | Runtime/GameCode/CallDelayed.cpp | 203 | ||||
-rw-r--r-- | Runtime/GameCode/CallDelayed.h | 102 | ||||
-rw-r--r-- | Runtime/GameCode/CloneObject.cpp | 303 | ||||
-rw-r--r-- | Runtime/GameCode/CloneObject.h | 29 | ||||
-rw-r--r-- | Runtime/GameCode/DestroyDelayed.cpp | 16 | ||||
-rw-r--r-- | Runtime/GameCode/DestroyDelayed.h | 10 | ||||
-rw-r--r-- | Runtime/GameCode/RootMotionData.h | 9 |
9 files changed, 1018 insertions, 0 deletions
diff --git a/Runtime/GameCode/Behaviour.cpp b/Runtime/GameCode/Behaviour.cpp new file mode 100644 index 0000000..49d5035 --- /dev/null +++ b/Runtime/GameCode/Behaviour.cpp @@ -0,0 +1,259 @@ +#include "UnityPrefix.h" +#include "Behaviour.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/BaseClasses/ManagerContext.h" +#include "Runtime/Misc/ReproductionLog.h" +#include "Runtime/Threads/Thread.h" + +Behaviour::~Behaviour () +{} + +void Behaviour::SetEnabled (bool enab) +{ + if ((bool)m_Enabled == enab) + return; + m_Enabled = enab; + UpdateEnabledState (IsActive ()); + SetDirty (); +} + +#if UNITY_EDITOR +void Behaviour::SetEnabledNoDirty (bool enab) +{ + if ((bool)m_Enabled == enab) + return; + m_Enabled = enab; + UpdateEnabledState (IsActive ()); +} +#endif + +void Behaviour::UpdateEnabledState (bool active) +{ + bool shouldBeAdded = active && m_Enabled; + if (shouldBeAdded == (bool)m_IsAdded) + return; + + // Set IsAdded flag before adding/removing from manager. Otherwise if we get enabled update + // from inside of AddToManager/RemoveFromManager, we'll early out in the check above because + // flag is not set yet! + if (shouldBeAdded) + { + m_IsAdded = true; + AddToManager (); + } + else + { + m_IsAdded = false; + RemoveFromManager (); + } +} + +void Behaviour::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad (awakeMode); + UpdateEnabledState (IsActive ()); +} + +void Behaviour::Deactivate (DeactivateOperation operation) +{ + UpdateEnabledState (false); + Super::Deactivate (operation); +} + +IMPLEMENT_OBJECT_SERIALIZE (Behaviour) +template<class TransferFunc> +void Behaviour::Transfer (TransferFunc& transfer) +{ + Super::Transfer (transfer); + transfer.Transfer (m_Enabled, "m_Enabled", kHideInEditorMask | kEditorDisplaysCheckBoxMask); + transfer.Align(); +} + + +// BEHAVIOURMANAGER +// -------------------------------------------------------------------------- + + +BaseBehaviourManager::~BaseBehaviourManager () +{ + for (Lists::iterator i=m_Lists.begin();i != m_Lists.end();i++) + { + Lists::mapped_type& listPair = i->second; + + Assert(listPair.first == NULL || listPair.first->empty()); + delete listPair.first; + + Assert(listPair.second == NULL || listPair.second->empty()); + delete listPair.second; + } + m_Lists.clear(); +} + +void BaseBehaviourManager::AddBehaviour (BehaviourListNode& p, int queueIndex) +{ + ASSERT_RUNNING_ON_MAIN_THREAD + + Lists::mapped_type& listPair = m_Lists[queueIndex]; + if (listPair.first == NULL) + { + Assert(listPair.second == NULL); + listPair.first = new BehaviourList(); + listPair.second = new BehaviourList(); + } + + listPair.second->push_back (p); +} + +void BaseBehaviourManager::RemoveBehaviour (BehaviourListNode& p) +{ + ASSERT_RUNNING_ON_MAIN_THREAD + p.RemoveFromList(); +} + +void BaseBehaviourManager::IntegrateLists() +{ + for (Lists::iterator i=m_Lists.begin();i!=m_Lists.end();i++) + { + Lists::mapped_type& listPair = (*i).second; + + listPair.first->append(*listPair.second); + Assert(listPair.second->empty()); + } +} + +template<typename T> +void BaseBehaviourManager::CommonUpdate () +{ + IntegrateLists(); + + for (Lists::iterator i=m_Lists.begin();i!=m_Lists.end();i++) + { + Lists::mapped_type& listPair = (*i).second; + + SafeIterator<BehaviourList> iterator (*listPair.first); + while (iterator.Next()) + { + Behaviour& behaviour = **iterator; + + #if SUPPORT_LOG_ORDER_TRACE + if (RunningReproduction()) + { + if (SUPPORT_LOG_ORDER_TRACE == 2) + { + LogString(Format("UpdateBehaviour %s (%s) [%d]", behaviour.GetName(), behaviour.GetClassName().c_str(), behaviour.GetInstanceID())); + } + else + { + LogString(Format("UpdateBehaviour %s (%s)", behaviour.GetName(), behaviour.GetClassName().c_str())); + } + } + #endif + + Assert(behaviour.IsAddedToManager ()); + + #if !UNITY_RELEASE + PPtr<Behaviour> behaviourPPtr (&behaviour); + #endif + T::UpdateBehaviour(behaviour); + + // Behaviour might get destroyed in the mean time, so we have to check if the object still exists first + #if !UNITY_RELEASE + AssertIf (behaviourPPtr.IsValid() && (behaviour.GetEnabled () && behaviour.IsActive ()) != behaviour.IsAddedToManager ()); + #endif + } + } +} + +class BehaviourManager : public BaseBehaviourManager +{ + public: + + virtual void Update() + { + BaseBehaviourManager::CommonUpdate<BehaviourManager>(); + } + + static inline void UpdateBehaviour(Behaviour& beh) + { + beh.Update(); + } +}; + +class FixedBehaviourManager : public BaseBehaviourManager { + public: + + + virtual void Update() + { + BaseBehaviourManager::CommonUpdate<FixedBehaviourManager>(); + } + + static inline void UpdateBehaviour(Behaviour& beh) + { + beh.FixedUpdate(); + } +}; + +class LateBehaviourManager : public BaseBehaviourManager +{ + public: + + virtual void Update() + { + BaseBehaviourManager::CommonUpdate<LateBehaviourManager>(); + } + + static inline void UpdateBehaviour(Behaviour& beh) + { + beh.LateUpdate(); + } +}; + +class UpdateManager : public BaseBehaviourManager { + public: + + + virtual void Update() + { + BaseBehaviourManager::CommonUpdate<BehaviourManager>(); + } + + static inline void UpdateBehaviour(Behaviour& beh) + { + beh.Update(); + } +}; + + + +#define GET_BEHAVIOUR_MANAGER(x) \ + x* s_instance##x; \ + BaseBehaviourManager& Get##x () { return reinterpret_cast<BaseBehaviourManager&> (*s_instance##x); } \ + void CreateInstance##x() { s_instance##x = new x; } \ + void ReleaseInstance##x() { delete s_instance##x; } + + +GET_BEHAVIOUR_MANAGER(BehaviourManager) +GET_BEHAVIOUR_MANAGER(FixedBehaviourManager) +GET_BEHAVIOUR_MANAGER(LateBehaviourManager) +GET_BEHAVIOUR_MANAGER(UpdateManager) + +void Behaviour::InitializeClass () +{ + CreateInstanceBehaviourManager(); + CreateInstanceFixedBehaviourManager(); + CreateInstanceLateBehaviourManager(); + CreateInstanceUpdateManager(); +} + +void Behaviour::CleanupClass () +{ + ReleaseInstanceBehaviourManager(); + ReleaseInstanceFixedBehaviourManager(); + ReleaseInstanceLateBehaviourManager(); + ReleaseInstanceUpdateManager(); + +} + +IMPLEMENT_CLASS_HAS_INIT (Behaviour) +INSTANTIATE_TEMPLATE_TRANSFER_EXPORTED(Behaviour) diff --git a/Runtime/GameCode/Behaviour.h b/Runtime/GameCode/Behaviour.h new file mode 100644 index 0000000..08c3d6a --- /dev/null +++ b/Runtime/GameCode/Behaviour.h @@ -0,0 +1,87 @@ +#ifndef BEHAVIOUR_H +#define BEHAVIOUR_H +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/BaseClasses/GameManager.h" +#include "Runtime/Utilities/LinkedList.h" +#include "Runtime/Modules/ExportModules.h" + +class EXPORT_COREMODULE Behaviour : public Unity::Component +{ + public: + + REGISTER_DERIVED_ABSTRACT_CLASS (Behaviour, Component) + DECLARE_OBJECT_SERIALIZE (Behaviour) + Behaviour (MemLabelId label, ObjectCreationMode mode) : Super(label, mode) { m_Enabled = true; m_IsAdded = false; } + + void AwakeFromLoad (AwakeFromLoadMode awakeMode); + + virtual void Update () {} + virtual void FixedUpdate () {} + virtual void LateUpdate () {} + + void Deactivate (DeactivateOperation operation); + /// Enable or disable updates of this behaviour + virtual void SetEnabled (bool enab); + bool GetEnabled () const { return m_Enabled; } + + bool IsAddedToManager () const { return m_IsAdded; } + + #if UNITY_EDITOR + void SetEnabledNoDirty (bool enab); + virtual bool ShouldDisplayEnabled () { return true; } + #endif + + + static void InitializeClass (); + static void CleanupClass (); + +// protected: + + + /// Override this to add the behaviour not to BehaviourManager but some other Manager + /// You should NOT call Super. + /// This is called when the behaviour has become enabled and its game object is disabled + /// You can rely on that AddToManager is only called once and will always be balanced out by RemoveFromManager before it is destroyed. + virtual void AddToManager () = 0; + virtual void RemoveFromManager () = 0; + + + private: + void UpdateEnabledState (bool active); + + ///@todo DO THIS PROPERLY. MORE SPACE EFFICIENT + UInt8 m_Enabled; + UInt8 m_IsAdded; +}; + +typedef ListNode<Behaviour> BehaviourListNode; + +class EXPORT_COREMODULE BaseBehaviourManager +{ + public: + virtual ~BaseBehaviourManager (); + + virtual void Update() = 0; + + void AddBehaviour (BehaviourListNode& node, int queue); + void RemoveBehaviour (BehaviourListNode& node); + + protected: + + template<typename T> void CommonUpdate (); + void IntegrateLists(); + + typedef List<BehaviourListNode> BehaviourList; + + // Need to use map instead of vector_map here, because it can change during iteration + // (Behaviours added in update calls). + typedef std::map<int, std::pair<BehaviourList*,BehaviourList*> > Lists; + Lists m_Lists; +}; + +EXPORT_COREMODULE BaseBehaviourManager& GetBehaviourManager (); +EXPORT_COREMODULE BaseBehaviourManager& GetFixedBehaviourManager (); +EXPORT_COREMODULE BaseBehaviourManager& GetLateBehaviourManager (); +EXPORT_COREMODULE BaseBehaviourManager& GetUpdateManager (); + +#endif diff --git a/Runtime/GameCode/CallDelayed.cpp b/Runtime/GameCode/CallDelayed.cpp new file mode 100644 index 0000000..ca41f6f --- /dev/null +++ b/Runtime/GameCode/CallDelayed.cpp @@ -0,0 +1,203 @@ +#include "UnityPrefix.h" +#include "CallDelayed.h" +#include "Runtime/Input/TimeManager.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/BaseClasses/ManagerContext.h" +#include "Runtime/Profiler/Profiler.h" + +DelayedCallManager::DelayedCallManager (MemLabelId label, ObjectCreationMode mode) + : Super(label, mode) +{ + m_NextIterator = m_CallObjects.end(); +} + +DelayedCallManager::~DelayedCallManager () { + ClearAll (); +} + +void CallDelayed (DelayedCall *func, PPtr<Object> o, float time, void* userData, float repeatRate, CleanupUserData* cleanup, int mode) +{ + DelayedCallManager::Callback callback; + if (time == 0.0F) + time = -1.0F; + + callback.time = time + GetCurTime (); + callback.frame = -1; + if (mode & DelayedCallManager::kWaitForNextFrame) + callback.frame = GetTimeManager().GetFrameCount() + 1; + callback.repeatRate = repeatRate; + callback.repeat = repeatRate != 0.0F; + AssertIf (callback.repeat && repeatRate < 0.00001F && (mode & DelayedCallManager::kWaitForNextFrame) == 0); + callback.userData = userData; + callback.call = func; + callback.cleanup = cleanup; + callback.object = o; + callback.mode = mode; + callback.timeStamp = GetDelayedCallManager ().m_TimeStamp; + GetDelayedCallManager ().m_CallObjects.insert (callback); +} + +void DelayedCallManager::CancelCallDelayed (PPtr<Object> o, DelayedCall* callback, ShouldCancelCall* shouldCancel, void* userData) +{ + Container::iterator next; + for (Container::iterator i=m_CallObjects.begin ();i != m_CallObjects.end ();i=next) + { + next = i; next++; + Callback &cb = const_cast<Callback&> (*i); + if (cb.object != o || callback != cb.call) + continue; + + if (shouldCancel == NULL || shouldCancel (cb.userData, userData)) + Remove (cb, i); + } +} + +void DelayedCallManager::CancelCallDelayed2 (PPtr<Object> o, DelayedCall* callback, DelayedCall* otherCallback) +{ + Container::iterator next; + for (Container::iterator i=m_CallObjects.begin ();i != m_CallObjects.end ();i=next) + { + next = i; next++; + Callback &cb = const_cast<Callback&> (*i); + if (cb.object == o && (callback == cb.call || cb.call == otherCallback)) + Remove (cb, i); + } +} + +void DelayedCallManager::CancelAllCallDelayed( PPtr<Object> o ) +{ + Container::iterator next; + for (Container::iterator i=m_CallObjects.begin ();i != m_CallObjects.end ();i=next) + { + next = i; next++; + Callback &cb = const_cast<Callback&> (*i); + if (cb.object != o) + continue; + + Remove (cb, i); + } +} + +bool DelayedCallManager::HasDelayedCall (PPtr<Object> o, DelayedCall* callback, ShouldCancelCall* shouldCancel, void* cancelUserData) +{ + for (Container::iterator i=m_CallObjects.begin ();i != m_CallObjects.end ();i++) + { + Callback &cb = const_cast<Callback&> (*i); + if (cb.object != o || callback != cb.call) + continue; + + if (shouldCancel == NULL || shouldCancel (cb.userData, cancelUserData)) + return true; + } + return false; +} + + +inline void DelayedCallManager::Remove (const Callback& cb, Container::iterator i) +{ + CleanupUserData* cleanup = cb.cleanup; + void* userData = cb.userData; + + if (m_NextIterator != i) + m_CallObjects.erase (i); + else + { + m_NextIterator++; + m_CallObjects.erase (i); + } + + if (cleanup && userData) + cleanup (userData); +} + +inline void DelayedCallManager::RemoveNoCleanup (const Callback& cb, Container::iterator i) +{ + if (m_NextIterator != i) + m_CallObjects.erase (i); + else + { + m_NextIterator++; + m_CallObjects.erase (i); + } +} + +//PROFILER_INFORMATION(gDelayedCallProfile, "Coroutines & Delayed Call", kProfilerOther) + +// Call all delayed functions when the time has come. +void DelayedCallManager::Update (int modeMask) +{ +// PROFILER_AUTO(gDelayedCallProfile, NULL) + + // For robustness we are using a iterator that is stored in the manager and when Remove is called + // it makes sure that the iterator is set to the next element so that we never end up with a stale ptr + float time = GetCurTime(); + int frame = GetTimeManager().GetFrameCount(); + Container::iterator i = m_CallObjects.begin (); + m_TimeStamp++; + + while (i != m_CallObjects.end () && i->time <= time) + { + m_NextIterator = i; m_NextIterator++; + + Callback &cb = const_cast<Callback&> (*i); + //- Make sure the mask matches. + //- We never execute delayed calls that are added during the DelayedCallManager::Update function + if ((cb.mode & modeMask) && cb.timeStamp != m_TimeStamp && cb.frame <= frame) + { + // avoid loading stuff from persistent manager in the middle of async loading + Object *o = Object::IDToPointer (cb.object.GetInstanceID ()); + + if (o) + { + void* userData = cb.userData; + DelayedCall* callback = cb.call; + // Cleanup and Removal is a bit hard + // Problems are + // - CancelCall might be called from inside the callback so the best way is to remove the callback structure before. + // - of course we still need the user data to be deallocated calling the callback not before + if (!cb.repeat) + { + // Remove callback structure from set + CleanupUserData* cleanup = cb.cleanup; + RemoveNoCleanup (cb, i); + //call callback + callback (o, userData); + //afterwards cleanup userdata + if (cleanup && userData) + cleanup (userData); + } + else + { + // Advance time and reinsert (We dont call the cleanup function because we are repeating the call. + // It can only be canceleld by CancelDelayCall) + cb.time += cb.repeatRate; + if (cb.mode & DelayedCallManager::kWaitForNextFrame) + cb.frame = GetTimeManager().GetFrameCount() + 1; + + m_CallObjects.insert (cb); + RemoveNoCleanup (cb, i); + // call callback + callback (o, userData); + } + } + else + Remove (cb, i); + } + + i = m_NextIterator; + } +} + +void DelayedCallManager::ClearAll () +{ + for (Container::iterator i=m_CallObjects.begin ();i != m_CallObjects.end ();i++) + { + Callback &cb = const_cast<Callback&> (*i); + if (cb.cleanup && cb.userData) + cb.cleanup (cb.userData); + } + m_CallObjects.clear (); +} + +IMPLEMENT_CLASS (DelayedCallManager) +GET_MANAGER (DelayedCallManager) diff --git a/Runtime/GameCode/CallDelayed.h b/Runtime/GameCode/CallDelayed.h new file mode 100644 index 0000000..e293789 --- /dev/null +++ b/Runtime/GameCode/CallDelayed.h @@ -0,0 +1,102 @@ +#ifndef CALLDELAYED_H +#define CALLDELAYED_H + +#include "Runtime/BaseClasses/GameManager.h" +#include "Runtime/Utilities/MemoryPool.h" +#include <set> + + + +/// Delayed call is called when the specified time has been reached. +/// o is always non-NULL +typedef void DelayedCall(Object* o, void* userData); +/// CleanupUserData should be used to prevent leaking userData +/// CleanupUserData is called whenever a registered callback is erased. +/// Doing cleanup inside DelayedCall doesnt work since the object might get destroyed or the scene unloaded before a pending function is called +/// CleanupUserData is called only when userData != NULL +typedef void CleanupUserData (void* userData); + +/** Class to call functions delayed in time + */ +class DelayedCallManager : public GlobalGameManager +{ + public: + REGISTER_DERIVED_CLASS (DelayedCallManager, GlobalGameManager) + + enum { + kRunFixedFrameRate = 1 << 0, + kRunDynamicFrameRate = 1 << 1, + kRunStartupFrame = 1 << 2, + kWaitForNextFrame = 1 << 3, + kAfterLoadingCompleted = 1 << 4, + kEndOfFrame = 1 << 5 + }; + + DelayedCallManager (MemLabelId label, ObjectCreationMode mode); + // virtual ~DelayedCallManager (); declared-by-macro + + /// Time is the time from now we need to exceed to call the function + /// repeatRate determines at which intervals the call should be repeated. If repeat rate is 0.0F it will not repeat. If it is -1.0 it will repeat but you must have kWaitForNextFrame enabled + friend void CallDelayed (DelayedCall *func, PPtr<Object> o, float time, void* userData, float repeatRate, CleanupUserData* cleanup, int mode); + friend void CallDelayedAfterLoading (DelayedCall *func, PPtr<Object> o, void* userData); + + /// Cancels all CallDelayed functions on object o if + /// - the callback is the same + /// - ShouldCancelCall returns true. (callBackUserData is the userdata stored with CallDelayed. cancelUserData is cancelUserData) + typedef bool ShouldCancelCall (void* callBackUserData, void* cancelUserdata); + void CancelCallDelayed (PPtr<Object> o, DelayedCall* callback, ShouldCancelCall* shouldCancel, void* cancelUserData); + void CancelCallDelayed2 (PPtr<Object> o, DelayedCall* callback, DelayedCall* otherCallback); + void CancelAllCallDelayed( PPtr<Object> o ); + + bool HasDelayedCall (PPtr<Object> o, DelayedCall* callback, ShouldCancelCall* shouldCancel, void* cancelUserData); + + virtual void Update (int mask); + + void ClearAll (); + + int GetNumCallObjects() const { return m_CallObjects.size(); } + + private: + + /// Struct used to store which functions to execute on which objects. + struct Callback { + float time; + int frame; + float repeatRate; + bool repeat; + void* userData; + DelayedCall *call; ///< The function call to execute. + CleanupUserData *cleanup; + PPtr<Object> object; ///< The object to pass to m_Call. + int mode; + int timeStamp; + + friend bool operator < (const Callback& lhs, const Callback& rhs) { return lhs.time < rhs.time; } + }; + +#if ENABLE_CUSTOM_ALLOCATORS_FOR_STDMAP + typedef std::multiset<Callback, std::less<Callback>, memory_pool<Callback> > Container; +#else + typedef std::multiset<Callback, std::less<Callback> > Container; +#endif + + void Remove (const Callback& cb, Container::iterator i); + void RemoveNoCleanup (const Callback& cb, Container::iterator i); + Container m_CallObjects; + Container::iterator m_NextIterator; + int m_TimeStamp; +}; + +/// Calls func in time seconds from now. When o is NULL when the call happens, only cleanup is called. +/// The function call is repeated at repeatRate if repeatRate is not 0.0F +/// cleanup is called whenever userData is non-NULL and a callback is removed (After calling of the delay function without repeat, object was destroyed or Shutdown of a scene) +void CallDelayed (DelayedCall *func, PPtr<Object> o, float time = -1.0F, void* userData = NULL, float repeatRate = 0.0F, CleanupUserData* cleanup = NULL, int mode = DelayedCallManager::kRunDynamicFrameRate | DelayedCallManager::kRunFixedFrameRate); +inline void CallDelayedAfterLoading (DelayedCall *func, PPtr<Object> o, void* userData = NULL) +{ + CallDelayed(func, o, -1.0F, userData, 0.0F, NULL, DelayedCallManager::kAfterLoadingCompleted); +} + + +DelayedCallManager& GetDelayedCallManager (); + +#endif diff --git a/Runtime/GameCode/CloneObject.cpp b/Runtime/GameCode/CloneObject.cpp new file mode 100644 index 0000000..8996055 --- /dev/null +++ b/Runtime/GameCode/CloneObject.cpp @@ -0,0 +1,303 @@ +#include "UnityPrefix.h" +#include "CloneObject.h" +#include "Runtime/Serialize/TransferUtility.h" +#include "Runtime/Serialize/TransferFunctions/RemapPPtrTransfer.h" +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/Serialize/TransferFunctions/StreamedBinaryWrite.h" +#include "Runtime/Serialize/TransferFunctions/StreamedBinaryRead.h" +#include "Runtime/Serialize/FileCache.h" +#include "Runtime/Misc/BuildSettings.h" +#include "Runtime/Mono/MonoBehaviour.h" +#include "Runtime/Serialize/AwakeFromLoadQueue.h" +#include "Runtime/Filters/Misc/Font.h" +#include "Runtime/Profiler/Profiler.h" +#include "Runtime/Allocator/MemoryMacros.h" +#include <map> + +using namespace std; +using namespace Unity; + +Object& ProduceClone (Object& object) +{ + Object* clone = Object::Produce (object.GetClassID ()); + +#if ENABLE_SCRIPTING + MonoBehaviour* cloneBehaviour = dynamic_pptr_cast<MonoBehaviour*> (clone); + if (cloneBehaviour) + { + MonoBehaviour& cloneSrc = static_cast<MonoBehaviour&> (object); + cloneBehaviour->SetScript(cloneSrc.GetScript()); + } +#endif + return *clone; +} + +void CollectAndProduceSingleObject (Object& singleObject, TempRemapTable* remappedPtrs) +{ + Object& clone = ProduceClone (singleObject); + + remappedPtrs->insert(make_pair(singleObject.GetInstanceID(), clone.GetInstanceID())); +} + +Transform* CollectAndProduceGameObjectHierarchy (GameObject& go, Transform* transform, TempRemapTable* remappedPtrs) +{ + GameObject* cloneGO = static_cast<GameObject*> (Object::Produce (ClassID(GameObject))); + remappedPtrs->insert(make_pair(go.GetInstanceID(), cloneGO->GetInstanceID())); + + GameObject::Container& goContainer = go.GetComponentContainerInternal(); + GameObject::Container& clonedContainer = cloneGO->GetComponentContainerInternal(); + + clonedContainer.resize(goContainer.size()); + for (int i=0;i<goContainer.size();i++) + { + Unity::Component& component = *goContainer[i].second; + Unity::Component& clone = static_cast<Unity::Component&> (ProduceClone(component)); + + clonedContainer[i].first = goContainer[i].first; + clonedContainer[i].second = &clone; + clone.SetGameObjectInternal(cloneGO); + + remappedPtrs->insert(make_pair(component.GetInstanceID(), clone.GetInstanceID())); + } + + if (transform) + { + Transform& cloneTransform = cloneGO->GetComponent(Transform); + + Transform::TransformComList& srcTransformArray = transform->GetChildrenInternal(); + Transform::TransformComList& dstTransformArray = cloneTransform.GetChildrenInternal(); + + dstTransformArray.resize_uninitialized(srcTransformArray.size(), false); + for (int i=0;i<srcTransformArray.size();i++) + { + Transform& curT = *srcTransformArray[i]; + GameObject& curGO = curT.GetGameObject(); + + Transform* curCloneTransform = CollectAndProduceGameObjectHierarchy(curGO, &curT, remappedPtrs); + curCloneTransform->GetParentPtrInternal() = &cloneTransform; + dstTransformArray[i] = curCloneTransform; + } + return &cloneTransform; + } + else + { + return NULL; + } +} + +inline GameObject* GetGameObjectPtr (Object& o) +{ + GameObject* go = dynamic_pptr_cast<GameObject*>(&o); + Unity::Component* component = dynamic_pptr_cast<Unity::Component*>(&o); + if (component != NULL && component->GetGameObjectPtr()) + go = component->GetGameObjectPtr(); + + return go; +} + +void CollectAndProduceClonedIsland (Object& o, TempRemapTable* remappedPtrs) +{ + AssertIf(!remappedPtrs->empty()); + + remappedPtrs->reserve(64); + + GameObject* go = GetGameObjectPtr(o); + if (go) + { + ///@TODO: It would be useful to lock object creation around a long instantiate call. + // Butwe have to be careful that we dont load anything during the object creation in order to avoid + // a deadlock: case 389317 + + // LockObjectCreation(); + + CollectAndProduceGameObjectHierarchy(*go, go->QueryComponent(Transform), remappedPtrs); + + // UnlockObjectCreation(); + } + else + CollectAndProduceSingleObject(o, remappedPtrs); + + remappedPtrs->sort(); +} + +void AwakeAndActivateClonedObjects (const TempRemapTable& ptrs) +{ + AwakeFromLoadQueue queue (kMemTempAlloc); + queue.Reserve(ptrs.size()); + + for (TempRemapTable::const_iterator i=ptrs.begin ();i!=ptrs.end ();++i) + { + Object& clone = *PPtr<Object> (i->second); + clone.SetHideFlags (0); + clone.SetDirty (); + + #if !UNITY_RELEASE + // we will clone that object - no need to call Reset as we will construct it fully + clone.HackSetResetWasCalled(); + #endif + + queue.Add(*PPtr<Object> (i->second)); + } + + queue.AwakeFromLoad ((AwakeFromLoadMode)(kDefaultAwakeFromLoad | kInstantiateOrCreateFromCodeAwakeFromLoad)); +} + +class RemapFunctorTempRemapTable : public GenerateIDFunctor +{ +public: + const TempRemapTable& remap; + + RemapFunctorTempRemapTable (const TempRemapTable& inRemap) : remap (inRemap) { } + + virtual SInt32 GenerateInstanceID (SInt32 oldInstanceID, TransferMetaFlags metaFlags = kNoTransferFlags) + { + AssertIf (metaFlags & kStrongPPtrMask); + + TempRemapTable::const_iterator found = remap.find (oldInstanceID); + // No Remap found -> set zero or dont touch instanceID + if (found == remap.end ()) + return oldInstanceID; + // Remap + else + return found->second; + } +}; + +static Object* CloneObjectImpl (Object* object, TempRemapTable& ptrs) +{ + // Since we will be creating a lot of objects here + // Just Lock the mutex all the time to avoid too many lock / unlock calls + CollectAndProduceClonedIsland (*object, &ptrs); + + TempRemapTable::iterator it; + +#if UNITY_FLASH + //specialcase for flash, as that needs to be able to assume linear memorylayout. + dynamic_array<UInt8> buffer(kMemTempAlloc); + MemoryCacheWriter cacheWriter (buffer); +#else + BlockMemoryCacheWriter cacheWriter (kMemTempAlloc); +#endif + + RemapFunctorTempRemapTable functor (ptrs); + RemapPPtrTransfer remapTransfer (kSerializeForPrefabSystem, true); + remapTransfer.SetGenerateIDFunctor (&functor); + + for (it=ptrs.begin ();it != ptrs.end ();it++) + { + Object& original = *PPtr<Object> (it->first); + + #if UNITY_EDITOR + original.WarnInstantiateDisallowed(); + #endif + + // Copy Data + Object& clone = *PPtr<Object> (it->second); + + StreamedBinaryWrite<false> writeStream; + CachedWriter& writeCache = writeStream.Init (kSerializeForPrefabSystem, BuildTargetSelection::NoTarget()); + writeCache.InitWrite (cacheWriter); + original.VirtualRedirectTransfer (writeStream); + writeCache.CompleteWriting(); + +#if UNITY_FLASH + MemoryCacheReader cacheReader (buffer); +#else + MemoryCacherReadBlocks cacheReader (cacheWriter.GetCacheBlocks (), cacheWriter.GetFileLength (), cacheWriter.GetCacheSize()); +#endif + StreamedBinaryRead<false> readStream; + CachedReader& readCache = readStream.Init (kSerializeForPrefabSystem); + + readCache.InitRead (cacheReader, 0, writeCache.GetPosition()); + clone.VirtualRedirectTransfer (readStream); + readCache.End(); + + if (!IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1)) + { + GameObject* clonedGameObject = dynamic_pptr_cast<GameObject*> (&clone); + if (clonedGameObject) + clonedGameObject->SetActiveBitInternal(true); + } + + #if UNITY_EDITOR + clone.CloneAdditionalEditorProperties(original); + #endif + + // Remap references + clone.VirtualRedirectTransfer (remapTransfer); + } + + + TempRemapTable::iterator found = ptrs.find (object->GetInstanceID ()); + AssertIf (found == ptrs.end ()); + object = PPtr<Object> (found->second); + + return object; +} + +PROFILER_INFORMATION(gInstantiateProfile, "Instantiate", kProfilerOther) + +Object& CloneObject (Object& inObject) +{ + PROFILER_AUTO(gInstantiateProfile, &inObject) + +#if !GAMERELEASE + // For context info see case 499663 + Font* font = dynamic_pptr_cast<Font*> (&inObject); + if (font && font->GetConvertCase() == Font::kDynamicFont) + ErrorString("Font Error: Cloning a dynamic font is not supported and may result in incorrect font rendering."); +#endif + + TempRemapTable ptrs; + Object* object = CloneObjectImpl(&inObject, ptrs); + + if (object) + object->SetName(Append (object->GetName(), "(Clone)").c_str()); + + AwakeAndActivateClonedObjects(ptrs); + + ANALYSIS_ASSUME(object); + return *object; +} + +Object& InstantiateObject (Object& inObject, const Vector3f& worldPos, const Quaternionf& worldRot, TempRemapTable& ptrs) +{ + PROFILER_AUTO(gInstantiateProfile, &inObject) + Object* object = CloneObjectImpl (&inObject, ptrs); + + // Get the transformComponent of the first object in the input objects + Transform *expTransform = NULL; + if (object) + { + Unity::Component* com = dynamic_pptr_cast<Unity::Component*> (object); + GameObject* go = dynamic_pptr_cast<GameObject*> (object); + if (com) + expTransform = com->QueryComponent (Transform); + else if (go) + expTransform = go->QueryComponent (Transform); + + object->SetName(Append (object->GetName(), "(Clone)").c_str()); + } + + // Set position + if (expTransform) + { + expTransform->SetPosition (worldPos); + expTransform->SetRotationSafe (worldRot); + } + + ANALYSIS_ASSUME(object); + return *object; +} + + +Object& InstantiateObject (Object& inObject, const Vector3f& worldPos, const Quaternionf& worldRot) +{ + TempRemapTable ptrs; + Object& obj = InstantiateObject (inObject, worldPos, worldRot, ptrs); + + AwakeAndActivateClonedObjects(ptrs); + + return obj; +} diff --git a/Runtime/GameCode/CloneObject.h b/Runtime/GameCode/CloneObject.h new file mode 100644 index 0000000..f6ee068 --- /dev/null +++ b/Runtime/GameCode/CloneObject.h @@ -0,0 +1,29 @@ +#ifndef CLONEOBJECT_H +#define CLONEOBJECT_H + +#include <vector> +#include "Runtime/Utilities/vector_map.h" +#include "Runtime/Allocator/MemoryMacros.h" + +class Object; +class Vector3f; +class Quaternionf; + +typedef std::pair<SInt32, SInt32> IntPair; +typedef vector_map<SInt32, SInt32, std::less<SInt32>, STL_ALLOCATOR(kMemTempAlloc, IntPair) > TempRemapTable; + +/// Clones a vector objects. Cloning will clone all islands of connected objects through pptrs. +/// This will not properly clone editorextensionimpl data and should only be used in gamemode +/// After all objects are loaded, all gameobjects will be Activated and if the original +/// was in an animation the clone it will be added to it +Object& CloneObject (Object& objects); + +/// The same as above, but set position and rotation of the first objects' transform component. +/// It also performs a delayed activation of all instantiated objects. +Object& InstantiateObject (Object& objects, const Vector3f& worldPos, const Quaternionf& worldRot); + +/// Use these in succession if you need to setup some data prior to activation/awake +Object& InstantiateObject (Object& inObject, const Vector3f& worldPos, const Quaternionf& worldRot, TempRemapTable& ptrs); +void AwakeAndActivateClonedObjects (const TempRemapTable& ptrs); + +#endif diff --git a/Runtime/GameCode/DestroyDelayed.cpp b/Runtime/GameCode/DestroyDelayed.cpp new file mode 100644 index 0000000..aeba252 --- /dev/null +++ b/Runtime/GameCode/DestroyDelayed.cpp @@ -0,0 +1,16 @@ +#include "UnityPrefix.h" +#include "DestroyDelayed.h" +#include "CallDelayed.h" +#include "Runtime/Misc/GameObjectUtility.h" + +void DelayedDestroyCallback (Object* o, void* userData); + +void DelayedDestroyCallback (Object* o, void* userData) +{ + DestroyObjectHighLevel (o); +} + +void DestroyObjectDelayed (Object* o, float time) +{ + CallDelayed (DelayedDestroyCallback, o, time); +} diff --git a/Runtime/GameCode/DestroyDelayed.h b/Runtime/GameCode/DestroyDelayed.h new file mode 100644 index 0000000..f017887 --- /dev/null +++ b/Runtime/GameCode/DestroyDelayed.h @@ -0,0 +1,10 @@ +#ifndef DESTROYDELAYED_H +#define DESTROYDELAYED_H + +class Object; + +/// Destroys object in time seconds +/// If time is not specified will be destroyed at the end of the frame +void DestroyObjectDelayed (Object* o, float time = -100.0F); + +#endif diff --git a/Runtime/GameCode/RootMotionData.h b/Runtime/GameCode/RootMotionData.h new file mode 100644 index 0000000..789a34a --- /dev/null +++ b/Runtime/GameCode/RootMotionData.h @@ -0,0 +1,9 @@ +#pragma once + +struct RootMotionData +{ + Vector3f deltaPosition; + Quaternionf targetRotation; + float gravityWeight; + bool didApply; +};
\ No newline at end of file |