游戏对象互相谈话 [英] Game Objects Talking To Each Other

查看:122
本文介绍了游戏对象互相谈话的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

什么是处理对象并让他们彼此交谈的好方法?



到目前为止我的所有游戏爱好/学生都很小,所以这个问题通常以相当丑陋的方式解决,这导致紧密集成循环依赖性。这对于我正在做的项目的大小是好的。



但是我的项目的规模和复杂性越来越大,现在我想开始重新使用代码,使我的头更简单的地方。



我遇到的主要问题是一般沿着玩家需要知道关于 Map 以及 Enemy ,这通常会降低到设置大量的指针和有很多依赖,这很快就变成了一个混乱。



我已经考虑了消息风格系统的线。但我不能真正看到这是如何减少依赖,因为我仍然发送指针无处不在。



谢谢。

>解决方案

编辑:下面我描述一个基本的事件消息传递系统我已经使用了一遍又一遍。它发生对我,两个学校项目是开放源代码和在网络上。您可以在 http://sourceforge.net/projects/bpfat/ 上找到此邮件系统的第二个版本(还有更多内容) / a> ..享受,下面阅读更多的系统描述!



我写了一个通用的消息传递系统,并将其引入少数几个游戏已经在PSP上发布以及一些企业级应用软件。消息系统的要点是仅传递处理消息或事件所需的数据,这取决于您要使用的术语,以便对象不必了解彼此。



快速运行完成此操作的对象列表如下:

  struct TEventMessage 
{
int _iMessageID;
}

class IEventMessagingSystem
{
Post(int iMessageId);
Post(int iMessageId,float fData);
Post(int iMessageId,int iData);
// ...
Post(TMessageEvent * pMessage);
Post(int iMessageId,void * pData);
}

typedef float(* IEventMessagingSystem :: Callback)(TEventMessage * pMessage);

class CEventMessagingSystem
{
Init();
DNit();
Exec(float fElapsedTime);

Post(TEventMessage * oMessage);

注册(int iMessageId,IEventMessagingSystem * pObject,FObjectCallback * fpMethod);
Unregister(int iMessageId,IEventMessagingSystem * pObject,FObjectCallback * fpMethod);
}

#define MSG_Startup(1)
#define MSG_Shutdown(2)
#define MSG_PlaySound(3)
#define MSG_HandlePlayerInput b $ b #define MSG_NetworkMessage(5)
#define MSG_PlayerDied(6)
#define MSG_BeginCombat(7)
#define MSG_EndCombat(8)

现在有一点解释。第一个对象TEventMessage是表示消息传递系统发送的数据的基础对象。默认情况下,它将始终具有正在发送的消息的Id,所以如果你想确保你收到一个消息,你期望你可以(一般我只是在调试中)。



接下来是Interface类,它为消息传递系统提供一个通用对象,用于在执行回调时进行转换。此外,这还提供了一个易于使用的界面Post()不同的数据类型到消息系统。



之后,我们有回调typedef,期望接口类的类型的对象,并且将传递一个TEventMessage指针...可选地,您可以使参数const,但是Ive在诸如堆栈调试和消息传递系统之类的事情之前使用了涓滴处理。



最后,核心是CEventMessagingSystem对象。此对象包含回调对象堆栈(或链接列表或队列,或者您想要存储数据)的数组。上面没有显示的回调对象需要维护(和唯一地由)指向对象的指针以及调用该对象的方法。当你Register(),你在消息id的数组位置下的对象堆栈添加一个条目。当你Unregister()你删除该条目。



这基本上是..现在这有规定,一切都需要知道关于IEventMessagingSystem和TEventMessage对象。 ..但是这个对象不应该经常改变,只传递那些对被调用的事件决定的逻辑至关重要的信息部分。这样玩家不需要知道地图或敌人直接发送事件给它。一个托管对象可以调用一个更大的系统API,也不需要知道任何东西。



例如:当一个敌人死亡,你想要它玩音效。假设你有一个继承IEventMessagingSystem接口的声音管理器,你将为消息传递系统设置一个回调,它将接受一个TEventMessagePlaySoundEffect或其他的东西。当声音效果启用时,声音管理器将注册此回调(或当您想要静音所有声音效果以方便开/关功能时,取消注册回调)。接下来,你的敌人对象也将从IEventMessagingSystem继承,放在一起TEventMessagePlaySoundEffect对象(需要MSG_PlaySound为其消息ID,然后声音效果的播放ID,无论是一个int ID或声音的名称效果),并简单地调用Post(& oEventMessagePlaySoundEffect)。



现在这只是一个非常简单的设计,如果你立即执行,那么你不需要缓冲TEventMessage对象(我在控制台游戏中使用的大多数)。如果您处于多线程环境中,那么对于在单独线程中运行的对象和系统来说,这是一种非常明确的方式,但是您需要保留TEventMessage对象,以便在处理时可以使用这些数据。 p>

另一个变化是对于只需要Post()数据的对象,你可以在IEventMessagingSystem中创建一组静态方法,所以他们不必从它们继承用于方便访问和回调能力,而不是直接用于Post()调用)。



对于所有提到MVC的人来说,模式,但你可以实现它在许多不同的方式和不同的水平。我目前专业工作的项目是一个MVC设置约3次,有全局MVC的整个应用程序,然后设计明智每个MV和C也是一个自包含的MVC模式..所以我试图做这里是解释如何使一个C是通用的,足以处理任何类型的M,不需要进入一个View ...



例如,一个对象,当它'死'可能想播放一个声音效果..你会做一个结构的声音系统,像TEventMessageSoundEffect从TEventMessage继承,并添加一个声音效果ID(是一个预加载的Int,或名称sfx文件,但是它们在您的系统中被跟踪)。然后所有的对象只需要放在一起TEventMessageSoundEffect对象与适当的死亡噪音和调用Post(& oEventMessageSoundEffect);对象。假设声音没有静音(你想要取消注册声音管理器。



编辑:为了澄清这一点有关下面的注释:
发送或接收消息的任何对象只需要知道IEventMessagingSystem接口,这是EventMessagingSystem需要知道的所有其他对象的唯一对象,这是给你分离的对象,任何想要接收一个简单的注册消息(MSG,Object,Callback)s,然后当一个对象调用Post(MSG,Data)时,它通过它知道的接口将它发送给EventMessagingSystem,EMS然后通知每个注册对象事件,你可以做一个MSG_PlayerDied,其他系统处理,或者播放器可以调用MSG_PlaySound,MSG_Respawn等让事情监听这些消息来对他们采取行动。把Post(MSG,Data)作为一个抽象的API,系统。



哦!另一件事是指出我。上面描述的系统适合观察者模式在给出的另一个答案。所以,如果你想要一个更一般的描述,使我的有点更有意义,那是一个简短的文章,给它一个很好的描述。



希望这有助于和享受!


What is a good way of dealing with objects and having them talk to each other?

Up until now all my games hobby/student have been small so this problem was generally solved in a rather ugly way, which lead to tight integration and circular dependencies. Which was fine for the size of projects I was doing.

However my projects have been getting bigger in size and complexity and now I want to start re-using code, and making my head a simpler place.

The main problem I have is generally along the lines of Player needs to know about the Map and so does the Enemy, this has usually descended into setting lots of pointers and having lots of dependencies, and this becomes a mess quickly.

I have thought along the lines of a message style system. but I cant really see how this reduces the dependencies, as I would still be sending the pointers everywhere.

Thanks.

PS: I guess this has been discussed before, but I don't know what its called just the need I have.

解决方案

EDIT: Below I describe a basic event messaging system I have used over and over.. And it occured to me that both school projects are open source and on the web. You can find the second version of this messaging system (and quite a bit more) at http://sourceforge.net/projects/bpfat/ .. Enjoy, and read below for a more through description of the system!

I've written a generic messaging system and introduced it into a handful of games that have been released on the PSP as well as some enterprise level application software. The point of the messaging system is to pass only the data around that is needed for processing a message or event, depending on the terminology you want to use, so that objects do not have to know about each other..

A quick run down of the list of objects used to accomplish this is something along the lines of:

struct TEventMessage
{
    int _iMessageID;
}

class IEventMessagingSystem
{
    Post(int iMessageId);
    Post(int iMessageId, float fData);
    Post(int iMessageId, int iData);
    // ...
    Post(TMessageEvent * pMessage);
    Post(int iMessageId, void * pData);
}

typedef float(*IEventMessagingSystem::Callback)(TEventMessage * pMessage);

class CEventMessagingSystem
{
    Init       ();
    DNit       ();
    Exec       (float fElapsedTime);

    Post       (TEventMessage * oMessage);

    Register   (int iMessageId, IEventMessagingSystem* pObject, FObjectCallback* fpMethod);
    Unregister (int iMessageId, IEventMessagingSystem* pObject, FObjectCallback * fpMethod);
}

#define MSG_Startup            (1)
#define MSG_Shutdown           (2)
#define MSG_PlaySound          (3)
#define MSG_HandlePlayerInput  (4)
#define MSG_NetworkMessage     (5)
#define MSG_PlayerDied         (6)
#define MSG_BeginCombat        (7)
#define MSG_EndCombat          (8)

And now a bit of an explanation. The first object, TEventMessage, is the base object to represent data sent by the messaging system. By default it will always have the Id of the message being sent so if you want to make sure you have received a message you were expecting you can (Generally I only do that in debug).

Next up is the Interface class that gives a generic object for the messaging system to use for casting while doing callbacks. Additionally this also provides an 'easy to use' interface for Post()ing different data types to the messaging system.

After that we have our Callback typedef, Simply put it expects an object of the type of the interface class and will pass along a TEventMessage pointer... Optionally you can make the parameter const but Ive used trickle up processing before for things like stack debugging and such of the messaging system.

Last and at the core is the CEventMessagingSystem object. This object contains an array of callback object stacks (or linked lists or queues or however you want to store the data). The callback objects, not shown above, need to maintain (and are uniquely defined by) a pointer to the object as well as the method to call on that object. When you Register() you add an entry on the object stack under the message id's array position. When you Unregister() you remove that entry.

That is basically it.. Now this does have the stipulation that everything needs to know about the IEventMessagingSystem and the TEventMessage object... but this object should Not be changing that often and only passes the parts of information that are vital to the logic dictated by the event being called. This way a player doesnt need to know about the map or the enemy directly for sending events off to it. A managed object can call an API to a larger system also, with out needing to know anything about it.

For example: When an enemy dies you want it to play a sound effect. Assuming you have a sound manager that inherits the IEventMessagingSystem interface, you would set up a callback for the messaging system that would accept a TEventMessagePlaySoundEffect or something of that ilk. The Sound Manager would then register this callback when sound effects are enabled (or unregister the callback when you want to mute all sound effects for easy on/off abilities). Next, you would have the enemy object also inherit from the IEventMessagingSystem, put together a TEventMessagePlaySoundEffect object (would need the MSG_PlaySound for its Message ID and then the ID of the sound effect to play, be it an int ID or the name of the sound effect) and simply call Post(&oEventMessagePlaySoundEffect).

Now this is just a very simple design with no implementation. If you have immediate execution then you have no need to buffer the TEventMessage objects (What I used mostly in console games). If you are in a multi-threaded environment then this is a very well defined way for objects and systems running in separate threads to talk to each other, but you will want to preserve the TEventMessage objects so the data is available when processing.

Another alteration is for objects that only ever need to Post() data, you can create a static set of methods in the IEventMessagingSystem so they do not have to inherit from them (That is used for ease of access and callback abilities, not -directly- needed for Post() calls).

For all the people who mention MVC, it is a very good pattern, but you can implement it in so many different manners and at different levels. The current project I am working on professionally is an MVC setup about 3 times over, there is the global MVC of the entire application and then design wise each M V and C also is a self contained MVC pattern.. So what I have tried to do here is explain how to make a C that is generic enough to handle just about any type of M with out the need to get into a View...

For example, an object when it 'dies' might want to play a sound effect.. You would make a struct for the Sound System like TEventMessageSoundEffect that inherits from the TEventMessage and adds in a sound effect ID (Be it a preloaded Int, or the name of the sfx file, however they are tracked in your system). Then all the object just needs to put together a TEventMessageSoundEffect object with the appropriate Death noise and call Post(&oEventMessageSoundEffect); object.. Assuming the sound is not muted (what you would want to Unregister the Sound Managers.

EDIT: To clarify this a bit in regards to the comment below: Any object to send or receive a message just needs to know about the IEventMessagingSystem interface, and this is the only object the EventMessagingSystem needs to know of all the other objects. This is what gives you the detachment. Any object who wants to receive a message simply Register(MSG, Object, Callback)s for it. Then when an object calls Post(MSG,Data) it sends that to the EventMessagingSystem via the interface it knows about, the EMS will then notify each registered object of the event. You could do a MSG_PlayerDied that other systems handle, or the player can call MSG_PlaySound, MSG_Respawn, etc to let things listening for those messages to act upon them. Think of the Post(MSG,Data) as an abstracted API to the different systems within a game engine.

Oh! One other thing that was pointed out to me.. The system I describe above fits the Observer pattern in the other answer given. So if you want a more general description to make mine make a bit more sense, that is a short article that gives it a good description.

Hope this helps and Enjoy!

这篇关于游戏对象互相谈话的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆