Merge branch 'feature/bulk-components' into develop
This commit is contained in:
commit
968880560c
|
|
@ -22,31 +22,9 @@ extension Nexus {
|
|||
}
|
||||
|
||||
public final func assign(component: Component, to entity: Entity) {
|
||||
let componentId: ComponentIdentifier = component.identifier
|
||||
let entityId: EntityIdentifier = entity.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
|
||||
}
|
||||
|
||||
// add component instances to uniform component stores
|
||||
if componentsByType[componentId] == nil {
|
||||
componentsByType[componentId] = ManagedContiguousArray<Component>()
|
||||
}
|
||||
componentsByType[componentId]?.insert(component, at: entityId.id)
|
||||
|
||||
// assigns the component id to the entity id
|
||||
if componentIdsByEntity[entityId] == nil {
|
||||
componentIdsByEntity[entityId] = Set<ComponentIdentifier>()
|
||||
}
|
||||
componentIdsByEntity[entityId]?.insert(componentId) //, at: componentId.hashValue)
|
||||
|
||||
update(familyMembership: entityId)
|
||||
|
||||
delegate?.nexusEvent(ComponentAdded(component: componentId, toEntity: entity.identifier))
|
||||
assign(component: component, entityId: entityId)
|
||||
delegate?.nexusEvent(ComponentAdded(component: component.identifier, toEntity: entity.identifier))
|
||||
}
|
||||
|
||||
public final func assign<C>(component: C, to entity: Entity) where C: Component {
|
||||
|
|
|
|||
|
|
@ -17,14 +17,14 @@ extension Nexus {
|
|||
@discardableResult
|
||||
public func createEntity(with components: Component...) -> Entity {
|
||||
let newEntity = createEntity()
|
||||
components.forEach { newEntity.assign($0) }
|
||||
assign(components: components, to: newEntity.identifier)
|
||||
return newEntity
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func createEntity<C>(with components: C) -> Entity where C: Collection, C.Element == Component {
|
||||
let entity = self.createEntity()
|
||||
components.forEach { entity.assign($0) }
|
||||
assign(components: components, to: entity.identifier)
|
||||
return entity
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
//
|
||||
// Nexus+FamilyUpdate.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 14.02.19.
|
||||
//
|
||||
|
||||
extension Nexus {
|
||||
/// will be called on family init
|
||||
final func onFamilyInit(traits: FamilyTraitSet) {
|
||||
guard familyMembersByTraits[traits] == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
familyMembersByTraits[traits] = UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id>()
|
||||
update(familyMembership: traits)
|
||||
}
|
||||
|
||||
final func update(familyMembership traits: FamilyTraitSet) {
|
||||
// FIXME: iterating all entities is costly for many entities
|
||||
var iter = entityStorage.makeIterator()
|
||||
while let entityId = iter.next() {
|
||||
update(membership: traits, for: entityId)
|
||||
}
|
||||
}
|
||||
|
||||
final func update(familyMembership entityId: EntityIdentifier) {
|
||||
// FIXME: iterating all families is costly for many families
|
||||
var iter = familyMembersByTraits.keys.makeIterator()
|
||||
while let traits = iter.next() {
|
||||
update(membership: traits, for: entityId)
|
||||
}
|
||||
}
|
||||
|
||||
final func update(membership traits: FamilyTraitSet, for entityId: EntityIdentifier) {
|
||||
guard let componentIds = componentIdsByEntity[entityId] else {
|
||||
// no components - so skip
|
||||
return
|
||||
}
|
||||
|
||||
let isMember: Bool = self.isMember(entity: entityId, inFamilyWithTraits: traits)
|
||||
if !exists(entity: entityId) && isMember {
|
||||
remove(entityWithId: entityId, fromFamilyWithTraits: traits)
|
||||
return
|
||||
}
|
||||
|
||||
let isMatch: Bool = traits.isMatch(components: componentIds)
|
||||
|
||||
switch (isMatch, isMember) {
|
||||
case (true, false):
|
||||
add(entityWithId: entityId, toFamilyWithTraits: traits)
|
||||
delegate?.nexusEvent(FamilyMemberAdded(member: entityId, toFamily: traits))
|
||||
|
||||
case (false, true):
|
||||
remove(entityWithId: entityId, fromFamilyWithTraits: traits)
|
||||
delegate?.nexusEvent(FamilyMemberRemoved(member: entityId, from: traits))
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
final func add(entityWithId entityId: EntityIdentifier, toFamilyWithTraits traits: FamilyTraitSet) {
|
||||
familyMembersByTraits[traits]!.insert(entityId, at: entityId.id)
|
||||
}
|
||||
|
||||
final func remove(entityWithId entityId: EntityIdentifier, fromFamilyWithTraits traits: FamilyTraitSet) {
|
||||
familyMembersByTraits[traits]!.remove(at: entityId.id)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
//
|
||||
// Nexus+Internal.swift
|
||||
// FirebladeECS
|
||||
//
|
||||
// Created by Christian Treffs on 14.02.19.
|
||||
//
|
||||
|
||||
extension Nexus {
|
||||
func assign<C>(components: C, to entityId: EntityIdentifier) where C: Collection, C.Element == Component {
|
||||
var iter = components.makeIterator()
|
||||
while let component = iter.next() {
|
||||
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
|
||||
}
|
||||
|
||||
// add component instances to uniform component stores
|
||||
insertComponentInstance(component, componentId, entityId)
|
||||
|
||||
// assigns the component id to the entity id
|
||||
assign(componentId, entityId)
|
||||
}
|
||||
|
||||
// Update entity membership
|
||||
update(familyMembership: entityId)
|
||||
}
|
||||
|
||||
func assign(component: Component, entityId: EntityIdentifier) {
|
||||
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
|
||||
}
|
||||
|
||||
// add component instances to uniform component stores
|
||||
insertComponentInstance(component, componentId, entityId)
|
||||
|
||||
// assigns the component id to the entity id
|
||||
assign(componentId, entityId)
|
||||
|
||||
// Update entity membership
|
||||
update(familyMembership: entityId)
|
||||
}
|
||||
|
||||
func insertComponentInstance(_ component: Component, _ componentId: ComponentIdentifier, _ entityId: EntityIdentifier) {
|
||||
if componentsByType[componentId] == nil {
|
||||
componentsByType[componentId] = ManagedContiguousArray<Component>()
|
||||
}
|
||||
componentsByType[componentId]?.insert(component, at: entityId.id)
|
||||
}
|
||||
|
||||
func assign(_ componentId: ComponentIdentifier, _ entityId: EntityIdentifier) {
|
||||
if componentIdsByEntity[entityId] == nil {
|
||||
componentIdsByEntity[entityId] = Set<ComponentIdentifier>(arrayLiteral: componentId)
|
||||
} else {
|
||||
componentIdsByEntity[entityId]?.insert(componentId)
|
||||
}
|
||||
}
|
||||
|
||||
func assign(_ componentIds: Set<ComponentIdentifier>, _ entityId: EntityIdentifier) {
|
||||
if componentIdsByEntity[entityId] == nil {
|
||||
componentIdsByEntity[entityId] = componentIds
|
||||
} else {
|
||||
componentIdsByEntity[entityId]?.formUnion(componentIds)
|
||||
}
|
||||
}
|
||||
|
||||
func update(familyMembership entityId: EntityIdentifier) {
|
||||
// FIXME: iterating all families is costly for many families
|
||||
// FIXME: this could be parallelized
|
||||
var iter = familyMembersByTraits.keys.makeIterator()
|
||||
while let traits = iter.next() {
|
||||
update(membership: traits, for: entityId)
|
||||
}
|
||||
}
|
||||
|
||||
/// will be called on family init
|
||||
func onFamilyInit(traits: FamilyTraitSet) {
|
||||
guard familyMembersByTraits[traits] == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
familyMembersByTraits[traits] = UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Id>()
|
||||
update(familyMembership: traits)
|
||||
}
|
||||
|
||||
func update(familyMembership traits: FamilyTraitSet) {
|
||||
// FIXME: iterating all entities is costly for many entities
|
||||
var iter = entityStorage.makeIterator()
|
||||
while let entityId = iter.next() {
|
||||
update(membership: traits, for: entityId)
|
||||
}
|
||||
}
|
||||
|
||||
func update(membership traits: FamilyTraitSet, for entityId: EntityIdentifier) {
|
||||
guard let componentIds = componentIdsByEntity[entityId] else {
|
||||
// no components - so skip
|
||||
return
|
||||
}
|
||||
|
||||
let isMember: Bool = self.isMember(entity: entityId, inFamilyWithTraits: traits)
|
||||
if !exists(entity: entityId) && isMember {
|
||||
remove(entityWithId: entityId, fromFamilyWithTraits: traits)
|
||||
return
|
||||
}
|
||||
|
||||
let isMatch: Bool = traits.isMatch(components: componentIds)
|
||||
|
||||
switch (isMatch, isMember) {
|
||||
case (true, false):
|
||||
add(entityWithId: entityId, toFamilyWithTraits: traits)
|
||||
delegate?.nexusEvent(FamilyMemberAdded(member: entityId, toFamily: traits))
|
||||
|
||||
case (false, true):
|
||||
remove(entityWithId: entityId, fromFamilyWithTraits: traits)
|
||||
delegate?.nexusEvent(FamilyMemberRemoved(member: entityId, from: traits))
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func add(entityWithId entityId: EntityIdentifier, toFamilyWithTraits traits: FamilyTraitSet) {
|
||||
familyMembersByTraits[traits]!.insert(entityId, at: entityId.id)
|
||||
}
|
||||
|
||||
func remove(entityWithId entityId: EntityIdentifier, fromFamilyWithTraits traits: FamilyTraitSet) {
|
||||
familyMembersByTraits[traits]!.remove(at: entityId.id)
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
/// an element from the sparse set.
|
||||
///
|
||||
/// See <https://github.com/bombela/sparseset/blob/master/src/lib.rs> for a reference implementation.
|
||||
public struct UnorderedSparseSet<Element, Key: Hashable & Codable> {
|
||||
public final class UnorderedSparseSet<Element, Key: Hashable & Codable> {
|
||||
/// An index into the dense store.
|
||||
public typealias DenseIndex = Int
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ public struct UnorderedSparseSet<Element, Key: Hashable & Codable> {
|
|||
@usableFromInline var dense: DenseStore
|
||||
@usableFromInline var sparse: SparseStore
|
||||
|
||||
public init() {
|
||||
public convenience init() {
|
||||
self.init(sparse: [:], dense: [])
|
||||
}
|
||||
|
||||
|
|
@ -44,7 +44,7 @@ public struct UnorderedSparseSet<Element, Key: Hashable & Codable> {
|
|||
|
||||
@inlinable
|
||||
public func contains(_ key: Key) -> Bool {
|
||||
find(at: key) != nil
|
||||
findIndex(at: key) != nil
|
||||
}
|
||||
|
||||
/// Inset an element for a given key into the set in O(1).
|
||||
|
|
@ -55,15 +55,15 @@ public struct UnorderedSparseSet<Element, Key: Hashable & Codable> {
|
|||
/// - key: the key
|
||||
/// - Returns: true if new, false if replaced.
|
||||
@discardableResult
|
||||
public mutating func insert(_ element: Element, at key: Key) -> Bool {
|
||||
if let (denseIndex, _) = find(at: key) {
|
||||
public func insert(_ element: Element, at key: Key) -> Bool {
|
||||
if let denseIndex = findIndex(at: key) {
|
||||
dense[denseIndex] = Entry(key: key, element: element)
|
||||
return false
|
||||
}
|
||||
|
||||
let nIndex = dense.count
|
||||
dense.append(Entry(key: key, element: element))
|
||||
sparse[key] = nIndex
|
||||
sparse.updateValue(nIndex, forKey: key)
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -73,16 +73,12 @@ public struct UnorderedSparseSet<Element, Key: Hashable & Codable> {
|
|||
/// - Returns: the element or nil of key not found.
|
||||
@inlinable
|
||||
public func get(at key: Key) -> Element? {
|
||||
guard let (_, element) = find(at: key) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return element
|
||||
findElement(at: key)
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func get(unsafeAt key: Key) -> Element {
|
||||
find(at: key).unsafelyUnwrapped.1
|
||||
findElement(at: key).unsafelyUnwrapped
|
||||
}
|
||||
|
||||
/// Removes the element entry for given key in O(1).
|
||||
|
|
@ -90,8 +86,8 @@ public struct UnorderedSparseSet<Element, Key: Hashable & Codable> {
|
|||
/// - Parameter key: the key
|
||||
/// - Returns: removed value or nil if key not found.
|
||||
@discardableResult
|
||||
public mutating func remove(at key: Key) -> Entry? {
|
||||
guard let (denseIndex, _) = find(at: key) else {
|
||||
public func remove(at key: Key) -> Entry? {
|
||||
guard let denseIndex = findIndex(at: key) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -105,7 +101,7 @@ public struct UnorderedSparseSet<Element, Key: Hashable & Codable> {
|
|||
}
|
||||
|
||||
@inlinable
|
||||
public mutating func removeAll(keepingCapacity: Bool = false) {
|
||||
public func removeAll(keepingCapacity: Bool = false) {
|
||||
sparse.removeAll(keepingCapacity: keepingCapacity)
|
||||
dense.removeAll(keepingCapacity: keepingCapacity)
|
||||
}
|
||||
|
|
@ -115,22 +111,30 @@ public struct UnorderedSparseSet<Element, Key: Hashable & Codable> {
|
|||
///
|
||||
/// - Parameter denseIndex: the dense index
|
||||
/// - Returns: the element entry
|
||||
private mutating func swapRemove(at denseIndex: Int) -> Entry {
|
||||
private func swapRemove(at denseIndex: Int) -> Entry {
|
||||
dense.swapAt(denseIndex, dense.count - 1)
|
||||
return dense.removeLast()
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func find(at key: Key) -> (Int, Element)? {
|
||||
func findIndex(at key: Key) -> Int? {
|
||||
guard let denseIndex = sparse[key], denseIndex < count else {
|
||||
return nil
|
||||
}
|
||||
return denseIndex
|
||||
}
|
||||
|
||||
@inlinable
|
||||
func findElement(at key: Key) -> Element? {
|
||||
guard let denseIndex = findIndex(at: key) else {
|
||||
return nil
|
||||
}
|
||||
let entry = self.dense[denseIndex]
|
||||
guard entry.key == key else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (denseIndex, entry.element)
|
||||
return entry.element
|
||||
}
|
||||
|
||||
@inlinable public var first: Element? {
|
||||
|
|
|
|||
|
|
@ -387,7 +387,7 @@ class SparseSetTests: XCTestCase {
|
|||
|
||||
func testSparseSetDoubleRemove() {
|
||||
class AClass { }
|
||||
var set = UnorderedSparseSet<AClass, Int>()
|
||||
let set = UnorderedSparseSet<AClass, Int>()
|
||||
let a = AClass()
|
||||
let b = AClass()
|
||||
set.insert(a, at: 0)
|
||||
|
|
@ -471,7 +471,7 @@ class SparseSetTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testSparseSetReduce() {
|
||||
var characters = UnorderedSparseSet<Character, Int>()
|
||||
let characters = UnorderedSparseSet<Character, Int>()
|
||||
|
||||
characters.insert("H", at: 4)
|
||||
characters.insert("e", at: 13)
|
||||
|
|
@ -497,7 +497,7 @@ class SparseSetTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testSubscript() {
|
||||
var characters = UnorderedSparseSet<Character, Int>()
|
||||
let characters = UnorderedSparseSet<Character, Int>()
|
||||
|
||||
characters[4] = "H"
|
||||
characters[13] = "e"
|
||||
|
|
@ -528,7 +528,7 @@ class SparseSetTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testStartEndIndex() {
|
||||
var set = UnorderedSparseSet<Character, Int>()
|
||||
let set = UnorderedSparseSet<Character, Int>()
|
||||
|
||||
set.insert("C", at: 33)
|
||||
set.insert("A", at: 11)
|
||||
|
|
@ -541,7 +541,7 @@ class SparseSetTests: XCTestCase {
|
|||
|
||||
func testAlternativeKey() {
|
||||
|
||||
var set = UnorderedSparseSet<Character, String>()
|
||||
let set = UnorderedSparseSet<Character, String>()
|
||||
|
||||
set.insert("A", at: "a")
|
||||
set.insert("C", at: "c")
|
||||
|
|
|
|||
Loading…
Reference in New Issue