Search code examples
swiftsprite-kit2d-games

SpriteKit - Border on all sides except bottom and top


I have a main player node and enemies coming down randomly, but parts of them are outside the frame, I want to make it so they (and the player) are not able to get out of frame, left and right borders. How can I achieve that?

Thank you for your time and effort :)

Here's my enemy line, not sure how this can help but here it is so I don't get reported for being 'too broad'

 func launchEnemy() {

    let randomX = arc4random_uniform( UInt32(screenWidth))

    EnemyMissile.position = CGPoint(  x: CGFloat(randomX)  - (screenWidth / 2), y: screenHeight + 50)


    let action = SKAction.move(by: CGVector(dx: 0, dy: -400 + speedScore), duration: 5.0)


   EnemyMissile.run(SKAction.repeatForever(action))        

     increaseSpeed()


    self.run(action, withKey:"LaunchEnemyAction")
}  

Here's my EnemyClass

     import Foundation
     import SpriteKit

class EnemyClass: SKNode {

var EnemyNode:SKSpriteNode = SKSpriteNode()

var hitsToKill:Int = 2
var hitCount:Int = 0
var damagePoints: Int = 2

//var missileAnimation:SKAction?

required init(coder aDecoder: NSCoder) {
    fatalError("init(coder:) had not been implemented")

}

override init () {

    super.init()

}



    func createEnemy ( _ theImage:String) {


EnemyNode = SKSpriteNode(imageNamed: theImage)
self.addChild(EnemyNode)

let body:SKPhysicsBody = SKPhysicsBody(circleOfRadius: EnemyNode.size.width / 2.25, center:CGPoint(x: 0,y: 0))
body.isDynamic = true
body.affectedByGravity = false
body.allowsRotation = false


  body.categoryBitMask = BodyType.enemy.rawValue
  body.contactTestBitMask =  BodyType.bullet.rawValue |              BodyType.player.rawValue


self.physicsBody = body

self.name = "EnemyClass"

}

func destroy() {
   self.removeFromParent()
   self.name = "removeNode"
}
func hit() -> Bool {

    hitCount += 1

    if ( hitCount == hitsToKill) {

        destroy()
        return true

    } else {
        damagePoints = 4

        EnemyNode.removeAction(forKey: "animation")
        return false
    }

}

}

Solution

  • Basically you have 2 issues:

    1. Your spawning points are messed up
    2. You have no boundary in place

    You have a few options for setting boundaries, one being a physicsBody, another being keepInBounds() method or such. Below I should a simple keep in bounds method:

    class GameScene: SKScene {
    
      func spawnEnemy() {
        let offset = CGFloat(5) // pixels, so enemy will have some spacing between borders.
    
        let enemy = SKSpriteNode(color: .blue, size: CGSize(width: 50, height: 100))
        enemy.name = "enemy"
        addChild(enemy)
    
        var randomX = CGFloat(arc4random_uniform(UInt32(self.size.width))) // This is not a perfect rando algo.
    
        // Because our random x was from 0 through screenwidth, but .position works
        // on -halfWidth through +halfWidth
        randomX -= (self.frame.size.width / 2)
    
        // Because spawning on the border will mean that the enemy is HALF out of view:
        /*
    
         right border:       |
         enemy @ frame.maxX: x
         - offset:          x|
         - size.w/2        x |
         */
        if randomX > self.frame.maxX - offset - (enemy.size.width / 2) {
           randomX = self.frame.maxX - offset - (enemy.size.width / 2)
        }
        else if randomX < self.frame.minX + offset + (enemy.size.width / 2) {
                randomX = self.frame.minX + offset + (enemy.size.width / 2)
        }
    
        enemy.position.x = randomX
      }
    
      func keepEnemyInBounds() {
        // A better way to do this would be to have a dictionary of enemies:
        for node in self.children {
          guard node.name == "enemy" else { continue }
          let enemy = node as! SKSpriteNode
    
          if enemy.position.x > frame.maxX - (enemy.size.width / 2) {
            enemy.position.x = frame.maxX - (enemy.size.width / 2)
          }
          else if enemy.position.x < frame.minX + (enemy.size.width / 2) {
            enemy.position.x = frame.minX + (enemy.size.width / 2)
          }
        }
      }
    
    
      override func update(_ currentTime: TimeInterval) {
        spawnEnemy()
      }
      override func didFinishUpdate() {
        keepEnemyInBounds()
      }
    }