Swift + Physics:碰撞时错误的角度计算

Posted

技术标签:

【中文标题】Swift + Physics:碰撞时错误的角度计算【英文标题】:Swift + Physics: Wrong angle calculation on collision 【发布时间】:2016-08-07 15:46:05 【问题描述】:

我有一个简单的SpriteKit 应用程序,带有墙壁和球。两者都使用SKPhysicsBody 设置。

当我在一个方向上施加力时,我希望球在碰撞时以相同的角度在墙壁上反射,但方向相反。

但有时我看到的角度很奇怪。我玩了很多 physicsBody 属性,但无法修复它。有时第一次反射看起来不错,但第三次或第六次,有时第一次反射的角度不对。

我从不同的帖子中读到,人们有点自我计算“正确的方向”。但我无法想象SpriteKits 物理引擎无法做到这一点。

查看我附上的图片以了解我的意思。有人可以帮忙吗?我不想开始使用 Box2d for Swift,因为这看起来对我来说太难集成到 Swift 中了。

这是我的 GameScene.swift 文件中的 physicsWorld init,我的所有元素都添加到该文件中:

self.physicsWorld.gravity = CGVectorMake(0, 0)

添加我所有的代码太多了,所以我添加了重要的部分。希望它足以进行分析。球、墙等所有元素都是SKSpriteNode

这是球的physicsBody 代码:

self.physicsBody = SKPhysicsBody(circleOfRadius: Constants.Config.playersize+self.node.lineWidth)
self.physicsBody?.restitution = 1
self.physicsBody?.friction = 0
self.physicsBody?.linearDamping = 1
self.physicsBody?.allowsRotation = false
self.physicsBody?.categoryBitMask = Constants.Config.PhysicsCategory.Player
self.physicsBody?.collisionBitMask = Constants.Config.PhysicsCategory.Wall | Constants.Config.PhysicsCategory.Enemy
self.physicsBody?.contactTestBitMask = Constants.Config.PhysicsCategory.Wall | Constants.Config.PhysicsCategory.Enemy

这是墙壁的物理体:

el = SKSpriteNode(color: UIColor.blueColor(), size: CGSize(width: Constants.Config.wallsize, height: Constants.Config.wallsize))
el.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: Constants.Config.wallsize, height: Constants.Config.wallsize))
el.physicsBody?.dynamic = false
el.physicsBody?.categoryBitMask = Constants.Config.PhysicsCategory.Wall
el.physicsBody?.collisionBitMask = Constants.Config.PhysicsCategory.Player
el.physicsBody?.contactTestBitMask = Constants.Config.PhysicsCategory.Player

最后我只是在球 physicsBody 上调用 applyImpulse 函数,让它在物理模拟中移动。

还请检查我附加的第二张图片/gif。它显示了一个简单的 skphysics 应用程序的边缘碰撞问题,没有任何特殊的参数化。只是一个带有球的矩形和一个作为脉冲应用的向量。

这是我使用 appzYourLife 解决方案的完整非工作代码。

class GameScene: SKScene 
private var ball:SKShapeNode!
override func didMoveToView(view: SKView) 
    let screen = UIScreen.mainScreen().bounds
    let p = UIBezierPath()
    p.moveToPoint(CGPointMake(0,0))
    p.addLineToPoint(CGPointMake(screen.width,0))
    p.addLineToPoint(CGPointMake(screen.width, screen.height))
    p.addLineToPoint(CGPointMake(0,screen.height))
    p.closePath()
    let shape = SKShapeNode(path: p.CGPath)
    shape.physicsBody = SKPhysicsBody(edgeLoopFromPath: p.CGPath)
    shape.physicsBody?.affectedByGravity = false
    shape.physicsBody?.dynamic = false
    shape.strokeColor = UIColor.blackColor()

    self.addChild(shape)

    ball = SKShapeNode(circleOfRadius: 17)
    ball.name = "player"
    ball.position = CGPoint(x: 20, y: 20)
    ball.fillColor = UIColor.yellowColor()
    ball.physicsBody = SKPhysicsBody(circleOfRadius: 17)
    ball.physicsBody?.angularDamping = 0
    ball.physicsBody?.linearDamping = 0
    ball.physicsBody?.restitution = 1
    ball.physicsBody?.friction = 0
    ball.physicsBody?.allowsRotation = false

    self.addChild(ball)
    self.ball.physicsBody?.applyImpulse(CGVector(dx: 2.4, dy: 9.7))

我刚刚意识到我的球精灵的质量和密度为零。为什么?即使设置它也保持为零。

【问题讨论】:

你能提供任何代码吗? 只是添加了一些代码,添加所有代码会太多,所有其他部分都是我的游戏和关卡层处理(作为 SKNode 的),我将元素放在其中,最后放在 GameScene 上。 我想知道是否没有对矩形墙的外侧进行碰撞,因为墙是体积矩形。也许使用bodyWithEdgeLoopFromRect 而不是rectangleOfSize Ok 将墙壁的 rectangleOfSize 更改为 edgeLoopF​​romRect。球的行为仍然相同。这不应该影响计算方式,因为这两种类型都是碰撞应该使用的实体。 【参考方案1】:
Since i cannot imagine this is some unknown bug in SpriteKits physics engine, 
i very hope someone can help me here as this is a full stopper for me.

这是 SpriteKits 物理引擎中的一个(已知)错误。但有时只使用值会使这个错误消失 - 例如,尝试在每次撞墙时改变球的速度(即使是很小的值)。

此处列出了其他解决方案: SpriteKit physics in Swift - Ball slides against wall instead of reflecting

(和你一样的问题,从 2014 年开始,仍然没有解决..)

【讨论】:

spritekits引擎是box2d是真的吗? 不,不同的引擎。使用 box2d 将消除此错误 使用 Box2D 消除了这个错误。这是真的,所以我至少为这个应用程序采用了 Box2D 的方式。希望苹果能尽快修复它。 @Roee84 你认为这是关闭轴的子弹物理吗?【参考方案2】:

SpriteKit 工作正常

我创建了一个简单的项目,它运行良好。

问题

正如 @Sulthan 已经在评论中建议的那样,我认为您代码中的问题是这一行

el.physicsBody = SKPhysicsBody(rectangleOfSize: CGSize(width: Constants.Config.wallsize, height: Constants.Config.wallsize))

这里不是为您的场景创建物理边界,而是创建一个矩形物理体,它与Ball 的物理体重叠,从而产生不切实际的行为。

为了创建场景的物理边界,我只是使用了这个

physicsBody = SKPhysicsBody(edgeLoopFromRect: frame)

完整代码

顺便说一下,这是我项目的完整代码

class GameScene: SKScene 
    override func didMoveToView(view: SKView) 
        let ball = Ball()
        ball.position = CGPoint(x:frame.midX, y:frame.midY)
        addChild(ball)
        physicsBody = SKPhysicsBody(edgeLoopFromRect: frame)
        ball.physicsBody!.applyImpulse(CGVector(dx:50, dy:70))
        physicsWorld.gravity = CGVector(dx:0, dy:0)
    


class Ball: SKSpriteNode 

    init() 
        let texture = SKTexture(imageNamed: "ball")
        super.init(texture: texture, color: .clearColor(), size: texture.size())

        let physicsBody = SKPhysicsBody(circleOfRadius: texture.size().width/2)
        physicsBody.restitution = 1
        physicsBody.friction = 0
        physicsBody.linearDamping = 0
        physicsBody.allowsRotation = false
        self.physicsBody = physicsBody
    

    required init?(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    


我还在GameViewController.swift 中使用了scene.scaleMode = .ResizeFill

【讨论】:

嗯,这是我一周前告诉他的,他说什么都没有改变 :) 我会告诉你实际创建一个演示的要点。 @Sulthan:嗨,你是对的,我没有注意到你的评论。我在回答中添加了对您评论的引用。 谢谢,我会尽快尝试。有人告诉我这是一个错误。无论如何,一旦它起作用,我会尝试将其标记为解决方案。 我使用 appzYourLife 解决方案添加了完整的 GameScene.swift 代码。还是行不通。这里有什么问题?顺便说一句,我之前在代码中使用的矩形块是可以的,因为我在场景中使用了这个绘制墙块,所以肯定没有重叠。但无论如何,即使我使用你的想法的第二个代码块也不适合我,同样的行为。 @NovumCoder:当施加在 x 上的脉冲低于6.0 时,似乎会发生这种情况。可以改成self.ball.physicsBody?.applyImpulse(CGVector(dx: 6.5, dy: 9.7)),别忘了去掉重力physicsWorld.gravity = CGVector(dx:0, dy:0)

以上是关于Swift + Physics:碰撞时错误的角度计算的主要内容,如果未能解决你的问题,请参考以下文章

Physics Experiment POJ 3684(弹性碰撞)

碰撞位掩码是如何工作的?斯威夫特/雪碧套件

POJ3684-Physics Experiment 弹性碰撞

Swift 物理致命错误:当两个字符发生碰撞时,在展开可选值时意外发现 nil

POJ 3684 Physics Experiment(弹性碰撞)

如何使 Physics2D.OverlapBoxAll() 活动 x 时间,并且只与它碰撞的新对象交互?