How to have a ball go through a hoop in spritekit? - swift

I have a basketball rim png that I want a basketball to go through if the user taps on the correct location above the rim but whenever I tap the screen for the ball to drop down, it always bounces off the rim even if it is directly above it and is supposed to go through it. How would I allow the basketball to fall directly through the hoop? Here is my code. Thanks.
import SpriteKit
import GameplayKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
//This is the basketball rim png that I put in my scene
let rim = SKSpriteNode(imageNamed: "resized Basketball hoop.png")
rim.position = CGPoint(x: 512, y: 384)
rim.physicsBody = SKPhysicsBody(texture: rim.texture!, size: rim.texture!.size())
rim.physicsBody?.isDynamic = false
addChild(rim)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
//This is when I touch the screen and a basketball falls from where I touched
if let touch = touches.first {
let location = touch.location(in: self)
let basketball = SKSpriteNode(imageNamed: "basketball png.png")
basketball.physicsBody = SKPhysicsBody(circleOfRadius: basketball.size.width / 16)
basketball.size.width = basketball.size.width / 8
basketball.size.height = basketball.size.height / 8
basketball.physicsBody?.restitution = 0.4
basketball.position = location
physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
addChild(basketball)
}
}
}

Related

Make sprite move with velocity

I am trying to make a sprite follow my finger using velocity so it doesn't phase through other sprite nodes, or push them around. I just want the sprite node, aka the ball, to simply hit the other sprite node and either bounce off or just simply hit it and sit there until its moved again.
At the moment I am using location based movement:
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
ball.position.x = location.x
ball.position.y = location.y
print("x: \(ball.position.x), y: \(ball.position.y)")
}
}
how can I make it so it doesn't move around other sprite nodes and actually reacts with categoryBitMask elements?
I'm new to this whole thing. Hopefully you understand :)
REST OF MY CODE:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var ball = SKSpriteNode()
var danger1 = SKSpriteNode()
var danger2 = SKSpriteNode()
var goal = SKSpriteNode()
override func didMove(to view: SKView) {
ball = self.childNode(withName: "ball") as! SKSpriteNode
danger1 = self.childNode(withName: "danger1") as! SKSpriteNode
danger2 = self.childNode(withName: "danger2") as! SKSpriteNode
goal = self.childNode(withName: "goal") as! SKSpriteNode
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.friction = 0
border.restitution = 0
danger1.physicsBody = SKPhysicsBody()
danger1.physicsBody?.categoryBitMask = PhysicsCategories.dangerCategory
danger2.physicsBody = SKPhysicsBody()
danger2.physicsBody?.categoryBitMask = PhysicsCategories.dangerCategory
ball.physicsBody = SKPhysicsBody()
ball.physicsBody?.categoryBitMask = PhysicsCategories.ballCategory
ball.physicsBody?.contactTestBitMask = PhysicsCategories.dangerCategory
ball.physicsBody?.collisionBitMask = PhysicsCategories.none
goal.physicsBody = SKPhysicsBody(circleOfRadius: goal.size.width/2)
goal.physicsBody?.categoryBitMask = PhysicsCategories.goalCategory
danger1.physicsBody?.isDynamic = true
ball.physicsBody?.isDynamic = true
goal.physicsBody?.isDynamic = true
danger2.physicsBody?.isDynamic = true
danger2.physicsBody!.affectedByGravity = false
danger1.physicsBody!.affectedByGravity = false
goal.physicsBody!.affectedByGravity = false
ball.physicsBody!.affectedByGravity = false
setupPhysics()
}
func setupPhysics() {
physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
physicsWorld.contactDelegate = self
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
ball.position.x = location.x
ball.position.y = location.y
print("x: \(ball.position.x), y: \(ball.position.y)")
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
extension GameScene: SKPhysicsContactDelegate {
func didBegin(_ contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
if contactMask == PhysicsCategories.ballCategory | PhysicsCategories.dangerCategory {
ball.position = CGPoint(x: 0, y: 550)
} else if contactMask == PhysicsCategories.ballCategory | PhysicsCategories.goalCategory {
print("Goal!")
}
}
}
Try commenting out this line:
ball.physicsBody?.collisionBitMask = PhysicsCategories.none
which prevents the ball from colliding with anything, i.e. it is unaffected by collisions with any object. Note - this does NOT mean that other objects are unaffected by collision with the ball. collision are 2-way - given 2 node A and B, you have to define if A collides with B and if B collides with A. This isn;t necessary for contact - it's enough to define that A contacts B. If it's actually b that moves into A, didBegin() will still get called. you don't have to define that B contact A also.
You haven't appeared to set the collisionBitMask on any other physics body, which means those will collide with everything.
this explains why the ball moves through the wall - because it isn't colliding with it, although every other object should get pushed around by the ball.
Try putting a:
print("Contact")
as the first line of your didBegin() to see if any contacts are being registered.
If you want to drag sprites with your finger then making them bounce off (collide with) other sprites can be tricky, because dragging implies that you want to set their position manually and bouncing off implies that your want the sprites to be moved by the Sprite-Kit engine and the 2 are not really compatible.
It's probably worth pointing our that 'collisions' are sprites bouncing off each other and are handled by the SK engine. The collisionBitMask controls what objects bounce off each other.
'Contacts' are just a way to get notified when 2 objects touch. They are controlled by the contactTestBitMask.
Both contact and collisions rely on the categoryBitMAsk
My step-by-step guide for collisions and contacts:
https://stackoverflow.com/a/51041474/1430420
And a guide to collision and contactTest bit masks:
https://stackoverflow.com/a/40596890/1430420
Manipulating bit masks to turn individual collision and contacts off and on. https://stackoverflow.com/a/46495864/1430420

Label not showing up (Swift 4 SpriteKit)

I am making a game in Xcode with Swift 4, SpriteKit. My crash detection is working, however when I try to make text appear onto the screen when the player crashes, the text doesn't appear. My game basically has the use control a rocket ship which must dodge meteors, the player also has the ability to fire bullets. I have detected when the rocket ship hits a meteor but cannot get text to appear onto the screen. I know for a fact that the crash detection is working, because whenever I add a print statement in the didBegin function, the print statement executes. So it must be that way im displaying the label, but im not sure what I'm doing wrong with the label.
Here's my code:
import SpriteKit
import GameplayKit
import UIKit
class GameScene: SKScene, SKPhysicsContactDelegate{
let player = SKSpriteNode(imageNamed: "spaceship")
let stars = SKSpriteNode(imageNamed: "stars")
let meteor = SKSpriteNode(imageNamed: "meteor")
override func didMove(to view: SKView) {
physicsWorld.contactDelegate = self
print(frame.size.width)
print(frame.size.height)
stars.position = CGPoint(x:0, y:0)
stars.zPosition = 1
player.position = CGPoint(x:0, y:-320)
player.zPosition = 4
player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.width / 2)
player.physicsBody?.affectedByGravity = false
player.physicsBody?.isDynamic = false
player.physicsBody?.categoryBitMask = 2
player.physicsBody?.collisionBitMask = 1
player.physicsBody?.contactTestBitMask = 1
self.addChild(player)
self.addChild(stars)
self.addMeteor()
}
func didBegin(_ contact: SKPhysicsContact) {
gameOver()
}
func addMeteor() {
meteor.physicsBody = SKPhysicsBody(circleOfRadius: meteor.size.width / 2)
meteor.physicsBody?.affectedByGravity = false
meteor.setScale(0.50)
meteor.position = CGPoint(x:Int(arc4random()%300),y:Int(arc4random()%600))
//meteor.position = CGPoint(x:0 , y:0)
meteor.zPosition = 4
meteor.physicsBody?.categoryBitMask = 1
meteor.physicsBody?.collisionBitMask = 0
meteor.physicsBody?.contactTestBitMask = 2
self.addChild(meteor)
}
func fireBullet() {
let bullet = SKSpriteNode(imageNamed: "bullet")
bullet.position = player.position
bullet.setScale(0.5)
bullet.zPosition = 3
self.addChild(bullet)
let moveBullet = SKAction.moveTo(y: self.size.height + bullet.size.height, duration: 1)
let deleteBullet = SKAction.removeFromParent()
let bulletSequence = SKAction.sequence([moveBullet, deleteBullet])
bullet.run(bulletSequence)
}
func gameOver() {
print("Game Over!")
var gameOverLabel: SKLabelNode!
gameOverLabel = SKLabelNode(fontNamed: "Chalkduster")
gameOverLabel.text = "Game Over! You lost!"
gameOverLabel.horizontalAlignmentMode = .right
gameOverLabel.position = CGPoint(x: 0, y:0)
self.addChild(gameOverLabel)
print("Label added")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
fireBullet()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let pointOfTouch = touch.location(in: self)
let previousPointOfTouch = touch.previousLocation(in: self)
let amountDragged = pointOfTouch.x - previousPointOfTouch.x
player.position.x += amountDragged
}
}
override func update(_ currentTime: TimeInterval) {
meteor.position.y -= 6
if meteor.position.y < player.position.y - 300{
meteor.removeFromParent()
addMeteor()
}
}
}
Not sure what I'm doing wrong? I call the gameOver() function in didBegin which is supposed to be executed every time collision is detected. But nothing happens? The meteor simply goes past the rocket ship. If someone could help me that'd be awesome!
As i see you are trying to center the label on the screen, you may try to add this to your code, on gameover func:
1.Try change the position to this
gameOverLabel.position = CGPoint(x: (self.scene!.frame.width / 2) - (self.scene!.frame.width * self.scene!.anchorPoint.x) / 2, y:(self.scene!.frame.height / 2) - (self.scene!.frame.height * self.scene!.anchorPoint.y))
2.I don't know if you have something that is over the label and affecting its view, so try to:
gameOverLabel.zPosition = 10 //A number higher than any other zPosition
3.The label may be too small...try:
gameOverLabel.fontSize = 30 //Adjust it to what you want
4.Just a tip:
move the:
var gameOverLabel: SKLabelNode!
to next line after the
let meteor = SKSpriteNode(imageNamed: "meteor")
This way you will have a universal label, and do not have to regenerate the label every time.
If you do this, just a final tip...I don't know if you have a func like gameRestart() or something like this...but make sure that when you restart the game, you remove the label:
gameOverLabel.removeFromParent()
If you don't do this, when the app try to add the label again it will crash!
Hope it helps!
You are not moving any of your objects as far as your physics in concerned. You need to apply forces instead of manually moving a position. Also your player dynamic is set to false, which means that he never moves, so contacts only happen on the meteor end.

How to move sprite relative to its original position with touch in spritekit

I looking to move a sprite relative to the player's touch. I am familiar with the moveTo SKActions, however I am wondering how to implement sprite movement where the sprite moves along with the user's touch movement.
For example, I have a sprite in the centre of the screen. If I apply a touch at the bottom of the screen and move my finger up , the sprite will move up from the centre(it's original position).
Thanks!
Try this :
import SpriteKit
class GameScene: SKScene {
var node = SKSpriteNode()
var nodePosition = CGPoint()
var startTouch = CGPoint()
override func didMove(to view: SKView) {
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
// node set up
node = SKSpriteNode(color: .red, size: CGSize(width: 50, height: 50))
node.position = CGPoint.zero
self.addChild(node)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
if let location = touch?.location(in: self){
startTouch = location
nodePosition = node.position
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
if let location = touch?.location(in: self){
node.run(SKAction.move(to: CGPoint(x: nodePosition.x + location.x - startTouch.x, y: nodePosition.y + location.y - startTouch.y), duration: 0.1))
}
}
}

sprite nodes on touch first one only takes physics body

I am using Xcode 8.1 Swift 3 to make small bouncing game.
The player is supposed to create walls around a bouncing ball and this ball is supposed to bounce on each wall.
On touch down, I move to point and on touch ended, I create a line share node between the two touches began and end.
I added the physics that were needed to my node, then I added child to this node (see my node below).
What happens is that for each touches began and touches ends, 'Swift' draws the line node and attaches it to self but only the first node bounces the ball.
All lines (walls) after the first one is not affecting the ball.
Here is my code the GameScene Swift file:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
// vars lets and nodes
let startingBall = SKShapeNode(circleOfRadius: 10)
let myPath = CGMutablePath()
let ballCategory : UInt32 = 1
let wallCategory : UInt32 = 2
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
myPath.move(to: t.location(in: self))
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
myPath.addLine(to: t.location(in: self))
let wallNode = SKShapeNode(path: myPath)
wallNode.lineWidth = 5.0
wallNode.fillColor = SKColor.green
wallNode.strokeColor = SKColor.green
wallNode.physicsBody = SKPhysicsBody(edgeLoopFrom: myPath)
wallNode.physicsBody?.categoryBitMask = wallCategory
self.addChild(wallNode)
}
}
override func didMove(to view: SKView) {
self.physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame)
self.physicsBody?.categoryBitMask = wallCategory
startingBall.fillColor = SKColor.red
startingBall.position = CGPoint(x: self.frame.width/2, y: self.frame.height/2)
startingBall.physicsBody = SKPhysicsBody(circleOfRadius: 10)
startingBall.physicsBody?.affectedByGravity = false
startingBall.physicsBody?.isDynamic = true
startingBall.physicsBody?.restitution = 1.0
startingBall.physicsBody?.friction = 0.0
startingBall.physicsBody?.linearDamping = 0.0
startingBall.physicsBody?.angularDamping = 0.0
self.addChild(startingBall)
startingBall.physicsBody?.applyImpulse(CGVector(dx: 3.0, dy: 3.0))
}
}
thanks all i've fixed it
actually mutable path myPath defines line between touch begin and touch end lines to draw shape node only ... while physics edge created for each touch series from node to node ...
in steps
1- i added start point and end point variables
2- pick up start node in touch begin and end node in touch end
3- i set physical body for from point to point start node and end node
1-
var startPoint : CGPoint = CGPoint.zero
var endPoint : CGPoint = CGPoint.zero
2- ..
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
startPoint = t.location(in: self)
myPath.move(to: t.location(in: self))
}
}
endPoint = t.location(in: self)
3- ..
wallNode.physicsBody = SKPhysicsBody(edgeFrom: startPoint, to: endPoint)
for help you can set show physics to true in skiver
skview.showsPhysics = true
thanks

swift 2: Sprite tilting effect

Looking for a method to tilt a sprite upwards on the right side when tapping the screen (like in flappy bird) Like a start of a rotation that returns to the place it were. I will use this as an effect of going faster then the sprite normally does.
I'll add the code I got so far below:
var player = SKSpriteNode()
override func didMoveToView(view: SKView) {
let playerTexture = SKTexture(imageNamed: "player")
player = SKSpriteNode(texture: playerTexture)
player.position = CGPoint(x: 200, y: 80)
player.physicsBody = SKPhysicsBody(texture: playerTexture, size: playerTexture.size())
player.physicsBody!.affectedByGravity = true
self.addChild(player)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
}