Add value getter and setter to entity
This commit is contained in:
parent
65b0644b5c
commit
bcf934ff7e
|
|
@ -32,22 +32,74 @@ extension Entity {
|
|||
return (compA, compB, compC)
|
||||
}
|
||||
|
||||
/// Get or set component instance by type via subscript.
|
||||
///
|
||||
/// **Behavior:**
|
||||
/// - If `Comp` is a component type that is currently *not* assigned to this entity,
|
||||
/// the new instance will be assigned to this entity.
|
||||
/// - If `Comp` is already assinged to this entity nothing happens.
|
||||
/// - If `Comp` is set to `nil` and an instance of `Comp` is assigned to this entity,
|
||||
/// `Comp` will be removed from this entity.
|
||||
@inlinable
|
||||
public subscript<C: Component, Value>(_ componentKeyPath: WritableKeyPath<C, Value>) -> Value? {
|
||||
public subscript<Comp>(_ componentType: Comp.Type) -> Comp? where Comp: Component {
|
||||
get { self.get(component: componentType) }
|
||||
nonmutating set {
|
||||
guard var comp = self.get(component: C.self),
|
||||
let value = newValue else {
|
||||
guard let newComponent = newValue else {
|
||||
self.remove(Comp.self)
|
||||
return
|
||||
}
|
||||
comp[keyPath: componentKeyPath] = value
|
||||
}
|
||||
get {
|
||||
self.get(component: C.self)?[keyPath: componentKeyPath]
|
||||
if self.get(component: componentType) === newComponent {
|
||||
return
|
||||
}
|
||||
self.assign(newComponent)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the value of a component using the key Path to the property in the component.
|
||||
/// - Parameter componentKeyPath: The `KeyPath` to the property of the given component.
|
||||
/// - Returns: If `Comp` is assigned to this entity the value at the given `KeyPath` is returned, nil otherwise.
|
||||
@inlinable
|
||||
public subscript<C: Component>(_ componentType: C.Type) -> C? {
|
||||
self.get(component: componentType)
|
||||
public func get<Comp, Value>(valueAt componentKeyPath: KeyPath<Comp, Value>) -> Value where Comp: Component {
|
||||
self.get(component: Comp.self)![keyPath: componentKeyPath]
|
||||
}
|
||||
|
||||
/// Get the value of a component using the key Path to the property in the component.
|
||||
@inlinable
|
||||
public subscript<Comp, Value>(_ componentKeyPath: KeyPath<Comp, Value>) -> Value where Comp: Component {
|
||||
self.get(valueAt: componentKeyPath)
|
||||
}
|
||||
|
||||
/// Set the value of a component using the key path to the property in the component.
|
||||
///
|
||||
/// **Behavior:**
|
||||
/// - If `Comp` is a component type that is currently *not* assigned to this entity,
|
||||
/// a new instance of `Comp` will be default initialized and `newValue` will be set at the given keyPath.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - newValue: The value to set.
|
||||
/// - componentKeyPath: The `ReferenceWritableKeyPath` to the property of the given component.
|
||||
/// - Returns: Returns true if an action was performed, false otherwise.
|
||||
@inlinable
|
||||
@discardableResult
|
||||
public func set<Comp, Value>(value newValue: Value, for componentKeyPath: ReferenceWritableKeyPath<Comp, Value>) -> Bool where Comp: Component & DefaultInitializable {
|
||||
guard has(Comp.self) else {
|
||||
let newInstance = Comp()
|
||||
newInstance[keyPath: componentKeyPath] = newValue
|
||||
return nexus.assign(component: newInstance, entityId: identifier)
|
||||
}
|
||||
|
||||
get(component: Comp.self).unsafelyUnwrapped[keyPath: componentKeyPath] = newValue
|
||||
return true
|
||||
}
|
||||
|
||||
/// Set the value of a component using the key path to the property in the component.
|
||||
///
|
||||
/// **Behavior:**
|
||||
/// - If `Comp` is a component type that is currently *not* assigned to this entity,
|
||||
/// a new instance of `Comp` will be default initialized and `newValue` will be set at the given keyPath.
|
||||
@inlinable
|
||||
public subscript<Comp, Value>(_ componentKeyPath: ReferenceWritableKeyPath<Comp, Value>) -> Value where Comp: Component & DefaultInitializable {
|
||||
get { self.get(valueAt: componentKeyPath) }
|
||||
nonmutating set { self.set(value: newValue, for: componentKeyPath) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,23 +21,30 @@ extension Nexus {
|
|||
componentIdsByEntity[entityId]?.count ?? 0
|
||||
}
|
||||
|
||||
public final func assign(component: Component, to entity: Entity) {
|
||||
@discardableResult
|
||||
public final func assign(component: Component, to entity: Entity) -> Bool {
|
||||
let entityId: EntityIdentifier = entity.identifier
|
||||
assign(component: component, entityId: entityId)
|
||||
delegate?.nexusEvent(ComponentAdded(component: component.identifier, toEntity: entity.identifier))
|
||||
defer { delegate?.nexusEvent(ComponentAdded(component: component.identifier, toEntity: entity.identifier)) }
|
||||
return assign(component: component, entityId: entityId)
|
||||
}
|
||||
|
||||
public final func assign<C>(component: C, to entity: Entity) where C: Component {
|
||||
@discardableResult
|
||||
public final func assign<C>(component: C, to entity: Entity) -> Bool where C: Component {
|
||||
assign(component: component, to: entity)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public final func get(component componentId: ComponentIdentifier, for entityId: EntityIdentifier) -> Component? {
|
||||
guard let uniformComponents = componentsByType[componentId] else {
|
||||
guard let uniformComponents = componentsByType[componentId], uniformComponents.contains(entityId.index) else {
|
||||
return nil
|
||||
}
|
||||
return uniformComponents.get(at: entityId.index)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public final func get<C>(componentId: ComponentIdentifier, entityId: EntityIdentifier) -> C? where C: Component {
|
||||
get(component: componentId, for: entityId) as? C
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public final func get(unsafeComponent componentId: ComponentIdentifier, for entityId: EntityIdentifier) -> Component {
|
||||
|
|
@ -90,11 +97,5 @@ extension Nexus {
|
|||
return removedAll
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public final func get<C>(componentId: ComponentIdentifier, entityId: EntityIdentifier) -> C? where C: Component {
|
||||
guard let uniformComponents = componentsByType[componentId] else {
|
||||
return nil
|
||||
}
|
||||
return uniformComponents.get(at: entityId.index) as? C
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
//
|
||||
|
||||
extension Nexus {
|
||||
@usableFromInline
|
||||
func assign<C>(components: C, to entityId: EntityIdentifier) where C: Collection, C.Element == Component {
|
||||
var iter = components.makeIterator()
|
||||
while let component = iter.next() {
|
||||
|
|
@ -28,14 +29,15 @@ extension Nexus {
|
|||
update(familyMembership: entityId)
|
||||
}
|
||||
|
||||
func assign(component: Component, entityId: EntityIdentifier) {
|
||||
@usableFromInline
|
||||
func assign(component: Component, entityId: EntityIdentifier) -> Bool {
|
||||
let componentId = component.identifier
|
||||
|
||||
// test if component is already assigned
|
||||
guard !has(componentId: componentId, entityId: entityId) else {
|
||||
delegate?.nexusNonFatalError("ComponentAdd collision: \(entityId) already has a component \(component)")
|
||||
assertionFailure("ComponentAdd collision: \(entityId) already has a component \(component)")
|
||||
return
|
||||
return false
|
||||
}
|
||||
|
||||
// add component instances to uniform component stores
|
||||
|
|
@ -46,8 +48,10 @@ extension Nexus {
|
|||
|
||||
// Update entity membership
|
||||
update(familyMembership: entityId)
|
||||
return true
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func insertComponentInstance(_ component: Component, _ componentId: ComponentIdentifier, _ entityId: EntityIdentifier) {
|
||||
if componentsByType[componentId] == nil {
|
||||
componentsByType[componentId] = ManagedContiguousArray<Component>()
|
||||
|
|
@ -55,10 +59,12 @@ extension Nexus {
|
|||
componentsByType[componentId]?.insert(component, at: entityId.index)
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func assign(_ componentId: ComponentIdentifier, _ entityId: EntityIdentifier) {
|
||||
componentIdsByEntity[entityId]!.insert(componentId)
|
||||
}
|
||||
|
||||
@usableFromInline
|
||||
func update(familyMembership entityId: EntityIdentifier) {
|
||||
// FIXME: iterating all families is costly for many families
|
||||
// FIXME: this could be parallelized
|
||||
|
|
|
|||
|
|
@ -10,17 +10,25 @@ import FirebladeECS
|
|||
class EmptyComponent: Component {
|
||||
}
|
||||
|
||||
class Name: Component {
|
||||
final class Name: Component, DefaultInitializable {
|
||||
var name: String
|
||||
init(name: String) {
|
||||
self.name = name
|
||||
}
|
||||
|
||||
convenience init() {
|
||||
self.init(name: "")
|
||||
}
|
||||
}
|
||||
|
||||
final class Position: Component {
|
||||
final class Position: Component, DefaultInitializable {
|
||||
var x: Int
|
||||
var y: Int
|
||||
|
||||
convenience init() {
|
||||
self.init(x: 0, y: 0)
|
||||
}
|
||||
|
||||
init(x: Int, y: Int) {
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
|
@ -28,11 +36,16 @@ final class Position: Component {
|
|||
}
|
||||
extension Position: Codable { }
|
||||
|
||||
class Velocity: Component {
|
||||
final class Velocity: Component, DefaultInitializable {
|
||||
var a: Float
|
||||
|
||||
init(a: Float) {
|
||||
self.a = a
|
||||
}
|
||||
|
||||
convenience init() {
|
||||
self.init(a: 0)
|
||||
}
|
||||
}
|
||||
|
||||
final class Party: Component {
|
||||
|
|
|
|||
|
|
@ -101,11 +101,23 @@ class EntityTests: XCTestCase {
|
|||
XCTAssertEqual(entity[\Name.name], "AnotherName")
|
||||
|
||||
entity[\Velocity.a] = 123
|
||||
XCTAssertNil(entity[\Velocity.a])
|
||||
XCTAssertEqual(entity[\Velocity.a], 123.0)
|
||||
|
||||
entity[Position.self]?.x = 1234
|
||||
XCTAssertEqual(entity[Position.self]?.x, 1234)
|
||||
XCTAssertNil(entity[Velocity.self]?.a)
|
||||
XCTAssertEqual(entity[Velocity.self]?.a, 123.0)
|
||||
|
||||
|
||||
// remove position component
|
||||
entity[Position.self] = nil
|
||||
XCTAssertNil(entity[Position.self])
|
||||
entity[Position.self] = pos // assign position comp instance
|
||||
XCTAssertTrue(entity[Position.self] === pos)
|
||||
entity[Position.self] = pos // re-assign
|
||||
XCTAssertTrue(entity[Position.self] === pos)
|
||||
entity[Position.self] = nil // remove position component
|
||||
XCTAssertNil(entity[Position.self])
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue