I'm my game I'm using a custom SKSpriteNode which the player are going to be able to drag and drop at a position of his/hers desire.
My custom node is made of this code in file DraggableNode.swift:
class DraggableNode : SKSpriteNode {
var nodeSelected : SKNode?
init() {
let texture = SKTexture(imageNamed: "draggableNode_1")
super.init(texture: texture, color: UIColor.white, size: texture.size())
self.isUserInteractionEnabled = true
self.zPosition = 1
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
let touchedNodes = self.nodes(at: location)
for node in touchedNodes.reversed() {
nodeSelected = node
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first, let node = self.nodeSelected {
let touchLocation = touch.location(in: self)
node.position = touchLocation
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
nodeSelected = nil
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
nodeSelected = nil
}
}
In my GameScene.swift I add the custom node in this way:
let node = DraggableNode()
node.position = CGPoint(x: randomX, y: randomY)
self.addChild(node)
Now, the problem is that when I start to drag the node it's all jump and is placed a total random place - sometimes even out of the screenarea.
I wonder if the problem consists of the fact that it's a custom node inherited from a SKSpriteNode?
If anyone can point me in the right direction, I'd be a happy man :-)
Related
I am trying to make a game where you can click a ball using SpriteKit. At the moment, I can click anywhere on the screen and the ball moves up.
I want it to only move or bounce when I click the ball. What am I doing wrong?
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var ball = SKSpriteNode()
var grass = SKSpriteNode()
override func didMove(to view: SKView) {
ball = (self.childNode(withName: "ball") as? SKSpriteNode)!
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.friction = 0
border.restitution = 1
self.physicsBody = border
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchDown(atPoint: t.location(in: self)) }
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchUp(atPoint: t.location(in: self)) }
}
func touchDown(atPoint pos: CGPoint) {
jump()
}
func touchUp(atPoint pos: CGPoint) {
ball.texture = SKTexture(imageNamed: "football-161132_640")
}
func jump() {
ball.texture = SKTexture(imageNamed: "football-161132_640")
ball.physicsBody?.applyImpulse(CGVector(dx: Int.random(in: -5...5), dy: 80))
}
}
Multiple touches:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if ball.contains(location) {
jump()
}
}
}
Single touch:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
if ball.contains(location) {
jump()
}
}
}
I want to make the image jump by clicking on the sprite, rather than clicking the screen to make it jump.
func jump() {
Football?.texture = SKTexture(imageNamed: "Football")
Football?.physicsBody?.applyImpulse(CGVector(dx: 100, dy: 2000))
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchUp(atPoint: t.location(in: self)) }
}
func touchUp(atPoint pos: CGPoint) {
Football?.texture = SKTexture(imageNamed: "Football")
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
this is a really basic way of testing if the sprite is touched
BTW your variable is named Football proper nomenclature says thay you should be using lower case variable names "football"
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
let touchLocation = touch.location(in: self)
if Football.contains(touchLocation) {
//jump code here
}
}
}
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´m making a game with Swift.
I need to add nodes where I press the screen, I know this is not from other planet but I can't do it.
Highly simplified example could look like this:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: self)
let node = SKNode()
node.position = location
addChild(node)
}
Here is how you can add an remove labels by tapping:
class ViewController: UIViewController{
private var labels = [UILabel]()
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {
return
}
for label in labels {
if label.frame.contains(touch.location(in: view)) {
label.removeFromSuperview()
return
}
}
let label = UILabel()
view.addSubview(label)
labels.append(label)
label.text = "touch"
label.sizeToFit()
label.center = touch.location(in: view)
}
}
All i'm trying to do is be able to drag and drop a sprite across the screen. I've tried the following code:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in (touches ) {
let location = touch.locationInNode(self)
if ball.containsPoint(location) {
ball.position = location
}
}
}
This code does work, however, when I drag the ball quite fast, I guess it detects that the "ball" no longer contains the point "location" and the ball stops, meaning I have pick the ball up again. I want the ball to be able to respond to my touches quickly, so that the ball wont stop moving. How would I do this?
This is the correct way to do it in Sprite Kit. Like I said in my comment, you need to assign the moving node to an activate state, in this case I use a variable called movableNode to act is my activate state. When you touch the ball, it becomes activated by assigning it to movableNode. Then as you drag your finger, movableNode will go with the drag, Then once you release, you enter a deactivate state by setting movableNode to nil. Note that this code will only work on single touch applications, if you want to handle multitouch, then you need to track which touch is doing the dragging.
var movableNode : SKNode?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.locationInNode(self)
if ball.containsPoint(location) {
movableNode = ball
movableNode!.position = location
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first where movableNode != nil {
movableNode!.position = touch.locationInNode(self)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first where movableNode != nil {
movableNode!.position = touch.locationInNode(self)
movableNode = nil
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
movableNode = nil
}
}
KnightOFDragon solution works just fine. I just added few lines if you don't want to move sprite centre position to position where your finger touched the screen and you would like to move sprite from its centre original position.
var movableNode : SKNode?
var ballStartX: CGFloat = 0.0
var ballStartY: CGFloat = 0.0
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.location(in: self)
if (map?.contains(location))! {
movableNode = ball
ballStartX = (movableNode?.position.x)! - location.x // Location X of your sprite when touch started
ballStartY = (movableNode?.position.y)! - location.y // Location Y of your sprite when touch started
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first, movableNode != nil {
let location = touch.location(in: self)
movableNode!.position = CGPoint(x: location.x+ballStartX, y: location.y+ballStartY) // Move node around with distance to your finger
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let _ = touches.first, movableNode != nil {
movableNode = nil
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
movableNode = nil
}
I have an implementation where I've subclassed a UIImageView and called it a "DraggableImage"
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
originalPosition = self.center
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let position = touch.location(in: self.superview)
self.center = CGPoint(x: position.x, y: position.y)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.center = originalPosition
}