2014-12-27 15 views
14

我一直在学习SpriteKit(使用Ray Wenderlich等人的教程中的iOS游戏)来创建基于Breakout的我自己的非常简单的测试游戏,以查看我是否可以应用我学到的概念。我决定通过使用.sks文件来创建精灵节点并替换我的手动边界检查和碰撞与物理实体来简化我的代码。但是,我的球在与陡峭的角度相撞时会与墙壁/其他矩形保持平行(如在上下滑动)。下面是相关的代码 - 我提出的物理性质身体成代码以使它们更明显:Swift中的SpriteKit物理效果 - 球滑过墙而不是反射

import SpriteKit 

struct PhysicsCategory { 
    static let None:  UInt32 = 0  // 0 
    static let Edge:  UInt32 = 0b1 // 1 
    static let Paddle:  UInt32 = 0b10 // 2 
    static let Ball:  UInt32 = 0b100 // 4 
} 

var paddle: SKSpriteNode! 
var ball: SKSpriteNode! 

class GameScene: SKScene, SKPhysicsContactDelegate { 

    override func didMoveToView(view: SKView) { 

    physicsWorld.gravity = CGVector.zeroVector 

    let edge = SKNode() 
    edge.physicsBody = SKPhysicsBody(edgeLoopFromRect: frame) 
    edge.physicsBody!.usesPreciseCollisionDetection = true 
    edge.physicsBody!.categoryBitMask = PhysicsCategory.Edge 
    edge.physicsBody!.friction = 0 
    edge.physicsBody!.restitution = 1 
    edge.physicsBody!.angularDamping = 0 
    edge.physicsBody!.linearDamping = 0 
    edge.physicsBody!.dynamic = false 
    addChild(edge) 

    ball = childNodeWithName("ball") as SKSpriteNode 
    ball.physicsBody = SKPhysicsBody(rectangleOfSize: ball.size)) 
    ball.physicsBody!.usesPreciseCollisionDetection = true 
    ball.physicsBody!.categoryBitMask = PhysicsCategory.Ball 
    ball.physicsBody!.collisionBitMask = PhysicsCategory.Edge | PhysicsCategory.Paddle 
    ball.physicsBody!.allowsRotation = false 
    ball.physicsBody!.friction = 0 
    ball.physicsBody!.restitution = 1 
    ball.physicsBody!.angularDamping = 0 
    ball.physicsBody!.linearDamping = 0 

    physicsWorld.contactDelegate = self 
} 

忘记之前提到这一点,但我添加了一个简单的touchesBegan功能调试反弹 - 它只是调整

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) { 
    let touch = touches.anyObject() as UITouch 
    let moveToward = touch.locationInNode(self) 
    let targetVector = (moveToward - ball.position).normalized() * 300.0 
    ball.physicsBody!.velocity = CGVector(point: targetVector) 
} 

归一化()函数只是降低了球/触摸位置增量为单位矢量,并且在减去操作,其允许CGPoint减法的控制装置:所述速度在触摸点指向球。

球/边缘碰撞应始终以恰好相反的角度反射球但由于某种原因,球真的似乎有一个正确的角度。我当然可以实施一些解决方法来手动反映球的角度,但重点是我想要使用SpriteKit中内置的物理功能来完成这一切。有什么明显的我失踪了?

+2

您将需要移动精灵向其物理体施加力量或冲动或通过设定其速度属性。如果您通过动作或直接设置其位置,精灵将不参与物理模拟。 – 0x141E 2014-12-27 22:10:40

+0

对不起,我遗漏了我的速度调整。我只是编辑它,但基本上它是一个触摸事件,它将球速度设置为SKScene中的触摸点。 – 2014-12-28 20:15:51

+0

您是否在接触发生时轻敲并松开手指或将其按住? – 0x141E 2014-12-29 06:25:56

回答

7

我想出了一个临时解决方案,工作出奇的好。只需在边界对面施加一个非常小的冲动。您可能需要根据系统中的质量更改strength

func didBeginContact(contact: SKPhysicsContact) { 

    let otherNode = contact.bodyA.node == ball.sprite ? contact.bodyB.node : contact.bodyA.node 

    if let obstacle = otherNode as? Obstacle { 
     ball.onCollision(obstacle) 
    } 
    else if let border = otherNode as? SKSpriteNode { 

     assert(border.name == "border", "Bad assumption") 

     let strength = 1.0 * (ball.sprite.position.x < frame.width/2 ? 1 : -1) 
     let body = ball.sprite.physicsBody! 
     body.applyImpulse(CGVector(dx: strength, dy: 0)) 
    } 
} 

在现实中,这不应该是必要的,因为在这个问题所描述的,无摩擦,完全弹性碰撞决定了球应该通过反转X速度(假设侧边框),不管多么小的碰撞反弹角度是。

相反,游戏中发生的事情就好像Sprite套件忽略X速度,如果它小于某个特定值,使球在墙壁上滑动而不会反弹。

最后请注意

阅读thisthis后,很明显,我认为真正的答案是什么严重的物理游戏,你有,你应该使用Box2D的替代。你从迁移中获得太多的特权。

+0

Box2D工作正常,没有像这种粘性边缘的问题? – Jurik 2015-05-27 11:13:37

+0

@Jurik这就是我参考的文章证明 – Mazyod 2015-05-27 15:21:04

+0

啊好吧 - 谢谢 - 因为我浏览了两篇文章,并没有找到属于这个bug的任何信息,或者说Box2D没有浮动舍入问题。 – Jurik 2015-05-27 15:24:41

8

这似乎是碰撞检测的一个问题。大多数人已经通过使用didBeginContact找到解决方案,并以相反的方向重新施加力。请注意,他说didMoveToView,但在稍后对didBeginContact的评论中纠正了自己。

在雷Wenderlich教程​​

我对球的问题的修复程序的底部看评论“坐轨”如果 罢工以小角度(@ aziz76和@colinf )。我添加了另一个 类别“BorderCategory”,并将其分配给我们在didMoveToView中创建的边框PhysicsBody 。

和一个类似的SO问题here解释它为什么会发生。

即使你做到这一点,虽然,很多物理引擎(包括 SpriteKit的)有这样的,因为 浮点舍入误差的情况下麻烦。我发现,当我想要一个物体在碰撞后保持恒定速度时,最好强制使用didEndContact:或didSimulatePhysics处理程序来重置移动的物体速度,以便它的速度与其相同在碰撞前(但在相反的方向) 。

而且我注意到另一件事是,你使用的是方形而不是为你的球,一个圆的,你可能要考虑使用...

ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.width/2) 

所以原来你是不是疯了这总是从别人那里得到好消息,并希望这能帮助您找到最适合您应用的解决方案。

+0

有关问题的详细解释。 +1 – sangony 2015-04-04 15:09:21

+0

就我个人而言,我已经找到冲动的解决方案,是最直观的方法,但同样,它不应该是必要的。我了解SpriteKit没有确定性的物理引擎,但对所有情况下更复杂的解决方案感兴趣。最后,球有一个圆形的身体。 – Mazyod 2015-04-04 16:43:19

+0

@Mazyod我同意。这不应该是一个问题。如果你不能相信物理引擎,那么你担心你的应用程序无法按预期工作,甚至可能不是你的错。对于像突围游戏这样的事情,在更新循环中处理自己的定位/速度可能会更好,但让SK在联系人开始时通知您。这样,您始终拥有一致的速度,因为您可以完全控制它。使用屏幕上的操纵杆移动角色也是类似的事情。希望Apple在下次更新中改进这一点。 – 2015-04-04 19:06:16

0

我看到完全相同的问题,但对我的修复与其他答案中提到的碰撞检测问题无关。原来我用永久重复的SKAction让球开始运动。我最终发现,这与SpriteKit的物理模拟相冲突,导致节点/球沿着墙壁传播,而不是反弹。

我假设重复SKAction继续应用并覆盖/冲突与物理模拟的球的physicsBody.velocity属性的自动调整。

此问题的解决方法是通过将velocity设置为physicsBody属性来将球置于运动状态。一旦我完成了这个球,球就开始正确反弹了。我猜测,通过使用力量和冲动来操纵其位置physicsBody也是可行的,因为它们是物理模拟的一部分。

我花了很多时间才意识到这个问题,所以我在这里发布这个信息,以便我可以节省一些时间。谢谢0x141e!你的comment把我(和我的球)放在正确的道路上。

1

只有在任一方向的速度很小时,才会出现此问题。但是,以减少它的效果是可以降低physicsWorld的速度,例如,

physicsWorld.speed = 0.1 

,然后增加physicsBody的速度,例如,

let targetVector = (moveToward - ball.position).normalized() * 300.0 * 10 
ball.physicsBody!.velocity = CGVector(point: targetVector) 
+0

这有效,但是...为什么?! – Norbert 2017-03-19 04:04:35

+0

好问题。我不知道,但我想这是由于物理:F = MA。假设m = 1且数值上a = dv/dt,则猜测是设置物理世界速度与设置dt相同,即,如果我们将pW-速度设置为0.1,则具有F = dv /(0.1 * dt )。因此,如果滑动发生在F CrazyChris 2017-03-20 09:38:18