我最近决定开始制作游戏引擎。我知道大多数人不会完成他们的任务,如果我说实话,我也可能不会。我这样做是因为我厌倦了搜索“ Cool C ++项目”,并做了每个用户给出的3个答案(这将是通讯录或类似的东西,井字游戏,以及报告卡生成器或类似的东西)那)。我喜欢编程,但是不幸的是我没有真正的用处。我会用它做的所有事情都可以通过另一种方式更快,更轻松地完成,或者已经存在解决方案。但是,为了努力学习更多的C ++基本知识并做一些可以教会我真正深入的事情,我撤销了这项政策,决定开始使用游戏引擎,因为我一直对此感兴趣。我决定采用Amazon的Lumberyard引擎对它进行宽松的建模,因为它几乎完全是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,它们定义了多少个处理程序可以连接到总线上的每个地址,以及总线是否在单个地址上工作(事件调用中不需要地址),或者是否可以使用地址将事件发送给侦听某个地址的处理程序。现在,我希望有一条简单的总线,如果您发送事件,所有处理程序都会接收它。
答案 0 :(得分:1)
您对给定的EBus
不熟悉,但是事件总线应该类似:一侧创建一个事件并将其放入列表,另一侧一个接一个地拾取事件并做出反应。
由于现代C ++为我们提供了闭包功能,因此现在更容易实现事件总线。
下面,我将举一个简单的示例,其中循环程序是事件总线。
请注意,互斥量和条件变量是生产此循环器所必需的。
#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;
}
};
答案 1 :(得分:0)
我不确定这到底是哪一部分令您惊讶。在没有签出此EBus库的情况下,显然NewEbusDealio
很可能从Broadcast()
继承了EbusThingy
,并且该函数基本上可以完成所有必要的工作。似乎Broadcast()
要求一个指向成员函数的指针以在每个实体上调用(我想您上面的代码在那里缺少&
)以及参数。然后,它只需在每个实体上使用给定的参数集调用给定的方法即可。