跳转至

显然我们不可能每次都在cpp代码中控制引擎中的某些参数,我们需要做一个像C#、Lua那样的脚本语言去连接引擎里的内容。这里还是先支持c++,后续考虑扩展

QA

  • 为什么需要添加cpp脚本组件

    • 引擎开发人员需要给引擎某个实体添加脚本功能,从而实现测试时候能运行想要的脚本功能。

      比如:像Unity那样在Scene界面,摄像机可以自由移动,这是内置脚本功能,肯定是使用原生cpp语言来实现

    • 使用这引擎的游戏开发人员有的人不喜欢用c#,想用cpp来实现脚本编写。

    ScriptableEntity

    • 提供getcomponent方法
    • 提供友元
    • 提供组件的实体引用
#pragma once
#include "Entity.h"

namespace CryDust {
    class ScriptableEntity
    {
    public:
        template<typename T>
        T& GetComponent()
        {
            return m_Entity.GetComponent<T>();
        }
    private:
        Entity m_Entity;
        friend class Scene;
    };
}

component新增

好像不是很ecs啊,要new和delete。 不过还是有的,指针变量啥的

构成

  • 五个生命周期的函数指针
        struct NativeScriptComponent
        {
            ScriptableEntity* Instance = nullptr;
            std::function<void()> InstantiateFunction;
            std::function<void()> DestroyInstanceFunction;
            std::function<void(ScriptableEntity*)> OnCreateFunction;
            std::function<void(ScriptableEntity*)> OnDestroyFunction;
            std::function<void(ScriptableEntity*, Timestep)> OnUpdateFunction;
            template<typename T>
            void Bind()
            {
                InstantiateFunction = [&]() { Instance = new T(); };
                DestroyInstanceFunction = [&]() { delete (T*)Instance; Instance = nullptr; };
                OnCreateFunction = [](ScriptableEntity* instance) { ((T*)instance)->OnCreate(); };
                OnDestroyFunction = [](ScriptableEntity* instance) { ((T*)instance)->OnDestroy(); };
                OnUpdateFunction = [](ScriptableEntity* instance, Timestep ts) { ((T*)instance)->OnUpdate(ts); };
            }
        };
    

Scene里update更新

  • 创建时调用一下创建方法
  • 更新的时候则循环调用
    // Update scripts
            {
                m_Registry.view<NativeScriptComponent>().each([=](auto entity, auto& nsc)
                    {
                        if (!nsc.Instance)
                        {
                            nsc.InstantiateFunction();
                            nsc.Instance->m_Entity = Entity{ entity, this };
                            if (nsc.OnCreateFunction)
                                nsc.OnCreateFunction(nsc.Instance);
                        }
                        if (nsc.OnUpdateFunction)
                            nsc.OnUpdateFunction(nsc.Instance, ts);
                    });
            }
    

本地脚本(Editor的Onattach方法中实现)

  •     //本地腳本
            class CameraController : public ScriptableEntity
            {
            public:
                void OnCreate()
                {
                }
                void OnDestroy()
                {
                }
                void OnUpdate(Timestep ts)
                {
                    auto& transform = GetComponent<TransformComponent>().Transform;
                    float speed = 5.0f;
                    if (Input::IsKeyPressed(KeyCode::A))
                        transform[3][0] -= speed * ts;
                    if (Input::IsKeyPressed(KeyCode::D))
                        transform[3][0] += speed * ts;
                    if (Input::IsKeyPressed(KeyCode::W))
                        transform[3][1] += speed * ts;
                    if (Input::IsKeyPressed(KeyCode::S))
                        transform[3][1] -= speed * ts;
                }
            };
            m_CameraEntity.AddComponent<NativeScriptComponent>().Bind<CameraController>();
    

Lambda

我们在外面绑定脚本语言,

m_CameraEntity.AddComponent<NativeScriptComponent>().Bind<CameraController>();
然后bind函数内部通过lambda转换模版参数-> c++ OnCreateFunction = [](ScriptableEntity* instance) { ((T*)instance)->OnCreate(); }; OnDestroyFunction = [](ScriptableEntity* instance) { ((T*)instance)->OnDestroy(); }; OnUpdateFunction = [](ScriptableEntity* instance, Timestep ts) { ((T*)instance)->OnUpdate(ts); };