<i id='Z9GSz'><tr id='Z9GSz'><dt id='Z9GSz'><q id='Z9GSz'><span id='Z9GSz'><b id='Z9GSz'><form id='Z9GSz'><ins id='Z9GSz'></ins><ul id='Z9GSz'></ul><sub id='Z9GSz'></sub></form><legend id='Z9GSz'></legend><bdo id='Z9GSz'><pre id='Z9GSz'><center id='Z9GSz'></center></pre></bdo></b><th id='Z9GSz'></th></span></q></dt></tr></i><div id='Z9GSz'><tfoot id='Z9GSz'></tfoot><dl id='Z9GSz'><fieldset id='Z9GSz'></fieldset></dl></div>
  • <small id='Z9GSz'></small><noframes id='Z9GSz'>

      • <bdo id='Z9GSz'></bdo><ul id='Z9GSz'></ul>

      <tfoot id='Z9GSz'></tfoot>

        <legend id='Z9GSz'><style id='Z9GSz'><dir id='Z9GSz'><q id='Z9GSz'></q></dir></style></legend>

        Python:如何序列化多人游戏的对象?

        时间:2024-08-11
        <tfoot id='ytLbt'></tfoot>

        <legend id='ytLbt'><style id='ytLbt'><dir id='ytLbt'><q id='ytLbt'></q></dir></style></legend>
          <tbody id='ytLbt'></tbody>

        <small id='ytLbt'></small><noframes id='ytLbt'>

        • <bdo id='ytLbt'></bdo><ul id='ytLbt'></ul>

                • <i id='ytLbt'><tr id='ytLbt'><dt id='ytLbt'><q id='ytLbt'><span id='ytLbt'><b id='ytLbt'><form id='ytLbt'><ins id='ytLbt'></ins><ul id='ytLbt'></ul><sub id='ytLbt'></sub></form><legend id='ytLbt'></legend><bdo id='ytLbt'><pre id='ytLbt'><center id='ytLbt'></center></pre></bdo></b><th id='ytLbt'></th></span></q></dt></tr></i><div id='ytLbt'><tfoot id='ytLbt'></tfoot><dl id='ytLbt'><fieldset id='ytLbt'></fieldset></dl></div>
                • 本文介绍了Python:如何序列化多人游戏的对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

                  问题描述

                  我们正在开发一款自上而下的、类似RPG的多人游戏,目的是为了学习(而且很有趣!)和几个朋友在一起。我们在游戏中已经有一些实体并且输入正在工作,但是网络实现让我们头疼:d

                  问题

                  尝试使用dict进行转换时,某些值仍将包含pygame.Surface,我不想传输它,并且在尝试对它们进行jsonize时会导致错误。我要以简化方式传输的其他对象(如矩形)无法自动转换。

                  已正常运行

                  • 客户端-服务器连接
                  • 双向传输JSON对象
                  • 异步联网同步入队

                  情况

                  新玩家连接到服务器,希望获取所有对象的当前游戏状态。

                  数据结构

                  我们使用基于"实体-组件"的架构,因此我们非常严格地将游戏逻辑分离到"系统"中,而数据则存储在每个实体的"组件"中。该实体是一个非常简单的容器,只有一个ID和一个组件列表。

                  示例实体(缩短以提高可读性):

                      Entity
                        |-- Component (Moveable)
                        |-- Component (Graphic)
                        |         |- complex datatypes like pygame.SURFACE
                        |         `- (...)
                         `- Component (Inventory)
                  

                  我们尝试了不同的方法,但似乎都不太适合或感觉"毛骨悚然"。

                  泡菜 非常接近Python,所以以后不容易实现其他客户端。我读到过一些关于从网络创建项目时的一些安全风险,这种动态方式提供了怎样的泡菜。它甚至不能解决曲面/矩形问题。

                  __DICT__

                  仍然包含对旧对象的引用,因此对不需要的数据类型的"清理"或"过滤"也会在源中发生。深度复制引发异常。

                  ...PythonPython36libcopy.py", line 169, in deepcopy
                      rv = reductor(4)
                  TypeError: can't pickle pygame.Surface objects
                  

                  显示一些代码

                  "EnitityManager"类的方法,它应该生成所有实体的快照,包括它们的组件。此快照应该转换为JSON,没有任何错误-如果可能的话,在此核心类中不需要太多配置。

                      class EnitityManager:
                          def generate_world_snapshot(self):
                              """ Returns a dictionary with all Entities and their components to send
                              this to the client. This function will probably generate a lot of data,
                              but, its to send the whole current game state when a new player
                              connects or when a complete refresh is required """
                              # It should be possible to add more objects to the snapshot, so we
                              # create our own Snapshot-Datastructure
                              result = {'entities': {}}
                              entities = self.get_all_entities()
                              for e in entities:
                                  result['entities'][e.id] = deepcopy(e.__dict__)
                                  # Components are Objects, but dictionary is required for transfer
                                  cmp_obj_list = result['entities'][e.id]['components']
                                  # Empty the current list of components, its going to be filled with
                                  # dictionaries of each cmp which are cleaned for the dump, because
                                  # of the errors directly coverting the whole datastructure to JSON
                                  result['entities'][e.id]['components'] = {}
                                  for cmp in cmp_obj_list:
                                      cmp_copy = deepcopy(cmp)
                                      cmp_dict = cmp_copy.__dict__
                                      # Only list, dict, int, str, float and None will stay, while
                                      # other Types are being simply deleted including their key
                                      # Lists and directories will be cleaned ob recursive as well
                                      cmp_dict = self.clean_complex_recursive(cmp_dict)
                                      result['entities'][e.id]['components'][type(cmp_copy).__name__] 
                                          = cmp_dict
                  
                              logging.debug("EntityMgr: Entity#3: %s" % result['entities'][3])
                              return result
                  

                  预期和实际结果

                  我们可以找到一种手动覆盖不需要的元素的方法。但是由于组件列表将会增加,我们必须将所有过滤逻辑放到这个核心类中,它不应该包含任何组件专门化。

                  我们真的需要将所有逻辑放到EntityManager中以筛选正确的对象吗?这感觉不太好,因为我希望在没有任何硬编码配置的情况下完成所有到JSON的转换。

                  如何以最通用的方法转换所有这些复杂数据?

                  感谢您阅读到目前为止,非常感谢您事先的帮助!

                  我们已经在撰写的有趣文章,可能对其他有类似问题的人有帮助

                  • https://gafferongames.com/post/what_every_programmer_needs_to_know_about_game_networking/
                  • http://code.activestate.com/recipes/408859/
                  • https://docs.python.org/3/library/pickle.html

                  更新:解决方案-thx 2懒惰

                  我们使用了以下体系结构的组合,到目前为止工作得非常好,维护起来也很好!

                  实体管理器现在调用实体的get_state()函数。

                  class EntitiyManager:
                      def generate_world_snapshot(self):
                          """ Returns a dictionary with all Entities and their components to send
                          this to the client. This function will probably generate a lot of data,
                          but, its to send the whole current game state when a new player
                          connects or when a complete refresh is required """
                          # It should be possible to add more objects to the snapshot, so we
                          # create our own Snapshot-Datastructure
                          result = {'entities': {}}
                          entities = self.get_all_entities()
                          for e in entities:
                              result['entities'][e.id] = e.get_state()
                          return result
                  


                  实体只有一些基本属性要添加到状态,并将get_state()调用转发给所有组件:

                  class Entity:
                      def get_state(self):
                          state = {'name': self.name, 'id': self.id, 'components': {}}
                          for cmp in self.components:
                              state['components'][type(cmp).__name__] = cmp.get_state()
                          return state
                  


                  组件本身现在继承了它们的新超类组件的get_state()方法,超类组件只关心所有简单的数据类型:

                  class Component:
                      def __init__(self):
                          logging.debug('generic component created')
                  
                      def get_state(self):
                          state = {}
                          for attr, value in self.__dict__.items():
                              if value is None or isinstance(value, (str, int, float, bool)):
                                  state[attr] = value
                              elif isinstance(value, (list, dict)):
                                  # logging.warn("Generating state: not supporting lists yet")
                                  pass
                          return state
                  
                  class GraphicComponent(Component):
                     # (...)
                  


                  现在,每个开发人员都有机会覆盖此函数,以便直接在组件类(如Graphic、Movement、Inventory等)中为复杂类型创建更详细的get_state()函数。如果需要以更准确的方式保护状态-这是未来维护代码的一件大事,请将这些代码片段放在一个类中。

                  下一步是实现静电方法,用于从同一个Class中的状态创建项。这使得这项工作非常顺利。
                  非常感谢你的帮助,懒惰。

                  推荐答案

                  是否确实必须将所有逻辑放入EntityManager以筛选正确的对象?

                  否,您应该使用polymorphism。

                  您需要一种以可在不同系统之间共享的形式表示游戏状态的方法;因此,可以为您的组件提供一个返回其所有状态的方法,以及一个允许您从该状态创建组件实例的工厂方法。

                  (Python已经有__repr__魔术方法,但您不必使用它)

                  因此,无需在实体管理器中进行所有筛选,只需让他在所有组件上调用此新方法,并让每个组件决定结果将是什么样子。

                  如下所示:

                  ...
                  result = {'entities': {}}
                  entities = self.get_all_entities()
                  for e in entities:
                      result['entities'][e.id] = {'components': {}}
                      for cmp in e.components:
                           result['entities'][e.id]['components'][type(cmp).__name__] = cmp.get_state()
                  ...
                  

                  组件可以这样实现它:

                  class GraphicComponent:
                      def __init__(self, pos=...):
                          self.image = ...
                          self.rect = ...
                          self.whatever = ...
                  
                      def get_state(self):
                          return { 'pos_x': self.rect.x, 'pos_y': self.rect.y, 'image': 'name_of_image.jpg' }
                  
                      @staticmethod
                      def from_state(state):
                          return GraphicComponent(pos=(state.pos_x, state.pos_y), ...)
                  

                  和从服务器接收状态的客户端EntityManager将迭代每个实体的组件列表,并调用from_state以创建实例。

                  这篇关于Python:如何序列化多人游戏的对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

                    <tbody id='1YYQD'></tbody>

                  <small id='1YYQD'></small><noframes id='1YYQD'>

                • <i id='1YYQD'><tr id='1YYQD'><dt id='1YYQD'><q id='1YYQD'><span id='1YYQD'><b id='1YYQD'><form id='1YYQD'><ins id='1YYQD'></ins><ul id='1YYQD'></ul><sub id='1YYQD'></sub></form><legend id='1YYQD'></legend><bdo id='1YYQD'><pre id='1YYQD'><center id='1YYQD'></center></pre></bdo></b><th id='1YYQD'></th></span></q></dt></tr></i><div id='1YYQD'><tfoot id='1YYQD'></tfoot><dl id='1YYQD'><fieldset id='1YYQD'></fieldset></dl></div>

                      <tfoot id='1YYQD'></tfoot>

                          <legend id='1YYQD'><style id='1YYQD'><dir id='1YYQD'><q id='1YYQD'></q></dir></style></legend>
                            <bdo id='1YYQD'></bdo><ul id='1YYQD'></ul>