美文网首页
Dragon Engine:事件系统

Dragon Engine:事件系统

作者: Dragon_boy | 来源:发表于2020-07-06 23:42 被阅读0次

本文同时发布在我的个人博客上:https://dragon_boy.gitee.io

事件系统的范围很广,这里我暂时只包含:键盘输入、鼠标输入、窗口事件、应用程序事件。

首先构建基本的事件类。

Event.h

namespace Dragon
{
    enum class EventType
    {
        None = 0,
        WindowClose, WindowResize, WindowFocus, WindowLostFocus, WindowMoved,
        AppTick, AppUpdate, AppRender,
        KeyPressed, KeyReleased, KeyTyped,
        MouseButtonPressed, MouseButtonReleased, MouseMoved, MouseScrolled
    };

    enum EventCategory
    {
        None = 0,
        EventCategoryApplication    = BIT(0),
        EventCategoryInput          = BIT(1),
        EventCategoryKeyboard       = BIT(2),
        EventCategoryMouse          = BIT(3),
        EventCategoryMouseButton    = BIT(4),
    };

#define EVENT_CLASS_TYPE(type) static EventType GetStaticType() {return EventType::##type;}\
                                virtual EventType GetEventType() const override {return GetStaticType();}\
                                virtual const char* GetName() const override {return #type;}

#define EVENT_CLASS_CATEGORY(category) virtual int GetCategoryFlags() const override { return category;}

我先定义事件的类型枚举类,接着定义事件种类枚举。这里使用的BIT(x)是一个宏定义:

#define BIT(x) (1 << x)

接着将获取事件类型和名字、种类的方法进行宏定义,##表示将前后进行连接,#表示将后方的变量转化为字符串。

上方代码的由三个重写虚方法,它们在下面事件类中定义:

    class Event
    {
    public:
        bool Handled = false;
        virtual EventType GetEventType() const = 0;
        virtual const char* GetName() const = 0;
        virtual int GetCategoryFlags() const = 0;
        virtual std::string ToString() const { return GetName(); }

        bool IsInCategory(EventCategory category)
        {
            return GetCategoryFlags() & category;
        }
    
    };

接下来的类比较重要,它用来派送事件:

class EventDispatcher
    {
        template<typename T>
        using EventFn = std::function<bool(T&)>;
    public:
        EventDispatcher(Event& event)
            : m_Event(event)
        {

        }

        template<typename T>
        bool Dispatch(EventFn<T> func)
        {
            if (m_Event.GetEventType() == T::GetStaticType())
            {
                m_Event.Handled = func(static_cast<T&>(m_Event));
                return true;
            }
            return false;
        }
    private:
        Event& m_Event;
    };

std::function<>是对C++中各种可调用实体(普通函数、Lambda表达式、函数指针、以及其它函数对象等)的封装,说白了就是可以简单粗暴的调用函数。

Dispatch方法将一个事件函数作为参数,检测事件的类型后,我们使用这个事件函数,并将调用结果存储在Event对象中。

有了Event基类后,我们就可以编写其它事件类,这里以键盘输入事件为例:
KeyEvents.h

namespace Dragon
{
    class  KeyEvent : public Event
    {
    public:
        inline int GetKeyCode() const { return m_KeyCode; }
        EVENT_CLASS_CATEGORY(EventCategoryKeyboard | EventCategoryInput)
    protected:
        KeyEvent(int keycode)
            : m_KeyCode(keycode){}
        int m_KeyCode;
    };

    class KeyPressedEvent : public KeyEvent
    {
    public:
        KeyPressedEvent(int keycode, int repeatCount)
            : KeyEvent(keycode), m_RepeatCount(repeatCount){}

        inline int GetRepeatCount() const { return m_RepeatCount; }

        std::string ToString() const override
        {
            std::stringstream ss;
            ss << "KeyPressedEvent: " << m_KeyCode << " (" << m_RepeatCount << " repeats)";
            return ss.str();
        }

        EVENT_CLASS_TYPE(KeyPressed)
    private:
        int m_RepeatCount;

    };

    class KeyReleasedEvent : public KeyEvent
    {
    public:
        KeyReleasedEvent(int keycode)
            : KeyEvent(keycode){}

        std::string ToString() const override
        {
            std::stringstream ss;
            ss << "KeyReleaseEvent: " << m_KeyCode;
            return ss.str();
        }

        EVENT_CLASS_TYPE(KeyReleased)
    };

    class KeyTypedEvent : public KeyEvent
    {
    public:
        KeyTypedEvent(int keycode)
            : KeyEvent(keycode) {}

        std::string ToString() const override
        {
            std::stringstream ss;
            ss << " KeyTypedEvent: " << m_KeyCode;
            return ss.str();
        }

        EVENT_CLASS_TYPE(KeyTyped)

    };
}

首先定义一个键盘输入基类,包含获取键值和事件种类方法,其它键盘输入类的区别分别是,键盘按下事件使用一个按下次数属性来判断持续按键,键盘松开事件和键盘输入事件没有多大的区别。

鼠标事件和应用程序事件没有多大的区别,这里不演示。

那么如何使用这些事件类?最重要的就是我们在Event.h中定义的EventDispatcher类。我们在需要进行事件调用的方法中,大概会这么使用,如OnEvent()

//创建dispatcher
void OnEvent(Event& e)
{
    EventDispatcheer dispatcher(e);
    dispacther<Dispatcher>(SomeEventType)(DG_BIND_EVENT_FN(OnSomeEventFunction));
}

这里的DG_BIND_EVENT_FN是一个使用std::bind()方法的宏定义:

#define DG_BIND_EVENT_FN(fn) std::bind(&fn, this, std::placeholders::_1)

std::bind()将参数传递给位于第一个参数的函数引用或指针,第二个参数表明传入函数的第一个参数,这里的this表明当前的对象,第三个参数为std::placeholders::_1表明这个参数由fn的第二个参数由fn传入的第一个参数决定。

比如我们定义一个具体的事件函数:

bool OnSomeEventFunction(SomeEventType& e)
{
    //someProcessing
}

那么上面的std::placeholders::_1对应的就是e,对应方法的第二个参数。方法的第一个参数默认是当前的类对象this。

下一节介绍层的概念。

项目github地址:https://github.com/Dragon-Baby/Dragon

相关文章

  • Dragon Engine:事件系统

    本文同时发布在我的个人博客上:https://dragon_boy.gitee.io 事件系统的范围很广,这里我暂...

  • Dragon Engine:材质系统

    这一节介绍材质系统,主要是进行着色器类的抽象: 这里设计了两个类,一个着色器类,一个着色器库类。着色器类的成员函数...

  • Dragon Engine:Debug

    本文同时发布在我的个人博客上:https://dragon_boy.gitee.io Debug,或者说日志系统,...

  • Dragon Engine:窗口

    本节介绍窗口的实现。这里使用开源库GLFW,之前也介绍过,只不过这里的主要任务就是将GLFW的API抽象出来,方便...

  • Dragon Engine:GUI

    这一节介绍GUI的实现。我们这里使用开源库ImGui。 首先我们将GUI分离为一个单独的层,创建ImGuiLaye...

  • Dragon Engine:层

    本文同时发布在我的个人博客上:https://dragon_boy.gitee.io/ 层的概念在许多软件中都存在...

  • Dragon Engine:编译项目

    编写大项目的时候,大部分文件可以复用,但构建项目的软件,使用的平台,可使用的外部资源因各种各不相同,所以大部分时候...

  • Dragon Engine:纹理模块

    这一节介绍纹理模块的实现。首先是纹理类的抽象: 纹理基类的构造不用多说,这里衍生出了两个纹理类,2D和立方体贴图。...

  • Dragon Engine:帧缓冲

    之前相关的图形学文章介绍过,帧缓冲非常有用,可以用来实现Post-Processing效果,以及阴影效果等,在进行...

  • Dragon Engine:基础渲染架构

    本节的目的是在已有的基础上构建出一个基础渲染架构。主要任务就是抽象。 一个简单的渲染架构包含很多模块,我之前发了许...

网友评论

      本文标题:Dragon Engine:事件系统

      本文链接:https://www.haomeiwen.com/subject/ddjnqktx.html