我目前正在与朋友一起使用Objective-C和SpriteKit进行游戏。我的朋友和我都是SpriteKit的新手,所以我们正在开展的这个项目更多的是学习经验。但当然,我们遇到了一些麻烦。
游戏相当简单:有一个球在屏幕周围飞行,并从iPhone屏幕的边缘和边缘反弹。用户控制屏幕底部的桨叶以使球向上偏转以防止球到达屏幕的底部。每次球击中球拍时,屏幕顶部的得分计数器递增1。
这是我们正在努力实现的目标。当游戏得分等于5时,我们会在场景中添加一个新球。新球开始自动移动,但每当我们试图检测新球是否已经击中屏幕边缘时,都没有检测到碰撞。
这是我们返回球对象的方法:
+(id)ball
{
// the ball is a random image from google
Ball *ball = [Ball spriteNodeWithImageNamed:@"ball"];
// set the position of the ball
ball.position = CGPointMake(0, 80);
// set ball name property
ball.name = @"ball";
// give the ball a physics body
ball.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ball.size];
ball.physicsBody.affectedByGravity = NO;
return ball;
}
调用此方法可以在场景中移动球:
-(void)move:(int)deltaX withDeltaY:(int)deltaY
{
SKAction *testMoveRight = [SKAction moveByX:deltaX y:deltaY duration:0.03];
// this will repeat the action over and over
SKAction *move = [SKAction repeatActionForever:testMoveRight];
[self runAction:move];
}
这些是我们的碰撞检测类别:
// this category is for the original ball in the game
static const uint32_t ballCategory = 0x1 << 0;
// this category is for the second ball that gets added to the game
static const uint32_t ball2Category = 0x1 << 1;
static const uint32_t paddleCategory = 0x1 << 2;
static const uint32_t topBarrierCategory = 0x1 << 3;
static const uint32_t leftBarrierCategory = 0x1 << 4;
static const uint32_t rightBarrierCategory = 0x1 << 5;
static const uint32_t gameOverBarrierCategory = 0x1 << 6;
这是我们的didBeginContact方法:
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else {
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
// first ball contact
if ((firstBody.categoryBitMask & ballCategory) != 0 && (secondBody.categoryBitMask & rightBarrierCategory) != 0) {
[ball move:-15 withDeltaY:0];
}
else if ((firstBody.categoryBitMask & ballCategory) != 0 && (secondBody.categoryBitMask & topBarrierCategory) != 0) {
[ball move:0 withDeltaY:-20];
}
else if ((firstBody.categoryBitMask & ballCategory) != 0 && (secondBody.categoryBitMask & leftBarrierCategory) != 0) {
[ball move:15 withDeltaY:0];
}
else if ((firstBody.categoryBitMask & ballCategory) != 0 && (secondBody.categoryBitMask & paddleCategory) != 0) {
[ball move:0 withDeltaY:20];
// increment score
self.score++;
// update the score label
self.deathLabel.text = [NSString stringWithFormat:@"%i", self.score];
// add a new ball to the scene if the score is 5
if (score == 5) {
[scene addChild:ball2];
[ball2 move:5 withDeltaY:10];
}
}
// second ball contact detection
if ((firstBody.categoryBitMask & ball2Category) != 0 && (secondBody.categoryBitMask & rightBarrierCategory) != 0) {
[ball2 move:-15 withDeltaY:0];
}
}
为什么我们添加到场景中的第二个球没有检测到它是否与右侧屏障(屏幕右边缘)发生碰撞?当它到达屏幕的右边缘时,它不会像我们想要的那样反弹并向左移动。有什么建议?
答案 0 :(得分:1)
简单地将physicsBody添加到球中是不够的,还必须设置类别,接触和碰撞位掩码。首先,我会将球的物理体改为
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.frame.size.width/2];
循环物理机构首先效率更高,第二更好。除此之外,每个屏障都不需要单独的类别。将它们合二为一:
self.size = self.view.bounds.size;
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 0, 0);
CGPathAddLineToPoint(path, NULL, 0, self.frame.size.height);
CGPathAddLineToPoint(path, NULL, self.frame.size.width, self.frame.size.height);
CGPathAddLineToPoint(path , NULL, self.frame.size.width, 0);
CGPathAddLineToPoint(path, NULL, 0, 0);
SKShapeNode *bounds = [SKShapeNode shapeNodeWithPath:path];
bounds.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromPath:path];
CGPathRelease(path);
bounds.physicsBody.categoryBitMask = boundsCategory;
正如你所看到的,每个SpriteNode都有一个categoryBitmask,它有一个碰撞位掩码(这意味着它与这些类别反弹/冲突)和一个联系位掩码(我没有在这里设置一个,但是当这种联系发生时,调用didBeginContact方法)。在您的代码中,不会调用didBeginContact方法,但您也不需要它来进行弹跳。为了澄清,弹跳将自行发生,这意味着你不需要任何接触方法或位掩码,只需对球施加一个力/冲动,当它触及边界时它就会反弹。
至于球,你不需要每个球的另一个类别 - 这是冗余。对于创建的每个球,只需将categoryBitmask设置为相同的东西:
ball.physicsBody.categoryBitMask = ballCategory;
ball.physicsBody.collisionBitMask = boundsCategory;
通过添加这些行,您告诉编译器每个球都是'ballCategory'并且它应该与任何'boundsCategory'对象发生冲突。希望这有帮助,祝你好运!