Skip to content

Commit ec1c6ce

Browse files
committed
Merge pull request #11 from HeavyEngine/feature/scenegraph_and_entity_improvements
Feature/scenegraph and entity improvements
2 parents e6ab5bb + 6780a33 commit ec1c6ce

File tree

5 files changed

+135
-20
lines changed

5 files changed

+135
-20
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//
2+
// BehaviorStore.swift
3+
// HeavyCore for iOS
4+
//
5+
// Created by Dylan Wreggelsworth on 5/18/16.
6+
// Copyright © 2016 Heavy. All rights reserved.
7+
//
8+
9+
/// Used for storing different behavior types so they can be retrieved easily.
10+
/// - Todo: Add pooling
11+
public struct BehaviorStore {
12+
typealias BehaviorID = Int
13+
typealias Behaviors = [Behavior]
14+
15+
private var storageIDs = [Behavior.Type]()
16+
private var storage = [BehaviorID: [Behavior]]()
17+
18+
public init() {}
19+
20+
private func has(type: Behavior.Type) -> BehaviorID? {
21+
guard let id = hasIndex(type) where storage[id] != nil else { return nil }
22+
return id
23+
}
24+
25+
private func hasIndex(type: Behavior.Type) -> BehaviorID? {
26+
guard let id = storageIDs.indexOf({$0 == type}) else { return nil }
27+
return id
28+
}
29+
30+
/// Find all of the `Behavior.Type` this particular store contains.
31+
///
32+
/// - parameter type: The `Behavior.Type` you wish to find.
33+
///
34+
/// - returns: A collection of the `Behavior.Type` you are looking for or `nil`.
35+
public func find<T: Behavior>(type: T.Type) -> [T]? {
36+
guard let id = has(type),
37+
store = storage[id] else { return nil }
38+
return store.flatMap { $0 as? T }
39+
}
40+
41+
/// Add a reference to a given `Behavior` to the store.
42+
///
43+
/// - parameter behavior: The `Behavior` reference you wish to store.
44+
public mutating func add<T: Behavior>(behavior: T) {
45+
let type = behavior.dynamicType
46+
let index = hasIndex(type)
47+
let id = index ?? storageIDs.count
48+
if index == nil {
49+
storageIDs.append(type)
50+
}
51+
if storage[id] == nil {
52+
storage[id] = [T]()
53+
}
54+
storage[id]?.append(behavior)
55+
}
56+
57+
/// Remove this `Behavior` from the store.
58+
///
59+
/// - parameter behavior: A reference to the behavior you wish to remove.
60+
public mutating func remove(behavior: Behavior) {
61+
guard let id = has(behavior.dynamicType)
62+
where storage[id]?.count > 0
63+
else {
64+
return
65+
}
66+
guard let index = storage[id]?.indexOf({$0 === behavior}) else {
67+
return
68+
}
69+
storage[id]?.removeAtIndex(index)
70+
}
71+
}
72+
73+
public struct BehaviorStoreGenerator: GeneratorType {
74+
var store: BehaviorStore
75+
var index = 0
76+
var localIndex = 0
77+
public init(store: BehaviorStore) {
78+
self.store = store
79+
}
80+
mutating public func next() -> Behavior? {
81+
guard let localStore = store.storage[index] else {
82+
return nil
83+
}
84+
if localIndex == localStore.count {
85+
index += 1
86+
localIndex = 0
87+
}
88+
let result = store.storage[index]?[localIndex]
89+
localIndex += 1
90+
return result
91+
}
92+
}
93+
94+
extension BehaviorStore: SequenceType {
95+
public func generate() -> BehaviorStoreGenerator {
96+
return BehaviorStoreGenerator(store: self)
97+
}
98+
}

HeavyCore/Common/Entities/Entity.swift

+11-10
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,23 @@
88

99
public struct Entity {
1010
var id: String = NSUUID().UUIDString
11-
public var behaviors = [Behavior]()
11+
public var behaviors = BehaviorStore()
1212

1313
public init() {}
1414

1515
/// Used to remove a given `Behavior` from an `Entity`.
1616
///
1717
/// - parameter behavior: The `Behavior` that is to be removed.
1818
public mutating func remove(behavior: Behavior) {
19-
guard let index = behaviors.indexOf({ $0 === behavior }) else {
20-
return
21-
}
22-
behaviors.removeAtIndex(index)
19+
behaviors.remove(behavior)
2320
}
2421

2522
/// Add a behavior to this entity to be processed on update.
2623
///
2724
/// - parameter behavior: A behavior to be processed on update.
28-
public mutating func add(behavior: Behavior) {
25+
public mutating func add<T: Behavior>(behavior: T) {
2926
behavior.parent = self
30-
behaviors.append(behavior)
27+
behaviors.add(behavior)
3128
}
3229

3330
/// Gets run on scene update and passes the delta time to
@@ -45,10 +42,14 @@ public struct Entity {
4542
/// - parameter type: The type of behavior that it should look for.
4643
///
4744
/// - returns: An `Array` of `Behavior` that match the type given.
48-
public func find<T: Behavior>(type: T.Type) -> [Behavior] {
49-
return behaviors.filter { (element: Behavior) -> Bool in
50-
return element.dynamicType == type
45+
public func find<T: Behavior>(behavior: T.Type) -> [T]? {
46+
guard let results = behaviors.find(behavior) else {
47+
return nil
5148
}
49+
return results
5250
}
5351

52+
public func has<T: Behavior>(behavior: T.Type) -> Bool {
53+
return find(behavior) != nil ? true : false
54+
}
5455
}

HeavyCore/Common/Utilities/Random.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,14 @@ public struct PRNG {
4848
}
4949
}
5050

51-
var seed: UInt
51+
public var seed: UInt
5252
private var rngState: [UInt] = [0, 0]
5353
private var generator: Xoroshiro128Plus
5454

5555
/// Initialize a PRNG based on the given seed. If a seed is not given it uses Timer.time as the seed.
5656
/// - parameter seed: UInt
5757
/// - returns: PRNG
58-
public init(seed: UInt = UInt(Timer().time)) {
58+
public init(seed: UInt = UInt(Timer().time * 100000)) {
5959
self.seed = seed
6060
self.generator = Xoroshiro128Plus(state: [0, 0])
6161
generateSeeds(seed)

HeavyCore/iOS/HeavyCore for iOS.xcodeproj/project.pbxproj

+7-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
FC6C80101CD963EE001E2F90 /* Quad.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC6C7FDF1CD96200001E2F90 /* Quad.swift */; };
1919
FC6C80111CD963EE001E2F90 /* Units.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC6C7FE01CD96200001E2F90 /* Units.swift */; };
2020
FC6C80121CD963EE001E2F90 /* Vector.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC6C7FE11CD96200001E2F90 /* Vector.swift */; };
21+
FC7C90011CECFF1900ADE499 /* BehaviorStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC7C90001CECFF1900ADE499 /* BehaviorStore.swift */; };
22+
FC7C90021CECFF1900ADE499 /* BehaviorStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC7C90001CECFF1900ADE499 /* BehaviorStore.swift */; };
2123
FCA4E4DF1CE685AC00362AD9 /* Math.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCA4E4DD1CE685AC00362AD9 /* Math.swift */; };
2224
FCA4E4E11CE685AC00362AD9 /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCA4E4DE1CE685AC00362AD9 /* Timer.swift */; };
2325
FCA4E4E51CE6860600362AD9 /* Angle.swift in Sources */ = {isa = PBXBuildFile; fileRef = FCA4E4E31CE6860600362AD9 /* Angle.swift */; };
@@ -60,6 +62,7 @@
6062
FC6C7FFF1CD962E5001E2F90 /* HeavyCoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeavyCoreTests.swift; sourceTree = "<group>"; };
6163
FC6C80011CD962E5001E2F90 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
6264
FC6C80081CD96376001E2F90 /* Core.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = Core.playground; path = ../../Playgrounds/Core.playground; sourceTree = "<group>"; };
65+
FC7C90001CECFF1900ADE499 /* BehaviorStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BehaviorStore.swift; path = ../../Common/Entities/BehaviorStore.swift; sourceTree = "<group>"; };
6366
FCA4E4DD1CE685AC00362AD9 /* Math.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Math.swift; sourceTree = "<group>"; };
6467
FCA4E4DE1CE685AC00362AD9 /* Timer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Timer.swift; sourceTree = "<group>"; };
6568
FCA4E4E31CE6860600362AD9 /* Angle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Angle.swift; sourceTree = "<group>"; };
@@ -90,6 +93,7 @@
9093
children = (
9194
FC3F309D1CD99DC20014ECD5 /* Behavior.swift */,
9295
FC3F309E1CD99DC20014ECD5 /* Entity.swift */,
96+
FC7C90001CECFF1900ADE499 /* BehaviorStore.swift */,
9397
);
9498
name = Entities;
9599
sourceTree = "<group>";
@@ -280,6 +284,7 @@
280284
FC3F30A11CD99DC20014ECD5 /* Entity.swift in Sources */,
281285
FC3F309F1CD99DC20014ECD5 /* Behavior.swift in Sources */,
282286
FCA4E4E91CE68B4300362AD9 /* Angle.swift in Sources */,
287+
FC7C90011CECFF1900ADE499 /* BehaviorStore.swift in Sources */,
283288
FCA4E4F21CE6CF9000362AD9 /* Numeric.swift in Sources */,
284289
FCA4E4ED1CE68B5300362AD9 /* Units.swift in Sources */,
285290
FC6C80091CD963ED001E2F90 /* Point.swift in Sources */,
@@ -307,6 +312,7 @@
307312
FC6C80121CD963EE001E2F90 /* Vector.swift in Sources */,
308313
FC6C80001CD962E5001E2F90 /* HeavyCoreTests.swift in Sources */,
309314
FCA4E4EF1CE68B5B00362AD9 /* Timer.swift in Sources */,
315+
FC7C90021CECFF1900ADE499 /* BehaviorStore.swift in Sources */,
310316
FCA4E4EE1CE68B5800362AD9 /* Math.swift in Sources */,
311317
);
312318
runOnlyForDeploymentPostprocessing = 0;
@@ -416,7 +422,7 @@
416422
MTL_ENABLE_DEBUG_INFO = YES;
417423
ONLY_ACTIVE_ARCH = YES;
418424
SDKROOT = iphoneos;
419-
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
425+
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
420426
TARGETED_DEVICE_FAMILY = "1,2";
421427
VERSIONING_SYSTEM = "apple-generic";
422428
VERSION_INFO_PREFIX = "";

Playgrounds/Core.playground/Pages/Entities.xcplaygroundpage/Contents.swift

+17-7
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,29 @@ class ColorBehavior: Behavior {
2828
var myEntity = Entity()
2929

3030
// Add the behaviors to the entity...
31-
myEntity.add(ColorBehavior())
31+
let colorB = ColorBehavior()
32+
myEntity.add(colorB)
33+
myEntity.add(MotionBehavior())
3234
myEntity.add(MotionBehavior())
35+
myEntity.remove(colorB)
36+
myEntity.has(MotionBehavior)
37+
myEntity.has(ColorBehavior)
38+
myEntity.add(colorB)
39+
myEntity.add(ColorBehavior())
40+
myEntity.add(ColorBehavior())
3341

3442
// Simulate a game loop...
3543
for time in 0.stride(to: 1, by: 1.0/15) {
3644
myEntity.update(time)
37-
guard let pos = myEntity.find(MotionBehavior).first as? MotionBehavior,
38-
color = myEntity.find(ColorBehavior).first as? ColorBehavior
39-
else {
40-
continue
45+
guard let movers = myEntity.find(MotionBehavior),
46+
let colors = myEntity.find(ColorBehavior)
47+
else { continue }
48+
for color in colors {
49+
print(color.color)
50+
}
51+
for mover in movers {
52+
print(mover.position)
4153
}
42-
pos.position
43-
color.color // Should alternate
4454
}
4555

4656
//: [Next](@next)

0 commit comments

Comments
 (0)