summaryrefslogtreecommitdiff
path: root/Runtime/GameCode
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/GameCode')
-rw-r--r--Runtime/GameCode/Behaviour.cpp259
-rw-r--r--Runtime/GameCode/Behaviour.h87
-rw-r--r--Runtime/GameCode/CallDelayed.cpp203
-rw-r--r--Runtime/GameCode/CallDelayed.h102
-rw-r--r--Runtime/GameCode/CloneObject.cpp303
-rw-r--r--Runtime/GameCode/CloneObject.h29
-rw-r--r--Runtime/GameCode/DestroyDelayed.cpp16
-rw-r--r--Runtime/GameCode/DestroyDelayed.h10
-rw-r--r--Runtime/GameCode/RootMotionData.h9
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