GPEngine 2.3.1

GPEngine 2.3.1

测试已测试
Lang语言 SwiftSwift
许可 MIT
发布上次发布2017年4月
SwiftSwift 版本3.0
SPM支持 SPM

Luiz Fernando 维护。



GPEngine 2.3.1

GPEngine

用 Swift 编写的 iOS 实体基础游戏框架。

该框架使用传统的实体-组件-系统,同时还包括空间的概念,这在以下文章中有描述:这篇文章

此 pod 还附带一个子 pod GPEngine/Serialization,允许使用 SwiftyJSON(见 序列化)将空间/子空间/实体/组件从 JSON 序列化到 JSON。

概念

此引擎与经典 ECS 引擎非常相似,但增加了 Spaces 的抽象。

经典 ECS 布局

╔══════════╗
║  Engine  ║
╚═════╤════╝ ╔════════╗╔════════╗╔════════╗
      ├──────╢ Entity ╟╢ Entity ╟╢ Entity ╟...
      │      ╚════════╝╚════════╝╚════════╝
      │      ╔════════╗╔════════╗╔════════╗
      └──────╢ System ╟╢ System ╟╢ System ╟...
             ╚════════╝╚════════╝╚════════╝

以这种方式,您不能轻松地将实体分组(例如,将敌人分为前景和背景敌人,背景敌人不与玩家交互)。这是可以通过组件/类型标志指定来实现的,但 Spaces 旨在将其作为一个显式的抽象。

基于 Spaces 的 ECS 布局

空间是实体的容器,实体独立地行动,从而在组合在一起的实体之间减少了一层抽象。为了帮助进行此类抽象,还提出了 Subspaces 的概念,该概念旨在针对系统分别分离空间/实体相关的数据(如单独的物理引擎实例,渲染相机位置等)。

╔══════════╗
║  Engine  ║
╚═════╤════╝
  ╔═══╧═══╗   ╔════════╗╔════════╗╔════════╗
  ║ Space ╟─┬─╢ Entity ╟╢ Entity ╟╢ Entity ╟...
  ╚═══╤═══╝ │ ╚════════╝╚════════╝╚════════╝
      │     │ ╔══════════╗
      │     └─╢ Subspace ║
      │       ╚══════════╝
  ╔═══╧═══╗   ╔════════╗╔════════╗╔════════╗
  ║ Space ╟─┬─╢ Entity ╟╢ Entity ╟╢ Entity ╟...
  ╚═══╤═══╝ │ ╚════════╝╚════════╝╚════════╝
      │     │ ╔══════════╗╔══════════╗
      │     └─╢ Subspace ╟╢ Subspace ║
      │       ╚══════════╝╚══════════╝
      │
      │      ╔════════╗╔════════╗╔════════╗
      └──────╢ System ╟╢ System ╟╢ System ╟...
             ╚════════╝╚════════╝╚════════╝

在此,实体被分组到空间中,其中每个空间完全隔离。还引入了子空间,这些空间有助于存储系统用来处理数据的状态。酷的一点是,空间只需要添加对其相关的子空间;如果子空间不需要渲染(例如,一个仍然“存活”但位于门后的不同游戏房间),则不需要添加 RenderingSubspace!

系统仍然是全局的,因为理想情况下它们应该是无状态的(通过子空间的帮助)。然后,系统将查询空间以获取具有相关组件的实体以及需要的子空间,如果可用,则使用其声明的逻辑对其采取行动。系统始终独立地对每个空间采取行动,就像它们是隔离的经典 ECS 引擎一样。

需求

支持Xcode 8.2 & Swift 3.0或更高版本。

安装

Swift包管理器

GPEngine也以Swift包的形式提供。

import PackageDescription

let package = Package(
    name: "project_name",
    targets: [],
    dependencies: [
        .Package(url: "https://github.com/LuizZak/GPEngine.git", majorVersion: 2, minor: 2)
    ]
)

作者

LuizZak, [email protected]

许可协议

GPEngine遵循MIT许可协议。更多详细信息请参阅LICENSE文件。

序列化

(此可选功能在

pod 'GPEngine/Serialization'

下可用)

您可以使用GameSerializer类序列化实体或整个空间(包括子空间/实体/组件)。

这允许您保存部分或完整的游戏状态,以及从预制的JSON结构中执行数据驱动的游戏状态初始化。

(有关如何将内容序列化的信息,请见下文的

序列化要求

)

let myProvider = MyTypeProvider() // Implements SerializationTypeProvider

let gameSerializer = GameSerializer(typeProvider: myProvider)
let mySerialized = try gameSerializer.serialize(myEntity)

// .serialize(_:) returns a Serialized object, call .serialize() on it to receive a JSON that you can store:
let json = mySerialized.serialized()

// Store json somewhere...

要反序列化实体,请使用以下过程

let json = // Retrieve entity JSON from somewhere...

// Retrieve it back to a Serialized object
// If this fails, that means you got a bad JSON :(
let mySerialized = try Serialized.deserialized(from: json)

// Requires explicit ': Entity' type annotation
let myEntity: Entity = try gameSerializer.extract(from: mySerialized)

// If we reached here, entity was deserialized correctly! Success!

Space的序列化和反序列化过程类似,并使用了相同的方法名。

Serialized.serialized()返回的序列化JSON遵循以下结构

{
    "contentType": "<String - one of the raw values of Serialized.ContentType>",
    "typeName": "<String - type name returned by your SerializationTypeProvider to retrieve the class to instantiate back>",
    "data": "<Any - this is the JSON returned by the object's serialize() method>"
}

序列化容器可以通过将它们添加到data字段中来嵌套于彼此之中,并通过

GameSerializer.extract<T: Serializable>(from: Serialized)

方法检索。但是,您必须实现自定义逻辑来执行此类操作。

SerializationTypeProvider

这是一个协议,必须实现以在反序列化期间提供您的自定义组件/子空间类型。

GameSerializer调用您的类型提供程序并提供序列化的类型名称,您必须返回Swift元类型(例如MyComponent.self)。

协议默认实现用于检索类型的序列化名称的方法,并返回

String(describing: Type.self)

可以使用一个数组来实现简单的类型提供程序,该数组存储游戏中所有已知可序列化类型,并使用预实现的

BasicSerializationTypeProvider

协议(假设每个可序列化类型最终都与它的类型匹配一个唯一的名称)。

class Provider: BasicSerializationTypeProvider {
    // Requirement from `BasicSerializationTypeProvider`
    var serializableTypes: [Serializable.Type] = [
        MySerializableComponent.self,
        MySerializableSubspace.self
    ]

    // Now `serializedName(for:)`/`deserialized(from:)` are automatically stubbed using `serializableTypes` array.
}

序列化要求

为了序列化实体和空间,您需要遵循以下要求

  • 对于实体,要添加到实体的每个

    Component

    都必须实现Serializable协议。
  • 对于空间,每个实体都必须遵循上述规则,以及每个也实现Serializable协议的子空间。

Serializable是用于通过JSON编码/解码对象的基本协议

/// Describes an object that can be serialized to and back from a JSON object.
/// Implementers of this protocol should take care of guaranteeing that the inner
/// state of the object remains the same when deserializing from a previously
/// serialized object.
public protocol Serializable {
    /// Initializes an instance of this type from a given serialized state.
    ///
    /// - Parameter json: A state that was previously serialized by an instance
    /// of this type using `serialized()`
    /// - Throws: Any type of error during deserialization.
    init(json: JSON) throws
    
    /// Serializes the state of this component into a JSON object.
    ///
    /// - Returns: The serialized state for this object.
    func serialized() -> JSON
}

要检查您的实体和空间是否完全可序列化,请使用实体的

GameSerializer.canSerialize(_:)

& GameSerializer.diagnoseSerialize(on:)方法或在您的空间上。

系统旨在为无状态,因此默认不支持序列化。但这不会阻止您将其添加到序列化类型提供程序中,并实现它们上的Serializable协议,从而使它们可序列化。