I need to spawn 8 random sprite objects on an iPad sprite game. The objects are fairly big, and with different sizes. They should not overlay. If one that is spawn on the scene overlays it will be removed (optional it removes the underlaying one).
I have been looking for a pixel perfect collision detection framework or helper class for Sprite Kit. So far I haven't found a tutorial or something alike. Most people use normal collision detection which will not be of any help since my objects are big. I tested standard approach but it creates rectangles which make the sprite area in my case even bigger. This is my sprite kit template testing project:
#import "WBMMyScene.h"
static const uint32_t randomObjectCategory = 0x1 << 0;
#interface WBMMyScene () <SKPhysicsContactDelegate>
#end
#implementation WBMMyScene
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
/* Setup your scene here */
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
self.physicsWorld.gravity = CGVectorMake(0, 0);
self.physicsWorld.contactDelegate = self;
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInNode:self];
SKSpriteNode *spaceship = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
//SKSpriteNode *spaceship = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:CGSizeMake(50, 50)];
spaceship.position = location;
[spaceship setSize:CGSizeMake(50, 50)];
[spaceship.texture setFilteringMode:SKTextureFilteringNearest];
//spaceship.texture setFilteringMode
spaceship.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:spaceship.size];
spaceship.physicsBody.dynamic = YES;
spaceship.physicsBody.categoryBitMask = randomObjectCategory;
spaceship.physicsBody.contactTestBitMask = randomObjectCategory;
spaceship.physicsBody.collisionBitMask = 0;
[self addChild:spaceship];
}
}
- (void)didBeginContact:(SKPhysicsContact *)contact
{
// 1
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
// 2
if (firstBody.categoryBitMask == secondBody.categoryBitMask)
{
[self projectile:(SKSpriteNode *) firstBody.node didColliteWithEachOther:(SKSpriteNode *) secondBody.node];
}
}
- (void)projectile:(SKSpriteNode *)object1 didColliteWithEachOther:(SKSpriteNode *)object2
{
NSLog(#"Hit");
[object1 removeFromParent];
[object2 removeFromParent];
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}
#end
Thanks for your time :)
As far as I'm aware iOS7 doesn't have per pixel physics, that is due to come with XCode 6 which is in its beta testing phase and available to developers now. Remember it is a beta and does have a few bugs, it's full release is likely to be in September with iOS8 and the new iPhone.
In the mean time you have two options, you could download the XCode 6 beta and work within that for per pixel physics. Personally however I got a little sick of using a beta for full development and instead went back to XCode 5. Within iOS7 and XCode 5 you have the option of defining a physicsBody with a path, which gives an accurate depiction of a sprites physical shape.
The tool I use is here and lets you drag and drop an image and then define path points graphically and returns the code, it really is handy.
I hope this helps!
This solution does not give pixel-perfect detection, so may not be exactly what you (or others finding this question) want, but it is a really good alternative that will be ideal for some.
It automatically calculates a polygon-based physics body by analysing the texture of your sprite. It basically draws a path around the non-transparent pixels and uses that for collision.
Instead of this:
spaceship.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:spaceship.size];
Use this:
spaceship.physicaBody = [SKPhysicsBody bodyWithTexture:spaceship.texture alphaThreshold:0.5f size:spaceship.size];
Related
I have been working on a SpriteKit game for a while now and I have an annoying problem that I can't get rid of. I don't want to tell too much about the game, but it is very simple. I generate some objects (SKSpriteNodes) each second that are falling down from the top of the screen using SpriteKits physics and the player is interacting with them.
Most of the time the game runs perfectly (constant 60 FPS). The problem is that sometimes (maybe once per minute or something like that), the game starts to lag a little bit for about 3-5 seconds (still at 60 FPS) and then it runs perfectly again (note: i'm running the game on an iPhone 5s). It seems to be because of the physics, because if I add a normal move-action on an object, it runs very smoothly while the nodes affected by the physics are lagging.
I tried to remove some particles and effects that I have and I reuse my objects, but I can't remove the lag. I decided to create a very simple test project to see if the lag would be gone but it is still there. Here is the code:
#import "GameScene.h"
static const uint32_t groundCategory = 1 << 0;
static const uint32_t objectCategory = 1 << 1;
#implementation GameScene
-(void)didMoveToView:(SKView *)view
{
// Init
self.backgroundColor = [UIColor blackColor];
self.physicsWorld.gravity = CGVectorMake(0.0f, -3.2f);
self.physicsWorld.contactDelegate = self;
// Ground
SKPhysicsBody* ground = [SKPhysicsBody bodyWithEdgeFromPoint:CGPointMake(0, -88) toPoint:CGPointMake(self.size.width, -88)];
ground.categoryBitMask = groundCategory;
self.physicsBody = ground;
// Start game loop
SKAction* waitAction = [SKAction waitForDuration:1];
SKAction* sequence = [SKAction sequence:#[[SKAction performSelector:#selector(addObject) onTarget:self], waitAction]];
SKAction* repeatAction = [SKAction repeatActionForever:sequence];
[self runAction:repeatAction withKey:#"fallingObjectsAction"];
}
- (void)addObject
{
SKSpriteNode* newObject = [SKSpriteNode spriteNodeWithColor:[UIColor whiteColor] size:CGSizeMake(88, 88)];
newObject.name = #"Object";
newObject.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:newObject.size];
newObject.position = CGPointMake(CGRectGetMidX(self.frame), self.frame.size.height + newObject.size.height);
newObject.physicsBody.categoryBitMask = objectCategory;
newObject.physicsBody.contactTestBitMask = groundCategory;
newObject.physicsBody.collisionBitMask = 0;
[self addChild:newObject];
}
- (void)didBeginContact:(SKPhysicsContact *)contact
{
if ([contact.bodyA.node.name isEqualToString:#"Object"])
[contact.bodyA.node removeFromParent];
else if ([contact.bodyB.node.name isEqualToString:#"Object"])
[contact.bodyB.node removeFromParent];
}
#end
I generate one object each second and let it fall down from the top of the screen. When it hits the ground, it is removed.
Am I using the physics wrong or is it SpriteKit's fault that it is lagging? It seems strange, because I'm running a very simple project using an iPhone 5s with iOS 8.
Okay I've been having the same issue with physics. The issue was objects are jittery despite low CPU usage and a consistent FPS. I've finally figured out why. The solution I found is do not set physicsWorld.speed to 1.0; set it to a .9999. Now everything runs smoothly.
I hope this helps.
I have a node which is placed under my screen and works as a platform. As soon as another node touches the node a boolean is set to NO.
When I define my node with the SKPhysicsBody properties for the collision the node ignores the affectedByGravity property.
My code:
+ (void)addNewNodeTo:(SKNode *)parentNode
{
//Correct image size
SKSpriteNode *desertBottom = [SKSpriteNode node];
desertBottom = [[SKSpriteNode alloc] initWithImageNamed:#"Giraffe.png"];
desertBottom.position = CGPointMake(0, -200);
desertBottom.zPosition = 2;
desertBottom.physicsBody.collisionBitMask = lionType;
desertBottom.physicsBody.categoryBitMask = terrainType;
desertBottom.physicsBody.contactTestBitMask = lionType;
desertBottom.zPosition = 2;
desertBottom.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(desertBottom.size.width, desertBottom.size.height)];
desertBottom.physicsBody.dynamic = YES;
desertBottom.physicsBody.affectedByGravity = NO;
[parentNode addChild:desertBottom];
}
My collision methods:
- (void)didBeginContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (lionType | terrainType)) {
self.lionNode.lionIsJumping = NO;
NSLog(#"%i", self.lionNode.lionIsJumping);
}
}
- (void)didEndContact:(SKPhysicsContact *)contact
{
uint32_t collision = (contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask);
if (collision == (lionType | terrainType)) {
self.lionNode.lionIsJumping = YES;
NSLog(#"%i", self.lionNode.lionIsJumping);
}
}
A physics body has a velocity. Gravity changes velocity over time. If the body is already traveling at a certain velocity due to gravity, and you disable gravity, it will continue to move according to its current velocity but no longer gain additional speed from gravity.
My guess is that you expect the body to stop when disabling gravity. If that's what you want you can do this manually by setting the y component of velocity to zero:
SKPhysicsBody* body = desertBottom.physicsBody;
body.velocity = CGVectorMake(body.velocity.x, 0.0);
If the platform should not move after a collision you have to set the dynamic property to false.
Maybe you have an issue with you bit masks?
Have you tried to log outside your if statement of the didBeginContact method?
I have created a new class with subclass of SKSpriteNode for the bottom and added it to my GameScene.
Hi I am trying to setup collision detection in my game and I want to add collision detection so when the balloons hit the spikes they pop. I have looked at Ray Wenderliches tutorial but I couldn't figure it out because it didn't apply to my case. Any ideas how to set it up for my case?
The spikes are at the top of the screen and the balloons spawn at the bottom.
The basics for setting upp collision between 2 objects is to first setting upp constant that represent the different objects that can collide. I usually make a constants.h file where i keep all the variables that will be used thru out the game/application.
Declare following in either a constants.h file or just declare them as global variables in the class:
static const int balloonHitCategory = 1;
static const int spikeHitCategory = 2;
What you want to do now is set up the physics for both your balloon and spikes
SKSpriteNode *ballooon = [SKSpriteNode spriteNodeWithImageNamed:#"yourimagefilename"];
ballooon.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:yourSize];
ballooon.physicsBody.categoryBitMask = balloonHitCategory;
ballooon.physicsBody.contactTestBitMask = spikeHitCategory;
ballooon.physicsBody.collisionBitMask = spikeHitCategory;
you should set your size and set your images for both of the spritenodes
SKSpriteNode *spikes = [SKSpriteNode spriteNodeWithImageNamed:#"yourimagefilename"];
spikes.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(yourSizeX, yourSizeY)];
spikes.physicsBody.categoryBitMask = spikeHitCategory;
spikes.physicsBody.contactTestBitMask = balloonHitCategory;
spikes.physicsBody.collisionBitMask = balloonHitCategory;
for collision set up the following method:
-(void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
firstBody = contact.bodyA;
secondBody = contact.bodyB;
if(firstBody.categoryBitMask == spikeHitCategory || secondBody.categoryBitMask == spikeHitCategory)
{
NSLog(#"balloon hit the spikes");
//setup your methods and other things here
}
}
Before the collision will work you should also add the . In your scenes .h file add .
#interface myScene : SKScene <SKPhysicsContactDelegate>
#end
and in the .m file in the init function add:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.physicsWorld.contactDelegate = self;
}
return self;
}
For more about collision handling check out apple documentation and adventure game example:
https://developer.apple.com/library/ios/documentation/GraphicsAnimation/Conceptual/CodeExplainedAdventure/HandlingCollisions/HandlingCollisions.html#//apple_ref/doc/uid/TP40013140-CH5-SW1
I have just released a plug-in 2D collision engine for Sprite Kit. It's still in its early stages but could be of some help to you: https://github.com/henryeverett/ToastCollisions2D
I'm trying to make a simple game with sprite kit.
The basic idea is that there is one player who can jump to avoid blocks.
But i have a problem I don't know how to make it that when the player hits the block the player disappears and the blood animation starts.
First of all i don't understand what this code does that I found on apples website.
static const uint32_t blockCategory = 0x1 <<0;
static const uint32_t playerCategory = 0x1 <<1;
Than i am calling the didBeginContact function and put a NSLOG("did call function") in it.
But i never receive the output in my debugger.
Here is my _player and _block code:
-(SKSpriteNode *)character {
_player = [SKSpriteNode spriteNodeWithImageNamed:#"soldier_run1"];
_player.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:_player.size.width /2 -5];
_player.physicsBody.dynamic = YES;
_player.physicsBody.usesPreciseCollisionDetection = YES;
_player.physicsBody.friction = 0;
_player.physicsBody.categoryBitMask = playerCategory;
_player.physicsBody.collisionBitMask = blokCategory;
_player.name = #"player";
SKAction *animAction = [SKAction animateWithTextures:playerTextures timePerFrame:0.1 resize:YES restore:YES];
My _player code:
[_player runAction:[SKAction repeatActionForever:animAction]];
return _player;
}
-(SKSpriteNode *)block {
_blok = [[SKSpriteNode alloc] initWithColor:[SKColor blackColor] size:CGSizeMake(15, 40)];
//physics
_blok.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_blok.size];
_blok.physicsBody.dynamic = NO;
_blok.name = #"block";
_blok.physicsBody.categoryBitMask = blokCategory;
_blok.physicsBody.collisionBitMask = playerCategory;
SKAction *moveBlock = [SKAction sequence:#[
[SKAction moveToX:-20 duration:2] ]];
[_blok runAction:moveBlock ];
return _blok;
}
Also i don't really understand what the categoryBitMask and collisionBitMask do.
After i have that working i would like to make the character disappear from the screen and the blood animation to start, but I have no idea how to let that happen. I think you have to do something like:
if(_player && _block didcollide) {
}
But I don't know how to do it exactly.
The categoryBitMask sets the category that the sprite belongs to, whereas the collisionBitMask sets the category with which the sprite can collide with and not pass-through them.
For collision detection, you need to set the contactTestBitMask. Here, you set the categories of sprites with which you want the contact delegates to be called upon contact.
What you have already done is correct. Here are a few additions you need to do:
_player.physicsBody.contactTestBitMask = blockCategory;
_blok.physicsBody.contactTestBitMask = playerCategory;
Afterwards, implement the contact delegate as follows:
-(void)didBeginContact:(SKPhysicsContact *)contact`
{
NSLog(#"contact detected");
SKPhysicsBody *firstBody;
SKPhysicsBody *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
//Your first body is the block, secondbody is the player.
//Implement relevant code here.
}
For a good explanation on implementing collision detection, look at this tutorial.
Handling collisions is a bit messy. :) There are a number of approaches, but one place you should start with is this fairly simple example from Apple. The readme provides a good intro, and then you can start poking around with the code.
Another approach (which Apple mentions in their guide) is to use Double Dispatching (see wikipedia for a long desc). I wouldn't start by trying to grok that approach right off however. It is a somewhat advanced approach given it depends on dynamic selectors and similar techniques to enable the magic to take place. However, even with that caveat, you can find a simple example someone put together on how to do it, along with a lot of supporting description here.
I have one sprite and my code is:
sprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:sprite.size.width/2];
sprite.physicsBody.dynamic = YES;
sprite.physicsBody.affectedByGravity = YES;
sprite.physicsBody.mass = 550.0f;
sprite.physicsBody.categoryBitMask = 1;
sprite.physicsBody.collisionBitMask = 1;
sprite.physicsBody.contactTestBitMask = 1;
I can detect collision in:
- (void)didBeginContact:(SKPhysicsContact *)contact {
// destroy contact.bodyA , contact.bodyB
}
But how can I destroy/remove these bodies from physics world?
I'm not sure if I completely understand, but if you're looking for a way of removing your sprites upon collision, you have to access each physics body's node property, and call its removeFromParent function.
[contact.bodyA.node removeFromParent];
[contact.bodyB.node removeFromParent];
Of course, you'll want to add additional logic to determine whether the bit masks of the colliding sprites are the same as the ones you wish to destroy during collision.