SKPhysicsBody and [SKNode setScale:]

9
在我目前使用SpriteKit的项目中,我有一堆需要在不同时间独立缩放的精灵。问题是,当我缩放节点时,物理体并没有随之缩放,这会影响物理效果。以下是我为了提问而准备的一个小例子:
CGSize objectSize = CGSizeMake(100, 100);
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(0, 0, self.size.width, self.size.height)];

SKSpriteNode *n1 = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:objectSize];
n1.position = CGPointMake(self.size.width/2, 2*self.size.height/3);
n1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:objectSize];
n1.physicsBody.affectedByGravity = YES;
[self addChild:n1];

SKSpriteNode *n2 = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:objectSize];
n2.position = CGPointMake(self.size.width/2, self.size.height/3);
n2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:objectSize];
n2.physicsBody.affectedByGravity = YES;
[self addChild:n2];

[n1 setScale:0.5];

请看蓝色精灵(缩小后)位于红色精灵之上,但可以看出其物理体仍具有我所说的尺寸,并且没有缩放。很明显,缩小节点并不会缩小物理体。那么我的问题是,如果我必须手动执行缩放,该怎么做?我尝试在缩放时将身体与正确尺寸之一进行交换,但是如果旧身体具有关节等,则情况会变得非常复杂... 如果我可以以某种方式缩放现有身体,那将更简单。

1
在Box2D中(SK使用的物理引擎),身体形状无法进行简单的缩放。 - CodeSmile
6个回答

7

你可以使用SKAction或更新循环来创建一个新的SKPhysicsBody,并应用到对象中。但是,这样做会丢失速度。为了解决这个问题,你可以进行以下操作:

SKPhysicsBody *newBody = [SKPhysicsBody bodyWithRectangleOfSize:boxObject.size];
newBody.velocity = boxObject.physicsBody.velocity;

boxObject.physicsBody = newBody;

这还不够,还涉及到其他参数,比如角速度等,而这些参数并不直接可用,因此您无法将相同的属性传播到新的物理体中。 - Alessandro Ornano

5

对于其他读者,我相信现在物理体会随着SKNode缩放。我使用的是Xcode 8.2.1和Swift 3.0.2。我开始一个新项目,选择游戏,填写细节。修改两个文件:

GameViewController.swift(添加一行)

view.showsFPS = true
view.showsNodeCount = true

// add this to turn on Physics Edges    
view.showsPhysics = true

将Game Scene.swift中的sceneDidLoad()函数替换为以下内容:

override func sceneDidLoad() {

    var found = false
    self.enumerateChildNodes(withName: "testSprite") {
        node, stop in
            found = true
    }
    if !found {
        let testSprite = SKSpriteNode(color: UIColor.white, size: CGSize(width: 200.0, height: 200.0))
        testSprite.physicsBody = SKPhysicsBody(polygonFrom: CGPath(rect: CGRect(x: -100.0, y: -100.0, width: 200.0, height: 200.0), transform: nil))
        testSprite.physicsBody?.affectedByGravity = false
        testSprite.name = "testSprite"
        let scaleAction = SKAction.repeatForever(SKAction.sequence([
            SKAction.scale(to: 2.0, duration: 2.0),
            SKAction.scale(to: 0.5, duration: 2.0)
            ]))
        testSprite.run(scaleAction)
        self.addChild(testSprite)
    }

}

物理边界随着精灵节点的缩放而变化。

2
我也注意到了这个问题:https://dev59.com/VYHba4cB1Zd3GeqPP1Z1#33572073 - Whirlwind
@Whirlwind,真希望在看到这篇文章之前能看到你的帖子。 - Christian Cerri

5

物理体形状无法缩放。这在Sprite Kit内部使用的Box2D中绝对行不通。

除了内部优化之外,缩放物理体将产生深远的影响。例如,将体形放大可能会使该物体陷入碰撞中而无法移动。它会影响关节的相互作用。它肯定会改变物体的密度或质量,从而影响其行为。

您可以使用SKNode添加没有物理体的精灵,然后再添加具有给定大小的其他SKNode的物体。然后,在开始缩放精灵时启用或禁用这些物体。如果您正确计时,玩家就不会注意到碰撞形状只是在精灵动画缩放转换期间从全尺寸变为半尺寸。

但是,您必须根据其大小计算物体的密度和其他属性。


好主意,但不幸的是对我的用例不适用:我有一个球(带有物体的SKShapeNode),当它撞击另一个物体时,会朝另一个方向弹开并变小。如果我只是停用物体并创建另一个物体,那么球将失去其速度和其他属性。你写道:
> 添加给定大小的附加SKNode与物体
据我所知,不可能将多个物体添加到单个SKNode中,不是吗?
- cocoapriest
我认为他的意思是你可以将多个SKNode添加为子节点到你的精灵节点中,每个子节点都将有自己的物理体。你可以启用/禁用这些物体的一种方法是改变它们的碰撞掩码。 - peacetype

3
我有完全相同的问题,我认为这是SpriteKit中的一个错误。
尝试使用缩放动作,在整个场景上运行此操作。
[self runAction:[SKAction scaleTo:0.5 duration:0]];

我最初的问题

这是您想要发生的吗?

- (void) anthoertest
{
    CGSize objectSize = CGSizeMake(100, 100);
    self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(0, 0, self.size.width, self.size.height)];

    SKSpriteNode *n1 = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:objectSize];
    n1.position = CGPointMake(self.size.width/2, 2*self.size.height/3);
    n1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:n1.size];
    n1.physicsBody.affectedByGravity = YES;
    [self addChild:n1];

    SKSpriteNode *n2 = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:objectSize];
    n2.position = CGPointMake(self.size.width/2, self.size.height/3);
    n2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:n2.size];
    n2.physicsBody.affectedByGravity = YES;
    [self addChild:n2];

    [self shrinkMe:n1];
}

- (void) shrinkMe:(SKSpriteNode *) s
{
    [s setScale:0.5];
    s.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:s.size];
}

enter image description here


我之前也读过你的帖子。如果我按那种方式缩放整个场景,它是有效的,但是如果在精灵上运行相同的操作,它会产生相同的错误结果。我正在尝试独立缩放这些节点,这让我感到很疯狂。 - mprivat
如果你将身体换成一个较小的,它有点能够工作。但是这会对现有的身体之间的连接产生影响。如果节点在运动中,重新创建连接就不起作用了。恐怕在框架做出调整之前,这将是一个不可能解决的问题。 - mprivat
我同意你的评论,需要一个SK修复。 - DogCoffee
2
这不是一个错误。缩放操作作用于视图,但物理体的形状无法缩放。Box2D不允许缩放形状,除非您删除并添加一个新的(缩放的)形状,这相当低效。缩放物理体将产生深远的影响,例如将身体形状放大可能会使身体在碰撞中卡住。 - CodeSmile

1
物理体依赖于SKNode的CGPath;你需要以某种方式改变它。你可以尝试使用以下方法缩放CGPath:
//scale up

CGFloat scaleFactor = 1.1;
CGAffineTransform scaleTransform = CGAffineTransformIdentity;
scaleTransform = CGAffineTransformScale(scaleTransform, scaleFactor, scaleFactor);
CGPathRef scaledPathRef = CGPathCreateCopyByTransformingPath(exampleNode.path, &scaleTransform);
exampleNode.path = scaledPathRef;

但请记住,这不会更新已经存在于SKScene中的节点的外观。您可能需要将CGPath的调整与缩放节点的SKAction相结合。


但是你无法访问PhysicsBody当前路径。 - Martin
身体依赖于如何创建它,而不一定(直接)依赖于CGPath - 它取决于在创建时的路径(或其他几何形状)。 - bshirley

0

我曾经遇到过同样的问题,我所做的就是在我的更新当前时间函数中定义了物理体,这样它就会随着时间的推移而缩放。

对于你来说,应该是这样的:

override func sceneDidLoad() {

SKSpriteNode *n1 = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:objectSize];
n1.position = CGPointMake(self.size.width/2, 2*self.size.height/3);

}
override func update(_ currentTime: TimeInterval) {

n1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:objectSize];
n1.physicsBody.affectedByGravity = YES;

}

唯一的问题是你必须在updateCurrentTime中重新定义物理属性。你可以将其中一些属性(如弹性)设置为变量,这样就可以通过在其他地方更改变量来更改其属性。在任何其他地方重新定义它只适用于下一帧被调用之前。

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