How to detect when two objects touch in SpriteKit
Not to be a stickler but you shouldn't start of a question with "I couldn't find anything on the internet" when thats blatantly not true. There is a million tutorials on collision detection around as its one of the basics in SpriteKit.
Now to your question. You did not give your sprites an actual physics body and your physics categories are set up weird. Change your code to this
struct PhysicsCategory {
static let cat:UInt32 = 0x1 << 0
static let ground:UInt32 = 0x1 << 1
}
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
physicsWorld.contactDelegate = self
cat.physicsBody = SKPhysicsBody(rectangleOfSize: cat.size) // FORGOT THIS
cat.physicsBody?.categoryBitMask = PhysicsCategory.cat
cat.physicsBody?.contactTestBitMask = PhysicsCategory.ground
ground.physicsBody = SKPhysicsBody(rectangleOfSize: ground.size) // FORGOT THIS
ground.physicsBody?.categoryBitMask = PhysicsCategory.ground
ground.physicsBody?.contactTestBitMask = PhysicsCategory.cat // You dont really need this line as long as you have set it on the other body.
}
func didBeginContact(contact: SKPhysicsContact) {
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if firstBody.categoryBitMask == PhysicsCategory.cat && secondBody.categoryBitMask == PhysicsCategory.ground {
print("contact")
}
}
}
If you dont want your objects to fall you have to turn gravity off, which is on by default.
cat.physicsBody?.affectedByGravity = false
ground.physicsBody?.affectedByGravity = false
Hope this helps
Detect touch on child node of object in SpriteKit
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
let touch = touches.anyObject() as UITouch
let touchLocation = touch.locationInNode(self)
if([yourSprite containsPoint: touchLocation])
{
//sprite contains touch
}
}
Source: http://www.raywenderlich.com/84434/sprite-kit-swift-tutorial-beginners
How to Detect collision in Swift, Sprite kit
- Define unique categories, ensure your class is a
SKPhysicsContactDelegate
and make yourself the physics contact delegate:
//Physics categories
let enemyCategory: UInt32 = 1 << 1
let bulletCategory: UInt32 = 1 << 2
class GameScene: SKScene, SKPhysicsContactDelegate {
physicsWorld.contactDelegate = self
Assign the categories (usually in
didMove(to view:)
:enemy.physicsBody.catgeoryBitMask = enemyCategory
bullet.physicsBody.catgeoryBitMask = bulletCategory
(Make sure you've created physics bodies for each node)
- Set up collisions:
enemy.physicsBody?.collisionBitMask = 0 // enemy collides with nothing
bullet.physicsBody?.collisionBitMask = 0 // bullet collides with nothing
or even:
for node in [enemy, bullet] {
node.physicsBody?.collisionBitMask = 0 // collides with nothing
}
Set up contacts
bullet.physicsBody?.collisionBitMask = enemyCategory // bullet contacts enemy
Make sure that at least one of the objects involved in each potential contact has the isDynamic
property on its physics body set to true
, or no contact will be generated. It is not necessary for both of the objects to be dynamic.
You should now get didBegin
called when the bullet and the enemy make contact. You could code didBegin
like this:
func didBegin(_ contact: SKPhysicsContact) {
print("didBeginContact entered for \(String(describing: contact.bodyA.node!.name)) and \(String(describing: contact.bodyB.node!.name))")
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case bulletCategory | enemyCategory:
print("bullet and enemy have contacted.")
let bulletNode = contact.bodyA.categoryBitMask == bulletCategory ? contact.bodyA.node : contact.bodyB.node
enemyHealth -= 10
bulletNode.removeFromParent
default:
print("Some other contact occurred")
}
}
Detect collision between two objects in Swift
Here are a few items missing from your checklist:
- One of the physics bodies must be dynamic
The physics bodies must be moved by a force/impulse or by setting their velocities- The positions of the physics bodies should match their corresponding sprite/shape nodes
For (2), you are moving each bullet by changing its position over time with an SKAction
.
For (3), the position of each bullet starts at (0, 0), while the shape is drawn at the top-center of the scene. Since the physics body is centered at the shape node's position, there is a mismatch between the locations of the shape and the physics body; the bullet's physics body never makes contact with the soldier. To resolve this, try
var bullet = SKShapeNode(rectOfSize: CGSizeMake(10, 40))
bullet.position = CGPointMake(self.size.width/2.0, self.size.height)
Also, the physics body of the soldier is half the radius of its shape.
SpriteKit collisions issue between two objects controlled by screen touch
Based on what I am seeing, you never assign "enemy2" to the name of your enemy, so enumerateChildNodes(withName: "enemy2") { node, _ in
let enemy2 = node as! SKSpriteNode
if enemy2.frame.intersects(
self.spaceship.frame) {
hitEnemies2.append(enemy2)
}
}
should be empty, meaning no node is hit.
How do i detect a touch location in SpriteKit?
When you create a new project and choose Game (SpriteKit game) if you look inside you will see that there is already shown how you handle touches:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(self)
let sprite = SKSpriteNode(imageNamed:"Spaceship")
sprite.xScale = 0.5
sprite.yScale = 0.5
sprite.position = location
let action = SKAction.rotateByAngle(CGFloat(M_PI), duration:1)
sprite.runAction(SKAction.repeatActionForever(action))
self.addChild(sprite)
}
}
As you can see, there is a variable location
which represent the touch location in the scene's coordinate system.
how to detect touch on node
every time you touch the screen you are cycling through all balls to see if you're touching one of them. if you have 50 balls on the screen it goes through them all to see if you are touching 1. that's not an efficient way of figuring out if you are touching 1.
There are many ways you can do this but what I would do is handle the touches inside of the Ball class. That way you don't have to figure out if you are touching a ball and which one it might be.
Explanation of protocol (to the best of my ability) this may seem a little much right now, but the faster you learn and understand protocols that better off you will be (IMO).
In this example we will use a protocol to setup a delegate of the
BallNode class. A protocol is a set user defined "rules" that must be
followed by any class that you designate compliant to that protocol.
In my example I state that for a class to be compliant to the
BallNodeDelegate protocol it must contain the didClick func. When you
add the BallNodeDelegate after GameScene you are stating that this
class will be compliant to that protocol. So if in GameScene you did
not have the didClick func it will cause an error. All this is put in
place so that you have an easy way to communicate between your
BallNode instances and your GameScene class (without having to pass
around references to your GameScene to each BallNode). Each BallNode
then has a delegate (GameScene) which you can pass back the
information to.
inside your BallNode class make sure you have isUserInteraction = true
outside of your BallNode class create a protocol that will send the touch info back to the GameScene
protocol BallNodeDelegate: class {
func didClick(ball: BallNode)
}
create a delegate variable in your BallNode class
weak var delegate: BallNodeDelegate!
move the touches began to you BallNode class
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.delegate?.didClick(ball: self)
}
in GameScene add the compliance to the BallNode protocol
class GameScene: SKScene, BallNodeDelegate
in GameScene when you create a Ball make sure you set it's delegate
let ball = BallNode()
ball.delegate = self
in GameScene add the nest. func to handle the clicks
func didClick(ball: BallNode) {
print("clicked ball")
}
How to check if two objects collided Objective C
You’ll first need to set the player’s and the enemy’s physic bodies categoryBitMask
property.
If you really mean ‘collide’ i.e. for the objects to bounce off each other, then this should happen automatically as this is controlled by the physics body’s collisionBitMask
but initially this is UInt32.Max
, so everything collides with everything.
If you actually mean ‘contact’, so that your didBegin
code gets called when the objects touch, you’ll need to set up the contactTestBitMask
as this is initially 0 i.e. nothing contacts anything.
Edit:
As Maria said - set the delegate and also make sure your class is an SKPhysicsContactDelegate
. Your code won’t be notified of any contacts if you don’t. (These steps are only required if you want contact detection - collisions dont need them.
class GameScene: SKScene, SKPhysicsContactDelegate {
physicsWorld.contactDelegate = self
I suspect that you want something to happen when the player and the enemy touch, so I think you really want contact detection, not collisions.
Related Topics
How to Get The Nearest Int Floored from a Sqrt of an Int
Stripe API Response: The Data Couldn't Be Read Because It Isn't in The Correct Format
Solve Equations of Type A*X = B Using Dgtsv_ or Sgtsv_
Do Protocols Have an Effect on The Retain Count
Switch Statement Where Value Is Int But Case Can Contain Array
How to Allow a Generic Collection to Perform Under The Hood Conversion in Swift
How to Make Alamofire Post Request with "Form-Data" Parameters
Upload Multiple Images to Ftp Server in iOS
Swiftui Section from Attribute of a Struct
How to Grab The Parent Object from a Subview
Can't Set @Ibinspectable Computed Property in UIview
How to Get a Class with Generic Type Accept an Array of Different by Same Generic Types
How to Pass Text from Cell to Textview in Another View Controller