How to Tell If a Node Is on the Screen Spritekit Swift

How do you tell if a node is on the screen spritekit swift

You can use the following to test if a node is in the scene:

if (!intersectsNode(sprite)) {
println("node is not in the scene")
}

This assumes that self is an SKScene subclass, such as GameScene.

Check if nodes are off the screen

Since you're trying to stop the sprite going off screen I'd recommend you use Sprite Kit's physics engine.

Firstly, add an SKPhysicsBody to your sprite, for example:

sprite.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)

Secondly, you need to add a physics body to the scene, for example:

override func didMoveToView(view: SKView) {
super.didMoveToView(view)
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: view.bounds)
}

Finally, since you want gravity disabled you can either disable it just for sprite:

sprite.physicsBody!.affectedByGravity = false
// force unwrapping here is okay because we're SURE sprite has a physics body.

or, disable gravity for everything in the scene:

self.physicsWorld.gravity = CGVector.zeroVector
// self in this case is your `SKScene` subclass.

For more information The Sprite Kit Programming Guide is really useful, in the case of physics see the Simulating Physics section.

best way to detect if node touch the frame

If I'm getting this right, when a ball hits the path node that is located at the top half of the screen you want a function to be called.

First, I'm not sure if a path node is more efficient than a sprite node, in fact I have never really used path nodes, but here is what you can do.

Spritekit

check out the link above. What you need to do is implement the SKPhysicsContactDelegate. This will allow you to access the functions didBegin() and didEnd(). These functions are called when a contact is made within the physicsworld.

class YourClass: SKScene, SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
}

func didEnd(_ contact: SKPhysicsContact) {
}
}

In order for these functions to be called, you need to set the physicsworld's contactDelegate to the class that will handle the calls. This would be your scene and a good place to set this is the didMove() function.

class YourClass: SKScene, SKPhysicsContactDelegate {
func didMove(to view: SKView) {
physicsWorld.contactDelegate = self
}

Now when a contact happens that is detectable, your didBegin() will be called, and when the contact ends the didEnd() will be called.

Now we need to give our nodes some physicsbodies and we can set the different bitmasks on them in order to detect collisions/contacts. These are the 3 bitmasks we are concerned about:

categoryTestBitMask
collisionTestBitMask
contactTestBitMask

categoryTestBitMask: You can give nodes of similar type a category, example "ball" could be the category. All your different ball objects could have this same category. I use the "noCollision" category for when I want to detect a contact, but I don't want a collision to happen. You are limited to 32 different categories though so don't go crazy with tons of different ones.

collisionTestBitMask: Give your "ball" a category that you want a collision to happen with. Ex: set the collisiontestmask for your "ball" to the category bitmask of "wall". A collision is when 2 objects will physically run into each other; so your ball will bounce off the walls.

contactTestBitMask: a Contact is when 2 nodes overlap. So instead of the ball bouncing off something, it would call the contact method for our delegate. Note that you can set both the collision and contact bit masks to the same thing.

Now how do we set these masks. I use a Struct so that I can assign names to the bitmasks and set these 3 different masks with code. Something like this:

struct Mask {
static var ball: UInt32 = 0b10 //2
static var wall: UInt32 = 0b100 //4
static var pathNode: UInt32 = 0b1000 //8
}

now within code you can set the masks:

let ball = SKSpriteNode()
ball.name = "ball"
ball.physicsBody = SKPhysicsBody()
ball.physicsBody.categoryTestBitMask = Mask.ball
ball.physicsBody.collisionTestBitMask = Mask.wall
ball.physicsBody.contactTestBitMask = Mask.pathNode | Mask.wall

let pathNode = SKSpriteNode()
pathNode.name = "pathNode"
pathNode.physicsBody = SKPhysicsBody()
pathNode.physicsBody.categoryTestBitMask = Mask.pathNode
pathNode.physicsBody.collisionTestBitMask = 0
pathNode.physicsBody.contactTestBitMask = Mask.pathNode

Lets look at what we are saying here. We create a ball object and we set its category to "ball", we say we want it to have collisions with "wall" objects and we want our contact delegate functions to trigger with "pathNode" objects OR "wall" objects. Our pathNode object will have no collisions, and will have contacts with the ball.

Basically the ball will bounce off the walls, and will pass through the pathNode. It will call the contact delegate functions didbegin() and didend() with both the pathNode and wall objects.

Not finished yet... So when the function is called, how do we handle this? When the didbegin or didend function is called, it has a parameter of "contact". this contact param has 2 bodies to work with and these are the bodies that contacted each other. There are multiple ways we can handle this, but I'll just show you a simple way for now.

func didBegin(_ contact: SKPhysicsContact) {
if contact.bodyA!.node!.name == "ball" {
// bodyA is our ball
switch contact.bodyB!.node!.name {
case "pathNode":
thisIsMyBallHitPathNodeFunction()
case "wall":
thisIsMyBallHitWallFunction()
default:
break
}
}
else if contact.bodyB!.node!.name == "ball" {
// bodyB is our ball
switch contact.bodyA!.node!.name {
case "pathNode":
thisIsMyBallHitPathNodeFunction()
case "wall":
thisIsMyBallHitWallFunction()
default:
break
}

}

}

Update:
What we are doing here is figuring out the type of bodyA and bodyB. So it starts with bodyA, if bodyA is a "ball", then we know that bodyA is a "ball and bodyB is the thing the ball came in contact with. We then use a switch statement to figure out what bodyB is. Once we know what bodyB is, we call the function that we need to call for that specific contact between these 2 nodes.

Then you just put your code into those specified functions of what you want to do.

This could be a lot to take in at once, If you are new I would suggest trying this out and trying to get it to work. After, I would youTube some videos on how to do this. It is a good idea to see how different people handle the same thing and then you can decide for your self on how to do it. This might not be the most elegant way of handling the contacts, but it works well and with some practice it will become second nature, Good luck!

How to detect if one node is pointing to the other in spritekit

Instead of using one image for your circle, divide your image into 4 nodes and assign a physicsbody to all of them separately. This way you can detect whether your colorbar node is touching the specific colour node .

Check if node is visible on the screen

Using a worldNode which includes a playerNode and having the camera center on this node, you can check on/off with this code:

float left = player.position.x - 700;
float right = player.position.x + 700;
float up = player.position.y + 450;
float down = player.position.y - 450;

if((object.position.x > left) && (object.position.x < right) && (object.position.y > down) && (object.position.y < up)) {
if((object.parent == nil) && (object.dead == false)) {
[worldNode addChild:object];
}
} else {
if(object.parent != nil) {
[object removeFromParent];
}
}

The numbers I used above are static. You can also make them dynamic:

CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat screenHeight = screenRect.size.height;

Diving the screenWidth by 2 for left and right. Same for screenHeight.

How to detect if child node has been touched: Sprite Kit

Here is one way to track continuous touches (i.e.. directional arrows).

You need to keep track of the touches, in the SKScene node, or in the parent view controller:

var touches = Set<UITouch>()

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.touches.formUnion(touches)
self.updateTouches()
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
self.touches.formUnion(touches)
self.updateTouches()
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.touches.subtract(touches)
self.updateTouches()
}

override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
self.touches.subtract(touches)
self.updateTouches()
}

For each touch, translate the coordinates to relative to the SKNode. Check if any of the touches is within the accumulated frame of the SKNode. The accumulated frame is the total bounding area of the node and all of its descendants.

private func isTouching(node: SKNode) -> Bool {

guard let parent = node.parent else {
return false
}

let frame = node.calculateAccumulatedFrame()

for touch in touches {

let coordinate = touch.location(in: parent)

if frame.contains(coordinate) {
return true
}
}

return false
}

For example:

override func update(_ currentTime: NSTimeInterval) {

let touchingJump = isTouching(node: jumpButtonNode)

if touchingJump {
print("jump")
}
}

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")
}

Detect if a SKSpriteNode left the screen at the bottom and call a function

In your scene you have to remember the reference for node you want to check and then in update method you just do the following:

    if node.position.y < -node.size.height/2.0 {
node.removeFromParent()
gameOver()

}

Edit:

class MyScene: SKScene {
// ....

//here is the list of nodes which you want to check
var nodesToCheck = [SKSpriteNode]()

//here is your spawn function

func spawnNewNode() {
var node = SKSpriteNode()

let nodeTexture = SKTexture(imageNamed: "node")
nodeTexture.filteringMode = .Nearest
node = SKSpriteNode(texture: nodeTexture)

let nodeFalling = SKAction.moveToY(-70, duration: 1.6)
let nodeRemoving = SKAction.removeFromParent()
node.runAction(SKAction.sequence([nodeFalling, nodeRemoving]))

self.addChild(node)
nodesToCheck.append(node)
}

//and here is the update method

override func update(currentTime: NSTimeInterval) {
super.update(currentTime)

// ... every other update logic

for node in nodesToCheck {
if node.position.y < -node.size.height/2.0 {
node.removeFromParent()
gameOver()
}
}
}

func gameOver() {
println("Damn!")
}

}

Dont forget to remove your node from nodeToCheck array when they are no longer scene members.



Related Topics



Leave a reply



Submit