SpriteKit 在接触后下降时继续沿当前方向移动玩家Ended

问题描述

我正在制作我自己的 Mario Bros. 第一级复制品,以学习如何使用我自己的资产在 iOS 上制作游戏。到目前为止,我已经成功地为控件(左、右、上)放置了三个 SKSpriteNodes,我的玩家节点可以在这三个方向上移动,但是如果我让我的玩家在向任一方向奔跑时跳跃,如当我将手指从“左控制”上移开时,玩家将失去所有动量并直接落在那里(就像撞墙一样)而不是跟随抛物线。

我不知道在这种情况下可能需要什么才能成为 MRE,所以这基本上是可以重现问题的全部内容,以及我为使其工作所做的一些尝试。

基本上我尝试施加脉冲/设置速度/直接改变位置,最后一个效果更好(但它仍然使玩家节点在我从方向控件上移开手指后立即下降).

此处是 a video 演示该问题。

这是GameScene

import SpriteKit
import GameplayKit

class GameScene: SKScene {
    private var player = SKSpriteNode()
    private var bg = SKSpriteNode()
    private var leftArrow = SKSpriteNode()
    private var rightArrow = SKSpriteNode()
    private var upArrow = SKSpriteNode()
    private var floor = [SKSpriteNode]()
    private var isLeftTouched = false
    private var isRightTouched = false
    private var selectednodes: [UITouch:SKSpriteNode] = [:]

    override func didMove(to view: SKView) {
        addBackground()
        addFloor()
        addplayer(xOffset: 0,yOffset: 0)
        addControls()
    }

    override func touchesBegan(_ touches: Set<UITouch>,with event: UIEvent?) {
        //player.physicsBody?.applyImpulse(CGVector(dx: 0,dy: 50))
        let touch = touches.first! as UITouch
        let positionInScene = touch.location(in: self)
        let touchednode = self.atPoint(positionInScene)

        for touch in touches {
            let location = touch.location(in:self)
            if let node = self.atPoint(location) as? SKSpriteNode {
                if let name = touchednode.name {
                    selectednodes[touch] = node
                    if name == "up" {
                        player.physicsBody?.applyImpulse(CGVector(dx: 0,dy: 60))
                    } else if name == "left" {
                        isLeftTouched = true
                    } else if name == "right" {
                        isRightTouched = true
                    }
                }
            }
        }

        if let name = touchednode.name {
            if name == "up" {
                player.physicsBody?.applyImpulse(CGVector(dx: 0,dy: 60))
            } else if name == "left" {
                isLeftTouched = true
            } else if name == "right" {
                isRightTouched = true
            }
        }
    }

    override func touchesMoved(_ touches: Set<UITouch>,with event: UIEvent?) {
        //let direction = ((touches.first?.location(in: self).x)! < (touches.first?.prevIoUsLocation(in: self).x)!) ? Direction.LEFT : Direction.RIGHT
        //runIn(direction: direction)
    }

    override func touchesEnded(_ touches: Set<UITouch>,with event: UIEvent?) {
        for touch in touches {
            if selectednodes[touch] != nil {
                if selectednodes[touch]?.name == "left" {
                    isLeftTouched = false
                } else if selectednodes[touch]?.name == "right" {
                    isRightTouched = false
                }
                selectednodes[touch] = nil
            }
        }
    }

    override func update(_ currentTime: TimeInterval) {
        // Called before each frame is rendered
        if isLeftTouched {
            runIn(direction: Direction.LEFT)
        }
        if isRightTouched {
            runIn(direction: Direction.RIGHT)
        }
    }

    // MARK: INteraCTION METHODS

    func runIn(direction: Direction) {
        let x = player.position.x + (direction == Direction.RIGHT ? 5 : -5)
        let position = CGPoint(x: x,y: player.position.y)
        if position.x >= self.frame.maxX || position.x <= self.frame.minX {
            return
        }
        player.position = position
        //player.physicsBody?.veLocity = CGVector(dx: direction == Direction.RIGHT ? 50 : -50,dy: 0)
        //player.physicsBody?.applyImpulse(CGVector(dx: direction == Direction.RIGHT ? 5 : -5,dy: 0))
    }

    // MARK: UI METHODS

    func addBackground() {
        let bgTexture = SKTexture(imageNamed: "bg")

        bg = SKSpriteNode(texture: bgTexture)
        bg.position = CGPoint(x: self.frame.midX,y: self.frame.midY)
        bg.size.height = self.frame.height
        bg.zPosition = -10

        self.addChild(bg)
    }

    func addplayer(xOffset: CGFloat,yOffset: CGFloat) {
        let playerTexture = SKTexture(imageNamed: "player")

        player = SKSpriteNode(texture: playerTexture)

        //let xPos = calculateXOffset(for: player,from: self.frame.midX,offset: xOffset)
        //let yPos = calculateXOffset(for: player,from: self.frame.midY,offset: yOffset)
        player.position = CGPoint(x: self.frame.midX,y: self.frame.midY)

        player.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: player.frame.width,height: player.frame.height))
        player.physicsBody?.isDynamic = true

        self.addChild(player)
    }

    func addFloor() {
        let blockTexture = SKTexture(imageNamed: "block")

        for i in 0 ... (Int) (self.frame.width / blockTexture.size().width) {
            let blockNode = SKSpriteNode(texture: blockTexture)

            blockNode.position = CGPoint(x: self.frame.minX + (blockNode.frame.width * CGFloat(i)),y: self.frame.minY + blockNode.frame.height / 2)

            blockNode.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: blockNode.frame.width,height: blockNode.frame.height))
            blockNode.physicsBody?.isDynamic = false

            floor.append(blockNode)
            self.addChild(blockNode)
        }
    }

    func addControls() {
        addLeftArrow()
        addRightArrow()
        addUpArrow()
    }

    func addLeftArrow() {
        let leftTexture = SKTexture(imageNamed: "left")
        leftArrow = SKSpriteNode(texture: leftTexture)

        leftArrow.name = "left"
        leftArrow.position = CGPoint(x: calculateXOffset(for: leftArrow,from: self.frame.minX,offset: 50),y: calculateXOffset(for: leftArrow,from: self.frame.minY,offset: 50))

        self.addChild(leftArrow)
    }

    func addRightArrow() {
        let rightTexture = SKTexture(imageNamed: "right")
        rightArrow = SKSpriteNode(texture: rightTexture)

        rightArrow.name = "right"
        rightArrow.position = CGPoint(x: calculateXOffset(for: rightArrow,offset: 150),y: calculateXOffset(for: rightArrow,offset: 50))

        self.addChild(rightArrow)
    }

    func addUpArrow() {
        let upTexture = SKTexture(imageNamed: "up")
        upArrow = SKSpriteNode(texture: upTexture)

        upArrow.name = "up"
        upArrow.position = CGPoint(x: calculateXOffset(for: upArrow,from: self.frame.maxX,offset: -(125 + upTexture.size().width)),y: calculateXOffset(for: upArrow,offset: 50))

        self.addChild(upArrow)
    }

    // MARK: UTILITY FUNCTIONS

    func calculateXOffset(for asset: SKSpriteNode,from coord: CGFloat,offset: CGFloat) -> CGFloat {
        let width = asset.frame.width

        return coord + offset + width;
    }

    func calculateYOffset(for asset: SKSpriteNode,offset: CGFloat) -> CGFloat {
        let height = asset.frame.height

        return coord + offset + height;
    }
}

我的 Direction 枚举:

enum Direction {
    case LEFT
    case RIGHT
    case UP
    case DOWN
}

我在 GameViewController 中所做的唯一更改是:

scene.scaleMode = .resizefill

我的 GameScene.sks 是 926 x 428,仅支持横向。由于 Xcode 12 中的错误,我还将 LaunchScreen 设置为 MainBackground is not filling the whole view SpriteKit

这些都是我的资产:

Enter image description here

Enter image description here

Enter image description here

Enter image description here

enter image description here

Enter image description here


编辑

我尝试在我的 runIn 方法中应用这样的冲动:

player.physicsBody?.applyImpulse(CGVector(dx: direction == Direction.RIGHT ? 2 : -2,dy: 0))

这使得玩家节点在抛物线上移动,但现在它不时被卡住,唯一让它移动的方法是让它跳跃直到它再次发生。

这里的 a video 再次演示了这个问题。

如果我尝试设置速度,那么我将无法在移动时跳跃,并且在跳跃和之后移动时似乎会滑行。

解决方法

我最终在上面的评论中遵循了@JohnL 的建议,以使用冲动来移动我的播放器节点:

player.physicsBody?.applyImpulse(CGVector(dx: direction == Direction.RIGHT ? 2 : -2,dy: 0))

在更改单个资产而不是多个相邻块的楼层时,玩家节点在移动时卡住的问题已被消除。