1

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.

dancingbush
  • 2,131
  • 5
  • 30
  • 66
  • The `GKComponentSystem` has a [removeComponent(foundIn:)](https://developer.apple.com/reference/gameplaykit/gkcomponentsystem/1501162-removecomponent) function that you could try calling when you're removing the entity, that should remove the component reference from the system. – Mark Brownsword Jan 27 '17 at 07:06
  • Thanks but shouldn't the entities components be removed form the scene when the entity itself is removed? – dancingbush Jan 27 '17 at 07:24
  • I would remove it explicitly, though you didn't show in your code how you associate components with systems. – Mark Brownsword Jan 27 '17 at 07:42
  • Under class ThrowWeaponEntity ..... entityRemoveComponentForClass.....but I am starting to wonder if this is actually removing the entity as there is a reference to the CKComponents remianjng – dancingbush Jan 27 '17 at 08:00
  • Yes, you've removed the node, but left the entity. – Mark Brownsword Jan 27 '17 at 08:04
  • So how do you remove the entity itself? – dancingbush Jan 27 '17 at 16:24
  • The entity is just a variable, so set it to `nil`. – Mark Brownsword Jan 27 '17 at 18:30
  • Issue is when assigning various properties i.e. A CGpoint position, to entity components the entity needs to be forced unwrapped so compiler won't allow assignment of nil – dancingbush Jan 28 '17 at 07:42
  • Cant find a method removeComponent(foundIn) for a Entity but there is a removeComponentForClass but still have same issue when implementing this – dancingbush Jan 28 '17 at 07:43
  • Seems you can remove an entity componments no issue but removing the entity is a different matter altogether – dancingbush Jan 28 '17 at 14:44

1 Answers1

1

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.

Mark Brownsword
  • 2,287
  • 2
  • 14
  • 23
  • Thanks, the func temoveComponents(foundIn:..,), are we removing the component from tej Entity here? Is this same as calling entity.removeComponentForClass...my problem is I have removed all components for an entity but t the entity still exists in the scene, when a component for that entity is called elsewhere it returns nil and crashes – dancingbush Jan 30 '17 at 09:28
  • This is Called in the update components func, So I'm figuring if I can remove the Entity from the scene it's reference to a component will not be called when I update components – dancingbush Jan 30 '17 at 09:32
  • The function `removeComponent(foundIn:)` is called on the system, not the entity. It removes all references to components belonging to the entity, so the components updates function will not be called. – Mark Brownsword Jan 30 '17 at 09:36