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")
}
}
Related
I have a menu button (skSpriteNode) and a small play icon inside this sprite
mainButton.addChild (playIcon)
mainButton.name = "xyz"
addchild (mainButton)
ok, I gave the button the name to check if a sprite with this name is touched.
when I touch the icon child inside the button, the touchedNode.name is nil. I set isUserInteractionEnabled = false for the icon child. I want to pass the touch to the parent. how can I do this.
for touch in touches {
let location = touch.location(in: self)
let touchedNode = self.atPoint(location)
if (touchedNode.name != nil) {
print ("node \(touchedNode.name!)")
} else {
continue
}
}
You have to implement touchesBegan in a subclass of SKSpriteNode and to set its isUserInteractionEnabled = true, in order to accept and process the touches for that particular node (button).
import SpriteKit
protocol ButtonDelegate:class {
func printButtonsName(name:String?)
}
class Button : SKSpriteNode{
weak var delegate:ButtonDelegate?
init(name:String){
super.init(texture: nil, color: .purple, size: CGSize(width: 250, height: 250))
self.name = name
self.isUserInteractionEnabled = true
let icon = SKSpriteNode(color: .white, size: CGSize(width:100, height:100))
addChild(icon)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
delegate?.printButtonsName(name: self.name)
}
}
class GameScene: SKScene, ButtonDelegate {
override func didMove(to view: SKView) {
let button = Button(name:"xyz")
button.delegate = self
button.position = CGPoint(x: frame.midX - 50.0, y: frame.midY)
addChild(button)
}
func printButtonsName(name: String?) {
if let buttonName = name {
print("Pressed button : \(buttonName) ")
}
}
}
Now, this button will swallow the touches and the icon sprite will be ignored.
I just modified the code from one of my answers to make an example specific to your needs, but you can read, in that link, about whole thing more in detail if interested.
I implemented the button Touchbegan and tried to implement a subclass for a slider with a background and the child thumb inside the slider. I added the sliderDelegate Protocoll in my GameScene. the down touch is ok, but I didn't get the moved in
import Foundation
import SpriteKit
protocol SliderDelegate:class {
func touchesBeganSKSpriteNodeSlider(name:String?, location: CGPoint?)
func touchesMovedSKSpriteNodeSlider(name:String?, location: CGPoint?)
func touchesEndedSKSpriteNodeSlider(name:String?, location: CGPoint?)
}
class SKSpriteNodeSlider : SKSpriteNode{
weak var delegate:SliderDelegate?
init(imageNamed: String){
// super.init(imageNamed: imageNamed)
let texture = SKTexture(imageNamed: imageNamed)
super.init(texture: texture, color: .white, size: texture.size())
// self.name = name
self.isUserInteractionEnabled = true
// let icon = SKSpriteNode(color: .white, size: CGSize(width:100, height:100))
// addChild(icon)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// for touch in touches {
if let touch = touches.first {
let loc = touch.location(in: self)
delegate?.touchesBeganSKSpriteNodeSlider(name: self.name, location: loc)
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
print ("touchesMoved")
// for touch in touches {
if let touch = touches.first {
let loc = touch.location(in: self)
delegate?.touchesMovedSKSpriteNodeSlider(name: self.name, location: loc)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let loc = touch.location(in: self)
delegate?.touchesEndedSKSpriteNodeSlider(name: self.name, location: loc)
}
}
}
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
}
}
}
}
I'm new to SpriteKit, and I'm following some tutorial, but I got a problem which does not mentioned in the tutorial.
Here is what I'm gonna do, when I touch the screen each time, the scene changes from A to B, and repeat.
I have GameScene.swift and MenuScene.swift, almost the same code.
(Xcode version 7.3.1)
GameScene.swift:
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
createScene()
}
func createScene() {
self.backgroundColor = SKColor.blueColor()
let myLabel = SKLabelNode(fontNamed:"Chalkduster")
myLabel.name = "label"
myLabel.text = "Hello, World!"
myLabel.fontSize = 45
myLabel.position = CGPoint(x:500, y:300)
self.addChild(myLabel)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let nextScene = MenuScene()
let doors = SKTransition.doorsOpenVerticalWithDuration(0.5)
self.view!.presentScene(nextScene, transition: doors)
}
override func update(currentTime: CFTimeInterval) {
}
}
MenuScene.swift:
import SpriteKit
class MenuScene: SKScene {
override func didMoveToView(view: SKView) {
createScene()
}
func createScene() {
self.backgroundColor = SKColor.redColor()
let myLabel = SKLabelNode(fontNamed:"Chalkduster")
myLabel.name = "label"
myLabel.text = "Hello, World!"
myLabel.fontSize = 45
myLabel.position = CGPoint(x:500, y:300)
self.addChild(myLabel)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let nextScene = GameScene()
let doors = SKTransition.doorsOpenVerticalWithDuration(0.5)
self.view!.presentScene(nextScene, transition: doors)
}
override func update(currentTime: CFTimeInterval) {
}
}
The problem is when I ran the code, it displayed the blue background and the text "hello world", which is what I expected, and I touched the screen, the background changed to red, but the text disappeared, then I touched again, the background changed back to blue, but the text is still disappeared, I could not find out why?
You need to set size of the scene and scale mode. Try this code:
import SpriteKit
class MenuScene: SKScene {
override func didMoveToView(view: SKView) {
createScene()
}
func createScene() {
self.backgroundColor = SKColor.redColor()
let myLabel = SKLabelNode(fontNamed:"Chalkduster")
myLabel.name = "label"
myLabel.text = "Hello, World!"
myLabel.fontSize = 45
myLabel.position = CGPoint(x:500, y:300)
self.addChild(myLabel)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let nextScene = GameScene(size: scene!.size)
nextScene.scaleMode = .AspectFill
let doors = SKTransition.doorsOpenVerticalWithDuration(0.5)
self.view!.presentScene(nextScene, transition: doors)
}
override func update(currentTime: CFTimeInterval) {
}
}
and
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
createScene()
}
func createScene() {
self.backgroundColor = SKColor.blueColor()
let myLabel = SKLabelNode(fontNamed:"Chalkduster")
myLabel.name = "label"
myLabel.text = "Hello, World!"
myLabel.fontSize = 45
myLabel.position = CGPoint(x:500, y:300)
myLabel.zPosition = 2.0
self.addChild(myLabel)
print(myLabel.position)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let nextScene = MenuScene(size: scene!.size)
nextScene.scaleMode = .AspectFill
let doors = SKTransition.doorsOpenVerticalWithDuration(0.5)
self.view!.presentScene(nextScene, transition: doors)
}
override func update(currentTime: CFTimeInterval) {
}
}
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.
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.