Sprite Kit Physics Collision Issue

自古美人都是妖i 提交于 2020-01-02 03:47:06

问题


I'm having an issue with some collisions. I have two objects equal size and mass. When one collides into another that is at rest I get the correct behavior (grey area in image). When I have two objects that are next to each other the behavior isn't quite right. Spritekit result on the left. Expected/needed result on right.

I think I know what is going on, but not sure what to do about it. If the object was one object with twice the mass then the spritekit behavior would be correct, but they are separate objects and the uppermost one should take the velocity of the incoming particle. It seems that it treats them as one object.

I've tried cheating and shrinking the radius after the two are touching to put a small gap, but then things get all messed up. Does someone know what I can do here? Thanks.


回答1:


Brief introduction

This can definitely be done, and there are no complex calculations necessary. SpriteKit is perfectly capable of handling this collision. I think you're misunderstanding the physics in play here. There is one simple rule to keep in mind in physics that you are breaking. Speaking in real-life terms here:

No two objects can occupy the same point in space. This goes for parts of objects as well. No parts of any two objects can occupy the same point in space. With this in mind, we can say that no matter how close two objects are, there is always some space between them.

In other words, the amount of space between two objects next to each other must be greater than zero. No matter how infinitely small that space is, it still has to be there.

The Problem

Your underlying problem is that you've left zero space between the blue circles. Let's assume the circles have a radius of 30 and the top circle has a y-position of 400.

Your math for positioning the middle circle is likely topCircle.position.y - (topCircle.height + middleCircle.height) / 2. Since your circles are all the same radius (30 in our example), you probably simplified that to topCircle.y - CIRCLE_DIAMETER. If you positioned the circles absolutely, this is probably the calculation you performed in your head.

With either equation, when you plug in the numbers, the y-position of the second ball comes out to 340.

First equation: 400 - (60 + 60) / 2 = 340
Second equation: 400 - 60 = 340
(Note: first equation is more flexible and will allow you to work with any size circles instead of uniform-size)

Herein lies the problem. It should be clear by now that the bottom of the top circle is occupying the same point in space as the top of the top of the middle circle.

Top circle: 400 - 30 = 370
Middle circle: 340 + 30 = 370
Space between: |370 - 370| = 0

The two physics bodies are attempting to occupy the same point in space, and thus have merged into one body, giving you the behavior you're seeing.

The solution

With all that being said, if you simply add a point (or less) of space between the circles, you will get the behavior you desire.
(Note: also make sure to set the restitution of each circle's physics body to 1)

This works with any number of circles. Here's a simple example scene with 1 circle contacting 4 others that exhibits this behavior. I ran this on an iPhone 6:

#import "GameScene.h"

@interface GameScene()

@property (strong, nonatomic) NSArray *nodes;

@end


@implementation GameScene

#pragma mark - View Lifecycle
-(void)didMoveToView:(SKView *)view
{
    self.backgroundColor = [SKColor blackColor];

    [self createNodes];
    [self positionNodes];
    [self addNodes];
}

#pragma mark - Setup
- (void)createNodes
{
    // You could use SKShapeNode as well
    self.nodes = @[[SKSpriteNode spriteNodeWithImageNamed:@"circle"],
                   [SKSpriteNode spriteNodeWithImageNamed:@"circle"],
                   [SKSpriteNode spriteNodeWithImageNamed:@"circle"],
                   [SKSpriteNode spriteNodeWithImageNamed:@"circle"],
                   [SKSpriteNode spriteNodeWithImageNamed:@"circle"]];

    // Use this if you don't have a 30-radius circle image at hand
//    self.nodes = @[[SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(60, 60)],
//                   [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(60, 60)],
//                   [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(60, 60)],
//                   [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(60, 60)],
//                   [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:CGSizeMake(60, 60)]];

    for(SKSpriteNode *node in self.nodes)
    {
        node.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:30];
        node.physicsBody.affectedByGravity = NO;
        node.physicsBody.restitution = 1;
    }
}

- (void)positionNodes
{
    SKSpriteNode *node = self.nodes.firstObject;
    node.position = CGPointMake(300, 400);

    for(NSInteger i = 1; i < self.nodes.count - 1; i++)
    {
        SKSpriteNode *prevNode = node;
        node = self.nodes[i];
        node.position = CGPointMake(300, prevNode.position.y - (prevNode.frame.size.height + node.frame.size.height) / 2 - 1);
    }

    node = self.nodes.lastObject;
    node.position = CGPointMake(300, 100);

    // Above created nodes at these positions:
    // (300, 400);
    // (300, 339);
    // (300, 278);
    // (300, 217);
    // (300, 100);
}

- (void)addNodes
{
    for(SKSpriteNode *node in self.nodes)
    {
        [self addChild:node];
    }
}

#pragma mark - Touch Events
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    SKSpriteNode *node = self.nodes.lastObject;
    [node.physicsBody applyImpulse:CGVectorMake(0, 20)];
}

@end



回答2:


What you could do, and this is only in theory , as I have not yet tried it myself, is inside the collision callback check if one of the colliders has intersection/collision with a third object and manually calculate its velocity too.

I suspect that you won't get a collision detection for the 2 stacked balls (since they start @ that position) so manually checking this and applying the appropriate velocities to the adjoining bodies might do the trick.

Might have to track which "body pairs" have already calculated their new velocities to avoid calculation cycles (which may be infinite).



来源:https://stackoverflow.com/questions/21315726/sprite-kit-physics-collision-issue

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!