TL; DR;

在以下情况下,不管如何SceneManager实际上如何找到任何SceneNode:

  • SceneManager::createSceneNode(...)方法明确声明所创建的节点不是图的一部分?¹和
  • SceneNode可以在不了解SceneManager的情况下独立创建自己的子级?²

  • ¹SM不会自动将其创建的场景节点转换为其他节点的子节点(例如,根节点);您必须为此在节点上手动调用addChild
    ²客户端只需写sceneManager->getRootSceneNode()->createChildSceneNode("Child");,SM就不会知道新 child 的存在

    背景

    我查看了OGRE3D中的源代码,并遇到了有关SceneManager类的以下文档(添加了>> <
    /** Retrieves a named SceneNode from the scene graph.
        @remarks
            If you chose to name a SceneNode as you created it, or if you
            happened to make a note of the generated name, you can look it
            up >>wherever it is in the scene graph<< using this method.
            @note Throws an exception if the named instance does not exist
    */
    virtual SceneNode* getSceneNode(const String& name) const;
    

    查看实现时,您会看到:
    SceneNode* SceneManager::getSceneNode(const String& name) const
    {
        SceneNodeList::const_iterator i = mSceneNodes.find(name);
    
        if (i == mSceneNodes.end())
        {
            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "SceneNode '" + name + "' not found.",
                "SceneManager::getSceneNode");
        }
    
        return i->second;
    }
    

    到现在为止还挺好。我们可以看到,SM在其自己的名为SceneNodeSceneNodeList中搜索您请求的mSceneNodes。我要弄清楚的部分是文档声称它可以“在场景图中的任何位置”找到一个节点。仅当使用SceneNode时,新的mSceneNodes才添加到SceneManager::createSceneNode(...)列表中。 SM的createSceneNode方法的文档说(添加了>> <
    /** Creates an instance of a SceneNode with a given name.
        @remarks
            Note that this >>does not add the SceneNode to the scene hierarchy<<.
            This method is for convenience, since it allows an instance to
            be created for which the SceneManager is responsible for
            allocating and releasing memory, which is convenient in complex
            scenes.
        @par
            To include the returned SceneNode in the scene, use the addChild
            method of the SceneNode which is to be it's parent.
        @par
            Note that this method takes a name parameter, which makes the node easier to
        retrieve directly again later.
    */
    virtual SceneNode* createSceneNode(const String& name);
    

    同时,如果您查看SceneNode类,它具有自己的createChild(const String& name, ...)方法,该方法显然不会将自己的子代添加到SceneManager的列表中,如下所示:
    SceneNode* SceneNode::createChildSceneNode(const Vector3& inTranslate,
        const Quaternion& inRotate)
    {
        return static_cast<SceneNode*>(this->createChild(inTranslate, inRotate));
    }
    //-----------------------------------------------------------------------
    SceneNode* SceneNode::createChildSceneNode(const String& name, const Vector3& inTranslate,
        const Quaternion& inRotate)
    {
        return static_cast<SceneNode*>(this->createChild(name, inTranslate, inRotate));
    }
    

    这意味着,如果客户端程序说node.createChildSceneNode(...);SceneManager将不会意识到新的子节点AFAIK的存在,因此它将永远无法找到它。

    我已经研究源代码已有一段时间了,但没有找到这个问题的答案。我查看了BspSceneManagerBspSceneNode只是看我是否还能发现其他东西,但是空了出来。

    为了完整性/引用,master分支中当前可用的最新提交是:
    commit 3b13abbdcce146b2813a6cc3bedf16d1d6084340
    Author: mkultra333 <unknown>
    Date:   Sun May 8 19:31:39 2016 +0800
    

    最佳答案

    难怪这部分会使您感到困惑,因为它是十多年前的OOP的一部分。有些喜欢它,有些讨厌它。

    但是,一旦知道了要查找的内容,答案就非常简单:Node::createChild的代码如下:

    Node* newNode = createChildImpl( sceneType );
    //...
    return newNode;
    

    它实际上将创建委托(delegate)给createChildImpl(“实现器”)。此函数是纯虚函数,因此SceneNode必须重载。

    当我们转到SceneNode::createChildImpl时,我们得到:
    Node* SceneNode::createChildImpl(const String& name)
    {
        return mCreator->_createSceneNode( name );
    }
    
    mCreatorSceneManager指针变量。因此,您可以执行以下操作:通过SceneManager创建SceneNode时,会通知createChildSceneNode

    但是请注意,一种实现方式(例如BspSceneNode中的BspSceneManager)可能会使createChildImpl重载而不会通知SceneManager;或者他们可能会。

    不过,在SceneManager::_createSceneNode中放置一个断点并 check out 调用栈将为您省去很多麻烦。

    08-28 16:25