How to force a button to constantly call a function on hold (SpriteKit)

I make a game using a set of sprites and I want my character to move around the screen when you hold the left / right navigation button. The problem is that it only moves when the button is pressed, and not on hold. I searched everywhere for a solution, but nothing works!

Here is my code;

class Button: SKNode { var defaultButton: SKSpriteNode // defualt state var activeButton: SKSpriteNode // active state var timer = Timer() var action: () -> Void //default constructor init(defaultButtonImage: String, activeButtonImage: String, buttonAction: @escaping () -> Void ) { //get the images for both button states defaultButton = SKSpriteNode(imageNamed: defaultButtonImage) activeButton = SKSpriteNode(imageNamed: activeButtonImage) //hide it while not in use activeButton.isHidden = true action = buttonAction super.init() isUserInteractionEnabled = true addChild(defaultButton) addChild(activeButton) } //When user touches button override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { action() //using timer to repeatedly call action, doesnt seem to work... self.timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(getter: Button.action), userInfo: nil, repeats: true) //swtich the image of our button activeButton.isHidden = false defaultButton.isHidden = true } code.......... 

In my game scene ...

 // *** RIGHT MOVEMENT *** let rightMovementbutton = Button(defaultButtonImage: "arrow", activeButtonImage: "arrowActive", buttonAction: { let moveAction = SKAction.moveBy(x: 15, y: 0, duration: 0.1) self.player.run(moveAction) }) 
+5
source share
1 answer

You know when the button is pressed because touchesBegan is called. Then you need to set a flag to indicate that the button is pressed.

 override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { let touch = touches.first! if leftButton.containsPoint(touch.locationInNode(self)) { leftButtonIsPressed = true } if rightButton.containsPoint(touch.locationInNode(self)) { rightButtonIsPressed = true } } 

In update() call a function that is true:

 update() { if leftButtonIsPressed == true { moveLeft() } if rightButtonIsPressed == true { moveRight() } } 

You set the flag false if touchesEnded is called for this button:

 override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) { let touch = touches.first! if leftButton.containsPoint(touch.locationInNode(self)) { leftButtonIsPressed = false } if rightButton.containsPoint(touch.locationInNode(self)) { rightButtonIsPressed = flase } } 

Edit:

As KoD pointed out, a cleaner way to do this (for the navigation buttons) is with SKAction , which eliminates the need for a flag:

  • Define SKActions for moveTo x:0 and moveTo x:frame.width in didMoveTo(View:)
  • In touchesBegan run the correct SKAction on the correct object, specifying the key for SKAction.
  • In touchesEnded remove the corresponding SKAction.

You will need to do some mathematical calculations to calculate how many points your object will move, and then set the duration for SKAction based on this distance and speed (in points per second).

Alternatively (thanks to KnightOfDragons for this) create an SKAction.MoveBy x: that moves a short distance (based on the desired speed) and lasts 1/60 seconds. Repeat this action permanently ( SKAction.repeatForever ) when the button touches and remove the repeating SKAction when the button is released.

+5
source

Source: https://habr.com/ru/post/1261490/


All Articles