Search code examples
swiftsprite-kitskspritenode

I'm trying to position my sprite at the bottom center of the phone


this is the function I call from didMoveToScene to add the player, my scene is anchored at 0.5, 0.5. The tileMapNode is positioned at 0, -800 to center it in the scene and it is anchored at 0.5, 0.5 also. no matter where I position the player, it is still dead center of the phone. What am I doing wrong.

func addPlayer() {
    player = Player(imageNamed: GameConstants.StringConstants.playerImageName)
    player.name = String(GameConstants.StringConstants.playerName)

    player.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    player.position = CGPoint(x: (scene?.frame.midX)!, y: (scene?.frame.minY)!)

    player.xScale = 1
    player.yScale = 1
    player.zPosition = GameConstants.ZPositions.playerZ
    player.lightingBitMask = 1

    PhysicsHelper.addPhysicsBody(to: player, with: GameConstants.StringConstants.playerName)

    addAttackArea()
    scene?.addChild(player)

    lightsCameraAction()

    player.playerState = .idle
}

Here is the complete GameScene file. How can I know when the scene is loading? I'm an old school programmer. Haven't written any code in about 24 years :). things have changed a little.

import SpriteKit

//MARK:--------------------------Global Variables
enum GameState {
case playing, paused, finished
}

// MARK: ----------------------------------GameScene
class GameScene: SKScene, SKPhysicsContactDelegate {

   // MARK: -----------------------------------Movement Variables
    let movePointsPerSecond: CGFloat = 250.0
    var velocity = CGVector(dx: 0.0, dy: 0.0)
    var lastUpdateTime: CFTimeInterval = 0

    // MARK: ----------------------------------Gesture Recognizer
    let singleTapRec = UITapGestureRecognizer()

    let lightNode: SKLightNode = SKLightNode()
    let cameraNode: SKCameraNode = SKCameraNode()
    let gameScene: SKScene = SKScene()

    var gameState = GameState.playing {
        willSet {
            switch newValue {
            case .playing:
                player.playerState = .idle
            case .finished:
                player.playerState = .idle
            case .paused:
                scene?.isPaused = true
            }
        }
    }

    // MARK: ---------------------------------didMove to view
    override func didMove(to view: SKView) {
        physicsWorld.contactDelegate = self

        switch gameState {
            case .playing:
                setupGestures()
                addPlayer()
                addEnemy()
                addEnemy()
                addEnemy()
                addEnemy()
                addEnemy()
                addEnemy()
            case .paused:
                scene?.isPaused = true
            default:
                break
        }
    }

    // MARK: ---------------------Touches Section
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
       // var touchedLocation = CGPoint()
        switch gameState {
            case .playing:
                //if let touch = touches.first {
                    //let touchLocation = touch.location(in: self)
                   // touchedLocation = touchLocation
                    player.playerState = .idle
                   // moveAndRotate(spriteNode: player, toPosition: touchedLocation)
               // }
            case .paused:
                scene?.isPaused = true
            default:
                break
            }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        var touchedLocation = CGPoint()
        switch gameState {
            case .playing:
                if let touch = touches.first {
                    let touchLocation = touch.location(in: self)
                    touchedLocation = touchLocation
                    player.playerState = .walking
                    moveAndRotate(spriteNode: player, toPosition: touchedLocation)
                }
            case .paused:
                scene?.isPaused = true
            default:
                break
        }
   }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        removeAllActions()
        switch gameState {
            case .playing:
                player.playerState = .idle
                player.removeAction(forKey: "RotateAction")
            case .paused:
                scene?.isPaused = true
            default:
                break
            }
    }

    // MARK:------------------------------------Physics contact
    func didBegin(_ contact: SKPhysicsContact) {
        var enemyIndex = 0
        let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask

        if contact.bodyA.node?.name !=  "Player" && contact.bodyA.node?.name != "AttackArea" {
            let node = contact.bodyA.node

            enemyIndex = findEnemy(contactName: (node?.name)!)

        } else {
            let node = contact.bodyB.node

            enemyIndex = findEnemy(contactName: (node?.name)!)

        }
        enemyID = enemyIndex

        switch contactMask {
            case GameConstants.PhysicsCategory.attackAreaCategory | GameConstants.PhysicsCategory.enemyCategory:
                handleEnemyContact(entity: enemyIndex)
            case GameConstants.PhysicsCategory.playerCategory | GameConstants.PhysicsCategory.enemyCategory:
                handleEnemyContact(entity: enemyIndex)
            default:
                break
        }
    }

    func didEnd(_ contact: SKPhysicsContact) {

    }

    // MARK: ---------------------Update Section
    override func update(_ currentTime: CFTimeInterval) {
        let deltaTime = max(1.0 / 30, currentTime - lastUpdateTime)
        lastUpdateTime = currentTime
        update(dt: deltaTime)
    }

    func update(dt: CFTimeInterval) {
        if player.playerState == .walking {
            let newX = player.position.x + velocity.dx * CGFloat(dt)
            let newY = player.position.y + velocity.dy * CGFloat(dt)
            player.position = CGPoint(x: newX, y: newY)
            cameraNode.position = player.position
            lightNode.position =  player.position
            newAttack.position = player.position
        }
    }
}

forgot to add the game scene extension.

    import SpriteKit

// MARK: ----------------------------------Enumerations
enum Animation: String {
    case Walking, Idle, Attacking, Waiting
}

enum RewardType: String {
    case LevelUp, MagicItem, DefeatEnemy, DefeatBoss, CompleteQuest
}

enum Dice: Int {
    case d20, d10, d8, d6, d4
}

// MARK: ----------------------------------GLobal Variables
var player: Player!
var enemy: Enemy!

let textureName: String = GameConstants.StringConstants.playerImageName
let playerTexture: SKTexture = SKTexture(imageNamed: GameConstants.StringConstants.playerImageName)
var playerPosition: CGPoint = CGPoint(x: 0, y: 0)
let attackAreaTexture: SKTexture = SKTexture(imageNamed: "AttackCircle")
var requiredXPForNextLevel = 0

let enemyTexture: SKTexture = SKTexture(imageNamed: GameConstants.StringConstants.enemyImageName)

var playerIsAttacking: Bool = false
var enemyIsAttacking: Bool = false

var playerIsDead: Bool = false
var enemyIsDead: Bool = false

var enemies: [Enemy] = []
var enemyID: Int = 0

var newAttack: SKSpriteNode!

extension GameScene {

    //MARK:--------------------------------------------Add Player
    func addPlayer() {
        player = Player(imageNamed: GameConstants.StringConstants.playerImageName)
        player.name = String(GameConstants.StringConstants.playerName)

        player.anchorPoint = CGPoint(x: 0.5, y: 0.5)
        player.position = CGPoint(x: 0, y: 0)

        player.xScale = 1
        player.yScale = 1
        player.zPosition = GameConstants.ZPositions.playerZ
        player.lightingBitMask = 1

        PhysicsHelper.addPhysicsBody(to: player, with: GameConstants.StringConstants.playerName)

        addAttackArea()
        scene!.addChild(player)

        lightsCameraAction()

        player.playerState = .idle
    }

    //MARK:------------------------------------Lights and Camera
    func lightsCameraAction() {
        let lightNode: SKLightNode = setupLighting()
        addChild(lightNode)

        let cameraNode: SKCameraNode = setupCamera()
        addChild(cameraNode)
    }

    //MARK:-------------------------------------------Add Enemy
    func addEnemy() {
        let enemyIndex = enemyID
        enemy = Enemy(imageNamed: GameConstants.StringConstants.enemyImageName )
        enemies.append(enemy)

        let randomX = Int.random(in: -100 ..< 1500)
        let randomY = Int.random(in: -100 ..< 1500)

        let currentEnemy = enemies[enemyIndex]

        currentEnemy.name = "Enemy\(enemyIndex + 1)"
        currentEnemy.stats.id = enemyID
        currentEnemy.position = CGPoint(x: randomX, y: randomY)
        currentEnemy.xScale = 1
        currentEnemy.yScale = 1
        currentEnemy.zPosition = GameConstants.ZPositions.enemyZ
        currentEnemy.lightingBitMask = 1

        PhysicsHelper.addPhysicsBody(to: currentEnemy, with: GameConstants.StringConstants.enemyName)

        addChild(currentEnemy)
        enemyID += 1
    }

    //MARK:-------------------------------Attack
    func addAttackArea() {
        newAttack = SKSpriteNode(texture: attackAreaTexture, color: UIColor.clear, size: player.size)
        newAttack.name = GameConstants.StringConstants.attackAreaName
        newAttack.position = player.position
        newAttack.size.width = player.size.width + 75
        newAttack.size.height = player.size.height + 75
        newAttack.zPosition = player.zPosition - 1
        PhysicsHelper.addPhysicsBody(to: newAttack, with: GameConstants.StringConstants.attackAreaName)

        addChild(newAttack)
    }

    func attack() {
        player.playerState = .attacking
        playerIsAttacking = true
        if enemyIsDead {
            playerIsAttacking = false
        }
    }

    //MARK:----------------------------Gestures
    func setupGestures() {
        singleTapRec.addTarget(self, action: #selector(singleTap))
        singleTapRec.numberOfTouchesRequired = 1
        singleTapRec.numberOfTapsRequired = 1
        view!.addGestureRecognizer(singleTapRec)
    }

    @objc func singleTap() {
        attack()
    }

    func cleanUp() {
        for gesture in (view?.gestureRecognizers)! {
            view?.removeGestureRecognizer(gesture)
        }
    }

    //MARK:-------------------------Lighting and Camera
    func setupLighting() -> SKLightNode {
        lightNode.lightColor = UIColor.white
        lightNode.ambientColor = UIColor.black
        lightNode.shadowColor = UIColor.black
        lightNode.falloff = 1.5
        lightNode.zPosition = GameConstants.ZPositions.objectZ
        lightNode.alpha = 1
        lightNode.position = player.position

        return lightNode
    }

    func setupCamera() -> SKCameraNode {
        camera = cameraNode
        cameraNode.position =  player.position

        return cameraNode
    }

    //MARK:-----------------------------Handle Enemy Contact
    func handleEnemyContact(entity: Int) {
        //var currentEnemy = enemies[entity]

        if enemies.count != 0 {
            if enemies[entity].stats.hp <= 0 {
                enemyIsDead = true
                //handlePlayerReward(level: enemyLevel)
                enemies[entity].removeFromParent()
                enemies.remove(at: entity)
            } else {
                print("\nAttacking: \(enemies[entity].name as Any)")
                print("enemyHP: \(enemies[entity].stats.hp)")
                enemies[entity].stats.hp -= 1
            }
        }
    }

    //MARK:-------------------------------------Find Enemy
    func findEnemy(contactName: String) -> Int {
        var enemiesIndex = 0
        var enemyIndex = 0
        for _ in enemies {
            let entityName = enemies[enemiesIndex].name
            if entityName == contactName {
                enemyIndex = enemiesIndex
                enemies[enemyIndex].stats.id = enemyIndex
            } else {
            enemiesIndex += 1
            }
        }
        return enemyIndex

    }

    //MARK:-------------------------------------Player Reward
    func handlePlayerReward(level: Int) {
        /*
        let playerXP = userData?.value(forKey: "PlayerXP") as? Int
        let newPlayerXP = (level * 10) + playerXP!

        if newPlayerXP > requiredXPForNextLevel {
            levelUp()
        }
        userData?["PlayerXP"] = newPlayerXP as Any
        */
    }

    //MARK:-----------------------------------------Level Up
    func levelUp() {
        /*
        var enemyLevel = userData?.value(forKey: "\(enemyID)Level") as! Int

        let playerXP = userData?.value(forKey: "PlayerXP") as! Int
        let newPlayerXP = (enemyLevel  * 10) + playerXP

        enemyLevel += 1

        userData?["P{layerXP"] = newPlayerXP
        userData?["\(enemyID)Level"] = enemyLevel

        requiredXPForNextLevel  = requiredXPForNextLevel * 2
        */
    }

//MARK-----------------------------------Roll Dice
    func rollDice(die: Dice) -> Int {
        switch die {
            case .d20:
                let d20 = Int(arc4random_uniform(20)) + 1
                return d20
           case .d10:
                let d10 = Int(arc4random_uniform(10)) + 1
                return d10
            case .d8:
                let d8 = Int(arc4random_uniform(8)) + 1
                return d8
            case .d6:
                let d6 = Int(arc4random_uniform(6)) + 1
                return d6
            case .d4:
                let d4 = Int(arc4random_uniform(4)) + 1
                return d4
            }
    }


    //MARK:-------------------------------Move and Rotate
    func moveAndRotate(spriteNode: SKSpriteNode, toPosition position: CGPoint) {

        let angle = atan2(position.y -  spriteNode.position.y, position.x - spriteNode.position.x)
        let rotateAction = SKAction.rotate(toAngle: angle + CGFloat.pi / 2, duration: 0, shortestUnitArc: true)

        if let _ = spriteNode.action(forKey: "RotateAction") {
            spriteNode.removeAction(forKey: "RotateAction")
            spriteNode.run(rotateAction, withKey: "RotateAction")
        } else {
            spriteNode.run(rotateAction, withKey: "RotateAction")
        }

        let offsetX = position.x - spriteNode.position.x
        let offsetY = position.y - spriteNode.position.y
        let normal = simd_normalize(simd_double2(x: Double(offsetX), y: Double(offsetY)))

        velocity = CGVector(dx: CGFloat(normal.x) * movePointsPerSecond, dy: CGFloat(normal.y) * movePointsPerSecond)
    }

    /*
    func whoIsThis(entity: String) {

        if entity != player.name! {
            print("Entity:  \(entity)")
            print("EnemyID:  \(enemyID)")
            print("EnemyHP:  \(String(describing: userData?.value(forKey: "HP")))")
            print("EnemyName:  \(String(describing: enemy.name))")
        } else {
            print("Entity:  \(entity)")
            print("PlayerHP:  \(String(describing: userData?.value(forKey: "HP")))")
            print("PlayerName:  \(String(describing: player.name))")
        }
    }
    */
}

Solution

  • If I am following your code correctly the issue could be that you are centering the camera on your player in your update loop.

    If you want him to be at the bottom of the screen you will want to offset this position not set it directly on your player's position otherwise he will always be in the center no matter where you move him.

    //cameraNode.position = player.position
    let yOffset = player.position.y+scene.height/2-player.size.height/2 
    cameraNode.position = CGPoint(player.position.x, yOffset) //might get you want you want.