首页 > 解决方案 > 创建 C++ 事件系统

问题描述

我最近决定开始制作游戏引擎。我知道大多数人没有完成他们的,如果我说实话,我可能也不会。我这样做是因为我厌倦了在谷歌上搜索“酷 C++ 项目”并做每个用户给出的 3 个答案(那是地址簿或类似的东西、井字游戏和成绩单生成器或类似的东西那)。我喜欢编程,但不幸的是我没有真正的用处。我将使用它的所有东西都可以通过另一种方式更快更容易地完成,或者已经存在解决方案。然而,为了学习比 C++ 基本水平更多的东西,并做一些能教给我真正深入的东西,我撤销了这个政策并决定开始一个游戏引擎,因为这是我一直感兴趣的东西在。我

现在进入实际问题:

我有一个可以工作的实体组件系统(耶),虽然它还处于早期阶段,而且功能还不是超级棒,但我对此感到非常自豪。老实说,我从来没有想过我会走到这一步。我目前正在使用事件总线系统。现在,我真的很喜欢 LY 的 EBus 系统。它非常易于使用且非常直接,但在编程新手的眼中,它是黑魔法和巫术。我不知道他们是如何做某些事情的,所以希望你能做到!

制作 EBus 是这样的:

 #include <EBusThingy.h>

 class NewEbusDealio
      : public EbusThingy
 {
 public:
 //Normally there's some setup work involved here, but I'm excluding it as I don't really feel that it's necessary for now. I can always add it later (see the footnote for details on what these actually are).

 //As if by magic, this is all it takes to do it (I'd like to clarify that I'm aware that this is a pure virtual function, I just don't get how they generate so much usage out of this one line):
 virtual void OnStuffHappening(arguments can go here if you so choose) = 0;
 };

就是这样......就像变魔术一样,当你去使用它时,你所要做的就是:

 #include "NewEbusDealio.h"

 class ComponentThatUsesTheBus
      : public NewEbusDealio::Handler
 {
 public:
      void Activate() override
      {
           NewEbusDealio::Handler::BusConnect();
      }
 protected:
      void OnStuffHappening(arguments so chosen)
      {
           //Do whatever you want to happen when the event fires
      }
 };

 class ComponentThatSendsEvents
 {
 public:
      void UpdateOrWhatever()
      {
           NewEbusDealio::Broadcast(NewEbusDealio::Events::OnStuffHappening, arguments go here)
      }
 };

我只是不明白如何通过向 NewEbusDealio 添加一个虚拟函数来完成这么多事情。非常感谢您对此的任何帮助。很抱歉有这么多文字墙,但我真的很想从中得到一些东西,而且我在这一点上碰到了一堵巨大的砖墙。对于我正在制作的东西来说,这可能有点过头了,而且它也可能最终导致工作量太大,以至于一个人不可能在合理的时间内完成,但如果这是一个简单的版本有可能我想试一试。

我把它放在这里,以便人们知道设置工作是什么。你要做的就是定义一个静态的 const EBusHandlerPolicy 和 EBusAddressPolicy,它定义了总线上每个地址可以连接多少个处理程序,以及总线是否工作在单个地址上(事件调用中不需要地址),或者你是否可以使用地址将事件发送到侦听某个地址的处理程序。现在,我想要一个简单的总线,如果你发送一个事件,所有的处理程序都会收到它。

标签: c++eventsgame-engine

解决方案


不熟悉EBus你给出的,但事件总线应该是相似的:一方创建一个事件并将其放入列表中,另一方一个接一个地拿起事件并做出反应。

由于现代 C++ 为我们提供了闭包功能,因此现在实现事件总线更加容易。

下面,我将举一个简单的例子,其中 looper 是一个事件总线。

请注意,在生产中此循环器需要互斥锁和条件变量。

#include <queue>
#include <list>
#include <thread>
#include <functional>

class ThreadWrapper {
 public:
  ThreadWrapper() = default;

  ~ThreadWrapper() { Detach(); }

  inline void Attach(std::thread &&th) noexcept {
    Detach();
    routine = std::forward<std::thread &&>(th);
  }

  inline void Detach() noexcept {
    if (routine.joinable()) {
      routine.join();
    }
  }

 private:
  std::thread routine{};
};

class Looper {
 public:

  // return ture to quit the loop, false to continue
  typedef std::function<void()> Task;
  typedef std::list<Task> MsgQueue;


  Looper() = default;

  ~Looper() {
    Deactivate();
  }

  // Post a method
  void Post(const Task &tsk) noexcept {
    Post(tsk, false);
  }

  // Post a method
  void Post(const Task &tsk, bool flush) noexcept {
    if(!running) {
      return;
    }
    if (flush) msg_queue.clear();
    msg_queue.push_back(tsk);
  }

  // Start looping
  void Activate() noexcept {
    if (running) {
      return;
    }

    msg_queue.clear();
    looping = true;
    worker.Attach(std::thread{&Looper::Entry, this});
    running = true;
  }

  // stop looping
  void Deactivate() noexcept {
    {
      if(!running) {
        return;
      }

      looping = false;
      Post([] { ; }, true);
      worker.Detach();
      running = false;
    }
  }

  bool IsActive() const noexcept { return running; }

 private:
  void Entry() noexcept {
    Task tsk;
    while (looping) {
      //if(msg_queue.empty()) continue;
      tsk = msg_queue.front();
      msg_queue.pop_front();

      tsk();
    }
  }

  MsgQueue msg_queue{};
  ThreadWrapper worker{};
  volatile bool running{false};
  volatile bool looping{false};
};

使用此 Looper 的示例:

class MySpeaker: public Looper{
 public:
  // Call SayHi without blocking current thread
  void SayHiAsync(const std::string &msg){
    Post([this, msg] {
      SayHi(msg);
    });
  }

 private:

  // SayHi will be called in the working thread
  void SayHi() {
    std::cout << msg << std::endl;
  }
};

推荐阅读