ECS (实体组件系统) 内核实现,用 Swift 编写。它没有发行依赖项,支持以下平台: iOS、macOS、tvOS、watchOS。
需求
开发
项目使用以下工具进行开发
- XCodeGen
- Cocoapods
- SwiftLint
- Sourcery
安装
要开始使用 LionECS,您首先需要决定如何将其集成到项目中。LionECS 支持以下工具
Cocoapods
要使用 Cocoapods 安装 LionECS,按照以下步骤进行
- 在您的 Podfile 中添加以下条目
pod 'LionECS'
- 然后运行
pod install
。
Carthage
使用Carthage安装LionECS,请按照以下步骤操作
- 在您的Cartfile中添加以下条目
github "LionGameEngine/LionECS"
- 然后运行
carthage update
Swift包管理器
使用Swift包管理器安装LionECS,请按照以下步骤操作
- 在您的
Package.swift
中添加以下包依赖项:.package(url: "https://github.com/LionGameEngine/LionECS.git", from: "0.0.4")
- 在您的
Package.swift
中添加以下目标依赖项:dependencies: ["LionECS"])
概览
以下是LionECS中使用的功能和概念的简要概述。
实体
实体代表ECS世界中的一个对象实例。它定义为一个包含id和版本的二元组
public struct Entity: Identifiable, Equatable, Hashable {
public let id: UInt64
public let version: UInt64
}
组件
组件是简单的结构体,用于存储实体的数据。它们描述了实体的实际构成。每个实体可以有一个指定类型的组件。要创建组件类型,您需要创建一个实现PComponent
协议的新结构体,这要求您返回组件标识符。不过,您可以通过简单地返回类型标识符作为默认标识符来做到这一点。例如
struct HealthComponent: PComponent {
var health: Float
}
系统
系统根据您的查询操作一个或多个组件。要创建系统,您可以选择实现PSystem
协议或从ComponentSystem
继承(这为您提供了对世界的访问),并实现func update()
。
世界
世界表示一个单独的模拟。它包含并允许访问:实体管理器、实体请求器、系统和组件管理器。当它被更新时,它也会更新你所有的系统。
组件管理器
提供对组件数据的访问。一切都是通用的组件管理器,因此核心组件管理逻辑可以轻松切换。
默认核心支持structs和枚举作为组件类型。
原型
描述实体实例具有的一组组件。
查询
查询
可以通过实体请求器执行查询。首先,你需要定义一个继承自PEntityQuery的查询。已经定义了EntityQuery。然后,使用实体请求器的queryEntities<Query: PEntityQuery>(query: Query)
方法。
查询结果
所有查询都必须定义其结果。为EntityQuery定义的是EntityQueryResult。它定义了遍历满足查询的实体的方法。
示例
以下为损害系统的示例。首先,我们需要实现所需的组件
struct DamageComponent: PComponent {
var damage: Float
}
struct HealthComponent: PComponent {
var health: Float
}
struct DeadComponent: PComponent {}
然后定义在组件上运行的系统
class DamageSystem: ComponentSystem<ComponentManager> {
override func update() {
let query = EntityQuery<ComponentManager>(filters: [Requires<DamageComponent>(), Requires<HealthComponent>(), Excludes<DeadComponent>()])
guard let result = try? entityRequester.queryEntities(query: query) else { return }
result.forEach { (entity: Entity, health: inout HealthComponent, damage: DamageComponent) in
defer {
try? componentManager.removeComponent(DamageComponent.self, fromEntity: entity)
}
health.health -= damage.damage
}
}
}
class DeathSystem: ComponentSystem<ComponentManager> {
override func update() {
let query = EntityQuery<ComponentManager>(filters: [Requires<HealthComponent>(), Excludes<DeadComponent>()])
guard let result = try? entityRequester.queryEntities(query: query) else { return }
result.forEach { (entity: Entity, health: HealthComponent) in
if health.health <= 0 {
try? componentManager.addComponent(DeadComponent(), toEntity: entity)
}
}
}
}
然后将其全部粘合在一起
class Game {
let world: World<ComponentManager>
let damageSystem: DamageSystem
let deathSystem: DeathSystem
let player: Entity
init() {
let playerPrototype = PrototypeBuilder()
.add(HealthComponent.self)
.build()
let manager: ComponentManager = ComponentManager()
world = World<ComponentManager>(componentManager: manager)
damageSystem = world.getOrCreateSystem()
deathSystem = world.getOrCreateSystem()
player = world.entityManager.createEntity(withPrototype: playerPrototype)
try! world.componentManager.updateComponent(HealthComponent(health: 123), ofEntity: player)
try! world.componentManager.addComponent(DamageComponent(damage: 23), toEntity: player)
gameloop {
world.update()
}
}
func gameloop(update: () -> Void) {
while true {
update()
}
}
}
欲查看更详细的示例,请参阅源代码。
贡献
该项目由 托马兹·莱万多夫斯基 创建。
如果您添加了新功能或修复了错误,您可以创建一个拉取请求。如果您有任何功能请求,请随时提交。
许可证
LionECS 在 MIT 许可证下发布。有关更多信息,请参阅 License.md。