Sprite Kit - 球有时会从屏幕上消失?

6

enter image description here

我刚开始学习 Sprite Kit。我尝试制作一个简单的球弹跳游戏,有两个玩家,另一个玩家慢慢追踪球的位置。但是我发现了一个问题:当我把线移动到球(和边缘)时,球会从屏幕上消失。有时候球会反弹,就没有问题了。这是什么问题?
我只有一个 GameScene、sks 文件和 ViewController。我的精灵节点来自于 sks 文件。如果有人能解释一下这种情况,那就太好了。我在下面附上了我的代码。
我的 GameScene:
class GameScene: SKScene {

var ball = SKSpriteNode()
var enemy = SKSpriteNode()
var main = SKSpriteNode()

override func didMove(to view: SKView) {

    ball = self.childNode(withName: "ball") as! SKSpriteNode
    enemy = self.childNode(withName: "enemy") as! SKSpriteNode
    main = self.childNode(withName: "main") as! SKSpriteNode

    ball.physicsBody?.applyImpulse(CGVector(dx: -20, dy: -20))
    ball.physicsBody?.linearDamping = 0
    ball.physicsBody?.angularDamping = 0

    let border = SKPhysicsBody(edgeLoopFrom: self.frame)
    border.friction = 0
    border.restitution = 1

    self.physicsBody = border

}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

    for touch in touches {

        let location = touch.location(in: self)
        main.run(SKAction.moveTo(x: location.x, duration: 0.2))
    }
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

    for touch in touches {

        let location = touch.location(in: self)
        main.run(SKAction.moveTo(x: location.x, duration: 0.2))
    }

}

override func update(_ currentTime: TimeInterval) {
    // Called before each frame is rendered

    enemy.run(SKAction.moveTo(x: ball.position.x, duration: 0.5))
}

视图控制器:

class GameViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()

    if let view = self.view as! SKView? {
        // Load the SKScene from 'GameScene.sks'
        if let scene = SKScene(fileNamed: "GameScene") {
            // Set the scale mode to scale to fit the window
            scene.scaleMode = .aspectFill

            // Present the scene
            view.presentScene(scene)
        }

        view.ignoresSiblingOrder = true


    }
}

override var prefersStatusBarHidden: Bool {
    return true
}

Pad设置:

pad

球设置:

ball

一些更新:
我尝试了一些更新功能中的消息,然后遇到了相同的情况,即球从设备左侧飞出(使用 iPhone 6S)。

enter image description here

enter image description here

2016年12月8日14:27:54.436485 Pong[14261:3102941] 致命错误:球出了左边界:文件

enter image description here


-474.846是什么?这是X还是Y位置?每个错误消息都有相同的数字吗,还是它们从0开始?这是在设备上还是模拟器中? - Steve Ives
@SteveIves 我的锚点是0.5,所以对于那个尺寸来说,-474并不意味着球会飞出屏幕左侧? - iamburak
3个回答

3
你正在墙上夹住球,与敌人一起。这意味着力量最终足够创建足够的球运动/力量速度来克服物理系统,因此它穿过了墙壁。如果在夹住球之前让你的敌人停下来,那么你就没问题了。
这种“夹紧”是由于以下代码行发生的:
enemy.run(SKAction.moveTo(x: ball.position.x, duration: 0.5))
这使得敌人追逐球,这对于球类比赛来说是一个好主意,但移动方式不正确。使用Action意味着敌人有无限的力量施加,且瞄准球的中心。
因此,当球到达墙时,它被静态力无限制地停在物理对象上,然后这个敌人从另一边施加无限力......球要么弹到敌人范围内,要么超出墙的另一侧,因为它被无限力压碎。
因此,您需要非常注意如何使用Actions来控制敌人,或者使用力来控制敌人,因为这些不会是无限的,并且物理系统将能够对抗敌人。

这是溢出吗?或许更简单的方法是设置球的速度上限...在更新时检查是否超过阈值,然后重置/限制它。这似乎是苹果应该已经实现的功能... - Fluidity
1
@Fluidity添加了一点回答来解释我所说的“pincing”的含义。 - Confused
1
这是很多初次使用物理引擎的用户经常遇到的问题,他们没有意识到他们需要专注于使用物理引擎或使用算法运动控制。两者混用常常会出现问题... 算法控制的对象具有无限的力量,这通常会导致物理引擎系统受损。所有的双关语都是故意的。 - Confused
所以.move和.applyForce之间有区别?好的。哦!这很有道理,因为.move不需要pb。那最好只使用其中一个,对吧? - Fluidity
1
如果使用物理引擎,最好对所有内容都使用物理引擎...这样可以减少问题,但有时需要传统的“到这里去,做这个”动画。电梯和升降机就是一个例子,通常最好使用“moveTo”等方法。@Fluidity - Confused

2
有多容易复现这个问题?在 update() 中,打印球的位置以查看其“消失”时的位置。(这将产生大量输出,所以请注意)。
从您发布的内容来看,似乎球没有设置与边框碰撞,这意味着球不会与边框发生反应(即弹跳),而边框本身是不动的(因为它是基于边缘的物理体)。这个问题结合一个高球速度(从一个硬击球中得到)可能会导致您用“主”精灵击中了球,使其穿过了边界-使用preciseCollisionDetection=true可能会解决这个问题,但首先要给边框添加一个类别,并将其添加到球的collisionBitMask中。

1
@Fluidity 我在模拟器中确实看到过快速移动的物体通过边界物理体(但是,是一个小故障)- 不确定我是否在设备上看到过。但是,如果球没有与边界发生碰撞,则它不会移动,因此球和边界可能重叠,然后物理引擎将尝试使它们不重叠,并且可能将球移动到边界的错误一侧。 - Steve Ives
1
需要记住的是,如果将bodyA设置为接触bodyB,则没有必要让bodyB接触bodyA。然而,在碰撞方面,如果碰撞设置不是相互的(即bodyA与bodyB发生碰撞,但碰撞的结果取决于bodyB是否与bodyA发生碰撞),情况就不同了。 - Steve Ives
你能详细说明一下上次的评论吗? - Fluidity
1
我已经附上了一些更新,球从设备的左侧使用6S滑出。@Fluidity 我现在会尝试你的提示。 - iamburak
@SteveIves 在update函数中是ball.position.x - iamburak
显示剩余8条评论

2
这是 Steve 在你的 .update() 中所说的例子。
if ball.position.x > frame.maxX { fatalError(" ball out of right bounds") }
if ball.position.x < frame.minX { fatalError(" ball out of left bounds") }
if ball.position.y > frame.maxY { fatalError(" ball out of top bounds") }
if ball.position.y < frame.minY { fatalError(" ball out of bottom bounds) }

您也可以只是在您的调试窗口中不断输出信息:
print(ball.position)

这将帮助您找出发生了什么 - 如果您的球穿过边界飞出去了,或者在某个地方被摧毁了,或者出现了其他可能的错误。

作为一种解决方法(暂时),我建议您将上述的“fatalError”替换为“ball.position = CGPoint(x: 0, y: 0)”或其他位置来“重置”球,以防它丢失。

您甚至可以将其最后的位置存储在一个变量中,然后在上述if语句触发时恢复它。

var lastBallLocation = CGPoint(x: 0, y: 0) // Just to initialize
override func update( prams ) {
  if ball.position.x > frame.maxX { ball.position = lastBallLocation }
  // .. copy the other three cases
  lastBallLocation = ball.position // update only on successful position

或者,您可以尝试让墙更厚(使用形状节点或精灵节点,并将它们放置在框架的外部,例如房屋的墙壁,您在屏幕上看到的是“房间”)。

make sure to give SKSpriteNode a physics body so will bounce off the red blocks

每堵墙也有一个物理体用于弹跳:

enter image description here


@tobeiosdev 好的。所以你的球肯定是穿过了墙壁。你尝试用我建议的重置球代码替换 fatalError 了吗?你也可以尝试让墙变厚一些(在每个边界上放置另一个节点作为墙)。 - Fluidity
正如您所看到的我的游戏场景GameScene,我该如何加厚墙壁呢?请详细说明。 - iamburak
是的,这个致命错误是由你最新的编辑建议引起的。 - iamburak
@tobeiosdev,请查看我的最后一个代码块。将fatalError替换为ball.position = lastBallLocation。另外,我添加了一张图片展示我在场景编辑器中创建的边界墙。 - Fluidity
self.frame比屏幕大。我认为球卡在了屏幕左下角。实现球和边界之间的碰撞。球有多大? - Steve Ives

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接