我试图将我的精灵定位在 phone 的底部中心

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

这是我从 didMoveToScene 调用的添加播放器的函数,我的场景锚定在 0.5, 0.5。 tileMapNode 位于 0、-800 以使其在场景中居中,并且它也锚定在 0.5、0.5。无论我将播放器放在哪里,它仍然是 phone 的死点。我做错了什么。

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
}

这是完整的 GameScene 文件。我怎么知道场景何时加载?我是一个老派的程序员。大约 24 年没有编写任何代码 :)。事情发生了一点变化。

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
        }
    }
}

忘记添加游戏场景扩展

    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))")
        }
    }
    */
}

如果我正确地遵循了您的代码,问题可能是您在更新循环中将相机对准了播放器。

如果你想让他在屏幕的底部,你需要偏移这个位置,而不是直接将它设置在你的玩家的位置上,否则无论你把他移到哪里,他都会一直在中间。

//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.