【问题标题】:SpriteKit keep moving player in current direction while falling after touchesEndedSpriteKit 在 touchesEnded 后下落时保持当前方向移动玩家
【发布时间】:2021-06-14 10:14:45
【问题描述】:

我正在制作自己的 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, from: self.frame.minX, offset: 150),
                                      y: calculateXOffset(for: rightArrow, from: self.frame.minY, 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, from: self.frame.minY, 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, from coord: CGFloat, 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

这些都是我的资产:


编辑

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

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

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

这里是a video 再次演示该问题。

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

【问题讨论】:

  • 我相信你是在使用冲动进行跳跃,但左右是通过在位置上加上或减去一个x值来完成的。当您释放左键或右键时,您不再移动(基本上是 player.position.x + 0),没有动力,因为您只是使用硬编码值而不是物理。跳跃是使用冲动移动,而重力则用于使您的角色向后退。如果你使用冲动来左右移动角色,它会更像马里奥。
  • 在这种情况下我应该使用冲动吗?还是来自physicsbody 的速度? @JohnL
  • this 回答有帮助吗?
  • 这有点帮助@JohnL,我编辑了我的问题
  • 好的,很高兴你能正常工作。仅供参考,在您的最新视频中,您的播放器会弹跳一点,请使用 player.restitution = 0 停止弹跳。

标签: ios swift sprite-kit game-physics skphysicsbody


【解决方案1】:

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

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

玩家节点在移动时卡住的问题已在更改单个资产而不是多个彼此相邻的块的地板时被移除。

【讨论】:

    猜你喜欢
    • 2022-11-14
    • 1970-01-01
    • 1970-01-01
    • 2017-04-10
    • 2021-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多