-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ECS に対応しやすいバインディングシステム #114
Labels
Comments
rrbox
added
type: Enhancement
New feature or request
type: Discussion
機能や利用方法などの考察をメモします
labels
Jul 14, 2023
Model to Widget (Output)Button
|
Widget to Model (Input)Node の状態を評価して Model に反映させる関数を用意することが最適だと思われます。 |
SpriteKit 側からの SKNode の更新(物理演算による座標, 回転など)はすべて無視しても問題ありません。なぜなら、このライブラリが UI 専用だからです。 |
データや UI モデルの更新を update で行うべきか? という提案もあります。 |
Property wrapper の活用についても考えましょう。 Widget protocol に準拠した構造体のインスタンスを格納した変数から SKNode をコントロールするコードを考えてみました。 まずは構造体のプロパティの変更を SKNode に伝達するための Attribute です。 import Combine
class Location<Wrapped> {
let publisher: CurrentValueSubject<Wrapped, Never>
init(value: Wrapped) {
self.publisher = CurrentValueSubject<Wrapped, Never>(value)
}
public func value<T>(keyPath: KeyPath<Wrapped, T>) -> T {
self.publisher.value[keyPath: keyPath]
}
}
@propertyWrapper
@dynamicMemberLookup
public struct WidgetBinding<Wrapped> {
let publisher: AnyPublisher<Wrapped, Never>
public var wrappedValue: Wrapped {
var result: Wrapped?
publisher.sink { result = $0 }
return result!
}
init<P: Publisher>(publisher: P) where P.Output == Wrapped, P.Failure == Never {
self.publisher = publisher.eraseToAnyPublisher()
}
public subscript<Subject>(dynamicMember keyPath: KeyPath<Wrapped, Subject>) -> WidgetBinding<Subject> {
WidgetBinding<Subject>(publisher: self.publisher.map { $0[keyPath: keyPath] })
}
}
@propertyWrapper public struct WidgetState<Wrapped> {
let location: Location<Wrapped>
public var projectedValue: WidgetBinding<Wrapped> {
WidgetBinding(publisher: self.location.publisher)
}
public var wrappedValue: Wrapped {
get {
self.location.publisher.value
}
set(v) {
self.location.publisher.send(v)
}
}
public init(wrappedValue: Wrapped) {
self.location = Location(value: wrappedValue)
}
}
extension Location {
func optional() -> Location<Wrapped?> {
Location<Wrapped?>(value: self.publisher.value)
}
}
extension WidgetBinding {
func optional() -> WidgetBinding<Wrapped?> {
WidgetBinding<Wrapped?>(publisher: self.publisher.map { Optional($0) })
}
}
extension WidgetState {
func optional() -> WidgetState<Wrapped?> {
WidgetState<Wrapped?>(wrappedValue: self.location.publisher.value)
}
} 使用例です public struct Text: Widget, CreateModels {
@WidgetBinding var text: String?
public var body: Never {
fatalError()
}
public func createModels() -> (SKLabelNode, AnyCancellable) {
let result = SKLabelNode(text: self.text)
let subscriber = self._text
.publisher
.assign(to: \.text, on: result)
return (result, subscriber)
}
public init(_ text: String) {
self._text = .init(publisher: Just(text).map { Optional($0) })
}
public init(_ text: WidgetBinding<String>) {
self._text = text.optional()
}
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
現在、UI とモデルとのやり取りをゲーム向けに最適化することが要求されています。また、ECS (Entity Component System) をゲームの主要な設計(構造)にすることも考えられています。そこで、UI とモデルとのやり取りを ECS で行えるような仕組みを作ってみようと考えています。
この issue では ECS に対応可能な API の設計を目標にします。
ECS は基本的に「データ指向」であるため、同じようにデータ指向の API にすることで ECS に対応しやすくなるはずです。
The text was updated successfully, but these errors were encountered: