SpriteKit:精灵之间没有碰撞检测。
SpriteKit: No Collision Detection between Sprites.
在 SpriteKit 中设置碰撞检测应该不是一件困难的事情,我以前也做过。然而,尽管在其他项目中成功地使用了它并且有大量关于该主题的文档,但我无法在我当前的项目中使用它,而且我不知道为什么。
上下文非常简单。我将球体 (SKSpriteNode) 添加到背景 Sprite,然后用手指移动它们,希望它们在相互接触时检测到碰撞。 (这不会发生)。
预期的结果是检测到碰撞。
场景设置:
@interface GameScene () <SKPhysicsContactDelegate>
...
@implementation GameScene
// Collision BitMask
static const uint32_t planetCategory = 0x1 << 0;
场景也将自己设置为 physicsWorld 的委托
[self.physicsWorld setContactDelegate:self];
我只为我的场景提供了一个碰撞位掩码,因为所有对象都是同一类型并且应该相互碰撞。我还设置了场景以遵守 SKPhysicsContactDelegate 协议。
精灵配置:
#pragma mark Protocol Methods (For Adding Planets)
-(void)addSKPlanetDescriptorObject:(SKPlanetDescriptor *)object
{
if (self.allowedNodes > 0){
SKObject *node = [[SKObject alloc]initWithImageNamed:object.imageName];
[node setDescriptor:object];
// Set Physics
[node.physicsBody setCategoryBitMask:planetCategory];
[node.physicsBody setContactTestBitMask:planetCategory];
[node.physicsBody setCollisionBitMask:planetCategory];
[self.background addChild:node];
[self.sceneObjects addObject:node];
[node setPosition:self.pendingSpawnPoint];
[self decreaseObjectCount];
} else {
[self indicateMinObjectCount];
}
}
最后,用于 SKObject 的初始化程序,SKSpriteNode
的子类
@implementation SKObject
-(instancetype)initWithImageNamed:(NSString *)name
{
self = [super initWithImageNamed:name];
if (self)
{
[self setPhysicsBody:[SKPhysicsBody bodyWithCircleOfRadius:self.size.width/2]];
[self.physicsBody setAffectedByGravity:NO];
[self.physicsBody setUsesPreciseCollisionDetection:YES];
[self.physicsBody setDynamic:YES];
[self.physicsBody setMass:1000];
}
return self;
}
我试过在 init 语句之外删除 setPhysicsBody 方法,我试过更改位掩码,我试过省略 setCollisionBitmask 等等,但我就是想不通为什么我没有遇到任何碰撞.
我也添加了
-(void)didBeginContact:(SKPhysicsContact *)contact
{
NSLog(@"Did Begin Contact");
}
如果检测到任何内容,使用换行符通知我。但是这个方法永远不会被解雇。
使用这个与你的代码非常相似的简单代码(只需尝试拖动精灵),我的一切都运行良好(使用 iPhone 6 模拟器)...请尝试此代码以查看是否可以适合你:
#import "GameScene.h"
static const uint32_t planetCategory = 0x1 << 0;
@interface GameScene () <SKPhysicsContactDelegate>
@property (nonatomic,strong) SKSpriteNode *ball1;
@property (nonatomic,strong) SKSpriteNode *ball2;
@end
@implementation GameScene
-(void)didMoveToView:(SKView *)view {
[self.physicsWorld setContactDelegate:self];
SKSpriteNode *ball1 = [self createBall];
// Set Physics
[ball1.physicsBody setCategoryBitMask:planetCategory];
[ball1.physicsBody setContactTestBitMask:planetCategory];
[ball1.physicsBody setCollisionBitMask:planetCategory];
ball1.position = CGPointMake(100,499);
ball1.name = @"ball1";
[self addChild:ball1];
SKSpriteNode *ball2 = [self createBall];
// Set Physics
[ball2.physicsBody setCategoryBitMask:planetCategory];
[ball2.physicsBody setContactTestBitMask:planetCategory];
[ball2.physicsBody setCollisionBitMask:planetCategory];
ball2.position = CGPointMake(100,123);
ball2.name = @"ball2";
[self addChild:ball2];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
if([[self nodeAtPoint:location].name isEqualToString:@"ball1"]){
[self childNodeWithName:@"ball1"].position = location;
}
if([[self nodeAtPoint:location].name isEqualToString:@"ball2"]){
[self childNodeWithName:@"ball2"].position = location;
}
}
}
-(SKSpriteNode*) createBall{
SKSpriteNode *ball = [[SKSpriteNode alloc] initWithImageNamed:@"balloon_large"];
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.size.width/2];
[ball.physicsBody setAffectedByGravity:NO];
[ball.physicsBody setUsesPreciseCollisionDetection:YES];
[ball.physicsBody setDynamic:YES];
[ball.physicsBody setMass:1000];
return ball;
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}
-(void)didBeginContact:(SKPhysicsContact *)contact
{
NSLog(@"Did Begin Contact");
}
@end
如果您无法使用此代码段重现良好的结果,那么您可能需要检查代码的其他部分以找出导致这些问题的原因。但是让我们先试试这个,现在让我谈谈结果...
结果这一切都是一个愚蠢的错误造成的。
为了暂停我的游戏,我有一个名为 BOOL isPaused
的 属性。这恰好也是SpriteKit用来暂停物理的一个属性。当我触发方法 [self setIsPaused:YES];
时,我无意中禁用了碰撞检测。当时我也未能正确取消暂停,因为我相信它什么也没做。我现在知道它实际上是一个 SpriteKit 方法。
我希望这对偶然发现同样问题的其他人有所帮助。始终确保您的 isPaused 属性(如果使用)在您希望物理工作时启用。
在 SpriteKit 中设置碰撞检测应该不是一件困难的事情,我以前也做过。然而,尽管在其他项目中成功地使用了它并且有大量关于该主题的文档,但我无法在我当前的项目中使用它,而且我不知道为什么。
上下文非常简单。我将球体 (SKSpriteNode) 添加到背景 Sprite,然后用手指移动它们,希望它们在相互接触时检测到碰撞。 (这不会发生)。
预期的结果是检测到碰撞。
场景设置:
@interface GameScene () <SKPhysicsContactDelegate>
...
@implementation GameScene
// Collision BitMask
static const uint32_t planetCategory = 0x1 << 0;
场景也将自己设置为 physicsWorld 的委托
[self.physicsWorld setContactDelegate:self];
我只为我的场景提供了一个碰撞位掩码,因为所有对象都是同一类型并且应该相互碰撞。我还设置了场景以遵守 SKPhysicsContactDelegate 协议。
精灵配置:
#pragma mark Protocol Methods (For Adding Planets)
-(void)addSKPlanetDescriptorObject:(SKPlanetDescriptor *)object
{
if (self.allowedNodes > 0){
SKObject *node = [[SKObject alloc]initWithImageNamed:object.imageName];
[node setDescriptor:object];
// Set Physics
[node.physicsBody setCategoryBitMask:planetCategory];
[node.physicsBody setContactTestBitMask:planetCategory];
[node.physicsBody setCollisionBitMask:planetCategory];
[self.background addChild:node];
[self.sceneObjects addObject:node];
[node setPosition:self.pendingSpawnPoint];
[self decreaseObjectCount];
} else {
[self indicateMinObjectCount];
}
}
最后,用于 SKObject 的初始化程序,SKSpriteNode
的子类@implementation SKObject
-(instancetype)initWithImageNamed:(NSString *)name
{
self = [super initWithImageNamed:name];
if (self)
{
[self setPhysicsBody:[SKPhysicsBody bodyWithCircleOfRadius:self.size.width/2]];
[self.physicsBody setAffectedByGravity:NO];
[self.physicsBody setUsesPreciseCollisionDetection:YES];
[self.physicsBody setDynamic:YES];
[self.physicsBody setMass:1000];
}
return self;
}
我试过在 init 语句之外删除 setPhysicsBody 方法,我试过更改位掩码,我试过省略 setCollisionBitmask 等等,但我就是想不通为什么我没有遇到任何碰撞.
我也添加了
-(void)didBeginContact:(SKPhysicsContact *)contact
{
NSLog(@"Did Begin Contact");
}
如果检测到任何内容,使用换行符通知我。但是这个方法永远不会被解雇。
使用这个与你的代码非常相似的简单代码(只需尝试拖动精灵),我的一切都运行良好(使用 iPhone 6 模拟器)...请尝试此代码以查看是否可以适合你:
#import "GameScene.h"
static const uint32_t planetCategory = 0x1 << 0;
@interface GameScene () <SKPhysicsContactDelegate>
@property (nonatomic,strong) SKSpriteNode *ball1;
@property (nonatomic,strong) SKSpriteNode *ball2;
@end
@implementation GameScene
-(void)didMoveToView:(SKView *)view {
[self.physicsWorld setContactDelegate:self];
SKSpriteNode *ball1 = [self createBall];
// Set Physics
[ball1.physicsBody setCategoryBitMask:planetCategory];
[ball1.physicsBody setContactTestBitMask:planetCategory];
[ball1.physicsBody setCollisionBitMask:planetCategory];
ball1.position = CGPointMake(100,499);
ball1.name = @"ball1";
[self addChild:ball1];
SKSpriteNode *ball2 = [self createBall];
// Set Physics
[ball2.physicsBody setCategoryBitMask:planetCategory];
[ball2.physicsBody setContactTestBitMask:planetCategory];
[ball2.physicsBody setCollisionBitMask:planetCategory];
ball2.position = CGPointMake(100,123);
ball2.name = @"ball2";
[self addChild:ball2];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
if([[self nodeAtPoint:location].name isEqualToString:@"ball1"]){
[self childNodeWithName:@"ball1"].position = location;
}
if([[self nodeAtPoint:location].name isEqualToString:@"ball2"]){
[self childNodeWithName:@"ball2"].position = location;
}
}
}
-(SKSpriteNode*) createBall{
SKSpriteNode *ball = [[SKSpriteNode alloc] initWithImageNamed:@"balloon_large"];
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.size.width/2];
[ball.physicsBody setAffectedByGravity:NO];
[ball.physicsBody setUsesPreciseCollisionDetection:YES];
[ball.physicsBody setDynamic:YES];
[ball.physicsBody setMass:1000];
return ball;
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}
-(void)didBeginContact:(SKPhysicsContact *)contact
{
NSLog(@"Did Begin Contact");
}
@end
如果您无法使用此代码段重现良好的结果,那么您可能需要检查代码的其他部分以找出导致这些问题的原因。但是让我们先试试这个,现在让我谈谈结果...
结果这一切都是一个愚蠢的错误造成的。
为了暂停我的游戏,我有一个名为 BOOL isPaused
的 属性。这恰好也是SpriteKit用来暂停物理的一个属性。当我触发方法 [self setIsPaused:YES];
时,我无意中禁用了碰撞检测。当时我也未能正确取消暂停,因为我相信它什么也没做。我现在知道它实际上是一个 SpriteKit 方法。
我希望这对偶然发现同样问题的其他人有所帮助。始终确保您的 isPaused 属性(如果使用)在您希望物理工作时启用。