Unity引擎源码解析(伪) - 5 GameObject

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 创建完成后会将其添加进 GameObjectManagerTransform 创建完成后会将其添加进 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 ()

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值