I have two SkSpriteNodes called left-ball and right-ball.THey flow from sides of the screen.
I implemented the collision detection for these two balls. However I noticed that before I add collision detection, the balls were moving smoothly without any shaking effect, but after I add the collision detection, the balls were shaking and moving in their path.
I am using the same bit category for both of these balls:
static const int ballHitCategory = 1;
without shaking:
with shaking:
Here is what I tried in code:
_leftBall.physicsBody=[SKPhysicsBody bodyWithRectangleOfSize:_leftBall.frame.size];
_leftBall.physicsBody.categoryBitMask=ballHitCategory;
_leftBall.physicsBody.contactTestBitMask=ballHitCategory;
_leftBall.physicsBody.collisionBitMask=ballHitCategory;
_leftBall.physicsBody.dynamic=YES;
_leftBall.physicsBody.usesPreciseCollisionDetection=YES;
_rightBall.physicsBody=[SKPhysicsBody bodyWithRectangleOfSize:_rightBall.frame.size];
_rightBall.physicsBody.categoryBitMask=ballHitCategory;
_rightBall.physicsBody.contactTestBitMask=ballHitCategory;
_rightBall.physicsBody.collisionBitMask=ballHitCategory;
_rightBall.physicsBody.dynamic=YES;
_rightBall.physicsBody.usesPreciseCollisionDetection=YES;
-(void)didBeginContact:(SKPhysicsContact *)contact
{
_leftBall = (SKSpriteNode*)contact.bodyA.node;
_rightBall = (SKSpriteNode *)contact.bodyB.node;
if(_leftBall.physicsBody .categoryBitMask == ballHitCategory || _rightBall.physicsBody.categoryBitMask == ballHitCategory)
{
NSLog(#" hit the ");
//setup your methods and other things here
[_leftBall removeFromParent];
[_rightBall removeFromParent];
}
}
Please note that shake happens only after I add the collision detection, If I remove the above code, everything is fine.
What is the proper way to add collision detection without effecting the skspritenode ?
so youre moving the sprites using non physics code.. but theyre still part of the physics simulation. I'm guessing theyre fighting against the force of gravity.
either set affectedByGravity on each sprite to false
or you can turn off gravity in your game completely
[self.physicsWorld setGravity:CGVectorMake(0, 0)];
Don't worry about the Gravity. You should set your spritenode:
_leftBall.physicsBody.dynamic=YES;
_leftBall.physicsBody.affectedByGravity=NO;
Same on _rightBall
Related
I have simple gameplay that functions like this:
My goal is to detect when the ball hits the white part (game over scene would load) or when the ball hits the gray part (game continues)
Because the shapes shrink and have a lot of stuff going on, I figured it would be a lot more efficient to just create the SKPhysicsBody when I need it, and then remove it after the collision checks happen. So when I click the screen this happens: (after 5*0.0325 seconds--how long it takes the circle to shrink--has passed, it will add the physicsbody to both the gray and white part so I can detect which the ball is touching)
DispatchQueue.main.asyncAfter(deadline: .now() + seconds, execute: {
let trackPhysics = SKPhysicsBody(texture: track.texture!, size: track.texture!.size())
trackPhysics.isDynamic = false
trackPhysics.affectedByGravity = false
track.physicsBody = trackPhysics
let goalPhysics = SKPhysicsBody(texture: goal.texture!, size: goal.texture!.size())
goalPhysics.isDynamic = false
goalPhysics.affectedByGravity = false
goal.physicsBody = goalPhysics
})
Which works. The SKPhysicsBody are applied to both the gray and white part perfectly without any loss in framerate and follow the rotation. The problem is... how do I detect which is the ball touching? Since they did not formally collide, it will not call the collision at all (which makes sense since they did not really collide
This is the basic logic, and it works perfectly until I want to check the results:
someone touches the screen
circles shrink
right after they shrink it will create an SKPhysicsBody for both the gray and white part based on its texture
Problem is here... how do I detect which the ball is touching?
I have tried this:
func didBegin(_ contact: SKPhysicsContact) {
print("touching!")
}
func didEnd(_ contact: SKPhysicsContact) {
print("not touching")
}
which has no messages at all, and have tried using allContactedBodies() 1 second after the SKPhysicsBody were applied, but returns a count with 0 for all of them. I have even tried making this 5 seconds and still not working
DispatchQueue.main.asyncAfter(deadline: .now() + seconds + 1, execute: {
print(track.physicsBody!.allContactedBodies().count)
print(goal.physicsBody!.allContactedBodies().count)
})
This is what it looks like with the SKPhysicsBody applied, you can see the physics is applied perfectly
Am I doing something wrong? Any ideas?
The solution (as discovered in the comments above), was:
Set the isDynamic property of nodes to true for collision detection to work properly.
Ensure that didBegin(contact:) is being called, by double-checking that the scene's physicsWorld.contactDelegate is set.
Ensure the masks and physics bodies are set at the proper times and locations.
For a game I'm creating, an SKSpriteNode gradually makes its way down the user's screen. There's another SKSpriteNode (position is static) near the bottom of the screen, leaving only a little bit of space for the original SKSpriteNode to fit in. I need to detect when the first SKSpriteNode is COMPLETELY below the second SKSpriteNode, which I'm having a bit of trouble with. Here's the code I'm currently using:
if (pos.y > barPosY) //pos.y = 1st SKSpriteNode, barPosY = 2nd SKSpriteNode
{
touchedTooEarly = true
}
For some reason, when the first SKSpriteNode goes just a little bit over the 2nd SKSpriteNode (not all the way), it still detects it as being completely over. Is there a coordinate space issue I'm missing?
The logic
A sprite a covers a sprite b if
b.frame is inside a.frame
b.zPosition is below a.zPosition
The extension
Now let's build an extension
extension SKSpriteNode {
func isCoveredBy(otherSprite: SKSpriteNode) -> Bool {
let otherFrame = CGRect(
origin: convertPoint(otherSprite.position, fromNode: otherSprite),
size: otherSprite.frame.size
)
return zPosition < otherSprite.zPosition && CGRectContainsRect(frame, otherFrame)
}
}
Problem #1: transparency
This mechanism totally ignores transparency.
Problem #2: same sprite
If you compare 2 sprites of the same type the function CGRectContainsRect will return true only when they are exactly aligned. Althoug you can solve this problem creating a smaller rectangle when you compare the sprites.
I'm using SpriteKit for a game where there is a ball and a character. The character is moving in a line with SKAction and the user can move the ball by touch. When the ball and character makes contact, I'd like the ball to be attached to the character.
func didBeginContact(contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask == charMask && contact.bodyB.categoryBitMask == ballMask{
println("contact")
if contactMade {
let movingBall = contact.bodyB.node!.copy() as! SKSpriteNode
//this line of code doesn't affect anything no matter what coordinates i set it to
movingBall.position = contact.bodyA.node!.position
contact.bodyB.node!.removeFromParent()
contact.bodyA.node!.removeAllActions()
contact.bodyA.node!.addChild(movingBall)
}
}
}
The current issue is that when I do contact.bodyA.node!.addChild(ball) (character node), the ball gets attached to the far left side of the character instead of on the character and it seems setting movingball.position does not affect it. I've also tried doing addChild to the scene and although it adds the ball to the correct place, it doesn't move with the character without another SKAction. What did I do wrong/what can I do to fix it?
Also, instead of using 2 sprites like that, should I use separate sprites so that theres a character sprite without a ball, character sprite with a ball and the ball itself, then just change the node's texture when they contact?
as movingBall is to be a child of bodyA.node, it's position needs to be in bodyA.node's coordinate space. So if you gave it coordinates of (0,0) it'd be in the middle:
Replace:
movingBall.position = contact.bodyA.node!.position
with
movingBall.position = CGPointZero
and see how that looks. the ball should be positioned directly on top of bodyA.node.
In my game, I need to do some setting changes after my SKSpriteNode stops moving.
First,
I apply force on my SKSpriteNode, so it starts moving due to the force acting on it.
Now, i need to know when it stops moving.
I know that SKSpriteNode has a speed property, but it is always set to 1.0 by default.
Any ideas?
You can check a node's velocity by using something like this:
if((self.physicsBody.velocity.dx == 0.0) && (self.physicsBody.velocity.dy == 0.0)) {
NSLog(#"node has stopped moving");
}
The usual place to put the above code would be in the update method.
Since you are using physics you can use the resting property of SKPhysicsBody.
if sprite.physicsBody?.resting == true {
println("The sprite is resting")
} else {
println("The sprite is moving")
}
Hope this helps.
You can subclass SKSpriteNode and add a previousPosition property on it, which you can update on -didFinishUpdateMethod. If there is no change in the position you can assume the node is stopped (you might have to get a median of the last few positions to smooth it out). The speed property is something different, according to the class reference, speed is:
A speed modifier applied to all actions executed by a node and its descendants.
Hope this helps!
Danny
I am really looking to try to have gravity in my game. I know everyone says use Box2D, but in my case I can't. I need to use Cocos2D for the gravity.
I know Cocos2D does not have any gravity API built in so I need to do something manually. The thing is there is like no tutorials or examples anywhere on the web that show this.
Can anyone show me what they have done or can some explain step by step on how to apply a non-constant gravity (One that gets slightly stronger while falling).
I think this will help a lot of people that are facing the same issue that I am having!
Thanks!
Gravity is nothing but a constant velocity applied to the body for every physics step. Have a look at this exemplary update method:
-(void) update:(ccTime)delta
{
// use a gravity velocity that "feels good" for your app
const CGPoint gravity = CGPointMake(0, -0.2);
// update sprite position after applying downward gravity velocity
CGPoint pos = sprite.position;
pos.y += gravity.y;
sprite.position = pos;
}
Every frame the sprite y position will be decreased. That's the simple approach. For a more realistic effect you will want to have a velocity vector for each moving object, and apply gravity to the velocity (which is also a CGPoint).
-(void) update:(ccTime)delta
{
// use a gravity velocity that "feels good" for your app
const CGPoint gravity = CGPointMake(0, -0.2);
// update velocity with gravitational influence
velocity.y += gravity.y;
// update sprite position with velocity
CGPoint pos = sprite.position;
pos.y += velocity.y;
sprite.position = pos;
}
This has the effect that velocity, over time, increases in the downward y direction. This will have the object accelerate faster and faster downwards, the longer it is "falling".
Yet by modifying velocity you can still change the general direction of the object. For instance to make the character jump you could set velocity.y = 2.0 and it would move upwards and come back down again due to the effect of gravity applied over time.
This is still a simplified approach but very common in games that don't use a "real" physics engine.