How to set up an ID system for enemies in Sprite Kit?

I'm in the process of making a 2d Space Shooter style game for iOS, and am having trouble creating an ID system for game elements and particularly enemies.

The tutorial I am using only shows how to make enemies explode after only one hit. It uses a function called didBegin(_ contact: SKPhysicsContact).

This function, from my understanding, evaluates each instance of contact, assigns two bodies two distinct roles, and then decides what to do with each body. I assume to have an enemy take three hits, a variable containing their remaining health would be used. Then, with each instance of contact, the variable would decrease by one, until if it is zero, the enemy is removed.

However, there is a deep problem in this. SincedidBegin(_ contact: SKPhysicsContact) evaluates only one instance of contact, it has no knowledge of other previous instances of contact. Essentially, when an enemy gets hit, there is no way to know if the enemy was hit before, has not been hit at all, or has been hit too many times. If there was only one enemy, then I could use one variable to keep track of its health. This is not the case, though, as there are multiple enemies on screen at once, and every time contact is made, there is no way to know if the previous hit was on this enemy, or another enemy.

If there is one enemy on screen, it is simple because every time contact is made it would have to be that one enemy. But if there are multiple entities on screen, then there is no way to know which enemy a contact applies to.

I know I must set up an ID system to be called after contact has been detected, I am just not sure how to do it.

I have tried to set up a dictionary and a class structure (I know you can do solve it like this but I haven't been able to figure it out).

Upvotes: 0

Views: 82

Answers (2)

Knight0fDragon
Knight0fDragon

Reputation: 16837

You do not have to do your contact code in didBegin, nothing is stopping you from doing it in didFinisheUpdate:

Here is an example of what you can do:

var contactedNodes = [SKNode]()
func didBegin(_ contact: SKPhysicsContact) {
    func storeContact(nodeA:SKNode,nodeB:SKNode){
        nodeA.userData = nodeA.userData ?? [:]()
        nodeA.userData["nodesHit"] += [nodeB]
        contactedNodes.append(nodeA)
    }
    storeContact(contact.bodyA.node,contact.bodyB.node)
    storeContact(contact.bodyB.node,contact.bodyA.node)

} 
func didFinishUpdate(){
    for node in contactedNodes{
        guard let userData = node.userData, let nodesHit = userData["nodesHit"] else {continue}
        //do any logic here with nodes hit
    }
}

As far as ID is concerned, you can also use userData to assign a userID and avoid having to worry about an extra class

Upvotes: 0

Paulw11
Paulw11

Reputation: 115041

Probably the simplest approach is to subclass SKNode and use that for your enemies. You can add a health property to your EnemyNode and initialise it to a suitable value.

Then in didBegin(_ contact: SKPhysicsContact) you can find the enemy, decrement its health and destroy it if appropriate.

func didBegin(_ contact: SKPhysicsContact) {
    var enemy: EnemyNode? = nil

    if contact.bodyA.node is EnemyNode {
        enemy = contact.bodyA.node 
    } else if contact.bodyB.node is EnemyNode {
        enemy = contact.bodyB.node
    }

    if let enemy = enemy {
        enemy.health -= 1
        if enemy.health == 0 {
           //TODO:  explode enemy
        }
    }
} 

Upvotes: 1

Related Questions