So I've got a class named FlashyPaddleEffect. I also mention it in another class, GameScene, but it doesn't apply any effect that it should. The paddle should be flashing blue and white colors, but it simply stays white. The paddle also should lose its physics body when it is blue.
If you need any other information, don't hesitate to ask.
NOTE: There might be problems with the code I gave (because of indents, it's quite tricky to make code by indenting 4 spaces every line, sorry).
import SpriteKit
import GameplayKit
class FlashyPaddleEffect {
var node = SKSpriteNode()
var ballNode = SKSpriteNode()
var updateTimer: Timer? = nil
var timer: Timer? = nil
#objc func changeNodeColor() {
switch node.color {
case SKColor.blue: node.color = SKColor.white
case SKColor.white: node.color = SKColor.blue
default: _ = 1 + 2
}
}
#objc func update() //I used the objc prefix to silence the warning the selectors of the timers produced. {
let previousPhysicsBody = node.physicsBody
if node.color == SKColor.blue {
node.physicsBody = nil
}
node.physicsBody = previousPhysicsBody
}
func make(applyEffectTo: SKSpriteNode, ball: SKSpriteNode) {
node = applyEffectTo
ballNode = ball
timer = Timer.scheduledTimer(timeInterval: 0.6, target: self, selector: #selector(changeNodeColor), userInfo: nil, repeats: true)
updateTimer = Timer.scheduledTimer(timeInterval: 0.3, target: self, selector: #selector(update), userInfo: nil, repeats: true)
_ = node
_ = timer
}
}
class GameScene: SKScene {
var ball = SKSpriteNode()
var player = SKSpriteNode()
var enemy = SKSpriteNode()
var scores = [0, 0]
var initScores = [0, 0]
var scoreLabels: [SKLabelNode]? = nil
let playLabel = SKLabelNode()
let timeLabel = SKLabelNode()
let timeUpLabel = SKLabelNode()
var secondsLeft: Int = 180
var initialTime: Int? = nil
var timer = Timer()
var amountOfPauseMenuCloses = 0
var resultsLabel = SKLabelNode()
let flashEffect = FlashyPaddleEffect() //I define a variable to mention the class easier later on.
override func didMove(to view: SKView) {
//Initialize nodes of the scene editor.
ball = self.childNode(withName: "Ball") as! SKSpriteNode
player = self.childNode(withName: "PPaddle") as! SKSpriteNode
enemy = self.childNode(withName: "EPaddle") as! SKSpriteNode
//Set styles for the Play Notification Label (referred to as PNL later)
playLabel.position = CGPoint(x: 0, y: 0)
playLabel.text = "Tap anywhere to play."
playLabel.name = "playLabel"
//Set styles for the Timer Label (referred to as Timer later)
timeLabel.position = CGPoint(x: 90, y: 0)
timeLabel.text = String(secondsLeft)
//Doing manipulations connected with scores here.
scores = [0, 0]
initScores = scores
scoreLabels = [self.childNode(withName: "PScoreLabel") as! SKLabelNode, self.childNode(withName: "EScoreLabel") as! SKLabelNode]
//Create a border for our ball to bounce off.
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.friction = 0
border.restitution = 1
border.linearDamping = 0
border.angularDamping = 0
self.physicsBody = border
//To avoid the ball's damping.
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.angularDamping = 0
showPause() //Show the (pause) menu at the beginning
//Set a variable to refer to as a time standard later.
initialTime = secondsLeft
//The game timer.
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(reduceSecondFromTimer), userInfo: nil, repeats: true)
flashEffect.make(applyEffectTo: player, ball: ball) //This is where I reference the class in another class.
}
//A function to show the (pause) menu.
func showPause(hideNodes: Bool = true) {
self.addChild(playLabel)
if hideNodes == true {
ball.removeFromParent()
player.removeFromParent()
enemy.removeFromParent()
scoreLabels?[0].removeFromParent()
scoreLabels?[1].removeFromParent()
}
}
override func update(_ currentTime: TimeInterval) {
var alreadyChangedTextLabel = false
for i in scoreLabels! {
if i.name == "PScoreLabel" {i.text = String(scores[0])}
if i.name == "EScoreLabel" {i.text = String(scores[1])}
}
if ball.position.y <= player.position.y {scores[1] += 1}
if ball.position.y >= enemy.position.y {scores[0] += 1}
if secondsLeft == 0 {
showPause(hideNodes: false)
if alreadyChangedTextLabel == false {
timeUpLabel.text = "TIME UP! \(whoIsWinning(scores: scores)) won!"
alreadyChangedTextLabel = true
}
timeUpLabel.name = "timeUpLabel"
timeUpLabel.position = CGPoint(x: 0, y: 180)
if !(self.children.contains(timeUpLabel)) {
self.addChild(timeUpLabel)
}
timeLabel.removeFromParent()
}
if self.children.contains(playLabel) {
secondsLeft = initialTime!
}
timeLabel.text = String(secondsLeft)
let alignWithBall = SKAction.move(to: CGPoint(x: ball.position.x, y: enemy.position.y), duration: 0.8)
enemy.run(alignWithBall)
}
func initGame() {
//Initializing process for every game.
scores = initScores
ball.position = CGPoint(x: 0, y: 0)
if amountOfPauseMenuCloses == 1 {ball.physicsBody?.applyImpulse(CGVector(dx: 15, dy: 15))}
secondsLeft = initialTime!
timeLabel.text = String(secondsLeft)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for childNode in self.children {
if childNode.name == "playLabel" {
if self.children.contains(playLabel) {
playLabel.removeFromParent()
}
if !(self.children.contains(player)) {
self.addChild(player)
}
if !(self.children.contains(enemy)) {
self.addChild(enemy)
}
if !(self.children.contains(ball)) {
self.addChild(ball)
}
if !(self.children.contains((scoreLabels?[0])!)) {
self.addChild((scoreLabels?[0])!)
}
if !(self.children.contains((scoreLabels?[1])!)) {
self.addChild((scoreLabels?[1])!)
}
if !(self.children.contains(timeLabel)) {
self.addChild(timeLabel)
}
if self.children.contains(timeUpLabel) {
timeUpLabel.removeFromParent()
}
amountOfPauseMenuCloses += 1
initGame()
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
if self.nodes(at: t.location(in: self)).contains(player) {player.position.x = t.location(in: self).x}
}
}
func reduceSecondFromTimer() {
secondsLeft -= 1
}
func whoIsWinning(scores: Array<Int>) -> String {
var r: String? = nil
if scores[0] >= scores[1] {
r = "You"
}
if scores[1] >= scores[0] {
r = "Enemy"
}
return r!
}
}
Thanks a lot for answers.
P.S It's my first question ever so don't judge me strictly.
1) Do not use NSTimer use SKAction. I can see the way you are doing it you stack timer after timer, this is bad.
2) Do not have your temp variable global (node in this case), it makes code hard to read
3) Do not remove your physics body, simply remove the category.
func make(applyEffectTo: SKSpriteNode, ball: SKSpriteNode) {
ballNode = ball
let blue = SKAction.colorize(with SKColor.blue, colorBlendFactor: 1.0, duration sec: 0)
let white = SKAction.colorize(with SKColor.white, colorBlendFactor: 1.0, duration sec: 0)
let wait = SKAction.wait(for:0.6)
let turnOnPhysics = SKAction.run({applyEffectTo.physicsBody?.categoryBitmask = #######})
let turnOffPhysics = SKAction.run({applyEffectTo.physicsBody?.categoryBitmask = 0})
let seq = [blue, turnOffPhysics,wait,white,turnOnPhysics,wait]
let repeat = SKAction.repeatForever(seq)
applyEffectTo.run(repeat, withKey:"flashing")
}
Note: I have no idea what your categoryBitmask is, you need to fill it in
Related
I'm trying to have a contact to be detected by SpriteKit in Swift 4. I set a flipper to a certain category and a ball to another one. Then I set their collision and contact mask to each other, I add a contactDelegate in didMove to, and print "contact" to inform that there is a contact in a didEnterFonction. However I cannot detect any contact at all. Even after following all the tutorials, and scouring through similar questions. I do not understand what I am doing wrong. My end goal is to have the ball stop from falling when it hits the flipper, but I am trying to detect any contact at all in the first place which do not work.
import SpriteKit
import GameplayKit
struct PhysicsCategory {
static let none : UInt32 = 0
static let all : UInt32 = UInt32.max
static let flip : UInt32 = 0b1
static let ball: UInt32 = 0b10 // 2
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var entities = [GKEntity]()
var graphs = [String : GKGraph]()
private var lastUpdateTime : TimeInterval = 0
private var label : SKLabelNode?
private var flip1: SKSpriteNode?
private var flip2: SKSpriteNode?
private var spinnyNode : SKShapeNode?
private var balle:SKSpriteNode?
override func sceneDidLoad() {
self.lastUpdateTime = 0
self.flip1 = self.childNode(withName:"//flip1") as? SKSpriteNode
self.flip2 = self.childNode(withName: "//flip2") as? SKSpriteNode
self.balle = self.childNode(withName: "//ball") as?SKSpriteNode
self.flip1?.physicsBody = SKPhysicsBody()
self.flip1?.physicsBody?.affectedByGravity = false
self.flip1?.physicsBody?.allowsRotation = true
self.balle?.physicsBody = SKPhysicsBody();
//self.balle?.physicsBody?.affectedByGravity = true
self.flip2?.physicsBody = SKPhysicsBody()
self.flip2?.physicsBody?.affectedByGravity = false
self.flip2?.physicsBody?.allowsRotation = true
self.balle?.physicsBody?.allContactedBodies()
self.flip1?.physicsBody?.usesPreciseCollisionDetection
= true
self.flip1?.physicsBody?.categoryBitMask = PhysicsCategory.flip
self.flip1?.physicsBody?.contactTestBitMask = PhysicsCategory.ball
self.flip1?.physicsBody?.collisionBitMask = PhysicsCategory.ball
self.flip2?.physicsBody?.categoryBitMask = PhysicsCategory.flip
self.flip2?.physicsBody?.contactTestBitMask = PhysicsCategory.ball
self.flip2?.physicsBody?.collisionBitMask = PhysicsCategory.ball
// Get label node from scene and store it for use later
self.label = self.childNode(withName: "//helloLabel") as? SKLabelNode
if let label = self.label {
label.alpha = 0.0
label.run(SKAction.fadeIn(withDuration: 2.0))
}
// Create shape node to use during mouse interaction
let w = (self.size.width + self.size.height) * 0.05
self.spinnyNode = SKShapeNode.init(rectOf: CGSize.init(width: w, height: w), cornerRadius: w * 0.3)
if let spinnyNode = self.spinnyNode {
spinnyNode.lineWidth = 2.5
spinnyNode.run(SKAction.repeatForever(SKAction.rotate(byAngle: CGFloat(Double.pi), duration: 1)))
spinnyNode.run(SKAction.sequence([SKAction.wait(forDuration: 0.5),
SKAction.fadeOut(withDuration: 0.5),
SKAction.removeFromParent()]))
}
}
override func keyDown(with event: NSEvent) {
switch event.keyCode {
case 0x31:
if let label = self.label {
label.run(SKAction.init(named: "Pulse")!, withKey: "fadeInOut")
}
case 123:
print("left")
self.flip1?.run(SKAction.rotate(byAngle: -1.5, duration: 0.2))
// self.flip1?.physicsBody?.applyAngularImpulse(600)
self.flip1?.run(SKAction.rotate(byAngle: 1.5, duration: 0.1))
case 124:
self.flip2?.run(SKAction.rotate(byAngle: 1.5, duration: 0.2))
self.flip2?.run(SKAction.rotate(byAngle: -1.5, duration: 0.1))
case 15:
self.balle?.run(SKAction.move(to: CGPoint(x: 50,y: 50), duration: 1))
default:
print("keyDown: \(event.characters!) keyCode: \(event.keyCode)")
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
// Initialize _lastUpdateTime if it has not already been
if (self.lastUpdateTime == 0) {
self.lastUpdateTime = currentTime
}
// Calculate time since last update
let dt = currentTime - self.lastUpdateTime
// Update entities
for entity in self.entities {
entity.update(deltaTime: dt)
}
self.lastUpdateTime = currentTime
}
override func didMove(to view: SKView) {
physicsWorld.contactDelegate = self
}
func didEnter(_ contact: SKPhysicsContact) {
print("contact")
}
}
You haven't given the physic bodies a shape or size. Take a look here and read the 'Creating a Body from a ___' sections.
I am in developing an application using Xcode and currently I am facing a issue regarding SKSpriteNodes. When there are more than a single SKSpriteNode and when the node is touched, the touched node is not removed but the other not touched is removed. I have also noticed that when there are multiple nodes on the screen only the latest node coming from the top of the screen is removed whilst others are still moving down, although they are being touched. Can someone help identify why this has occurred and the methods of preventing such mistakes please?
For reference, I am have included the class in which the bug is in.
import SpriteKit
import GameplayKit
class GameScene: SKScene {
private var gameCounter: Int = 0
private var currentLevel: Int = 0
private var debug: SKLabelNode?
private var increasedTouchArea:SKSpriteNode?
private var generatedNode:SKSpriteNode?
private var moveAndRemove:SKAction?
private var moveNode:SKAction?
// Here we set initial values of counter and level. Debug label is created here as well.
override func didMove(to view: SKView) {
print(action(forKey: "counting") == nil)
gameCounter = 0
currentLevel = 1
//backgroundColor = SKColor.gray
debug = SKLabelNode(fontNamed: "ArialMT")
debug?.fontColor = SKColor.red
debug?.fontSize = 30.0
debug?.position = CGPoint(x: frame.midX, y: frame.midY)
debug?.text = "Counter : [ \(gameCounter) ], Level [ \(currentLevel) ]"
if let aDebug = debug {
addChild(aDebug)
}
}
//Method to start a timer. SKAction is used here to track a time passed and to maintain current level
func startTimer() {
print("Timer Started...")
weak var weakSelf: GameScene? = self
//make a weak reference to scene to avoid retain cycle
let block = SKAction.run({
weakSelf?.gameCounter = (weakSelf?.gameCounter ?? 0) + 1
//Maintaining level
if (weakSelf?.gameCounter ?? 0) < 5 {
//level 1
weakSelf?.currentLevel = 1
} else if (weakSelf?.gameCounter ?? 0) >= 5 && (weakSelf?.gameCounter ?? 0) < 10 {
//level 2
weakSelf?.currentLevel = 2
} else {
//level 3
weakSelf?.currentLevel = 3
}
weakSelf?.debug?.text = "Counter : [ \(Int(weakSelf?.gameCounter ?? 0)) ], Level [ \(Int(weakSelf?.currentLevel ?? 0)) ]"
})
run(SKAction.repeatForever(SKAction.sequence([SKAction.wait(forDuration: 1), block])), withKey: "counting")
}
//Method for stopping timer and reseting everything to default state.
func stopTimer() {
print("Timer Stopped...")
if action(forKey: "counting") != nil {
removeAction(forKey: "counting")
}
gameCounter = Int(0.0)
currentLevel = 1
debug?.text = "Counter : [ \(gameCounter) ], Level [ \(currentLevel) ]"
}
//Get current speed based on time passed (based on counter variable)
func getCurrentSpeed() -> CGFloat {
if gameCounter < 30 {
//level 1
return 1.0
} else if gameCounter >= 31 && gameCounter < 49 {
//level 2
return 2.0
} else {
//level 3
return 3.0
}
}
//Method which stop generating stones, called in touchesBegan
func stopGeneratingStones() {
print("STOPPED GENERATING STONES...")
if action(forKey: "spawning") != nil {
removeAction(forKey: "spawning")
}
}
func randomFloatBetween(_ smallNumber: CGFloat, and bigNumber: CGFloat) -> CGFloat {
let diff: CGFloat = bigNumber - smallNumber
return CGFloat(arc4random() % (UInt32(RAND_MAX) + 1)) / CGFloat(RAND_MAX) * diff + smallNumber
}
//Method for generating stones, you run this method when you want to start spawning nodes (eg. didMoveToView or when some button is clicked)
func generateStones() {
print("Generating Stones...")
let delay = SKAction.wait(forDuration: 1, withRange: 0.5) //Change forDuration: delay decreases as game progresses.
//randomizing delay time
weak var weakSelf: GameScene? = self
//make a weak reference to scene to avoid retain cycle
let block = SKAction.run({
let stone: SKSpriteNode? = weakSelf?.spawnNodes(withSpeed: weakSelf?.getCurrentSpeed() ?? 0.0)
stone?.zPosition = 20
if let aStone = stone {
weakSelf?.addChild(aStone)
}
})
run(SKAction.repeatForever(SKAction.sequence([delay, block])), withKey: "spawning")
}
func spawnNodes(withSpeed stoneSpeed: CGFloat) -> SKSpriteNode? {
let nodeSize = CGSize(width: 60, height: 60) //size of shape.
let initalNodePosition = CGPoint(x: randomFloatBetween(0.0, and: (self.view?.bounds.size.width)!) - 110, y: frame.maxY)
generatedNode = SKSpriteNode(color: SKColor.green, size: nodeSize)
generatedNode?.position = initalNodePosition
moveNode = SKAction.moveBy(x: 0, y: self.view!.scene!.frame.minY + self.view!.scene!.frame.minY , duration: 5.25)
generatedNode?.isUserInteractionEnabled = false //Allows users to touch shape.
increasedTouchArea = SKSpriteNode(color: UIColor.clear, size: CGSize(width: nodeSize.width * 1.35, height: nodeSize.height * 1.35))
increasedTouchArea?.name = "generatedNode"
increasedTouchArea?.isUserInteractionEnabled = false
generatedNode?.addChild(increasedTouchArea!)
moveNode?.speed = stoneSpeed
moveAndRemove = SKAction.sequence([moveNode!, SKAction.removeFromParent()])
generatedNode?.run(moveAndRemove!, withKey: "moving")
generatedNode?.name = "generatedNode"
return generatedNode
}
func deleteNode(){
moveAndRemove = SKAction.sequence([SKAction.removeFromParent()])
generatedNode?.run(moveAndRemove!, withKey: "moving")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent? {
for touch in touches {
let locationTocuhed = touch.location(in: self)
let touchedNode : SKNode = self.atPoint(locationTocuhed)
if touchedNode.name == generatedNode?.name {
print("Node touched")
deleteNode()
}
}
if action(forKey: "counting") == nil {
print("Game started...")
startTimer()
generateStones()
} else {
print("Game paused...")
}
}
}
Your method deleteNode() runs the deletion animation on the node pointed to by generatedNode, not the node last touched:
func deleteNode(){
moveAndRemove = SKAction.sequence([SKAction.removeFromParent()])
generatedNode?.run(moveAndRemove!, withKey: "moving")
}
If you want to delete the node last touched, pass a reference to this node to the method deleteNode and then run your deletion animation in that, not generatedNode.
Keep in mind this is in Sprite Kit. Basically I want a way to have my in-game upgrades on a timer that allows the user to get the upgrade after the timer has finished. The user would touch the button once to start the timer, and once it gets to zero, it stops itself. I can't get it to stop, however.
import Foundation
import SpriteKit
var timer1Cycle = 0
var timer1Time = 10000
var timer1 = Timer()
var timer1IsOn = false
class SkillPointsScene: SKScene{
func updateTimer1() {
timer1Time -= 1
timer1Label.text = " \(timer1Time) :Kepler-22b units"
}
func startTimer1(){
if timer1IsOn == false {
timer1 = Timer.scheduledTimer(timeInterval: 0.0004, target: self, selector: #selector(SkillPointsScene.updateTimer1), userInfo: nil, repeats: true)
timer1IsOn = true
}
}
func stopTimer1() {
timer1.invalidate()
timer1Time = 0
timer1Label.text = "Research Complete"
timer1IsOn = false
}
Here is the image button to start counting down from 10000:
let oneLifeImage = SKSpriteNode(imageNamed: "gun1")
let timer1Label = SKLabelNode(fontNamed: "theBoldFontt")
// runs as soon as the screen loads every time
override func didMove(to view: SKView) {
if timer1Time <= 0 {
self.stopTimer1()
}
let background = SKSpriteNode(imageNamed: "background")
background.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
background.zPosition = 0
self.addChild(background)
timer1Label.text = " "
timer1Label.fontSize = 35
timer1Label.fontColor = SKColor.white
timer1Label.position = CGPoint(x: self.size.width*0.5, y: self.size.height*0.82)
timer1Label.zPosition = 100
self.addChild(timer1Label)
oneLifeImage.setScale(1)
oneLifeImage.position = CGPoint(x: self.size.width/2, y: self.size.height*0.75)
oneLifeImage.zPosition = 2
self.addChild(oneLifeImage)
And here is where the button is touched:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches{
let pointOfTouch = touch.location(in: self)
let wait = SKAction.wait(forDuration: 1.5)
if oneLifeImage.contains(pointOfTouch){
if timer1Cycle == 0 {
timer1Time = 10000
}
if timer1Cycle == 1 {
timer1Time = 25000
}
if timer1IsOn == false {
startTimer1()
timer1Cycle -= 1
}
I get it to start the count down, but it never stops on its own. I don't know what to do now; I can't find any information on this because it is in Swift 3 with Sprite Kit, not the normal single view that everybody seems to know how to use.
I have SpritNodes that are in my scene and I want a method to be called when I touch it. I have isUserInteractionEnabled set to true for my node, but touchesBegan still does not get called when I touch the nodes. (Note: I am using Swift 3.0)
Code:
import SpriteKit
class MainScene: SKScene, SKPhysicsContactDelegate {
var didStart = false
var background = SKSpriteNode(imageNamed: "background")
var backDrop = SKShapeNode()
var emailNodes: [SKSpriteNode] = []
let emailCatagory: UInt32 = 0x1 << 0
let dropCatagory: UInt32 = 0x1 << 1
override func sceneDidLoad() {
startCountDown()
}
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
background.position = CGPoint(x: frame.midX, y:frame.midY)
}
public func startCountDown(){
var times = 4
let countdownTimer = SKLabelNode()
countdownTimer.text = "3"
countdownTimer.position = CGPoint(x: frame.midX, y: frame.midY)
countdownTimer.fontSize = 120.0
countdownTimer.fontName = "Lao MN"
countdownTimer.fontColor = UIColor.black()
backDrop = SKShapeNode()
backDrop = SKShapeNode(rectOf: CGSize(width: frame.width, height: 100))
backDrop.position = CGPoint(x: frame.midX, y: 10)
backDrop.physicsBody = SKPhysicsBody(rectangleOf: backDrop.frame.size)
//backDrop.size = CGSize(width: 1000, height: 2)
backDrop.physicsBody?.affectedByGravity = false
backDrop.physicsBody?.usesPreciseCollisionDetection = true
backDrop.name = "backDrop"
backDrop.physicsBody?.collisionBitMask = 0
backDrop.physicsBody?.categoryBitMask = dropCatagory
addChild(countdownTimer)
addChild(backDrop)
//addChild(background)
Timer.every(1.2.seconds) { (timer: Timer) in
if(times<=0){
timer.invalidate()
countdownTimer.removeFromParent()
self.didStart = true
self.startDropping()
}else{
print("\(times)")
times = times - 1
countdownTimer.text = "\(times)"
}
}
}
func startDropping(){
Timer.every(1.2.seconds) { (timer: Timer) in
let which = Int(arc4random_uniform(2) + 1)
let ee = self.getEmailNode(type: which)
self.addChild(ee)
ee.physicsBody?.applyImpulse(CGVector(dx: 0.0, dy: -5.0))
}
}
func getEmailNode(type: Int) -> SKSpriteNode{
var email = SKSpriteNode()
if(type == 1){
email = SKSpriteNode(imageNamed: "normal_email")
email.name = "normal_email"
}
if(type == 2){
email = SKSpriteNode(imageNamed: "classified_email")
email.name = "classified_email"
}
email.setScale(3)
email.position = CGPoint(x: getRandomColumn(), y: frame.height)
email.physicsBody = SKPhysicsBody(rectangleOf: email.frame.size)
email.physicsBody?.usesPreciseCollisionDetection = true
email.physicsBody?.categoryBitMask = emailCatagory
email.isUserInteractionEnabled = true
email.physicsBody?.affectedByGravity = false
email.physicsBody?.collisionBitMask = 0
email.physicsBody?.contactTestBitMask = emailCatagory | dropCatagory
emailNodes.append(email)
return email
}
func getRandomColumn() -> CGFloat{
let which = Int(arc4random_uniform(3) + 1)
let gg = frame.size.width/3
switch(which){
case 1:
return gg / 2
case 2:
return frame.midX
case 3:
return (gg * 3) - gg / 2
default:
return (gg * 3) + gg / 2
}
}
func didBegin(_ contact: SKPhysicsContact) {
if (contact.bodyA.categoryBitMask == dropCatagory) &&
(contact.bodyB.categoryBitMask == emailCatagory) {
let node = contact.bodyB.node as! SKSpriteNode
node.removeFromParent()
while emailNodes.contains(node) {
if let itemToRemoveIndex = emailNodes.index(of: node) {
emailNodes.remove(at: itemToRemoveIndex)
}
}
}
}
func doesContainNode(sk: SKSpriteNode) -> Bool {
for it in emailNodes{
if(it == sk){
return true
}
}
return false
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
enumerateChildNodes(withName: "//*", using:
{ (node, stop) -> Void in
print("\(node.name)")
print("\(node)")
if((node.name?.contains("email")) != nil){
print("Touched!")
}
})
}
}
You should try the following in order to be able to get the user's position and know when the sprite has been touched. Add the following inside the touchesEnabled function.
for touch in touches {
let userTouch = touch.locationInNode(self)
}
And then check if the sprite was touched by using:
node.containsPoint(userTouch)
See if that works. The way you have your code setup, you might need to nest the above function right after checking if it's nil. As for the userInteractionEnabled, I don't use it at all when using the above code.
You do not want userInteractionEnabled set on any of your nodes, you want that only on the scene. Use userInteractionEnabled only when you are subclassing your node, this way you can use touchesBegan inside your subclassed file. What is happening is your touch is going into your node and being absorbed, which does nothing, and is being ignored by the scene since the node absorbed it.
Edit: Sorry #MarkBrownsword I did not see your comment, if you post it as an answer, I will upvote and delete my answer.
I found a solution. I removed isUserInteractionEnabled and touchesBegan was still not being called. So I went through each of the properties of the "email" node and for some reason the following properties made it where touchesBegan would not be called.
email.physicsBody?.affectedByGravity = false
email.physicsBody?.collisionBitMask = 0
So I removed those and now touchesBegan is being properly called.
I'm creating a game that involves a timer that spawns objects every second that fall down the screen. To gain points you must catch the objects.
I want the rate of spawning to increase once the player reaches a certain amount of points.
I have tried to do this by assigning the interval of the timer an integer (SpeedNumber) value of 1 second to start with and have created and if statement that is supposed to change that integer to 0.5 once the player reaches a certain amount of points. This makes sense to me but it is not working.
Why is this not working and what should I change?
import SpriteKit
struct physicsCatagory {
static let person : UInt32 = 0x1 << 1
static let Ice : UInt32 = 0x1 << 2
static let IceTwo : UInt32 = 0x1 << 3
static let IceThree : UInt32 = 0x1 << 4
static let Score : UInt32 = 0x1 << 5
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var scorenumber = Int()
var lifenumber = Int()
var SpeedNumber : Double = 0.5
var person = SKSpriteNode(imageNamed: "Person")
let Score = SKSpriteNode()
var ScoreLable = SKLabelNode()
override func didMoveToView(view: SKView) {
self.scene?.backgroundColor = UIColor.purpleColor()
physicsWorld.contactDelegate = self
self.scene?.size = CGSize(width: 640, height: 1136)
lifenumber = 0
SpeedNumber = 1
Score.size = CGSize(width: 648, height: 1)
Score.position = CGPoint(x: 320, y: -90)
Score.physicsBody = SKPhysicsBody(rectangleOfSize: Score.size)
Score.physicsBody?.affectedByGravity = false
Score.physicsBody?.dynamic = false
Score.physicsBody?.categoryBitMask = physicsCatagory.Score
Score.physicsBody?.collisionBitMask = 0
Score.physicsBody?.contactTestBitMask = physicsCatagory.IceThree
Score.color = SKColor.blueColor()
self.addChild(Score)
person.position = CGPointMake(self.size.width/2, self.size.height/12)
person.setScale(0.4)
person.physicsBody = SKPhysicsBody(rectangleOfSize: person.size)
person.physicsBody?.affectedByGravity = false
person.physicsBody?.categoryBitMask = physicsCatagory.person
person.physicsBody?.contactTestBitMask = physicsCatagory.Ice
person.physicsBody?.collisionBitMask = physicsCatagory.Ice
person.physicsBody?.dynamic = false
ScoreLable.position = CGPoint(x: self.frame.width / 2, y: 1000)
ScoreLable.text = "\(scorenumber)"
ScoreLable.fontColor = UIColor.yellowColor()
ScoreLable.fontSize = 100
ScoreLable.fontName = "Zapfino "
self.addChild(ScoreLable)
var IceThreeTimer = NSTimer.scheduledTimerWithTimeInterval(SpeedNumber, target: self, selector: ("spawnThirdIce"), userInfo: nil, repeats: true)
self.addChild(person)
}
func didBeginContact(contact: SKPhysicsContact) {
let firstBody = contact.bodyA
let secondBody = contact.bodyB
if firstBody.categoryBitMask == physicsCatagory.person && secondBody.categoryBitMask == physicsCatagory.IceThree || firstBody.categoryBitMask == physicsCatagory.IceThree && secondBody.categoryBitMask == physicsCatagory.person{
scorenumber++
if scorenumber == 5 {
SpeedNumber = 0.5
}
ScoreLable.text = "\(scorenumber)"
CollisionWithPerson(firstBody.node as! SKSpriteNode, Person: secondBody.node as! SKSpriteNode)
}
if firstBody.categoryBitMask == physicsCatagory.Score && secondBody.categoryBitMask == physicsCatagory.IceThree ||
firstBody.categoryBitMask == physicsCatagory.IceThree && secondBody.categoryBitMask == physicsCatagory.Score{
lifenumber++
if lifenumber == 3{
self.view?.presentScene(EndScene())
}
//self.view?.presentScene(EndScene())
}
}
func CollisionWithPerson (Ice: SKSpriteNode, Person: SKSpriteNode){
Person.removeFromParent()
}
func spawnThirdIce(){
var Ice = SKSpriteNode(imageNamed: "Ice")
Ice.setScale(0.9)
Ice.physicsBody = SKPhysicsBody(rectangleOfSize: Ice.size)
Ice.physicsBody?.categoryBitMask = physicsCatagory.IceThree
Ice.physicsBody?.contactTestBitMask = physicsCatagory.person | physicsCatagory.Score
Ice.physicsBody?.affectedByGravity = false
Ice.physicsBody?.dynamic = true
let MinValue = self.size.width / 8
let MaxValue = self.size.width - 20
let SpawnPoint = UInt32(MaxValue - MinValue)
Ice.position = CGPoint(x: CGFloat(arc4random_uniform(SpawnPoint)), y: self.size.height)
self.addChild(Ice)
let action = SKAction.moveToY(-85, duration: 2.5)
let actionDone = SKAction.removeFromParent()
Ice.runAction(SKAction.sequence([action,actionDone]))
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
person.position.x = location.x
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
person.position.x = location.x
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
You could do what you want as follows:
First declare 2 properties (in your class but outside all the function definitions)
var timeOfLastSpawn: CFTimeInterval = 0.0
var timePerSpawn: CFTimeInterval = 1.0
Then, in Update, check to see if the timePerSpawn has been exceeded. If so, call your spawn process which spawns new objects and then reset the time since the last spawn:
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if (currentTime - timeOfLastSpawn > timePerSpawn) {
spawnObject()
self.timeOfLastSpawn = currentTime
}
}
func spawnObject() {
// Your spawn code here
}
The advantage of making the spawn process a separate function is that you can call it from didMoveToView or any other place to spawn objects outside of the normal time-controlled cycle.
You can change the value of timePerSpawn as necessary to control the rate at which objects are spawned.
You could also look into creating an SKAction that runs spawnObject at specified time intervals, but I think to change the rate at which objects are spawned, you'll have to delete and re-create the SKAction, but your could do this in a setter for timePerSpawn.
You shouldn't really use NSTimer is SpriteKit, as the SpriteKit engine will be unaware of what the timer is doing and can't control it (one example is that the timer keeps running if the set the scene to paused).
You should move your declaration of var IceThreeTimer to the class level (outside of the method, right where you declare SpeedNumber. This will make sure that your handle to the pointer will be available in both the didMoveToView and didBeginContact methods.
In didMoveToView you change your declaration to:
// I removed the "var"
IceThreeTimer = NSTimer.scheduledTimerWithTimeInterval(SpeedNumber, target: self, selector: ("spawnThirdIce"), userInfo: nil, repeats: true)
Then in didBeginContact you modify:
if scorenumber == 5 {
SpeedNumber = 0.5
// Stop the already running timer
IceThreeTimer.invalidate()
// Schedule a new timer
IceThreeTimer = NSTimer.scheduledTimerWithTimeInterval(SpeedNumber, target: self, selector: ("spawnThirdIce"), userInfo: nil, repeats: true)
}