Update Label when 2 SKSpriteNodes collide - sprite-kit

I want to add 1 point to the topLbl whenever the ball collides with the main. How would I do that? Thanks!
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var ball = SKSpriteNode()
var main = SKSpriteNode()
var topLbl = SKLabelNode()
var score = [Int]()
override func didMove(to view: SKView) {
startGame()
self.physicsWorld.contactDelegate = self
topLbl = self.childNode(withName: "topLabel") as! SKLabelNode
ball = self.childNode(withName: "ball") as! SKSpriteNode
main = self.childNode(withName: "main") as! SKSpriteNode
ball.physicsBody?.applyImpulse(CGVector(dx: 8, dy: 8))
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.friction = 0
border.restitution = 1
self.physicsBody = border
}
func startGame() {
score = [0]
topLbl.text = "\(score[0])"
}
func addScore(byTouch : SKSpriteNode){
if byTouch == main {
score[0] += 1
}
print(score)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
main.run(SKAction.moveTo(x: location.x, duration: 0))
main.run(SKAction.moveTo(y: location.y, duration: 0))
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
main.run(SKAction.moveBy(x: 4444, y: 4444, duration: 0))
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
func didBegin(contact: SKPhysicsContact) {
if (contact.bodyA.categoryBitMask == 1 && contact.bodyB.categoryBitMask == 2) {
if ball == contact.bodyA.node as! SKSpriteNode {
score[0] += 1
}
} else if (contact.bodyA.categoryBitMask == 2 && contact.bodyB.categoryBitMask == 1) {
if ball == contact.bodyB.node as! SKSpriteNode {
score[0] += 1
}
}
}
}

Related

Move SKSpriteNode on Touch to create Button effect

I am currently creating the main menu of a mobile game, and it works fine however To make it look nicer I would like to add an effect when I touch one of my SKSpriteNodes which is an image of a button. I would like it to slightly move down then back up before transitioning to the game, im having some difficulty. Currently when the button is pressed it changed scenes to the game scene.
class MainMenuScene: SKScene{
let startGame = SKSpriteNode(imageNamed: "StartButton")
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "Menu")
background.size = self.size
background.position = CGPoint(x: self.size.width/2, y:self.size.height/2)
background.zPosition = 0
self.addChild(background)
startGame.position = CGPoint(x: self.size.width/2, y: self.size.height*0.6)
startGame.zPosition = 1
startGame.name = "startButton"
self.addChild(startGame)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches{
let pointOfTouch = touch.location(in: self)
let nodeITapped = atPoint(pointOfTouch)
if nodeITapped.name == "startButton" {
let sceneToMoveTo = GameScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTransition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTransition)
}
}
}
}
You could make your logic in the function touchesEnded and then you call a SKAction in your button, passing the moveToScene logic as completion.
Something like:
var shouldMoveToNewScene: Bool = False
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches{
let pointOfTouch = touch.location(in: self)
let nodeITapped = atPoint(pointOfTouch)
shouldMoveToNewScene = nodeITapped.name == "startButton" ? True : False
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard shouldMoveToNewScene, touces.count == 1 else { return }
startButton.run(myAnimation, completion: {
moveToScene()
})
}
private func moveToScene() {
//do your transition scene logic here
{
I haven't tried it yet, but hopefully you could get the idea.
If you're not familiar with SKAction you should consider follow this guide.

whose view is not in the window hierarchy

I am developing a game and I am trying to move from my game scene to my view controller but it gives me the error:
Warning: Attempt to present <Breakout.GameViewController: 0x7f8874d19ed0> on whose view is not in the window hierarchy!.
My game is set up so I do level 1 and finish it then it takes me to the vc where I can do level 1. That works, but then when I finish level 2 and try to go back to the vc, it does not work and gives me the error.
I have looked at other posts but nothing has directly linked to this.
My Game Scene Controller:
class Level1: SKScene,SKPhysicsContactDelegate {
var ball:SKSpriteNode!
var paddle:SKSpriteNode!
var scoreLabel:SKLabelNode!
var score:Int = 0 {
didSet{
scoreLabel.text = "Score:\(score)"
}
}
override func didMove(to view: SKView) {
ball = self.childNode(withName: "Ball") as! SKSpriteNode
paddle = self.childNode(withName: "Paddle") as! SKSpriteNode
scoreLabel = self.childNode(withName: "Score") as! SKLabelNode
ball.physicsBody?.applyImpulse(CGVector(dx: 50, dy: 50))
ball.physicsBody?.mass=0.1
let border = SKPhysicsBody(edgeLoopFrom: (view.scene?.frame)!)
border.friction = 0
self.physicsBody = border
ball.physicsBody?.mass = 0.09
self.physicsWorld.contactDelegate = self
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let touchLocation = touch.location(in: self)
paddle.position.x = touchLocation.x
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let touchLocation = touch.location(in: self)
paddle.position.x = touchLocation.x
}
}
func didBegin(_ contact: SKPhysicsContact) {
let bodyAName = contact.bodyA.node?.name
let bodyBName = contact.bodyB.node?.name
if bodyAName == "Ball" && bodyBName == "Brick" || bodyAName == "Brick" && bodyBName == "Ball"{
if bodyAName == "Brick" {
contact.bodyA.node?.removeFromParent()
score += 1
} else if bodyBName == "Brick" {
contact.bodyB.node?.removeFromParent()
score += 1
}
}
}
override func update(_ currentTime: TimeInterval) {
if (score == 9) {
scoreLabel.text = "You Won!"
self.view?.isPaused = true
sleep(2)
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = mainStoryboard.instantiateViewController(withIdentifier: "GameViewController")
self.view?.window?.rootViewController?.present(vc, animated: true, completion: nil)
level1done = true
}
if (ball.position.y < paddle.position.y) {
scoreLabel.text = "You Lost!"
self.view?.isPaused = true
sleep(2)
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = mainStoryboard.instantiateViewController(withIdentifier: "GameViewController")
self.view?.window?.rootViewController?.present(vc, animated: true, completion: nil)
}
}
}

How to add 1 point every time SKNode is touched

I created a game involving falling balls. Every time the player taps a ball, they're supposed to get a point. I set up the score label, but it just stays at 0.
Is there something wrong with my code?
I use a GameElements.swift file as an extension. Here is the file:
extension GameScene {
//1st Ball//
func createPlayer() -> SKNode {
let playerNode = SKNode()
playerNode.position = CGPoint(x:self.size.width / 2, y:440)
let sprite = SKSpriteNode(imageNamed: "Ball")
sprite.name = "ballPoints"
playerNode.addChild(sprite)
playerNode.physicsBody = SKPhysicsBody(circleOfRadius: 120)
playerNode.physicsBody?.dynamic = true
playerNode.physicsBody?.allowsRotation = false
playerNode.physicsBody?.restitution = 3
playerNode.physicsBody?.friction = 0
playerNode.physicsBody?.angularDamping = 0
playerNode.physicsBody?.linearDamping = 0
playerNode.physicsBody?.usesPreciseCollisionDetection = true
playerNode.physicsBody?.categoryBitMask = CollisionBitMask.Player
playerNode.physicsBody?.categoryBitMask = 0
return playerNode
}
}
Here is the GameScene.swift file:
class GameScene: SKScene {
var foreground: SKNode!
var hud: SKNode!
var firstBall: SKNode!
var scoreLabel: SKLabelNode!
private var score = 0
override func didMoveToView(view: SKView) {
scoreLabel = SKLabelNode(fontNamed:"Geared-Slab")
scoreLabel.fontColor = UIColor.blackColor()
scoreLabel.position = CGPoint( x: self.frame.midX, y: 3 * self.frame.size.height / 4 )
scoreLabel.fontSize = 100.0
scoreLabel.zPosition = 100
scoreLabel.text = String(score)
self.addChild(scoreLabel)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if(firstBall.containsPoint(location)) {
firstBall.physicsBody?.velocity = CGVectorMake(0, 600)
firstBall.physicsBody?.applyImpulse(CGVectorMake(0, 1100))
}
if
let touch : UITouch! = touches.first,
let tappedSprite = nodeAtPoint(touch!.locationInNode(self)) as? SKSpriteNode,
let scoreLabel = childNodeWithName("scoreLabel") as? SKLabelNode
where tappedSprite.name == "ballPoints" {
score += 1
scoreLabel.text = "Score: \(score)"
}
}
}
override init(size:CGSize) {
super.init(size: size)
foreground = SKNode()
addChild(foreground)
//1st Ball//
firstBall = createPlayer()
foreground.addChild(firstBall)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I know this is a lot of code, but I want experts to be able to test the code themselves. For background, I was following this tutorial: https://www.youtube.com/watch?v=0gOi_2Jwt28 up until a certain point.
Step 1
First of all let's create a class for the Ball
class Ball: SKSpriteNode {
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let scene = self.scene as! GameScene
scene.score += 1
}
}
Step 2
Then inside createPlayer() let's replace this
let sprite = SKSpriteNode(imageNamed: "Ball")
with this
let sprite = Ball(imageNamed: "Ball")
sprite.userInteractionEnabled = true
Step 3
Let's remove the touchesBegan from GameScene.
Update
This code is working as expected for me
class GameScene: SKScene {
var scoreLabel: SKLabelNode!
var ball: Ball!
private var score = 0 {
didSet {
scoreLabel.text = "\(score)"
print(score)
}
}
override func didMoveToView(view: SKView) {
let ball = Ball()
ball.position = CGPoint(x:frame.midX, y: frame.midY)
addChild(ball)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
// this is called when you tap outside of the Ball
// use self.ball to make the ball to jump
}
}
class Ball: SKSpriteNode {
init() {
let texture = SKTexture(imageNamed: "ball")
super.init(texture: texture, color: .clearColor(), size: texture.size())
userInteractionEnabled = true
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let scene = self.scene as! GameScene
scene.score += 1
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

How to Create "identical" SpriteNodes that hold the same methods?

I'm super new to coding (like I started yesterday new). I'm trying to make the same SpriteNode appear multiple times that follow the same methods. For example, the game starts off with one Node that reacts to touch and after you reach 5 points another appears, after 15 another and so on... I have been able to spawn a spriteCopy at 5 points; however I cannot get it to respond to touch like the original node. I'm sure it's something super simple I just don't have the experience. Thanks.
Here is my code so far:
class GameScene: SKScene {
var sprite: SKSpriteNode!
var touchPoint: CGPoint = CGPoint()
var touching: Bool = false
var score: Int = 0
let scoreTextNode = SKLabelNode(fontNamed: "Helvetica Neue UltraLight")
override func didMoveToView(view: SKView) {
sprite = SKSpriteNode(imageNamed: "PongBall")
sprite.position = CGPoint(x: 500,y: 500)
sprite.size = CGSize(width: 75, height: 75)
sprite.physicsBody = SKPhysicsBody(circleOfRadius: 37.5)
self.addChild(sprite)
scoreTextNode.text = "\(score)"
scoreTextNode.fontSize = 70
scoreTextNode.fontColor = SKColor.blackColor()
scoreTextNode.position = CGPoint (x: 680.734, y: 700.941)
self.addChild(scoreTextNode)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first as UITouch!
let location = touch.locationInNode(self)
if sprite.frame.contains(location) {
touchPoint = location
touching = true
}
if touchPoint == location {
self.score += 1
self.scoreTextNode.text = "\(score)"
}
let spriteCopy = sprite.copy() as! SKSpriteNode
if score == 5 {
self.addChild(spriteCopy)
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first as UITouch!
let location = touch.locationInNode(self)
touchPoint = location
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
touching = false
}
override func update(currentTime: CFTimeInterval) {
if touching {
let dt:CGFloat = 1.0/30.0
let distance = CGVector(dx: touchPoint.x-sprite.position.x, dy: touchPoint.y-sprite.position.y)
let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
sprite.physicsBody!.velocity=velocity
}
}
}
Your problem is you have multiple sprites appearing on your screen, but you do not do anything with them. To keep things real simple for you, take this approach.
class GameScene: SKScene {
var sprite: SKSpriteNode!
var touchPoint: CGPoint = CGPoint()
var touching: Bool = false
var score: Int = 0
let scoreTextNode = SKLabelNode(fontNamed: "Helvetica Neue UltraLight")
var spriteList = [SKSpriteNode]() //Add this line
override func didMoveToView(view: SKView) {
sprite = SKSpriteNode(imageNamed: "PongBall")
sprite.position = CGPoint(x: 500,y: 500)
sprite.size = CGSize(width: 75, height: 75)
sprite.physicsBody = SKPhysicsBody(circleOfRadius: 37.5)
self.addChild(sprite)
spriteList.append(sprite) //Add This line
scoreTextNode.text = "\(score)"
scoreTextNode.fontSize = 70
scoreTextNode.fontColor = SKColor.blackColor()
scoreTextNode.position = CGPoint (x: 680.734, y: 700.941)
self.addChild(scoreTextNode)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first as UITouch!
let location = touch.locationInNode(self)
for sprite in spriteList{ //Add This line
if sprite.frame.contains(location) {
touchPoint = location
touching = true
}
if touchPoint == location {
self.score += 1
self.scoreTextNode.text = "\(score)"
}
let spriteCopy = sprite.copy() as! SKSpriteNode
if score == 5 {
self.addChild(spriteCopy)
spriteList.append(spriteCopy) //Add this line
}
} //Add this line
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first as UITouch!
let location = touch.locationInNode(self)
touchPoint = location
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
touching = false
}
override func update(currentTime: CFTimeInterval) {
if touching {
for sprite in spriteList{ //Add this line
let dt:CGFloat = 1.0/30.0
let distance = CGVector(dx: touchPoint.x-sprite.position.x, dy: touchPoint.y-sprite.position.y)
let velocity = CGVector(dx: distance.dx/dt, dy: distance.dy/dt)
sprite.physicsBody!.velocity=velocity
} //Add this line
}
}
}
The basic concept is you maintain it in an array. This should be a good starting point for a beginner. After you learn how to override classes, you can learn that the touch code can be done inside of the sprite instead of inside of the scene. This would actually be my preferred method.

Player not moving when tapped

I have it set up so that my player should jump to wherever I tap on the screen, however, it won't budge. I triple checked my code and can't find anything wrong. Can someone please help?
Here is my code for my GameScene:
import SpriteKit
var player: Player!
var snowflake: Snowflake!
var isFingerOnPlayer = false
var gameOver = false
var playerTouched = false
var touching: Bool = false
var lastTouch: CGPoint? = nil
let xPlayerForce: CGFloat = 30
let yPlayerForce: CGFloat = 40
var touchPoint: CGPoint = CGPoint()
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMoveToView(view: SKView) {
player = Player()
addChild(player)
backgroundColor = UIColor.whiteColor()
snowflake = Snowflake()
addChild(snowflake)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
lastTouch = location
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
lastTouch = nil
}
override func update(currentTime: NSTimeInterval) {
if let touch = lastTouch {
var xForce: CGFloat = 0.0
var yForce: CGFloat = 0.0
let xTouchOffset = (touch.x - player.position.x)
let yTouchOffset = (touch.y - player.position.y)
if xTouchOffset > 0.0
{
xForce = xPlayerForce
}
else if xTouchOffset < 0.0
{
xForce = -xPlayerForce
} // else we do nothing
if yTouchOffset > 0.0
{
yForce = yPlayerForce
} // here you can choose whether you want it to push
// the player node down, using similar code from the
// above if statement
let impulseVector = CGVector(dx: xForce, dy: yForce)
player.physicsBody?.applyImpulse(impulseVector)
}
}
You are right by saying you don't NEED code in touches began, but from what you are saying you should probably. I would try just setting the location of the object in touches began and then updating it in touches moved.