Java 游戏引擎设计:客户端和服务器之间的干净动作流
为了学习,我正在设计一款多人在线游戏 多个客户端连接到一个服务器,该服务器包含游戏世界的真实表示。每个客户机都跟踪该表示的子集。我将表示存储在Java 游戏引擎设计:客户端和服务器之间的干净动作流,java,oop,design-patterns,game-engine,Java,Oop,Design Patterns,Game Engine,为了学习,我正在设计一款多人在线游戏 多个客户端连接到一个服务器,该服务器包含游戏世界的真实表示。每个客户机都跟踪该表示的子集。我将表示存储在上下文中。客户端和服务器通过传递各种操作类来更改全局上下文的状态。一个例子是MoveEntityAction,另一个例子是TalkAction,它们都实现了一个名为Action的接口。原则上,游戏由一系列动作实例进行 然而,来回传递动作需要一些技巧。首先,提供如何在网络上读写它们的定义。第二,在全局和局部上下文(不同的坐标系等)之间来回转换它们的协议。第三
上下文中
。客户端和服务器通过传递各种操作类来更改全局上下文的状态。一个例子是MoveEntityAction
,另一个例子是TalkAction
,它们都实现了一个名为Action
的接口。原则上,游戏由一系列动作
实例进行
然而,来回传递动作需要一些技巧。首先,提供如何在网络上读写它们的定义。第二,在全局和局部上下文(不同的坐标系等)之间来回转换它们的协议。第三,让服务器确定哪些操作需要在另一个操作之前进行(我希望下面的伪代码可以清楚地说明这一点)
现在,这个过程依赖于几个助手类,每个类都实现了一个称为Writer
、Reader
、ContextSwitcher
和PrerequisiteFinder
的接口
这让我做了以下几点:
- 创建一个查找表,
,该表定义了一些方法,如ActionLibrary
,getReader(int-actionId)
getWriter(仅阅读你的部分文章,我认为在所有网络对象上使用Java
接口可能会带来一次大的清理。这将大大简化你所有的读者和作者。其次,我会让客户端和服务器都使用相同的坐标系。尝试使用不同的坐标系感觉就像射击一样我可以看出,这将是太多的工作了。不同的坐标系只是一个例子。动作可能看起来不同,这取决于它们所处的环境。一个实体可能部分失明,因此无法看到谁实施了动作,但对于其他实体来说,这可能是完全清楚的。ent的环境ity有助于改变一个“真实”的行为以适应他对世界的感知。我很欣赏这个技巧,但如果我使用Serializable
,我只会解决一半的问题,因为我仍然需要能够切换上下文并找到先决条件的行动。解决一半的问题比什么都不解决要好。:-)但是,当你描述处于不同状态的实体以及它们如何对输入做出反应时,听起来似乎试图将所有这些逻辑放在一个大环境中也可能是问题的一部分。看看让实体根据自己的状态对输入做出反应。C.f.马克·布朗的“系统”视频游戏:是的!这是一个很好的回答,但我仍然希望避免Serializable
(请参阅问题中我的编辑)。很抱歉从一开始就不清楚。关于上下文:这基本上只是游戏世界的一堆状态变量。全局上下文用作另一个类的输入,其中包含选择其子集的逻辑。子集是另一个上下文,是实体(玩家、暴徒)的透视图.上面我展示的ContextSwitcher类基本上是一种将一个上下文中的操作与另一个上下文中的操作关联起来的方法)。我会看一看那个视频!再次感谢。我看了视频,虽然我觉得它很有趣,但我不确定它是否能解决我的问题。他描述的实体输入似乎类似于我所说的本地上下文。对于玩家实体,上下文也将用于呈现。:)Serializable
writer = ActionLibrary.getWriter(moveEntityAction); writer.writeTo(outputStream, moveEntityAction);
reader = ActionLibrary.getReader(Id); localAction = reader.readFrom(inputStream); globalAction = ActionLibrary.getContextSwitcher(localAction.getClass(), playerContext, globalContext).apply(localAction);
List<Entity> spectators = globalContext.getActionSpectators(globalAction); for (Entity spectator : spectators) { List<Action> prerequisiteActions = ActionLibrary.getPrerequisiteFinder(globalAction.getClass()).get(spectator, globalAction, globalContext); spectatorContext = globalContext.getContextOf(spectator); // Broadcast prerequisite actions (if any) for (Action globalPreAction : prerequisiteActions) { localPreAction = ActionLibrary.getContextSwitcher(globalPreAction.getClass(), globalContext, spectatorContext).apply(globalPreAction); writer = ActionLibrary.getWriter(localPreAction); writer.writeTo(outputStream); } // Finally, send the instigating action: localAction = ActionLibrary.getContextSwitcher(globalAction.getClass(), globalContext, spectatorContext).apply(globalAction); writer = ActionLibrary.getWriter(localAction); writer.writeTo(outputStream); } // Execute the action on the globalContext globalContext = globalAction.execute(globalAction);
reader = ActionLibrary.getReader(Id); localAction = reader.readFrom(inputStream); context = localAction.execute(context);