Swift + Physics: 错误的碰撞角度计算
Swift + Physics: Wrong angle calculation on collision
我有一个简单的 SpriteKit
应用程序,有墙和球。两者都设置为 SKPhysicsBody
.
当我在一个方向上施加力时,我希望球在碰撞时以相同的角度在墙上反射,但方向相反。
但有时我看到的角度很奇怪。我玩了很多 physicsBody
属性,但无法修复它。有时第一次反射看起来不错,但第三次或第六次,有时第一次反射角度不对。
我看了不同的帖子,人们有点自算 "correct direction"。但我无法想象SpriteKits
物理引擎无法做到这一点。
查看我的附加图片以了解我的意思。有人可以帮忙吗?我不想为 Swift 开始玩 Box2d,因为这看起来对我来说太难融入 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
最后我只是在球上调用 applyImpulse 函数 physicsBody
使其在物理模拟中移动。
同时检查我附加的第二个 image/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))
}
我刚刚意识到我的球精灵的质量和密度为零。为什么?即使设置它也保持为零。
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年了,还没解决。。)
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")
}
}
I also used scene.scaleMode = .ResizeFill
inside GameViewController.swift
.
我有一个简单的 SpriteKit
应用程序,有墙和球。两者都设置为 SKPhysicsBody
.
当我在一个方向上施加力时,我希望球在碰撞时以相同的角度在墙上反射,但方向相反。
但有时我看到的角度很奇怪。我玩了很多 physicsBody
属性,但无法修复它。有时第一次反射看起来不错,但第三次或第六次,有时第一次反射角度不对。
我看了不同的帖子,人们有点自算 "correct direction"。但我无法想象SpriteKits
物理引擎无法做到这一点。
查看我的附加图片以了解我的意思。有人可以帮忙吗?我不想为 Swift 开始玩 Box2d,因为这看起来对我来说太难融入 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
最后我只是在球上调用 applyImpulse 函数 physicsBody
使其在物理模拟中移动。
同时检查我附加的第二个 image/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))
}
我刚刚意识到我的球精灵的质量和密度为零。为什么?即使设置它也保持为零。
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年了,还没解决。。)
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")
}
}
I also used
scene.scaleMode = .ResizeFill
insideGameViewController.swift
.