Reputation: 2281
I am using GamePlay kits Entity - Component system for a 2d game. Each entity has a sersis of GKComponents - spriteComponent, physicComponent, etc and a GKComponent (ChaseScrollMovement.swift) that takes a Entities spriteComponent and moves it across the screen.
Adding a ChaseScrollMovment Component to the Enemy Enity
addComponent(physicsCompnment);
scrollerComponent = ChaseScrollComponent(entity: self, isEnemey : true);
addComponent(scrollerComponent);
Problem is when I remove the EnemyEnity form the GameScence (when contact is made with an WeaponEnity):
Class ThrowWeaponEnity.....didBginContact...
case "enemyEntity":
if let spriteComponentEnemy = entity.componentForClass(SpriteComponent.self){
let pos = spriteComponentEnemy.node.position;
//Remove enemy sprote and its entiy
spriteComponentEnemy.node.parent?.runAction(SKAction.playSoundFileNamed("110548__noisehag__comp-cover-02.wav", waitForCompletion: false));
spriteComponentEnemy.node.removeAllActions();
spriteComponentEnemy.node.removeFromParent();
//Remove the entire entity
entity.removeComponentForClass(SpriteComponent.self);
emitterExplosion(pos);
}
break;
....The EnemeyEnitiy is removed form the GameScene, the GameScene update method then calls Component update method....
Components in GameScene
lazy var componentSystems: [GKComponentSystem] = {
let parallaxSystem = GKComponentSystem(componentClass: ParallaxComponent.self)
let animationSystem = GKComponentSystem(componentClass: AnimationComponent.self)
let scrollerSystem = GKComponentSystem(componentClass: ChaseScrollComponent.self)
return [animationSystem, parallaxSystem, scrollerSystem]
}()
let scrollerSystem = FullControlComponentSystem(componentClass: FullControlComponent.self).......
//MARK: Life Cycle
override func update(currentTime: CFTimeInterval) {
if !pauseLoop {........
// Update Components
for componentSystem in componentSystems {
componentSystem.updateWithDeltaTime(deltaTime)
}
.....so then the ChaseScroll Component a cant find the spriteCompnment of the EnemyEntity as it is removed form the scene and a exception is thrown here....
class ChaseScrollComponent: GKComponent {
var spriteComponent: SpriteComponent {
//Called after Init() and during GaemplayMode.updat / componments.udate
guard let spriteComponent = entity?.componentForClass(SpriteComponent.self)
else{
fatalError("SpriteCompnemnet Missing");
}
print("SpriteCompnemnt for CHaseScroll GKConponent : \(spriteComponent.node.name)")
return spriteComponent;
}
I am new to Enity-Compnent system so I am wondering what the best approach is to prevent a exception when a single EnemyEntity (there are many in a scene) is removed form the scene and allowing the Components to successfully update during the GameScene life cycle.
Thanks In advance
Update: Tried removing the Components from the system so the components upDate method would not be called in the game scene update:
//In our game scene
func removeEntity(entity : GKEntity){
print("Enity hashValue and \(entity.hash)description: \(entities.count)");
if entities.contains(entity){
//Remove the entities components
let entiyRemoved = entities.remove(entity);
entity.removeComponentForClass(PhysicsComponent.self);
entity.removeComponentForClass(ChaseScrollComponent.self);
}
//Remove entity from array of enities
entities.remove(entity); print(entities.count);
for componentsytem in self.componentSystems{
//Remove componment is from the system not the entity, so if removed the component update will not be called
componentsytem.removeComponentWithEntity(entity); }
}
But the Component that should of been removed has its update still called, within this update the initialisation of a entity spriteComponent fails as expected because we have removed the component from the entity (and tried to remove form the system!) :
var spriteComponent: SpriteComponent {
guard let spriteComponent = entity?.componentForClass(SpriteComponent.self)
else{
fatalError("SpriteCompnemnet Missing");
}
print("SpriteCompnemnt for CHaseScroll GKConponent : \(spriteComponent.node.name)")
return spriteComponent;
}
So still cannot seem to remove the Component from the system as its update is still called or cannot remove the Entity from the system.
Upvotes: 1
Views: 663
Reputation: 2307
The following is an example of how I have initialised the systems in an ECS
. This code is in the GameScene. Note the Entity creates its own components.
var entities: [GKEntity]!
var componentSystems: [GKComponentSystem<GKComponent>]!
override func didMove(to view: SKView) {
self.setupEntities()
self.setupComponentSystems()
}
override func update(_ currentTime: TimeInterval) {
// Update ComponentSystems
for componentSystem in componentSystems {
componentSystem.update(deltaTime: dt)
}
}
func setupEntities() {
self.entities = [GKEntity]()
// Initialise Rover
self.roverEntity = RoverEntity()
self.entities.append(self.roverEntity as! GKEntity)
// Add entites to scene
for entity in self.entities {
if let visualEntity = entity as? VisualEntity {
self.addChild(visualEntity.node)
}
}
}
func setupComponentSystems() {
self.healthSystem = HealthSystem()
self.healthSystem.addComponents(from: self.entities)
self.healthSystem.delegate = self
self.moveSystem = MoveSystem()
self.moveSystem.addComponents(from: self.entities)
self.moveSystem.delegate = self
self.selectionSystem = SelectionSystem()
self.selectionSystem.addComponents(from: self.entities)
self.selectionSystem.delegate = self
self.componentSystems = [
self.healthSystem,
self.moveSystem,
self.selectionSystem
]
}
When an entity needs to be completed removed, there is a function removeComponent(foundIn:)
on GKComponentSystem
. This is Swift 3, so expect that Swift 2 naming will be different.
for componentSystem in componentSystems {
componentSystem.removeComponent(foundIn: self.roverEntity as! GKEntity)
}
This components associated with this entity are now removed from all systems in the ECS
and the entities node can be removed from the scene.
Upvotes: 1