Sprite-Kit Began Touch Error - swift

Im trying to create a method that when I touch a sprite named StartSprite Through my touchesbegan function it will print out something in my console. But for some reason when I click on the sprite nothing happens. This is my code.
import SpriteKit
class GameScene: SKScene {
let StartSprite = SKSpriteNode(imageNamed: "startLabel")
override func didMoveToView(view: SKView) {
let borderRect = CGRect(x: 0 , y: 0 , width: 400, height: 725)
let welcomeLabel = SKLabelNode(fontNamed: "welcome");
welcomeLabel.text = "Welcome";
welcomeLabel.fontColor = UIColor.whiteColor()
welcomeLabel.fontSize = 65
welcomeLabel.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2 + 150)
self.addChild(welcomeLabel)
//StartLabel
let rectangleBorder = SKShapeNode(rect: borderRect)
rectangleBorder.position = CGPoint(x: 315, y: 25)
rectangleBorder.strokeColor = UIColor.whiteColor()
self.addChild(rectangleBorder)
self.backgroundColor = UIColor.grayColor()
StartSprite.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
self.addChild(StartSprite)
println("Hello")
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
if touchLocation == StartSprite.position{
println("Touches")
}
}
}

Your code requires that you touch exactly the one pixel at StartSprite.position.
Try this instead:
let p = StartSprite.convertPoint(touchLocation, fromNode: self)
if StartSprite.containsPoint(p) {
print("touched StartSprite")
}
If you're going to add more buttons, you might want to do this instead:
let target = nodeAtPoint(touchLocation)
if target == StartSprite {
print("touched StartSprite"
}
// check other buttons here

Related

In swift for spritekit, how would I change the textures of nodes when they are placed?

I’m currently working on a matching game and when the user touches the nodes (Fruit match cards) I want them to display different images when a user clicks the nodes (Fruit match cards).
This is my current code:
import Foundation
import SpriteKit
class EasyScreen: SKScene {
override func didMove(to view: SKView) {
var background = SKSpriteNode(imageNamed: "Easy Screen Background")
let timerText = SKLabelNode(fontNamed: "Arial")
timerText.fontSize = 40
timerText.fontColor = SKColor.white
timerText.position = CGPoint(x: 20, y: 400)
timerText.zPosition = 1
var counter:Int = 120
timerText.run(
SKAction.repeatForever(
SKAction.sequence(
[
SKAction.run {
counter -= 1
timerText.text = " Time: \(counter)"
print("\(counter)")
if counter <= 0 {
let newScene = TryAgainScreen(fileNamed: "Try Again Screen")
newScene?.scaleMode = .aspectFill
self.view?.presentScene(newScene)
}
},
SKAction.wait(forDuration: 1)
]
)
)
)
background.position = CGPoint(x: 0, y: 0)
background.size.width = self.size.width
background.size.height = self.size.height
background.anchorPoint = CGPoint(x: 0.5,y: 0.5)
let matchCardOne = SKSpriteNode(imageNamed: "Fruit Match Card")
let matchCardTwo = SKSpriteNode(imageNamed: "Fruit Match Card")
let matchCardThree = SKSpriteNode(imageNamed: "Fruit Match Card")
let matchCardFour = SKSpriteNode(imageNamed: "Fruit Match Card")
matchCardOne.name = "FruitMatchCard1"
matchCardTwo.name = "FruitMatchCard2"
matchCardThree.name = "FruitMatchCard3"
matchCardFour.name = "FruitMatchCard4"
matchCardOne.size = CGSize(width: 150, height: 300)
matchCardTwo.size = CGSize(width: 150, height: 300)
matchCardThree.size = CGSize(width: 150, height: 300)
matchCardFour.size = CGSize(width: 150, height: 300)
matchCardOne.zPosition = 1
matchCardTwo.zPosition = 1
matchCardThree.zPosition = 1
matchCardFour.zPosition = 1
matchCardOne.anchorPoint = CGPoint(x: 0.5, y: 0.5)
matchCardTwo.anchorPoint = CGPoint(x: 0.5, y: 0.5)
matchCardThree.anchorPoint = CGPoint(x: 0.5, y: 0.5)
matchCardFour.anchorPoint = CGPoint(x: 0.5, y: 0.5)
matchCardOne.position = CGPoint(x: -125, y: 60)
matchCardTwo.position = CGPoint(x: -125, y: -260)
matchCardThree.position = CGPoint(x: 70, y: 60)
matchCardFour.position = CGPoint(x: 70 , y: -260)
addChild(background)
addChild(matchCardOne)
addChild(matchCardTwo)
addChild(matchCardThree)
addChild(matchCardFour)
addChild(timerText)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view?.isMultipleTouchEnabled = false
let touch = touches.first
let positionInScene = touch!.location(in: self)
let touchedCardOneNode = self.atPoint(positionInScene)
if let name = touchedCardOneNode.name {
if name == "FruitMatchCard1" {
let newTexture = SKTexture(imageNamed: "Apple")
FruitMatchCard1.init(texture: newTexture)
}
}
let touchTwo = touches.first
let positionInSceneTwo = touch!.location(in: self)
let touchedCardTwoNode = self.atPoint(positionInScene)
if let name = touchedCardTwoNode.name {
if name == "FruitMatchCard2" {
FruitMatchCard2.init(imageNamed: "Banana")
}
}
let touchThree = touches.first
let positionInSceneThree = touch!.location(in: self)
let touchedCardThreeNode = self.atPoint(positionInScene)
if let name = touchedCardThreeNode.name {
if name == "FruitMatchCard3" {
FruitMatchCard3.init(imageNamed: "Apple")
}
}
let touchFour = touches.first
let positionInSceneFour = touch!.location(in: self)
let touchedCardFourNode = self.atPoint(positionInScene)
if let name = touchedCardFourNode.name {
if name == "FruitMatchCard4" {
FruitMatchCard4.init(imageNamed: "Banana")
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
I’m trying to change the textures of the nodes in this part of the code. Inside the “if name == “FruitMatchCard” { } “ part of the code. However, when I launch the Xcode simulator the node’s textures aren't changing.
Any advice on how I can do this? Thanks!
Your fundamental problem is that when someone taps a card, you are creating a new card with the new texture and not doing anything with it. Take the following code you have:
FruitMatchCard2.init(imageNamed: "Banana")
This code creates a match card, but you don't do anything with the card.
To change the texture of the card, you must get the sprite node of the tapped card and change its texture. I see in your code you have the following variable:
let touchedCardTwoNode = self.atPoint(positionInScene)
This variable has the card that was touched. Set its texture to the texture you want to use.
touchedCardTwoNode.texture = SKTexture(imageNamed: "Banana")
My SpriteKit is a bit rusty so I can't guarantee that example will compile, but it gives you an idea of what you need to do to change the texture when tapping a card.

Display upperLimit SKRange

So here is my code:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var ship1 = [2,1]
var ship2 = [1,2]
let jonahSpriteNode = SKSpriteNode(imageNamed: "jonah_spaceship")
let georgeSpriteNode = SKSpriteNode(imageNamed: "george_spaceship")
override func didMove(to view: SKView) {
//var jonahFrames = [SKTexture]()
jonahSpriteNode.position = CGPoint(x: 30, y: frame.midY)
jonahSpriteNode.size = CGSize(width: 100.0, height: 100.0)
addChild(jonahSpriteNode)
georgeSpriteNode.position = CGPoint(x: 628, y: frame.midY)
georgeSpriteNode.size = CGSize(width: 100.0, height: 100.0)
addChild(georgeSpriteNode)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches{
var touchLocation = touch.location(in: self)
var angle1 = atan2(jonahSpriteNode.position.y - touchLocation.y , jonahSpriteNode.position.x - touchLocation.x)
var angle = angle1 - CGFloat(Double.pi / 1)
makeCircularRange(to: jonahSpriteNode)
let rotate = SKAction.rotate(toAngle: angle, duration: 1.0)
let move = SKAction.move(to: CGPoint(x: touchLocation.x, y: touchLocation.y), duration: 2.5)
let sequence = SKAction.sequence([rotate, move])
jonahSpriteNode.run(sequence)
}
}
func makeCircularRange(to node: SKNode) {
let range = SKRange(lowerLimit: 0, upperLimit: 400)
let constraint = SKConstraint.distance(range, to: .zero)
node.constraints = [constraint]
}
}
I wanted to display the SKRange by showing upperLimit in a certain color. I tried making nodes around the limit and then coloring the nodes but it just showed a bunch of errors. If you have any ideas please answer.
Something like this:
The sprite node will be in the center and the circle will show where it can move.
You can make a circular shape using range.upperLimit as a radius an add to scene.
func drawCircularRangeBy(range: SKRange) {
let radius = range.upperLimit
let node = SKShapeNode(circleOfRadius: radius)
node.strokeColor = .white
addChild(node)
}
Take a look of this example: https://github.com/Maetschl/SpriteKitExamples/blob/master/CircularRange/CircularRange/GameScene.swift
If you really need dots instead of a line please see this answer: Drawing dashed line in Sprite Kit using SKShapeNode

Missing Type UIEvent?

I'm using XCode 10.2 with SpriteKit and swift 5 to create a Mac Application, and I cannot for the life of me find a working example of handling click events on a node.
The most updated info I can find suggests using the following:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { }
But XCode errors, saying
Use of undeclared type 'UIEvent'
Use of undeclared type 'UITouch'
I'm not sure how to proceed cause all the info I find suggests using the above code. Everything else is working OK so far though. Here is my GameScene Class:
import SpriteKit
class GameScene: SKScene {
var circle: SKShapeNode = SKShapeNode();
var label: SKLabelNode = SKLabelNode();
var ground: SKShapeNode = SKShapeNode();
/**
* Did Move Callback
*/
override func didMove(to view: SKView) {
initGround(view: view);
initLabel(view: view);
let recognizer = NSClickGestureRecognizer(target: self, action: #selector(tap));
view.addGestureRecognizer(recognizer);
}
/**
* Click Handler
*/
#objc func tap(recognizer: NSGestureRecognizer) {
let viewLocation = recognizer.location(in: view);
let sceneLocation = convertPoint(fromView: viewLocation);
addCircle(view: view!, position: sceneLocation);
}
// Not Working
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
/**
* Init Ground
*/
func initGround(view: SKView) {
let path = CGMutablePath();
path.move(to: CGPoint(x: 0, y: 30));
path.addLine(to: CGPoint(x: view.frame.width, y: 30));
path.addLine(to: CGPoint(x: view.frame.width, y: view.frame.height));
path.addLine(to: CGPoint(x: 0, y: view.frame.height));
path.addLine(to: CGPoint(x: 0, y: 30));
ground.path = path;
ground.lineWidth = 5;
ground.strokeColor = SKColor.brown;
let borderBody = SKPhysicsBody(edgeLoopFrom: path);
borderBody.friction = 5;
self.physicsBody = borderBody;
addChild(ground);
}
/**
* Add Label
*/
func initLabel(view: SKView) {
label = SKLabelNode(text: "No Gravity");
label.name = "no gravity button";
label.position = CGPoint(x: label.frame.width / 2, y: 5);
label.fontSize = 12;
label.fontColor = SKColor.yellow;
label.fontName = "Avenir";
label.isUserInteractionEnabled = true;
addChild(label);
}
/**
* Add Circle
*/
func addCircle(view: SKView, position: CGPoint) {
let size = Int.random(in: 1 ..< 50);
let color = getRandomColor();
circle = SKShapeNode(circleOfRadius: CGFloat(size));
// Set Position
circle.position = position;
// Set Physics
circle.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(size));
circle.physicsBody?.affectedByGravity = false;
circle.physicsBody?.usesPreciseCollisionDetection = true;
circle.physicsBody?.restitution = 1; //0.8
circle.physicsBody?.linearDamping = 0;
circle.physicsBody?.friction = 0; //0.2
circle.physicsBody?.isDynamic = true;
circle.physicsBody?.mass = CGFloat(size);
circle.physicsBody?.allowsRotation = true;
circle.physicsBody?.velocity = CGVector(dx: 0, dy: -1000);
circle.strokeColor = color;
circle.glowWidth = 1.0;
circle.fillColor = color;
addChild(circle)
}
func getRandomColor() -> SKColor {
return SKColor(red: .random(in: 0...1),
green: .random(in: 0...1),
blue: .random(in: 0...1),
alpha: 1.0)
}
}
Does anyone have experience with using Swift 5 and handling clicks / touches in SpriteKit? Or maybe just why XCode says that UIEvent and UITouch aren't real things?
Thanks!
On Mac, you’ll implement
-(void)mouseDown:(NSEvent *)theEvent {
}
- (void)mouseUp:(NSEvent *)theEvent {
}
- (void)mouseExited:(NSEvent *)theEvent {
}
In each case you will call
CGPoint loc = [theEvent locationInNode:self];
to learn where the click happened within the scene.
User Martin R pointed out the UIEvent and UITouch classes are included only in iOS projects. I'm creating a Mac Application, what I didn't realize was that I already had the click handler in my GameScene class:
override func didMove(to view: SKView) {
let recognizer = NSClickGestureRecognizer(target: self, action: #selector(click));
view.addGestureRecognizer(recognizer);
}
This registers a callback to a click handler, which I already had defined:
/**
* Click Handler
*/
#objc func tap(recognizer: NSGestureRecognizer) {
let viewLocation = recognizer.location(in: view);
let sceneLocation = convertPoint(fromView: viewLocation);
let touchedNode = self.atPoint(sceneLocation);
if let name = touchedNode.name {
if name == "no gravity button" {
print("Touched")
}
}
addCircle(view: view!, position: sceneLocation);
}
I was able to get the clicked node name and act upon it as I originally intended.

Swift Sprite Kit Began Touch Function

Im messing around with an xcode project and I am trying to make a red ball SKShapeNode move around when I touch it. My own source code in the touch began class is not doing the trick. What is the flaw with my code and what do I need to do in order to fix it.
import SpriteKit
class GameScene: SKScene {
//Rectangle Skeletions
var leftWallRect = CGRect(x: 0, y: 0, width: 40, height: 1000)
var ball = SKShapeNode(circleOfRadius: 25);
override func didMoveToView(view: SKView) {
// Declare the Sprites in your scene
var ball = SKShapeNode(circleOfRadius: 25);
let leftWall = SKShapeNode(rect: leftWallRect);
let rightWall = SKShapeNode(rect: leftWallRect);
let pin = SKSpriteNode(imageNamed: "pin");
// Ball Properties
ball.strokeColor = UIColor.blackColor()
ball.fillColor = UIColor.redColor()
ball.position = CGPointMake(self.frame.width / 2 , self.frame.height / 4 - 100 )
self.addChild(ball)
// Left Wall Properties
leftWall.fillColor = UIColor.purpleColor()
leftWall.strokeColor = UIColor.blackColor()
leftWall.position = CGPointMake(self.frame.size.width / 3 - 45, self.frame.size.height / 2 - 400)
self.addChild(leftWall)
//Right Wall Properties
rightWall.fillColor = UIColor.purpleColor()
rightWall.strokeColor = UIColor.blackColor()
rightWall.position = CGPointMake(self.frame.size.width / 2 + 175, self.frame.size.height / 2 - 400)
self.addChild(rightWall)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
if touchLocation == ball.position{
ball.position = touchLocation
println("Touches");
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
LOL wow, this is a funny one, if touchLocation == ball.position I would be impressed if you can hit the exact pixel location of your ball. What you want is this:
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(touchLocation)
if touchedNode == ball{
...
But personally, what I would do is make a sub class of SKSpriteNode, and do your ball's touch code inside this child's touch routine

TextField does not work

I have a text field to type the word that, when (auto) and press the button (checkbutton) it should print out (correct). The problem is that it's not working.
When I'm in office touchesBegan: if (TextField.text == "auto") lists me an error:
Use Unresolved identifier "TextField".
When this condition removed as everything works fine and writes to me (correct).
I tried complet function touchesBegan moved to override func didMoveToView (view: SKView) and TextField.text is resolved but it is not working.
I work in SpriteKit
Where I make mistakes ?.
Thank you
Here is my code:
class level1: SKScene{
let logo = SKSpriteNode(imageNamed: "Q1")
let check = SKSpriteNode(imageNamed: "check")
let number = SKSpriteNode(imageNamed: "N1")
override func didMoveToView(view: SKView) {
scene!.scaleMode = .AspectFill
scene!.scaleMode = SKSceneScaleMode.ResizeFill
var background = SKSpriteNode(imageNamed: "Background")
background.size = CGSizeMake(self.size.width, self.size.height)
background.position = CGPointMake(self.size.width/2, self.size.height/2)
self.addChild(background)
let screenSize: CGRect = UIScreen.mainScreen().bounds
let screenWidth = screenSize.width;
let screenHeight = screenSize.height;
var TextField = UITextField(frame : CGRect(x:10, y:(screenHeight/2.25), width:(screenWidth - 20), height:(screenHeight/15) ))
self.view!.addSubview(TextField)
TextField.backgroundColor = UIColor.whiteColor()
TextField.textAlignment = .Center
TextField.font = UIFont(name: "Helvetica Neue", size: 23)
number.position = CGPoint(x: size.width * 0.5, y: size.height * 0.95)
addChild(number)
logo.position = CGPoint(x: size.width * 0.5, y: size.height * 0.72)
addChild(logo)
check.position = CGPointMake(size.width/2,size.height/2 - 43)
check.name = "checkbutton"
addChild(check)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
let touch = touches.first as! UITouch
let touchLocation = touch.locationInNode(self)
let touchedNode = self.nodeAtPoint(touchLocation)
if(touchedNode.name == "checkbutton"){
if(TextField.text == "auto"){
println("CORRECT")
}
} else {
}
}
}
Your var TextField is local to the method didMoveToView so cannot be accessed from inside touchesBegan. Move this var outside of didMoveToView into the class level.
Also, by convention vars should start with a lower case letter: textField rather than TextField.