PhysicsBody 内的 Swift SpriteKit 接触检查

Posted

技术标签:

【中文标题】PhysicsBody 内的 Swift SpriteKit 接触检查【英文标题】:Swift SpriteKit Contact check inside the PhysicsBody 【发布时间】:2015-10-18 00:34:21 【问题描述】:

我正在研究 PhysicsBody 并遇到了一些麻烦。 首先,我现在真的不知道这是否可以使用 PhysicsBody 或仅使用像素检查。

问题是:接触检查工作得很好,但如果一个节点位于 PhysicsBody 内,它将无法查看它是否发生碰撞。 在图片中,它得到了很好的解释。

Picture 1: This works

Picture 2: This won't

一些想法它是如何工作的? 也许 PhysicsBody(Green Line) 用 SKNode 填充?

这里有一些代码: (有人解释说:如果 Human 在 Object 内部移动并且 Touched 为 false,则什么都没有发生,这没关系,但是如果 Human 在 Object 内部移动并且 Touched 为 True,那么什么也没有发生,这是我的问题。)

    enum myContacts: UInt32 
    case None   = 0
    case All    = 0xFFFFFFFF
    case Object  = 0b001
    case Human = 0b010


class whatever...
    let Object = SKSpriteNode(imageNamed: "xyz")
    Object.position = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame))

    Object.physicsBody = SKPhysicsBody(circleOfRadius: Object.size.width/2)
    Object.physicsBody?.dynamic = true
    Object.physicsBody?.categoryBitMask = myContacts. Object.rawValue
    Object.physicsBody?.contactTestBitMask = myContacts.Human.rawValue
    Object.physicsBody?.collisionBitMask = 0
    Object.physicsBody?.usesPreciseCollisionDetection = true
    addChild(Object)

    let Human = SKSpriteNode(imageNamed: "zyx")
    Human.position = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame))

    Human.physicsBody = SKPhysicsBody(circleOfRadius: Human.size.width/2)
    Human.physicsBody?.dynamic = true
    Human.physicsBody?.categoryBitMask = myContacts. Human.rawValue
    Human.physicsBody?.contactTestBitMask = myContacts.Object.rawValue
    Human.physicsBody?.collisionBitMask = 0
    Human.physicsBody?.usesPreciseCollisionDetection = true
    addChild(Human)


func didBeginContact(contact: SKPhysicsContact) 
    if touched 
        let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
        switch contactMask 
        case myContacts.Object.rawValue | myContacts.Human.rawValue:
            print("Human touched")
        default:
            break
        
    


override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) 
    touched = true


override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) 
    touched = false

【问题讨论】:

【参考方案1】:

不确定我是否完全理解您的要求,但如果您只想在节点在另一个节点“占用”的空间内产生节点时检测联系人,则此代码将起作用:

#import "GameScene.h"

static const uint32_t playerCategory     =  0x1 << 0;
static const uint32_t obstacleCategory   =  0x1 << 1;

@interface GameScene()<SKPhysicsContactDelegate>

@property (nonatomic, strong) SKSpriteNode *player;

@property (nonatomic, strong) SKSpriteNode *obstacle;

@end

@implementation GameScene

-(void)didMoveToView:(SKView *)view 
    /* Setup your scene here */

    self.physicsWorld.contactDelegate = self;

    [self initializeNodes];





-(void)initializeNodes


    self.player =  [SKSpriteNode spriteNodeWithColor:[SKColor greenColor] size:CGSizeMake(50,50)];

    self.player.name = @"player";

    self.player.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));

    self.player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.player.size];
    self.player.physicsBody.collisionBitMask = 0;
    self.player.physicsBody.categoryBitMask = playerCategory;
    self.player.physicsBody.affectedByGravity = NO;
    self.player.physicsBody.contactTestBitMask = obstacleCategory;

    self.player.zPosition = 10;

    [self addChild:self.player];


    self.obstacle =  [SKSpriteNode spriteNodeWithColor:[SKColor yellowColor] size:CGSizeMake(100,100)];

    self.obstacle.name = @"obstacle";

    self.obstacle.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));

    self.obstacle.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.obstacle.size];
    self.obstacle.physicsBody.collisionBitMask = 0;
    self.obstacle.physicsBody.categoryBitMask = obstacleCategory;
    self.obstacle.physicsBody.affectedByGravity = NO;
    self.obstacle.physicsBody.contactTestBitMask = playerCategory;

    self.obstacle.zPosition = 8;

    [self addChild:self.obstacle];







-(void)didBeginContact:(SKPhysicsContact *)contact
    NSLog(@"Contact detected!");



-(void)update:(CFTimeInterval)currentTime 
    /* Called before each frame is rendered */


@end

在这里,一切都非常简单:

我创建了两个节点并为它们分配物理实体 我将这两个节点放在同一位置 因此检测到联系人

如果您复制并粘贴此代码并运行它,您将在控制台中看到的消息是:“检测到联系人”。

我想这与第二张图片中的情况相同(您说的是未检测到接触)。

此外,如果您继续移动玩家(当玩家在障碍物范围内时),将不会检测到进一步的接触。玩家必须先离开障碍物的边界。但是,您仍然可以检查 allContactBodies 方法的返回值,该方法返回当前身体(玩家的物理身体)接触的 SKPhysicsBody 对象数组。

【讨论】:

我的问题是,如果它不再触及绿线,它就不会向我显示联系人,因此如果检查联系人的值变为真,它将不起作用,因为它会检查联系人只有当它发生新的。 allContactedBodies 是个好主意,但如果我有 500 个快速移动的物体,可能会很慢?编辑:使用 Node.intersectsNode(AnotherNode) 可能更容易? @Lirf 是的,在这种情况下 allContactBodies 可能不是一个高性能的解决方案。 intersectsNode: 可以工作,但是您应该使用可以重现您所说内容的代码来更新您的问题。这样你可能会得到更好的答案。 @Lirf 没什么好说的......你所经历的在 SpriteKit 中是完全正常的,这就是物理引擎的工作原理。 didBeginContact 仅在两个物体之间发生接触时触发。因此,即使您将变量设置为 true,也不会在您期望它的那一刻触发 didBeginContact(在此之前可能已经进行了联系)。如果您认为循环遍历 allContactBodies() 方法返回的主体数组时会遇到性能问题,则应该尝试使用 if node.intersectsNode。 有没有办法在节点中添加更多物理点?例如对象内的更多绿线?

以上是关于PhysicsBody 内的 Swift SpriteKit 接触检查的主要内容,如果未能解决你的问题,请参考以下文章

无法将 PhysicsBody 添加到 SKSpriteNode(游戏崩溃)

精灵会自动移除吗?

如何使用 SKPhysicsJointPin 连接两个 SKSpriteNode - swift

Swift Spritekit 应用旋转脉冲

SpriteKit 展开可选错误 (Swift)

Swift Jump方法碰撞物体和地面