嗯,我不确定你遇到了什么问题的完整情况,但根据你目前所提供的一些信息,我有几个可能性。可能会重复建议一些已经完成的事情,因为我不确定从简略描述中是否足够了解全貌。
模型
从你的描述中,我认为最重要的是你需要开始使用类模型实现常见功能;你需要使用接口或基类从中派生出高级对象。
这样可以在很少额外工作的情况下以一致的方式处理事情。我认为“架构层次”的概念可以作为第一步考虑它的方式(例如,低级硬件、套接字处理等,然后是中间层的事情,例如游戏中发生的事情以及游戏机制背后的细节等,高级层次的事情,例如PC或NPC可以做什么,环境正在做什么等,并且永远不要“跳”层次)。但是,重要的是找到适合你的游戏的正确抽象方法,并始终保持组织良好,以便你永远不会感觉到你正在处理的代码正在做两种完全不同类型的事情。
首先,让我们看看自然而然地有很多事物与世界状态交互的事实。对于这样的问题,最好将大量的“东西”分解到一个类中,然后主要只让一个类来完成这项工作。理想情况下,在其自己的小组中实现事件通信/消息传递,这样就不需要用琐碎的处理方式污染您的高级对象。
例如,你想在高级对象中集中关注某些事物正在做什么:在AI中可能是“开始移动到位置”,“设置我的速度”,“停止移动”;在环境子系统中是“开始下雨”,“增加风速”,“调暗灯光”;在用户类中是“开火”,“睡眠”,“施法”。但我不希望我的任何高级类甚至知道诸如“向世界发送消息”,“重置口渴计时器”,“接收套接字数据”,“健康周期滴答声”之类的事情。(这些只是阐述,而非建议。;D)
事件
例如,我认为让一个对象负责向世界分派事件可能是有价值的,这样你就不再需要每个人都互相交流了。我可能会创建一组通用的处理事件的东西,例如
EventMain
和一个
enumEvents
,你可以使用它来给每种类型的事件赋予特殊的 ID。然后将 Event 作为需要额外功能的特定事件的基类。(我考虑了 ID 和派生模型,这样像 Dispatcher 这样只需要知道事件的基本信息的东西就不必也要了解派生类的信息。例如,Dispatcher 可以接收事件并发送出去,而无需了解派生事件的内部信息。这可能有用,也可能没有用,但拥有这些选项很好。)你还可以拥有一个
EventDispatcher
,它有一个事件队列,用于发送到其他子系统。
你需要为接收和发送事件创建一个共同的东西。你可以创建一个独立的
EventSourcer
和
EventSinker
类,可以在任何生成或接收事件的类中设置它们。或者,你可以使用
IEventSource
和
IEventSink
,这样你可以在多个类上实现一个公共接口,或者使用一个公共类
EventSourceAndSink
来实现两者,并将其作为你的基类模型的一部分,这样任何可能需要处理事件的东西都可以从中派生出来。
我可能会创建
ProtocolEncoder
和
ProtocolDecoder
类。你总是可以将它们合并为一个对象,但如果充分做好了,将它们作为两个独立的代码片段也可能是有价值的。你还可以拥有一个
ProtocolHelper
类,它将两者之间的任何共同点因素提取出来。编码器的唯一工作是从网络接收消息并将其转换为游戏事件,然后将其传递给
EventDispatcher
。解码器类将从需要发送到网络的调度程序中获取事件,并获取其中的数据并将其发送出去。
如何到达目的地
既然你已经有了可用的代码,我建议你只需在自然的位置开始执行。这可能是使你陷入困境的事情,或者是你注意到在不同的地方非常相似的事情,你可以使用一个类或其他类型的抽象将其变得规律化,然后将旧的东西拿出来,放入新的东西。一旦你找到了一个可行的第一版类模型,那么应该会根据你已经拥有的想法给你带来更多的想法,随着你的前进不断重新考虑你的模型,修复问题,重复上述步骤。
不需要太多工作,事实上,我在编写代码时最令人满意的时刻之一就是当我能够进行整洁的重构,将以前丑陋混乱的代码变得更易于理解,并用更少的代码行数代替了难以理解的代码,为下一步的添加铺平了道路,使其成为一种愉悦而不是又一次“天啊,我不必再碰那个代码了吧?”的时刻。
祝好运,以下是我所说的事情的名义指南;第一部分更加详细,因为主要事件类是更重要的概念之一,然后我试图简要概述类和它们如何相互作用。我相信我可以花更多的时间来做这件事,但现在我只想说:如果你有问题,请问我,我会尽力给你一个好的答案:)
代码中的思想
哦,还有一件值得注意的事情是,如果你有多个线程,我没有处理增加的复杂性;如果你这样做,管理所有这些东西的事情从简单到复杂都有,但这是另一种练习。:)
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using EventHandlingLibrary;
namespace EventHandlingLibrary
{
public class EventOfHurt
{
#region Event Definitions
protected enum EEventType
{
SystemInitializing,
SubsystemInitComplete,
FatalErrorNotification,
SubsystemPingReponse,
SubsystemPingRequest,
FrameRateError,
ThroughputData,
ServerTimeout,
ServerPingRequest,
ServerPingResponse,
WeaponsFire,
MovementNotification,
FatigueUpdate
}
protected enum ESubsystem
{
System,
Dispatcher,
TickerTimer,
WorldEntity,
WorldTaskManager,
UserMessageProcessor,
NetworkListener,
NetworkTransmitter,
ProtocolEncoder,
ProtocolDecoder,
PlayerCharacter,
NonPlayerCharacter,
EventSink,
EventSource
}
#endregion
#region Event Information
public Guid EventId { get; protected set; }
public EEventType EventType { get; protected set; }
public ESubsystem SourceSubsystem { get; protected set; }
public ESubsystem DestSubsystem { get; protected set; }
private List<Tuple<EventOfHurt, DateTime>>
myEventReferences;
public Tuple<EventOfHurt, DateTime>[]
EventReferences
{
get { return myEventReferences.ToArray(); }
}
public DateTime Timestamp { get; private set; }
#endregion
private EventOfHurt(
EEventType evt,
ESubsystem src,
ESubsystem dest = ESubsystem.Dispatcher
)
{
EventType = evt;
SourceSubsystem = src;
DestSubsystem = dest;
EventId = Guid.NewGuid();
Timestamp = DateTime.UtcNow;
}
protected static EventOfHurt CreateGeneric(
EEventType evt, ESubsystem src,
ESubsystem dest = ESubsystem.Dispatcher,
Tuple<EventOfHurt, DateTime>[] reasons = null
)
{
EventOfHurt RetVal;
if (dest == null)
dest = ESubsystem.Dispatcher;
List<Tuple<EventOfHurt, DateTime>> ReasonList =
new List<Tuple<EventOfHurt,DateTime>>();
if (reasons != null)
ReasonList.AddRange(reasons);
RetVal = new EventOfHurt(evt, src) {
myEventReferences = ReasonList
};
return RetVal;
}
public static EventOfHurt CreateTickerTimerEvent(
EEventType evt, ESubsystem dest
)
{
ESubsystem src = ESubsystem.TickerTimer;
return CreateGeneric(evt, src, dest, null);
}
public static EventOfHurt CreatePlayerActionEvent(
EEventType evt, ESubsystem dest,
Tuple<EventOfHurt, DateTime>[] reasons
)
{
PlayerEvent PE = new PlayerActionEvent(42);
return PE;
}
}
public class PlayerEvent :
EventOfHurt
{
};
public class PlayerActionEvent :
PlayerEvent
{
public PlayerActionEvent(int ExtraInfo)
{
}
};
}
namespace EntitiesOfHurt
{
public class LatchedBool
{
private bool myValue = false;
public bool Value
{
get { return myValue; }
set {
if (!myValue)
myValue = value;
}
}
}
public class EventOfHurtArgs :
EventArgs
{
public EventOfHurtArgs(EventOfHurt evt)
{
myDispatchedEvent = evt;
}
private EventOfHurt myDispatchedEvent;
public EventOfHurt DispatchedEvent
{
get { return myDispatchedEvent; }
}
}
public class MultiDispatchEventArgs :
EventOfHurtArgs
{
public MultiDispatchEventArgs(EventOfHurt evt) :
base(evt)
{
}
public LatchedBool isHandled;
}
public interface IEventSink
{
void MultiDispatchRecieve(object sender, MultiDispatchEventArgs e);
void EventOfHurt(object sender, EventOfHurtArgs e);
void AttachEventSource(IEventSource evtSource);
void DetachEventSource(IEventSource evtSource);
}
public interface IEventSource
{
event EventHandler<MultiDispatchEventArgs> onMultiDispatchEvent;
event EventHandler<EventOfHurtArgs> onEventOfHurt;
void FireEventOfHurt(EventOfHurt newEvent);
void FireMultiDispatchEvent(EventOfHurt newEvent);
void AttachEventSink(IEventSink evtSink);
void DetachEventSink(IEventSink evtSink);
}
public class Dispatcher :
IEventSource, IEventSink
{
}
public class ProtocolDecoder :
IEventSource
{
}
public class ProtocolEncoder :
IEventSink
{
}
public class NetworkListener
{
private ProtocolEncoder myEncoder;
private ProtocolDecoder myDecoder;
}
public class TheWorld :
IEventSink, IEventSource
{
}
public class Character
{
}
public class NonPlayerCharacter :
Character,
IEventSource, IEventSink
{
}
public class PlayerCharacter :
Character,
IEventSource, IEventSink
{
}
}