使用“bodyWithTexture”(SpriteKit)时更改 SKPhysicsBody 的中心点
Posted
技术标签:
【中文标题】使用“bodyWithTexture”(SpriteKit)时更改 SKPhysicsBody 的中心点【英文标题】:Changing center point of SKPhysicsBody while using 'bodyWithTexture' (SpriteKit) 【发布时间】:2015-05-21 23:45:56 【问题描述】:在使用“bodyWithTexture”时,Spritekit 的 API 似乎不允许更改物理体的中心(或者我可能遗漏了一些东西)。
我想知道在使用像素精度物理体时如何解决这个问题。
//Upper Claw
SKSpriteNode *claw = [SKSpriteNode spriteNodeWithImageNamed:BOSS_CLAW_SPRITE];
claw.position = CGPointMake(800, 520);
claw.anchorPoint = CGPointMake(1, .7);
//physics
claw.physicsBody = [SKPhysicsBody bodyWithTexture:claw.texture size:claw.size];
可以清楚的看到物理体的中心位置就是锚点所在的位置。
另外,bodyWithCircle / bodyWithRectangle 有一个 'center' 属性。但是,它不够精确,需要编写大量代码(不可扩展)。
-(SKPhysicsBody*)getPhysicsForClaw:(BOOL)isUpperClaw
NSInteger reverseConstant = 1;
if (!isUpperClaw)
reverseConstant = -1;
SKPhysicsBody *clawTip1 = [SKPhysicsBody bodyWithCircleOfRadius:5 center:CGPointMake(-545, -140*reverseConstant)];
SKPhysicsBody *clawTip2 = [SKPhysicsBody bodyWithCircleOfRadius:6 center:CGPointMake(-540, -130*reverseConstant)];
SKPhysicsBody *clawTip3 = [SKPhysicsBody bodyWithCircleOfRadius:7 center:CGPointMake(-535, -120*reverseConstant)];
SKPhysicsBody *clawTip4 = [SKPhysicsBody bodyWithCircleOfRadius:8 center:CGPointMake(-530, -110*reverseConstant)];
SKPhysicsBody *clawTip5 = [SKPhysicsBody bodyWithCircleOfRadius:8 center:CGPointMake(-525, -100*reverseConstant)];
SKPhysicsBody *clawTip6 = [SKPhysicsBody bodyWithCircleOfRadius:9 center:CGPointMake(-515, -90*reverseConstant)];
SKPhysicsBody *clawTip7 = [SKPhysicsBody bodyWithCircleOfRadius:11 center:CGPointMake(-508, -78*reverseConstant)];
SKPhysicsBody *clawTip8 = [SKPhysicsBody bodyWithCircleOfRadius:12 center:CGPointMake(-495, -65*reverseConstant)];
SKPhysicsBody *clawTip9 = [SKPhysicsBody bodyWithCircleOfRadius:13 center:CGPointMake(-480, -50*reverseConstant)];
SKPhysicsBody *clawTip10 = [SKPhysicsBody bodyWithCircleOfRadius:14 center:CGPointMake(-465, -35*reverseConstant)];
SKPhysicsBody *clawTeeth1 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(6, 40) center:CGPointMake(-433, -70)];
SKPhysicsBody *clawTeeth2 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(15, 20) center:CGPointMake(-420, -60)];
SKPhysicsBody *clawTeeth3 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(9, 40) center:CGPointMake(-395, -70)];
SKPhysicsBody *clawTeeth4 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(15, 20) center:CGPointMake(-382, -60)];
SKPhysicsBody *clawTeeth5 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(9, 40) center:CGPointMake(-345, -70)];
SKPhysicsBody *clawTeeth6 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(10, 20) center:CGPointMake(-334, -60)];
SKPhysicsBody *clawTeeth7 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(9, 30) center:CGPointMake(-295, -60)];
SKPhysicsBody *clawTeeth8 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(9, 30) center:CGPointMake(-255, -42)];
SKPhysicsBody *clawBody1 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(100, 45) center:CGPointMake(-400, -30)];
SKPhysicsBody *clawBody2 = [SKPhysicsBody bodyWithCircleOfRadius:26 center:CGPointMake(-325, -25*reverseConstant)];
SKPhysicsBody *clawBody3 = [SKPhysicsBody bodyWithCircleOfRadius:28 center:CGPointMake(-290, -12*reverseConstant)];
SKPhysicsBody *clawBody4 = [SKPhysicsBody bodyWithCircleOfRadius:29 center:CGPointMake(-250, 0*reverseConstant)];
SKPhysicsBody *clawBody5 = [SKPhysicsBody bodyWithCircleOfRadius:28 center:CGPointMake(-210, 10*reverseConstant)];
SKPhysicsBody *clawBody6 = [SKPhysicsBody bodyWithCircleOfRadius:30 center:CGPointMake(-165, 24*reverseConstant)];
SKPhysicsBody *clawBase1 = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(120, 55) center:CGPointMake(-75, 24)];
SKPhysicsBody *claw = [SKPhysicsBody bodyWithBodies:@[clawTip1, clawTip2, clawTip3, clawTip4, clawTip5, clawTip6, clawTip7, clawTip8, clawTip9, clawTip10, clawTeeth1, clawTeeth2, clawTeeth3, clawTeeth4, clawTeeth5, clawTeeth6, clawTeeth7, clawTeeth8, clawBody1, clawBody2, clawBody3, clawBody4, clawBody5, clawBody6, clawBase1]];
return claw;
【问题讨论】:
您解决了这个问题吗? @0x141E 不。我不确定他们是否在新版本的 SpriteKit 中修复了这个问题。我刚刚完成了很长的路要走,使用bodyWithRectangleOfSize / bodyWithCircleOfRadius
并相应地移动中心。
您考虑过使用销接头吗?
【参考方案1】:
我对这种情况的解决方案是使用不可见的 SKNode。
没有改变锚点的爪子,使其与身体对齐。然后将该节点作为一个空白 SKNode 的父节点,并相应地移动爪子。现在,通过旋转空白 SKNode,您可以获得更改锚点的效果,同时物理主体仍然对齐。
【讨论】:
在body不是圆形或矩形的情况下,您的解决方案更实用。我还在这里写了一个示例实现http://***.com/a/35171584/1135503【参考方案2】:锚点对物理体没有影响。有几个物理物体可以定义一个中心点。
(SKPhysicsBody *)bodyWithCircleOfRadius:(CGFloat)r
center:(CGPoint)center
(SKPhysicsBody *)bodyWithRectangleOfSize:(CGSize)s
center:(CGPoint)center
不幸的是bodyWithTexture:
没有这样的能力。作为一个技巧,您可以使用许多不同大小的矩形,将它们旋转到所需的角度,然后将它们与(SKPhysicsBody *)bodyWithBodies:(NSArray *)bodies
连接在一起。这将使您几乎可以覆盖您的纹理。
另外一个好处是,使用矩形代替 bodyWithTexture 对 FPS 的负担也更小。
【讨论】:
这正是我迄今为止一直在做的事情。只是想我会伸出手看看是否有不同的解决方法。您无法为 bodyWithTexture 定义中心点似乎很愚蠢。也许在下一版本的 SpriteKit 中。我还注意到使用 bodyWithTexture 时性能有所下降,这很好。 @JonnyRamos - 同意。不幸的是,SK 在其当前版本中缺少一些基本但必要的功能。这只是其中之一。【参考方案3】:primaryartemis解决方案中的代码示例,适用于除圆形和矩形之外的任何其他形状,更实用。
换句话说,我们使用SKNode
来包含我们的精灵,SKNode
的位置将起到锚点的作用,我们将精灵从这里移开SKNode
容器到我们期望的位置,以便我们的转换按预期工作。
这是一个图形,一个简单的半圆弧,大小为 (width:100, height: 50) ,我们希望这个图形在 (0.5, 0.0) 的锚点处旋转。
class SemiArc: SKNode
var sprite: SKSpriteNode
override init()
sprite = SKSpriteNode(imageNamed: "semi_arc")
super.init()
// Move away from this container SKNode, this SKNode's position act like anchorPoint
sprite.position.y = sprite.size.height / 2
// The physicsBody will move along as well
sprite.physicsBody = SKPhysicsBody(texture: sprite.texture!, size: sprite.size)
// Add the sprite to this container SKNode
addChild(sprite)
// To test the transformation from the anchor point we desire
let rotationForever = SKAction.repeatActionForever(SKAction.rotateByAngle(CGFloat(M_PI) * 2, duration: 2.0))
runAction(rotationForever)
然后我们可以将这个 SemiArc
SKNode
放到我们的场景中,它将锚定在我们想要的位置。
我们可以为这个SKNode
容器创建一个助手,这样我们就可以更轻松地为简单的精灵重用它。
extension SKNode
class func containerNodeWithSprite(sprite: SKSpriteNode, withAnchorPoint anchorPoint: CGPoint) -> SKNode
let containerNode = SKNode()
sprite.position.x = (sprite.size.width / 2) - ( sprite.size.width * anchorPoint.x)
sprite.position.y = (sprite.size.height / 2) - ( sprite.size.height * anchorPoint.y)
containerNode.addChild(sprite)
return containerNode
示例用法:
// Somewhere in a scene class
let sprite = SKSpriteNode(imageNamed: "semi_arc")
sprite.physicsBody = SKPhysicsBody(texture: sprite.texture!, size: sprite.size)
let container = SKNode.containerNodeWithSprite(sprite, withAnchorPoint: CGPointMake(0.5 , 0.0))
container.position = view!.center
let rotationForever = SKAction.repeatActionForever(SKAction.rotateByAngle(CGFloat(M_PI) * 2, duration: 2.0))
containedNode.runAction(rotationForever)
addChild(container)
注意事项和解决方法
我发现此解决方案的一个警告是,当您想根据物理模拟位置删除此 SKNode
时将不起作用,因为您没有给容器 SKNode
一个物理体,所以它永远不会改变它位置。您检查它是否超出界限的逻辑将不起作用,因为您正在检查此容器。当你检测到接触和碰撞时,这也是一样的,它是容器内部的物理连接节点导致接触和碰撞,而不是容器本身。但是你仍然可以对接触和碰撞做出正确的反应,只有当你想删除这个SKNode
时才会出现问题@
解决方法一:计算其子节点与物理体加上容器位置的相对位置。
enumerateChildNodesWithName("fallingObjectWithMiddleBottomAnchor")
(node, stop) in
if ((node.children.first?.position.y)! + node.position.y) < 0
node.removeFromParent()
解决方法 2: 不要将它用于动态体,不要与动态体的锚点混淆,因为它的属性应该是模拟的,而不是手动更新。
解决方法 3: 更好的解决方案是使用 SKConstraint 来实现您所需要的。
【讨论】:
以上是关于使用“bodyWithTexture”(SpriteKit)时更改 SKPhysicsBody 的中心点的主要内容,如果未能解决你的问题,请参考以下文章