Merge branch 'feature/obsolete-relatives' into develop

This commit is contained in:
Christian Treffs 2020-08-06 15:58:33 +02:00
commit 680ab42176
No known key found for this signature in database
GPG Key ID: 49A4B4B460BE3ED4
9 changed files with 0 additions and 328 deletions

View File

@ -203,37 +203,6 @@ class GameLogicSystem {
``` ```
### 👫 Relatives (deprecated)
This ECS implementation provides an integrated way of creating a [directed acyclic graph (DAG)](https://en.wikipedia.org/wiki/Directed_acyclic_graph) hierarchy of entities by forming parent-child relationships. Entities can become children of a parent entity. In family terms they become **relatives**. Families provide iteration over these relationships.
The entity hierarchy implementation does not use an additional component therefore keeping the hierarchy intact over different component-families.
This feature is especially useful for implementing a [scene graph](https://en.wikipedia.org/wiki/Scene_graph).
```swift
// create entities with 0 to n components
let parent: Entity = nexus.createEntity(with: Position(x: 1, y: 1), SomeOtherComponent(...))
let child: Entity = nexus.createEntity(with: Position(x: 2, y: 2))
let child2: Entity = nexus.createEntity(with: Position(x: 3, y: 3), MySpecialComponent(...))
// create relationships between entities
parent.addChild(child)
child.addChild(child2)
// or remove them
// parent.removeChild(child)
// iterate over component families descending the graph
nexus.family(requires: Position.self)
.descendRelatives(from: parent) // provide the start entity (aka root "node")
.forEach { (parent: Position, child: Position) in
// parent: the current parent component
// child: the current child component
// update your components hierarchically
child.x += parent.x
child.y += parent.y
}
```
### 🔗 Serialization ### 🔗 Serialization

View File

@ -102,34 +102,6 @@ public struct Entity {
nexus.destroy(entity: self) nexus.destroy(entity: self)
} }
/// Add an entity as child.
/// - Parameter entity: The child entity.
@discardableResult
@available(*, deprecated, message: "This will be removed in the next minor update.")
public func addChild(_ entity: Entity) -> Bool {
nexus.addChild(entity, to: self)
}
/// Remove entity as child.
/// - Parameter entity: The child entity.
@discardableResult
@available(*, deprecated, message: "This will be removed in the next minor update.")
public func removeChild(_ entity: Entity) -> Bool {
nexus.removeChild(entity, from: self)
}
/// Removes all children from this entity.
@available(*, deprecated, message: "This will be removed in the next minor update.")
public func removeAllChildren() {
nexus.removeAllChildren(from: self)
}
/// Returns the number of children for this entity.
@available(*, deprecated, message: "This will be removed in the next minor update.")
public var numChildren: Int {
nexus.numChildren(for: self)
}
/// Returns an iterator over all components of this entity. /// Returns an iterator over all components of this entity.
@inlinable @inlinable
public func makeComponentsIterator() -> ComponentsIterator { public func makeComponentsIterator() -> ComponentsIterator {

View File

@ -130,61 +130,6 @@ extension Family {
extension Family.EntityComponentIterator: LazySequenceProtocol { } extension Family.EntityComponentIterator: LazySequenceProtocol { }
// MARK: - relatives iterator
extension Family {
@inlinable
public func descendRelatives(from root: Entity) -> RelativesIterator {
RelativesIterator(family: self, root: root)
}
public struct RelativesIterator: IteratorProtocol {
@usableFromInline unowned let nexus: Nexus
@usableFromInline let familyTraits: FamilyTraitSet
@usableFromInline var relatives: ContiguousArray<(EntityIdentifier, EntityIdentifier)>
public init(family: Family<R>, root: Entity) {
self.nexus = family.nexus
self.familyTraits = family.traits
// FIXME: this is not the most efficient way to aggregate all parent child tuples
// Problems:
// - allocates new memory
// - needs to be build on every iteration
// - relies on isMember check
self.relatives = []
self.relatives.reserveCapacity(family.memberIds.count)
aggregateRelativesBreathFirst(root.identifier)
relatives.reverse()
}
mutating func aggregateRelativesBreathFirst(_ parent: EntityIdentifier) {
guard let children = nexus.childrenByParentEntity[parent] else {
return
}
children
.compactMap { child in
guard nexus.isMember(child, in: familyTraits) else {
return nil
}
relatives.append((parent, child))
return child
}
.forEach { aggregateRelativesBreathFirst($0) }
}
public mutating func next() -> R.RelativesDescending? {
guard let (parentId, childId) = relatives.popLast() else {
return nil
}
return R.relativesDescending(nexus: nexus, parentId: parentId, childId: childId)
}
}
}
extension Family.RelativesIterator: LazySequenceProtocol { }
// MARK: - member creation // MARK: - member creation
extension Family { extension Family {
/// Create a new entity with components required by this family. /// Create a new entity with components required by this family.

View File

@ -9,7 +9,6 @@ public protocol FamilyRequirementsManaging {
associatedtype Components associatedtype Components
associatedtype ComponentTypes associatedtype ComponentTypes
associatedtype EntityAndComponents associatedtype EntityAndComponents
associatedtype RelativesDescending
init(_ types: ComponentTypes) init(_ types: ComponentTypes)
@ -17,7 +16,5 @@ public protocol FamilyRequirementsManaging {
static func components(nexus: Nexus, entityId: EntityIdentifier) -> Components static func components(nexus: Nexus, entityId: EntityIdentifier) -> Components
static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> EntityAndComponents static func entityAndComponents(nexus: Nexus, entityId: EntityIdentifier) -> EntityAndComponents
static func relativesDescending(nexus: Nexus, parentId: EntityIdentifier, childId: EntityIdentifier) -> RelativesDescending
static func createMember(nexus: Nexus, components: Components) -> Entity static func createMember(nexus: Nexus, components: Components) -> Entity
} }

View File

@ -60,8 +60,6 @@ extension Nexus {
return false return false
} }
removeAllChildren(from: entityId)
if removeAll(components: entityId) { if removeAll(components: entityId) {
update(familyMembership: entityId) update(familyMembership: entityId)
} }

View File

@ -1,55 +0,0 @@
//
// Nexus+SceneGraph.swift
//
//
// Created by Christian Treffs on 30.09.19.
//
extension Nexus {
@available(*, deprecated, message: "This will be removed in the next minor update.")
public final func addChild(_ child: Entity, to parent: Entity) -> Bool {
let inserted: Bool
if childrenByParentEntity[parent.identifier] == nil {
childrenByParentEntity[parent.identifier] = [child.identifier]
inserted = true
} else {
let (isNewMember, _) = childrenByParentEntity[parent.identifier]!.insert(child.identifier)
inserted = isNewMember
}
if inserted {
delegate?.nexusEvent(ChildAdded(parent: parent.identifier, child: child.identifier))
}
return inserted
}
@available(*, deprecated, message: "This will be removed in the next minor update.")
public final func removeChild(_ child: Entity, from parent: Entity) -> Bool {
removeChild(child.identifier, from: parent.identifier)
}
@available(*, deprecated, message: "This will be removed in the next minor update.")
@discardableResult
public final func removeChild(_ child: EntityIdentifier, from parent: EntityIdentifier) -> Bool {
let removed: Bool = childrenByParentEntity[parent]?.remove(child) != nil
if removed {
delegate?.nexusEvent(ChildRemoved(parent: parent, child: child))
}
return removed
}
@available(*, deprecated, message: "This will be removed in the next minor update.")
public final func removeAllChildren(from parent: Entity) {
self.removeAllChildren(from: parent.identifier)
}
@available(*, deprecated, message: "This will be removed in the next minor update.")
public final func removeAllChildren(from parentId: EntityIdentifier) {
childrenByParentEntity[parentId]?.forEach { removeChild($0, from: parentId) }
return childrenByParentEntity[parentId] = nil
}
@available(*, deprecated, message: "This will be removed in the next minor update.")
public final func numChildren(for entity: Entity) -> Int {
childrenByParentEntity[entity.identifier]?.count ?? 0
}
}

View File

@ -23,10 +23,6 @@ public final class Nexus {
/// Each element is a component identifier associated with this entity. /// Each element is a component identifier associated with this entity.
@usableFromInline final var componentIdsByEntity: [EntityIdentifier: Set<ComponentIdentifier>] @usableFromInline final var componentIdsByEntity: [EntityIdentifier: Set<ComponentIdentifier>]
/// - Key: A parent entity id.
/// - Value: Adjacency Set of all associated children.
@usableFromInline final var childrenByParentEntity: [EntityIdentifier: Set<EntityIdentifier>]
/// - Key: FamilyTraitSet aka component types that make up one distinct family. /// - Key: FamilyTraitSet aka component types that make up one distinct family.
/// - Value: Tightly packed EntityIdentifiers that represent the association of an entity to the family. /// - Value: Tightly packed EntityIdentifiers that represent the association of an entity to the family.
@usableFromInline final var familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Idx>] @usableFromInline final var familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Idx>]
@ -41,7 +37,6 @@ public final class Nexus {
componentsByEntity: [:], componentsByEntity: [:],
entityIdGenerator: EntityIdentifierGenerator(), entityIdGenerator: EntityIdentifierGenerator(),
familyMembersByTraits: [:], familyMembersByTraits: [:],
childrenByParentEntity: [:],
codingStrategy: DefaultCodingStrategy()) codingStrategy: DefaultCodingStrategy())
} }
@ -50,13 +45,11 @@ public final class Nexus {
componentsByEntity: [EntityIdentifier: Set<ComponentIdentifier>], componentsByEntity: [EntityIdentifier: Set<ComponentIdentifier>],
entityIdGenerator: EntityIdentifierGenerator, entityIdGenerator: EntityIdentifierGenerator,
familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Idx>], familyMembersByTraits: [FamilyTraitSet: UnorderedSparseSet<EntityIdentifier, EntityIdentifier.Idx>],
childrenByParentEntity: [EntityIdentifier: Set<EntityIdentifier>],
codingStrategy: CodingStrategy) { codingStrategy: CodingStrategy) {
self.entityStorage = entityStorage self.entityStorage = entityStorage
self.componentsByType = componentsByType self.componentsByType = componentsByType
self.componentIdsByEntity = componentsByEntity self.componentIdsByEntity = componentsByEntity
self.familyMembersByTraits = familyMembersByTraits self.familyMembersByTraits = familyMembersByTraits
self.childrenByParentEntity = childrenByParentEntity
self.entityIdGenerator = entityIdGenerator self.entityIdGenerator = entityIdGenerator
self.codingStrategy = codingStrategy self.codingStrategy = codingStrategy
} }
@ -71,7 +64,6 @@ public final class Nexus {
componentsByType.removeAll() componentsByType.removeAll()
componentIdsByEntity.removeAll() componentIdsByEntity.removeAll()
familyMembersByTraits.removeAll() familyMembersByTraits.removeAll()
childrenByParentEntity.removeAll()
} }
} }

View File

@ -1,132 +0,0 @@
//
// SceneGraphTests.swift
// FirebladeECSTests
//
// Created by Christian Treffs on 30.09.19.
//
import FirebladeECS
import XCTest
@available(*, deprecated, message: "This will be removed in the next minor update.")
class SceneGraphTests: XCTestCase {
var nexus: Nexus!
override func setUp() {
super.setUp()
nexus = Nexus()
}
override func tearDown() {
super.tearDown()
nexus = nil
}
func testAddChild() {
let nttParrent = nexus.createEntity()
let nttChild1 = nexus.createEntity()
XCTAssertEqual(nttParrent.numChildren, 0)
XCTAssertTrue(nttParrent.addChild(nttChild1))
XCTAssertEqual(nttParrent.numChildren, 1)
XCTAssertFalse(nttParrent.addChild(nttChild1))
XCTAssertEqual(nttParrent.numChildren, 1)
}
func testRemoveChild() {
let nttParrent = nexus.createEntity()
let nttChild1 = nexus.createEntity()
XCTAssertEqual(nttParrent.numChildren, 0)
XCTAssertTrue(nttParrent.addChild(nttChild1))
XCTAssertEqual(nttParrent.numChildren, 1)
XCTAssertTrue(nttParrent.removeChild(nttChild1))
XCTAssertEqual(nttParrent.numChildren, 0)
XCTAssertFalse(nttParrent.removeChild(nttChild1))
XCTAssertEqual(nttParrent.numChildren, 0)
XCTAssertTrue(nttParrent.addChild(nttChild1))
XCTAssertEqual(nttParrent.numChildren, 1)
}
func testRemoveAllChildren() {
let nttParrent = nexus.createEntity()
let nttChild1 = nexus.createEntity()
let nttChild2 = nexus.createEntity()
XCTAssertEqual(nttParrent.numChildren, 0)
nttParrent.addChild(nttChild1)
nttParrent.addChild(nttChild2)
XCTAssertEqual(nttParrent.numChildren, 2)
nttParrent.removeAllChildren()
XCTAssertEqual(nttParrent.numChildren, 0)
}
func testDescendRelativesSimple() {
let nttParrent = nexus.createEntity(with: Position(x: 1, y: 1))
let child1Pos = Position(x: 2, y: 2)
let nttChild1 = nexus.createEntity(with: child1Pos)
nttParrent.addChild(nttChild1)
let family = nexus.family(requires: Position.self)
var counter: Int = 0
XCTAssertEqual(child1Pos.x, 2)
XCTAssertEqual(child1Pos.y, 2)
family
.descendRelatives(from: nttParrent)
.forEach { (parent: Position, child: Position) in
defer { counter += 1 }
child.x += parent.x
child.y += parent.y
}
XCTAssertEqual(counter, 1)
XCTAssertEqual(child1Pos.x, 3)
XCTAssertEqual(child1Pos.y, 3)
}
func testDescendRelativesOnlyFamilyMembers() {
let otherComponents: [Component] = [Position(x: 0, y: 0), Velocity(a: 0), Party(partying: true), Color(), Name(name: "")]
func addChild(to parent: Entity, index: Int) -> Entity {
let randComp = otherComponents.randomElement()!
let badChild = nexus.createEntity(with: randComp)
let child = nexus.createEntity(with: Index(index: index))
parent.addChild(child)
parent.addChild(badChild)
return child
}
let root = nexus.createEntity(with: Index(index: 0))
var parent: Entity = root
for i in 1..<10 {
parent = addChild(to: parent, index: i)
}
XCTAssertEqual(nexus.numEntities, 19)
var parentSum: Int = 0
var childSum: Int = 0
var lastIndex: Int = -1
nexus
.family(requires: Index.self)
.descendRelatives(from: root)
.forEach { (parent: Index, child: Index) in
XCTAssertEqual(parent.index + 1, child.index)
XCTAssertGreaterThan(parent.index, lastIndex)
lastIndex = parent.index
parentSum += parent.index
childSum += child.index
}
XCTAssertEqual(parentSum, 36)
XCTAssertEqual(childSum, 45)
}
}

View File

@ -235,19 +235,6 @@ extension NexusTests {
] ]
} }
extension SceneGraphTests {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__SceneGraphTests = [
("testAddChild", testAddChild),
("testDescendRelativesOnlyFamilyMembers", testDescendRelativesOnlyFamilyMembers),
("testDescendRelativesSimple", testDescendRelativesSimple),
("testRemoveAllChildren", testRemoveAllChildren),
("testRemoveChild", testRemoveChild)
]
}
extension SingleTests { extension SingleTests {
// DO NOT MODIFY: This is autogenerated, use: // DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain` // `swift test --generate-linuxmain`
@ -310,7 +297,6 @@ public func __allTests() -> [XCTestCaseEntry] {
testCase(FamilyTraitsTests.__allTests__FamilyTraitsTests), testCase(FamilyTraitsTests.__allTests__FamilyTraitsTests),
testCase(HashingTests.__allTests__HashingTests), testCase(HashingTests.__allTests__HashingTests),
testCase(NexusTests.__allTests__NexusTests), testCase(NexusTests.__allTests__NexusTests),
testCase(SceneGraphTests.__allTests__SceneGraphTests),
testCase(SingleTests.__allTests__SingleTests), testCase(SingleTests.__allTests__SingleTests),
testCase(SparseSetTests.__allTests__SparseSetTests), testCase(SparseSetTests.__allTests__SparseSetTests),
testCase(SystemsTests.__allTests__SystemsTests) testCase(SystemsTests.__allTests__SystemsTests)