0

PLEASE HELP!!! I have been trying to figure this out for along time. I have searched the internet and i cannot find anything that will help me.

I am currently making a game in which you are a space ship in the middle and enemy ships are moving towards you and you have to shoot them. some enemies have different lives. for example: a red ship takes one shot to explode, the blue ship takes 3, etc. I have everything to work only the lives. for example: whenever a blue ship is called on to the screen i shoot it once so its life goes down to 2. but whenever a another blue ship is called the first blue ship has its life reset back to 3 again. Is there anyway I can make it so that whenever a ship looses lives it remains that way even if other ships are called ?

this is my ship function that gets called and adds enemy space ships onto the screen:

func VillainRight(){
    let TooMuch = self.size.width
    let point = UInt32(TooMuch)
    life = 3
    let VillainR = SKSpriteNode(imageNamed: "BlueVillain")

    VillainR.zPosition = 2
    VillainR.position = CGPoint(x: self.frame.minX,y: CGFloat(arc4random_uniform(point)))

    //This code makes the villain's Zposition point towards the SpaceShip
    let angle = atan2(SpaceShip.position.y - VillainR.position.y, SpaceShip.position.x - VillainR.position.x)
    VillainR.zRotation = angle - CGFloat(M_PI_2)


    let MoveToCenter = SKAction.move(to: CGPoint(x: self.frame.midX, y: self.frame.midY), duration: 15)

    //Physics World
    VillainR.physicsBody = SKPhysicsBody(rectangleOf: VillainR.size)
    VillainR.physicsBody?.categoryBitMask = NumberingPhysics.RightV
    VillainR.physicsBody?.contactTestBitMask = NumberingPhysics.Laser | NumberingPhysics.SpaceShip
    VillainR.physicsBody?.affectedByGravity = false
    VillainR.physicsBody?.isDynamic = true






    VillainR.run(MoveToCenter)
    addChild(VillainR)
    }

This is the code that calls this function:

_ = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(Level1.VillainRight), userInfo: nil, repeats: true)

I am using the spritekit in Swift. Thank You Very Much in advance!

Whirlwind
  • 14,286
  • 11
  • 68
  • 157
Alex Merlin
  • 341
  • 1
  • 3
  • 12
  • 1
    Note that using Timer in SpriteKit can lead you into some issues if you haven't implemented a [custom pause feature](http://stackoverflow.com/a/34398582/3402095). For example, if you go into a background, and return to a game after minute or two, you will likely have a screen full of enemies. This is because Timer is not affected by node's scene's or view's paused state, and it will not be paused immediately after the app goes into background. On the other side, with SKActions, this is handled automatically. – Whirlwind Mar 04 '17 at 07:05
  • 1
    Also, you should try to follow naming conventions : names of types and protocols are UpperCamelCase. Everything else is lowerCamelCase. Means `villainR.run(moveToCenter)` rather than `VillainR.run(MoveToCenter)` ;) This will make your code easier to understand for others ... – Whirlwind Mar 04 '17 at 07:18

1 Answers1

1

That is happening because life variable is declared as a property of a scene and it is not local to a specific node (enemy ship). You can solve this in a few ways... First way would be using node's userData property:

import SpriteKit

let kEnergyKey = "kEnergyKey"

class GameScene: SKScene, SKPhysicsContactDelegate {

    override func didMove(to view: SKView) {


        let blueShip = getShip(energy: 3)
        let greenShip = getShip(energy: 2)
        let redShip = getShip(energy: 1)

        if let blueShipEnergy = blueShip.userData?.value(forKey: kEnergyKey) as? Int {

            print("Blue ship has \(blueShipEnergy) lives left")
            //hit the ship
            blueShip.userData?.setValue(blueShipEnergy-1, forKey: kEnergyKey)

            if let energyAfterBeingHit = blueShip.userData?.value(forKey: kEnergyKey) as? Int {

                print("Blue ship has \(energyAfterBeingHit) lives left")
            }
        }
    }

    func getShip(energy:Int)->SKSpriteNode{
        //determine which texture to load here based on energy value
        let ship = SKSpriteNode(color: .purple, size: CGSize(width: 50, height: 50))

        ship.userData = [kEnergyKey:energy]

        return ship
    }
}

This is what the docs say about userData property:

You use this property to store your own data in a node. For example, you might store game-specific data about each node to use inside your game logic. This can be a useful alternative to creating your own node subclasses to hold game data.

As you can see, an alternative to this is subclassing of a node (SKSpriteNode):

class Enemy:SKSpriteNode {

    private var energy:Int
    //do initialization here
}
Whirlwind
  • 14,286
  • 11
  • 68
  • 157
  • Ok ..... And can you please tell me what would need to go in the did beginContact function whenever the bullet node makes contact with the enemy node so that the life would decrease by one ? – Alex Merlin Mar 06 '17 at 21:36
  • @AlexMerlin It simple. In `didBegin(contact:)` You access the ship, then access its userData, then decrease the `energy` variable and based on kind of a ship, eventually remove it? Make sense? – Whirlwind Mar 06 '17 at 21:39
  • Ok great ... I will implement this ASAP and ill let you know how it goes. Thank you very much – Alex Merlin Mar 06 '17 at 23:09
  • hey I cannot access the "ship" in didBegin(contact:) because it is not global. can you tell me how I would access it ? – Alex Merlin Mar 12 '17 at 21:45
  • @AlexMerlin That is a complete different question and it is not related to what was your original question. Since you already have your answer, you should ask another question to avoid going off-topic. But in short, ship doesn't have to be global. That is a whole point. You extract the ship from contact variable (bodyX.node) in didBegin(contact:)... – Whirlwind Mar 12 '17 at 21:51