rrbox / ecs-swift

Entity Component System for swift
MIT License
3 stars 0 forks source link

Systems #28

Closed rrbox closed 8 months ago

rrbox commented 9 months ago

新しい System の実装です。

18 Schedule の登場により、これまでの Set up systems, Update systems, Event systems が 1 つに統合され、Systems というモジュールで実装されます。

rrbox commented 9 months ago
open class SystemExecute {
    public func execute(_ bufferRef: BufferRef) {

    }
}

final public class SystemStorage {
    final class SystemRegistry: WorldStorageElement {
        var systems: [Schedule: [SystemExecute]] = [:]
    }

    func setUpSystemRegistry() {
        self.buffer.map.push(SystemRegistry())
    }

    func registerSystemRegistry(schedule: Schedule) {
        self.buffer.map.valueRef(ofType: SystemRegistry.self)!.body.systems[schedule] = []
    }

    public func systems(_ schedule: Schedule) -> [SystemExecute] {
        self.buffer.map.valueRef(ofType: SystemRegistry.self)!.body.systems[schedule]!
    }

    func addSystem(_ schedule: Schedule, _ system: SystemExecute) {
        self.buffer.map.valueRef(ofType: SystemRegistry.self)!.body.systems[schedule]!.append(system)
    }
}

public extension WorldStorageRef {
    var systemStorage: SystemStorage {
        SystemStorage(buffer: self)
    }
}
rrbox commented 9 months ago
class System<P: SystemParameter>: SystemExecute {
    let execute: (P) -> ()

   init(_ execute: @escaping (P) -> ()) {
        self.execute = execute
    }

    func execute(_ worldStorageRef: WorldStorageRef) {
        self.execute(P.getParameter(from: worldStorageRef))
    }
}

public extension World {
    func addSystem<P: SystemParameter>(_ schedule: Schedule, _ system: @escaping (P) -> ()) {
        self.worldStorage.systemStorage.addSystem(schedule, System<P>(system))
        P.register(to: self.worldStorageRef)
    }
}
rrbox commented 9 months ago
class System2<P0: SystemParameter, P1: SystemParameter>: SystemExecute {
    let execute: (P0, P1) -> ()

   init(_ execute: @escaping (P0, P1) -> ()) {
        self.execute = execute
    }

    func execute(_ worldStorageRef: WorldStorageRef) {
        self.execute(P0.getParameter(from: worldStorageRef), P1.getParameter(from: worldStorageRef))
    }
}

public extension World {
    func addSystem<P0: SystemParameter, P1: SystemParameter>(_ schedule: Schedule, _ system: @escaping (P0, P1) -> ()) {
        self.worldStorage.systemStorage.addSystem(schedule, System2<P0, P1>(system))
        P0.register(to: self.worldStorageRef)
        P1.register(to: self.worldStorageRef)
    }
}
rrbox commented 9 months ago
class System3<P0: SystemParameter, P1: SystemParameter, P2: SystemParameter>: SystemExecute {
    let execute: (P0, P1, P2) -> ()

    init(_ execute: @escaping (P0, P1, P2) -> ()) {
        self.execute = execute
    }

    override func execute(worlfBuffer buffer: WorldBuffer) {
        self.execute(P0.getParameter(from: buffer)!,
                     P1.getParameter(from: buffer)!,
                     P2.getParameter(from: buffer)!)
    }
}

public extension World {
    @discardableResult func addSystem<P0: SystemParameter, P1: SystemParameter, P2: SystemParameter>(
        _ schedule: Schedule,
        _ system: @escaping (P0, P1, P2) -> ()
    ) -> World {
        self.worldStorage.systemStorage.addSystem(schedule, System3(system))
        P0.register(to: buffer)
        P1.register(to: buffer)
        P2.register(to: buffer)
        return self
    }
}
rrbox commented 9 months ago
class System4<P0: SystemParameter, P1: SystemParameter, P2: SystemParameter, P3: SystemParameter>: SystemExecute {
    let execute: (P0, P1, P2, P3) -> ()

    init(_ execute: @escaping (P0, P1, P2, P3) -> ()) {
        self.execute = execute
    }

    override func execute(worlfBuffer buffer: WorldBuffer) {
        self.execute(P0.getParameter(from: buffer)!,
                     P1.getParameter(from: buffer)!,
                     P2.getParameter(from: buffer)!,
                     P3.getParameter(from: buffer)!)
    }
}

public extension World {
    @discardableResult func addSystem<P0: SystemParameter, P1: SystemParameter, P2: SystemParameter, P3: SystemParameter>(
        _ schedule: Schedule,
        _ system: @escaping (P0, P1, P2, P3) -> ()
    ) -> World {
        self.worldStorage.systemStorage.addSystem(schedule, System4(system))
        P0.register(to: buffer)
        P1.register(to: buffer)
        P2.register(to: buffer)
        P3.register(to: buffer)
        return self
    }
}
rrbox commented 9 months ago
class System5<P0: SystemParameter, P1: SystemParameter, P2: SystemParameter, P3: SystemParameter, P4: SystemParameter>: SystemExecute {
    let execute: (P0, P1, P2, P3, P4) -> ()

    init(_ execute: @escaping (P0, P1, P2, P3, P4) -> ()) {
        self.execute = execute
    }

    override func execute(worlfBuffer buffer: WorldBuffer) {
        self.execute(P0.getParameter(from: buffer)!,
                     P1.getParameter(from: buffer)!,
                     P2.getParameter(from: buffer)!,
                     P3.getParameter(from: buffer)!,
                     P4.getParameter(from: buffer)!)
    }
}

public extension World {
    @discardableResult func addSystem<P0: SystemParameter, P1: SystemParameter, P2: SystemParameter, P3: SystemParameter, P4: SystemParameter>(
        _ schedule: Schedule,
        _ system: @escaping (P0, P1, P2, P3, P4) -> ()
    ) -> World {
        self.worldStorage.systemStorage.addSystem(schedule, System5(system))
        P0.register(to: buffer)
        P1.register(to: buffer)
        P2.register(to: buffer)
        P3.register(to: buffer)
        P4.register(to: buffer)
        return self
    }
}
rrbox commented 9 months ago

ジェネリックなシステムを定義する方法

上記の実装では、System 型が全て internal なので、ジェネリックなシステムを定義することが難しいです。

ジェネリックなシステムの例

struct Contaner<Value>: Component {
    let value: Value
}

func genericSystem<Value>(query: Query<Container<Value>>) {
    query.update { _, container in
        print(container.value)
    }
}

// 関数として定義はできるが、World に追加する方法がない。
let world = World()
    .addSystem(.update, genericSystem(query:)) // コンパイルエラー!! 型パラメータが推論できない…
    .addSystem(.update, genericSystem<String>(query:)) // コンパイルエラー!!Swift ではこの記述ができない…

これはクロージャにジェネリクスを適用できないこと、Swift では関数に型パラメータを記述できないこと (必ず引数を利用して明示する必要がある) ことが主な原因です。

解決策

現在の Swift の仕様(Swift 5.9)では ecs-swift 側からこの問題を解決する方法が考えられません。そこで、以下のようにしてジェネリックなシステムを定義してもらうように Wikis などで促すことにしましょう。

enum GenericSystem<Value> {
    static func system(query: Query<Container<Value>>) {
        query.update { _, container in
            print(container.value)
        }
    }
}

現状、最も短いコードで system をジェネリックにすることができる方法だと思います。

// 追加も簡単寄り。
let world = World()
    .addSystem(.update, GenericSystem<String>.system(query:)) // OK!
rrbox commented 8 months ago

23 完了しました