触摸移动SKSpriteNode,半屏点加速

Moving SKSpriteNode with touch, speeds up at half screen point

我正在开发 2-d 平台游戏风格的游戏。我对 IOS 和 Swift 开发还很陌生。我正在尝试使用一个按钮(一个不同的节点)将我的角色从左向右移动到屏幕上。它工作正常,直到我到达中间点然后它急剧加速并且松开按钮并不总是停止它。有时它需要另一个触摸。此外,背景似乎跟不上播放器。一旦玩家到达中间屏幕,背景应该随着玩家继续移动而移动。

我已经从多个教程 SO 示例中拼凑了我所做的事情,但我坚持这一点。

class StoryModeScene: SKScene, SKPhysicsContactDelegate {

var tileMap = JSTileMap(named: "legend1Level1.tmx")
var tileSize:CGSize!
var xPointsToMovePerSecond:CGFloat = 0
var rightMoveButton = SKSpriteNode(imageNamed: "right-move")
var leftMoveButton = SKSpriteNode(imageNamed: "left-move")
var jumpButton = SKSpriteNode(imageNamed: "a-button")
var fireButton = SKSpriteNode(imageNamed: "b-button")
var forwardMarch:Bool = false
var mightAsWellJump:Bool = false
var onGround:Bool = true

//CREATE THE PLAYER ATLAS FOR ANIMATION
let playerAtlas = SKTextureAtlas(named:"legend1")
var playerSprites = Array<Any>()
var player = SKSpriteNode(imageNamed: "legend1")
var repeatActionPlayer = SKAction()

override func didMove(to view: SKView) {
    /* Setup your scene here */
    setupScene()
    addPlayer()

    //PREPARE TO ANIMATE THE PLAYER AND REPEAT THE ANIMATION FOREVER
    let animatedPlayer = SKAction.animate(with: self.playerSprites as! [SKTexture], timePerFrame: 0.1)
    self.repeatActionPlayer = SKAction.repeatForever(animatedPlayer)

    leftMoveButton.position.x = 64
    leftMoveButton.position.y = 64
    leftMoveButton.name = "moveLeft"
    addChild(leftMoveButton)

    rightMoveButton.position.x = 124
    rightMoveButton.position.y = 64
    rightMoveButton.name = "moveRight"
    addChild(rightMoveButton)

    jumpButton.position.x = 771
    jumpButton.position.y = 64
    jumpButton.name = "jumpButton"
    addChild(jumpButton)

    fireButton.position.x = 836
    fireButton.position.y = 64
    fireButton.name = "fireButton"
    addChild(fireButton)

}

override func update(_ currentTime: TimeInterval) {
    if (forwardMarch) {
        //let moveAction = SKAction.moveBy(x: 3, y: 0, duration: 1)
        //let repeatForEver = SKAction.repeatForever(moveAction)
        //let seq = SKAction.sequence([moveAction, repeatForEver])

        //run the action on your ship
        //player.run(seq)
        player.position.x = player.position.x + 3
        setViewpointCenter(player.position)
    }

    if (mightAsWellJump) {
        let jumpForce = CGPoint(x: 0.0, y: 310.0)
        let jumpCutoff: Float = 150.0

        if mightAsWellJump && onGround {
            player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx + jumpForce.x, dy: player.physicsBody!.velocity.dy + jumpForce.y)
            onGround = false
        } else if !mightAsWellJump && player.physicsBody!.velocity.dy > CGFloat(jumpCutoff) {
            player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx, dy: CGFloat(jumpCutoff))
        }


        player.position = CGPoint(x: player.position.x, y: player.position.y + 5);
    }
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    for touch in (touches) {
        let positionInScene = touch.location(in: self)
        let touchedNode = self.atPoint(positionInScene)
        if let name = touchedNode.name {
            if name == "jumpButton" {
                mightAsWellJump = true
                player.texture = SKTexture(imageNamed: "legend1_jump")
            }
            if name == "moveRight" {
                forwardMarch = true
                self.player.run(repeatActionPlayer)
            }
        }
    }
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    let touch = touches.first!
    if rightMoveButton.contains(touch.location(in: self)) {
        forwardMarch = false
        player.removeAllActions()
        player.texture = SKTexture(imageNamed: "legend1")
    }
    if jumpButton.contains(touch.location(in: self)) {
        mightAsWellJump = false
        player.removeAllActions()
        player.texture = SKTexture(imageNamed: "legend1")
    }
}

func setViewpointCenter(_ position: CGPoint) {
    var x = max(position.x, size.width / 2)
    var y = max(position.y, size.height / 2)
    x = min(x, (tileMap!.mapSize.width * tileMap!.tileSize.width) - size.width / 2)
    y = min(y, (tileMap!.mapSize.height * tileMap!.tileSize.height) - size.height / 2)
    let actualPosition = CGPoint(x: CGFloat(x), y: CGFloat(y))
    let centerOfView = CGPoint(x: size.width / 2, y: size.height / 2)
    let viewPoint = CGPoint(x: (centerOfView.x - actualPosition.x) * 3, y: centerOfView.y - actualPosition.y)
    tileMap!.position = viewPoint

}


func setupScene() {
    playerSprites.append(playerAtlas.textureNamed("legend1_0"))
    playerSprites.append(playerAtlas.textureNamed("legend1_1"))
    playerSprites.append(playerAtlas.textureNamed("legend1_2"))
    playerSprites.append(playerAtlas.textureNamed("legend1_3"))
    playerSprites.append(playerAtlas.textureNamed("legend1_4"))
    playerSprites.append(playerAtlas.textureNamed("legend1_5"))

    backgroundColor = UIColor(red: 165.0/255.0, green: 216.0/255.0, blue: 255.0/255.0, alpha: 1.0)

    physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)

    anchorPoint = CGPoint(x: 0, y: 0)
    position = CGPoint(x: 0, y: 0)

    let point = tileMap!.calculateAccumulatedFrame()
    print (point)
    tileMap!.position = CGPoint(x: 0, y: 0)
    addChild(tileMap!)

    addFloor()
}

func addFloor() {
    for a in 0..<Int(tileMap!.mapSize.width) {
        for b in 0..<Int(tileMap!.mapSize.height) {
            let layerInfo:TMXLayerInfo = tileMap!.layers.firstObject as! TMXLayerInfo
            let point = CGPoint(x: a, y: b)
            let walls = tileMap!.layerNamed("Walls")
            let wallInfo:TMXLayerInfo = walls!.layerInfo
            let wallGIDs = wallInfo.layer.tileGid(at: wallInfo.layer.point(forCoord: point))

            if wallGIDs > 0 {
                //print (wallGIDs)
                //let node = walls
                let node = wallInfo.layer.tile(atCoord: point)
                node!.physicsBody = SKPhysicsBody(rectangleOf: node!.size)
                node!.physicsBody?.isDynamic = false
            }
        }
    }
}

func addPlayer() {
    tileSize = tileMap?.tileSize
    player.position = CGPoint(x: tileSize.width + player.size.width/2, y: tileSize.height + player.size.height*8)
    let rect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 50, height: 95))
    player.physicsBody = SKPhysicsBody(rectangleOf: rect.size)
    player.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)
    player.physicsBody!.isDynamic = true
    player.physicsBody!.restitution = 0
    player.physicsBody!.allowsRotation = false
    player.physicsBody!.friction = 1.0
    addChild(player)
}

}

按住 rightMoveButton 应该以一致的速度向右移动。当玩家到达屏幕中间时,背景的视点应该移动,直到到达背景的尽头,此时玩家可以离开屏幕并完成关卡。释放按钮应该允许播放器停止。

您可以通过创建 SKCameraNode 来随着角色移动而移动场景。然后您可以选择何时移动相机以产生正确的效果。确保将相机设置为游戏的相机。您可以了解相机 here. As for the speeding up, I assume it has something to do with your physicsBody. If you’re interested about player movement, you could look here or here。网上还有许多其他很棒的视频和网站,您可以通过搜索“SpriteKit player movement”找到它们。

Eli Front 的回答为我指明了正确的方向。我对这段代码仍有一些问题,但正如 Eli 指出的那样,我的问题的答案是使用 SKCamera 节点。我只是想 post 有效的代码,所以如果有人有类似的问题,这里有一个关于摄像机随播放器移动的代码示例。速度问题本质上是背景移动太快的错觉。

请注意,控件是相机的子控件。也就是说,它们会随着相机移动,否则当您的节点移动时,控件会移出屏幕。

import UIKit
import SpriteKit
import GameKit

class StoryModeScene: SKScene, SKPhysicsContactDelegate {

    var tileMap = JSTileMap(named: "legend1Level1.tmx")
    var tileSize:CGSize!
    var xPointsToMovePerSecond:CGFloat = 0
    var rightMoveButton = SKSpriteNode(imageNamed: "right-move")
    var leftMoveButton = SKSpriteNode(imageNamed: "left-move")
    var jumpButton = SKSpriteNode(imageNamed: "a-button")
    var fireButton = SKSpriteNode(imageNamed: "b-button")
    var forwardMarch:Bool = false
    var mightAsWellJump:Bool = false
    var onGround:Bool = true

    //CREATE THE Player ATLAS FOR ANIMATION
    let playerAtlas = SKTextureAtlas(named:"legend1")
    var playerSprites = Array<Any>()
    var player = SKSpriteNode(imageNamed: "legend1")
    var repeatActionPlayer = SKAction()
    let cam = SKCameraNode()

    var previousUpdateTime: TimeInterval = 0

    override func didMove(to view: SKView) {
        // SETUP CAMERA
        self.camera = cam
        scene?.addChild(cam)
        cam.position.x = 448
        cam.position.y = 212

        setupScene()
        addPlayer()

        //PREPARE TO ANIMATE THE PLAYER AND REPEAT THE ANIMATION FOREVER
        let animatedPlayer = SKAction.animate(with: self.playerSprites as! [SKTexture], timePerFrame: 0.1)
        self.repeatActionPlayer = SKAction.repeatForever(animatedPlayer)

        // SETUP CONTROLS
        leftMoveButton.position.x = -338
        leftMoveButton.position.y = -112
        leftMoveButton.name = "moveLeft"
        leftMoveButton.zPosition = 5
        cam.addChild(leftMoveButton)

        rightMoveButton.position.x = -278
        rightMoveButton.position.y = -112
        rightMoveButton.name = "moveRight"
        cam.addChild(rightMoveButton)

        jumpButton.position.x = 278
        jumpButton.position.y = -112
        jumpButton.name = "jumpButton"
        jumpButton.zPosition = 5
        cam.addChild(jumpButton)

        fireButton.position.x = 338
        fireButton.position.y = -112
        fireButton.name = "fireButton"
        cam.addChild(fireButton)

    }

    override func update(_ currentTime: TimeInterval) {

        if (forwardMarch) {
            if player.position.x > 448 && player.position.x < 1800 {
                cam.position.x = player.position.x
            } else if player.position.x >= 1800 {
                cam.position.x = 1800
            }
            setViewpointCenter(player.position)
            player.position.x = player.position.x + 3
        }

        if (mightAsWellJump) {
            let jumpForce = CGPoint(x: 0.0, y: 310.0)
            let jumpCutoff: Float = 150.0

            if mightAsWellJump && onGround {
                player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx + jumpForce.x, dy: player.physicsBody!.velocity.dy + jumpForce.y)
                onGround = false
            } else if !mightAsWellJump && player.physicsBody!.velocity.dy > CGFloat(jumpCutoff) {
                player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx, dy: CGFloat(jumpCutoff))
            }

            player.position = CGPoint(x: player.position.x, y: player.position.y + 5);
        }
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in (touches) {
            let positionInScene = touch.location(in: self)
            let touchedNode = self.atPoint(positionInScene)
            if let name = touchedNode.name {
                if name == "jumpButton" {
                    mightAsWellJump = true
                    player.texture = SKTexture(imageNamed: "legend1_jump")
                }
                if name == "moveRight" {
                    player.physicsBody!.velocity.dy = 0.0
                    forwardMarch = true
                    self.player.run(repeatActionPlayer)
                }
            }
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        for touch in (touches) {
            let positionInScene = touch.location(in: self)
            let touchedNode = self.atPoint(positionInScene)
            if let name = touchedNode.name {
                if name == "jumpButton" {
                    mightAsWellJump = false
                    player.removeAllActions()
                    player.texture = SKTexture(imageNamed: "legend1")
                    player.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)
                }
                if name == "moveRight" {
                    player.removeAllActions()
                    player.texture = SKTexture(imageNamed: "legend1")
                    forwardMarch = false
                }
            }
        }
    }

    func setViewpointCenter(_ position: CGPoint) {
        var x = max(position.x, size.width / 2)
        var y = max(position.y, size.height / 2)
        x = min(x, (tileMap!.mapSize.width * tileMap!.tileSize.width) - size.width / 2)
        y = min(y, (tileMap!.mapSize.height * tileMap!.tileSize.height) - size.height / 2)
        let actualPosition = CGPoint(x: CGFloat(x), y: CGFloat(y))
        let centerOfView = CGPoint(x: size.width / 2, y: size.height / 2)
        let viewPoint = CGPoint(x: centerOfView.x - actualPosition.x, y: centerOfView.y - actualPosition.y)
        if (actualPosition.x > 1800) {
            tileMap!.position = tileMap!.position
        } else {
            tileMap!.position = viewPoint
        }
    }

    func setupScene() {
        // ADD PLAYER SPRITES @TODO MAKE THIS DYNAMIC
        playerSprites.append(playerAtlas.textureNamed("legend1_0"))
        playerSprites.append(playerAtlas.textureNamed("legend1_1"))
        playerSprites.append(playerAtlas.textureNamed("legend1_2"))
        playerSprites.append(playerAtlas.textureNamed("legend1_3"))
        playerSprites.append(playerAtlas.textureNamed("legend1_4"))
        playerSprites.append(playerAtlas.textureNamed("legend1_5"))

        backgroundColor = UIColor(red: 165.0/255.0, green: 216.0/255.0, blue: 255.0/255.0, alpha: 1.0)
        anchorPoint = CGPoint(x: 0, y: 0)
        position = CGPoint(x: 0, y: 0)

        // let point = tileMap!.calculateAccumulatedFrame()
        tileMap!.position = CGPoint(x: 0, y: 0)
        addChild(tileMap!)

        addFloor()
    }

    func addFloor() {
        for a in 0..<Int(tileMap!.mapSize.width) {
            for b in 0..<Int(tileMap!.mapSize.height) {
                // let layerInfo:TMXLayerInfo = tileMap!.layers.firstObject as! TMXLayerInfo
                let point = CGPoint(x: a, y: b)
                let walls = tileMap!.layerNamed("Walls")
                let wallInfo:TMXLayerInfo = walls!.layerInfo
                let wallGIDs = wallInfo.layer.tileGid(at: wallInfo.layer.point(forCoord: point))

                if wallGIDs > 0 {
                    let node = wallInfo.layer.tile(atCoord: point)
                    node!.physicsBody = SKPhysicsBody(rectangleOf: node!.size)
                    node!.physicsBody?.isDynamic = false
                }
            }
        }
    }

    func addPlayer() {
        tileSize = tileMap?.tileSize
        player.position = CGPoint(x: tileSize.width + player.size.width/2, y: tileSize.height + player.size.height*8)
        let rect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 50, height: 95))
        player.physicsBody = SKPhysicsBody(rectangleOf: rect.size)
        player.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)
        player.physicsBody!.isDynamic = true
        player.physicsBody!.restitution = 0
        player.physicsBody!.allowsRotation = false
        player.physicsBody!.friction = 1.0
        player.physicsBody!.mass = 1.0
        addChild(player)
    }

}

正如我提到的,当我同时 jump/run 时,我仍然有一些问题,例如播放器节点 floating/flying。第一次跳跃是正确的高度,所有后续跳跃都较小等,但我正在努力解决这些问题。