GameObject 是Unity场景中所有实体的基类,也是十分重要的类型。每个GameObj又有一个必须的组件 Transform。
Transform 类提供多种方式来通过脚本处理游戏对象的位置、旋转和缩放,以及与父和子游戏对象的层级关系。C++中 Transform 直接继承自 Unity::Component。
引擎底层每个场景都对应一个C++类 UnityScene,其中有成员变量 RootTransformList m_Roots;
typedef List<ListNode<Transform> > RootTransformList; //类型定义
//加入场景
void UnityScene::AddRootToScene(UnityScene& scene, Transform& t)
{
SceneRootNode& rootNode = t.m_SceneRootNode; //找到当前t的场景包装类型
if (rootNode.IsInScene())
return;
DebugAssert(t.GetParent() == NULL);
...
scene.m_Roots.push_back(rootNode.m_ListNode); //加入当前场景
rootNode.m_UnityScene = &scene; //保存一下场景
}
//场景包装类型
struct SceneRootNode
{
ListNode<Transform> m_ListNode;
UnityScene* m_UnityScene;
#if UNITY_EDITOR
ListNode<Transform> m_SortedListNode;
#endif
SceneRootNode(Transform* t)
: m_ListNode(t)
, m_UnityScene(NULL)
#if UNITY_EDITOR
, m_SortedListNode(t)
#endif
{}
inline bool IsInScene() const
{
...
return m_ListNode.IsInList();//链表节点如果被加入了链表,说明它之前肯定加入了 UnityScene
}
};
m_Roots 根节点是一个 Transform 的链表,每个Transform创建的时候都会通过函数 AddRootToScen() 添加进场景,方便在场景里统一管理。
API分析:new GameObject() 执行细节:
先看mono绑定代码:
using System;
...
namespace UnityEngine
{
[ExcludeFromPreset]
[UsedByNativeCode]
[NativeHeader("Runtime/Export/Scripting/GameObject.bindings.h")]
public sealed partial class GameObject : Object
{
public GameObject(string name)
{
Internal_CreateGameObject(this, name);
}
public GameObject()
{
Internal_CreateGameObject(this, null);
}
public GameObject(string name, params Type[] components)
{
Internal_CreateGameObject(this, name);
foreach (Type t in components)
AddComponent(t);
}
[FreeFunction(Name = "GameObjectBindings::Internal_CreateGameObject")]
static extern void Internal_CreateGameObject([Writable] GameObject self, string name);
}
}
最终调用到 Internal_CreateGameObject() 函数中。
inline void Internal_CreateGameObject(ScriptingObjectOfType<GameObject> self, const char* name)
{
GameObject* go = MonoCreateGameObject(name);
Scripting::ConnectScriptingWrapperToObject(self.GetScriptingObject(), go);
}
GameObject* MonoCreateGameObject(const char* name)
{
core::string cname;
if (!name)
{
cname = "New Game Object";
}
else
{
cname = name;
}
return &CreateGameObject(cname, "Transform", NULL);
}
GameObject& CreateGameObject(const core::string& name, const char* componentName, ...)
{
// Create game object with name
GameObject &go = *NEW_OBJECT(GameObject);
ActivateGameObject(go, name);
// Add components with class names. 其中的组件就是 Transform
va_list ap;
va_start(ap, componentName);
AddComponentsFromVAList(go, componentName, ap);
va_end(ap);
return go;
}
void ActivateGameObject(GameObject& go, const core::string& name)
{
go.Reset();
go.SetName(name.c_str());
go.AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad);
go.Activate();
}
new 一个 GameObject 所做的事情其实整体看起来很简单,new一个类型,再添加一个默认的 Transform 而已。AwakeFromLoad 这个函数很熟悉了,所有的组件创建完成之后都会触发,GameObject 也不例外。
//GameObject
void GameObject::AwakeFromLoad(AwakeFromLoadMode awakeMode)
{
...
SetSupportedMessagesDirty();
UpdateActiveGONode();
if (awakeMode != kPersistentManagerAwakeFromLoadMode && s_SetGONameCallback != nullptr)
s_SetGONameCallback(this);
...
}
void GameObject::UpdateActiveGONode()
{
m_ActiveGONode.RemoveFromList();
bool canAddToList = IsActive();
...
//将新创建的 GameObject 加入一个统一的管理器,方便后面各种查询
if (canAddToList)
{
if (m_Tag != 0)
GetGameObjectManager().GetTaggedNodes(m_Tag).push_back(m_ActiveGONode);
else
GetGameObjectManager().m_ActiveNodes.push_back(m_ActiveGONode);
}
}
//Transform
void Transform::AwakeFromLoad(AwakeFromLoadMode awakeMode)
{
...
//如果它已经在场景中,就不将其移动了。否则,移动到当前激活的场景中。
if (!m_SceneRootNode.IsInScene())
{
UnityScene* scene = GetSceneManager().GetSceneIntegratingOnMainThread();
if (scene == NULL)
scene = GetSceneManager().GetActiveScene();
if (IsSceneRoot() && scene)
{
UnityScene::AddRootToScene(*scene, *this);
if (GetGameObjectPtr())
UnityScene::OnGameObjectChangedScene(GetGameObject(), scene, NULL);
}
}
...
}
总结:GameObject 创建完成后会将其添加进 GameObjectManager。Transform 创建完成后会将其添加进 UnityScene。
API分析:Object.Instantiate() 执行细节:
unity创建一个游戏物体最常用的并不是 new GameObject 而是 Object.Instantiate 函数。
先看mono绑定代码:
using System;
...
namespace UnityEngine
{
[StructLayout(LayoutKind.Sequential)]
[RequiredByNativeCode(GenerateProxy = true)]
[NativeHeader("Runtime/Export/Scripting/UnityEngineObject.bindings.h")]
[NativeHeader("Runtime/GameCode/CloneObject.h")]
[NativeHeader("Runtime/SceneManager/SceneManager.h")]
public partial class Object
{
[TypeInferenceRule(TypeInferenceRules.TypeOfFirstArgument)]
public static Object Instantiate(Object original)
{
CheckNullArgument(original, objectIsNullMessage);
var obj = Internal_CloneSingle(original);
if (obj == null)
throw new UnityException(cloneDestroyedMessage);
return obj;
}
[TypeInferenceRule(TypeInferenceRules.TypeOfFirstArgument)]
public static Object Instantiate(Object original, Transform parent)
{
return Instantiate(original, parent, false);
}
[TypeInferenceRule(TypeInferenceRules.TypeOfFirstArgument)]
public static Object Instantiate(Object original, Transform parent, bool instantiateInWorldSpace)
{
if (parent == null)
return Instantiate(original);
CheckNullArgument(original, objectIsNullMessage);
var obj = Internal_CloneSingleWithParent(original, parent, instantiateInWorldSpace);
if (obj == null)
throw new UnityException(cloneDestroyedMessage);
return obj;
}
[TypeInferenceRule(TypeInferenceRules.TypeOfFirstArgument)]
public static Object Instantiate(Object original, Vector3 position, Quaternion rotation)
{
CheckNullArgument(original, objectIsNullMessage);
if (original is ScriptableObject)
throw new ArgumentException("Cannot instantiate a ScriptableObject with a position and rotation");
var obj = Internal_InstantiateSingle(original, position, rotation);
if (obj == null)
throw new UnityException(cloneDestroyedMessage);
return obj;
}
[TypeInferenceRule(TypeInferenceRules.TypeOfFirstArgument)]
public static Object Instantiate(Object original, Vector3 position, Quaternion rotation, Transform parent)
{
if (parent == null)
return Instantiate(original, position, rotation);
CheckNullArgument(original, objectIsNullMessage);
var obj = Internal_InstantiateSingleWithParent(original, parent, position, rotation);
if (obj == null)
throw new UnityException(cloneDestroyedMessage);
return obj;
}
[FreeFunction("CloneObject")]
extern static Object Internal_CloneSingle(Object data);
[FreeFunction("CloneObject")]
extern static Object Internal_CloneSingleWithParent(Object data, Transform parent, bool worldPositionStays);
[FreeFunction("InstantiateObject")]
extern static Object Internal_InstantiateSingle(Object data, Vector3 pos, Quaternion rot);
[FreeFunction("InstantiateObject")]
extern static Object Internal_InstantiateSingleWithParent(Object data, Transform parent, Vector3 pos, Quaternion rot);
}
}
可以看到最终会调到C++中的2个函数 CloneObject() 和 InstantiateObject()
Object* CloneObject(Object& inObject)
{
PROFILER_AUTO(gInstantiateProfile, &inObject);
TempRemapTable ptrs;
Object* object = CloneObjectImpl(&inObject, NULL, ptrs); //111
if (object)
object->SetName(Append(object->GetName(), "(Clone)").c_str()); //设置一个默认名,强行加一个前缀 “(Clone) ”
AwakeAndActivateClonedObjects(&object, ptrs); //222
return object;
}
Object* CloneObject(Object& inObject, Transform& newFather, bool worldPositionStays)
{
...
TempRemapTable ptrs;
Object* object = CloneObjectImpl(&inObject, &newFather, ptrs); //111
if (object)
{
object->SetName(Append(object->GetName(), "(Clone)").c_str());
}
//当在世界空间中定位新对象时,略
if (worldPositionStays)
CopyWorldSpaceTransformDataWithoutNotifications(&inObject, object);
AwakeAndActivateClonedObjects(&object, ptrs); //222
//给父节点发送消息
newFather.SetDirty();
GetTransformHierarchyChangeDispatch().DispatchSelfAndParents(newFather.GetTransformAccess(), TransformHierarchyChangeDispatch::kInterestedInChildHierarchy);
newFather.SendMessage(kTransformChildrenChanged);
return object;
}
Object* InstantiateObject(Object& inObject, const Vector3f& worldPos, const Quaternionf& worldRot)
{
PROFILER_AUTO(gInstantiateProfile, &inObject);
TempRemapTable ptrs;
Object* obj = InstantiateObject(inObject, NULL, worldPos, worldRot, ptrs);
AwakeAndActivateClonedObjects(&obj, ptrs); //222
return obj;
}
Object* InstantiateObject(Object& inObject, Transform& newFather, const Vector3f& worldPos, const Quaternionf& worldRot)
{
...
TempRemapTable ptrs;
Object* obj = InstantiateObject(inObject, &newFather, worldPos, worldRot, ptrs);
AwakeAndActivateClonedObjects(&obj, ptrs); //222
//给父节点发送消息
newFather.SetDirty();
GetTransformHierarchyChangeDispatch().DispatchSelfAndParents(newFather.GetTransformAccess(), TransformHierarchyChangeDispatch::kInterestedInChildHierarchy);
newFather.SendMessage(kTransformChildrenChanged);
return obj;
}
Object* InstantiateObject(Object& inObject, Transform* newFather, const Vector3f& worldPos, const Quaternionf& worldRot, TempRemapTable& ptrs)
{
Object* object = CloneObjectImpl(&inObject, newFather, ptrs); //111
Transform *expTransform = NULL;
if (object)
{
expTransform = GetTransformFromComponentOrGameObject(object);
object->SetName(Append(object->GetName(), "(Clone)").c_str());
}
//设置变换
if (expTransform)
{
bool isRectTransform = expTransform->GetType() == TypeOf<UI::RectTransform>();
if (isRectTransform)
expTransform->AwakeFromLoad(kDefaultAwakeFromLoad);
expTransform->SetPosition(worldPos);
expTransform->SetRotation(worldRot);
if (isRectTransform)
static_cast<UI::RectTransform*>(expTransform)->UpdateIfTransformDispatchIsDirty();
}
return object;
}
CloneObject() 和 InstantiateObject() 这两个函数处理逻辑只有小部分差异,他们的核心逻辑函数都是分为:克隆函数CloneObjectImpl() 和 激活函数AwakeAndActivateClonedObjects()
static Object* CloneObjectImpl(Object* object, Transform* newFather, TempRemapTable& ptrs)
{
CollectAndProduceClonedIsland(*object, newFather, ptrs); //克隆自己和所有子节点的 Gameobject、Component
PROFILER_AUTO(gInstantiateProfileCopy, object);
TempRemapTable::iterator it;
...
//这里要将
for (it = ptrs.begin(); it != ptrs.end(); it++)
{
Object& original = *PPtr<Object>(it->first); //被克隆的对象,数据源
if (original.GetType() == TypeOf<Transform>() || original.GetType() == TypeOf<GameObject>())
continue;
#if UNITY_EDITOR
original.WarnInstantiateDisallowed();
#endif
// Copy Data
Object& clone = *PPtr<Object>(it->second); //克隆创建的对象,目标
//设置写入流,将 original 序列化数据取出
StreamedBinaryWrite writeStream;
CachedWriter& writeCache = writeStream.Init(kSerializeForPrefabSystem, BuildTargetSelection::NoTarget());
writeCache.InitWrite(cacheWriter);
original.VirtualRedirectTransfer(writeStream);
writeCache.CompleteWriting();
MemoryCacherReadBlocks cacheReader(cacheWriter.GetCacheBlocks(), cacheWriter.GetFileLength(), cacheWriter.GetCacheSize());
//设置读取流,将取出的数据写入 clone
StreamedBinaryRead readStream;
CachedReader& readCache = readStream.Init(kSerializeForPrefabSystem | kDontCreateMonoBehaviourScriptWrapper | kIsCloningObject, clone.GetMemoryLabel());
readCache.InitRead(cacheReader, 0, writeCache.GetPosition().Cast<size_t>());
clone.VirtualRedirectTransfer(readStream);
readCache.End();
#if UNITY_EDITOR
clone.CloneAdditionalEditorProperties(original);
#endif
clone.VirtualRedirectTransfer(remapTransfer);
}
//取出克隆出来的主GameObject
TempRemapTable::iterator found = ptrs.find(object->GetInstanceID());
Assert(found != ptrs.end());
object = PPtr<Object>(found->second);
return object;
}
void CollectAndProduceClonedIsland(Object& o, Transform* newFather, TempRemapTable& remappedPtrs)
{
PROFILER_AUTO(gInstantiateProfileProduce, &o)
remappedPtrs.reserve(64);
GameObject* go = GetGameObjectPtr(o);
LockObjectCreation();
if (go) //如果当前被克隆的对象是 GameObject,就克隆包含子节点和一大堆组件
CollectAndProduceGameObjectHierarchy(go->GetComponent<Transform>(), newFather, remappedPtrs);
else //只克隆自身
CollectAndProduceSingleObject(o, remappedPtrs);
UnlockObjectCreation();
remappedPtrs.sort();
}
对象在克隆时候是需要判断是不是 GameObject 特殊处理,还会在 Hierarchy 查找它的层级关系,并一起克隆。
TempRemapTable ptrs; 该变量会从一开始就传递进去,用来记录克隆的 源对象 和 生成对象 的ID对。类型的定义是
#define UNITY_VECTOR_MAP(label, key, value) vector_map<key, value, std::less<key>, stl_allocator<std::pair<key, value>, label##Id> >
typedef UNITY_VECTOR_MAP (kMemTempAlloc, InstanceID, InstanceID) TempRemapTable;
看名称就知道 TempRemapTable 是键值对,即 unity 自己写的一个字典。跟C++标准库的容器 std::set 相同。
再看 AwakeAndActivateClonedObjects() 具体实现:
void AwakeAndActivateClonedObjects(Object** inOutInstantiatedObject, const TempRemapTable& ptrs)
{
PROFILER_AUTO(gInstantiateProfileAwake);
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(Object::kHideFlagsNone);
clone.SetDirty();
#if !UNITY_RELEASE
clone.SetResetCalledInternal();
#endif
queue.Add(*PPtr<Object>(i->second));
}
//将所有克隆的对象一起触发 AwakeFromLoad 函数
queue.AwakeFromLoad((AwakeFromLoadMode)(kDefaultAwakeFromLoad | kInstantiateOrCreateFromCodeAwakeFromLoad));
...
}
代码其实非常简单,就是将 TempRemapTable& ptrs 中之前克隆出来的所有的对象(GameObject、Component) 收集到一起,一并去调用他们各自的 AwakeFromLoad ()