Einrichten von Schaltflächen in SKScene

81

Ich stelle fest, dass UIButtonsdas nicht sehr gut funktioniert SKScene. Also versuche ich, eine Unterklasse SKNodezu erstellen , um einen Button zu erstellen SpriteKit.

Ich möchte, dass es funktioniert: Wenn ich eine Schaltfläche in initialisiere SKSceneund Berührungsereignisse aktiviere, ruft die Schaltfläche eine Methode in meinem auf, SKScenewenn sie gedrückt wird.

Ich würde mich über jeden Rat freuen, der mich dazu führen würde, die Lösung für dieses Problem zu finden. Vielen Dank.

AlexHeuman
quelle
2
Ich suche mehr nach einer Lernerfahrung als nach einer Lösung. Ich denke, die richtige Lösung wäre, die SKScene als Delegierten der Schaltfläche einzurichten, aber ich bin mir nicht sicher, wie ich das tun soll. Kann ich die SKScene als Instanzvariable der Schaltfläche einrichten und eine Methode dafür aufrufen?
AlexHeuman
Sie können eine Reihe von Dingen erledigen, delegieren oder NSNotification flexibler verwenden, damit jeder Knoten darauf reagieren kann. Wenn Sie delegate verwenden, müssen Sie die delegate-Eigenschaft auf schwach setzen.
LearnCocos2D
Ich habe festgestellt, dass dieser Code beim Erstellen von Sprite-Kit-Schaltflächen hilfreich ist. Es erweitert SKSpriteKitNode und ermöglicht das einfache Hinzufügen von Text zu Schaltflächen.
sager89

Antworten:

102

Sie können einen SKSpriteNode als Schaltfläche verwenden. Wenn der Benutzer ihn berührt, überprüfen Sie, ob dieser Knoten berührt wurde. Verwenden Sie die Namenseigenschaft von SKSpriteNode, um den Knoten zu identifizieren:

//fire button
- (SKSpriteNode *)fireButtonNode
{
    SKSpriteNode *fireNode = [SKSpriteNode spriteNodeWithImageNamed:@"fireButton.png"];
    fireNode.position = CGPointMake(fireButtonX,fireButtonY);
    fireNode.name = @"fireButtonNode";//how the node is identified later
    fireNode.zPosition = 1.0;
    return fireNode;
}

Fügen Sie Ihrer Szene einen Knoten hinzu:

[self addChild: [self fireButtonNode]];

Griffberührungen:

//handle touch events
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInNode:self];
    SKNode *node = [self nodeAtPoint:location];

    //if fire button touched, bring the rain
    if ([node.name isEqualToString:@"fireButtonNode"]) {
         //do whatever...
    }
}
AndyOS
quelle
8
Wenn Sie ein iVar als Schaltfläche hinzufügen, können Sie die Namensprüfung entfernen und einfach verwenden, wenn ([_fireNode enthältPoint: location]) dasselbe nur anders macht.
DogCoffee
3
Das Vergleichen von Strings ist eine schmutzige Lösung. Obwohl die Lösung von @Smick besser ist, gibt es keinen anderen saubereren Weg, dies zu erreichen?
Esteban Bouza
Hey, wir können keine Schaltfläche in SpriteKit wie SkLabelNode hinzufügen?
Omer Obaid
Ermöglicht dies Multi-Touch-Ereignisse? Zum Beispiel 2 Schaltflächen gleichzeitig geklickt? einer von ihnen ist ein Bewegungsknopf, ein anderer ist ein Feuerknopf.
Duxfox
52

Ich habe meine eigene Button-Klasse gemacht, mit der ich arbeite. SKButton.h:

#import <SpriteKit/SpriteKit.h>
@interface SKButton : SKSpriteNode

@property (nonatomic, readonly) SEL actionTouchUpInside;
@property (nonatomic, readonly) SEL actionTouchDown;
@property (nonatomic, readonly) SEL actionTouchUp;
@property (nonatomic, readonly, weak) id targetTouchUpInside;
@property (nonatomic, readonly, weak) id targetTouchDown;
@property (nonatomic, readonly, weak) id targetTouchUp;

@property (nonatomic) BOOL isEnabled;
@property (nonatomic) BOOL isSelected;
@property (nonatomic, readonly, strong) SKLabelNode *title;
@property (nonatomic, readwrite, strong) SKTexture *normalTexture;
@property (nonatomic, readwrite, strong) SKTexture *selectedTexture;
@property (nonatomic, readwrite, strong) SKTexture *disabledTexture;

- (id)initWithTextureNormal:(SKTexture *)normal selected:(SKTexture *)selected;
- (id)initWithTextureNormal:(SKTexture *)normal selected:(SKTexture *)selected disabled:(SKTexture *)disabled; // Designated Initializer

- (id)initWithImageNamedNormal:(NSString *)normal selected:(NSString *)selected;
- (id)initWithImageNamedNormal:(NSString *)normal selected:(NSString *)selected disabled:(NSString *)disabled;

/** Sets the target-action pair, that is called when the Button is tapped.
 "target" won't be retained.
 */
- (void)setTouchUpInsideTarget:(id)target action:(SEL)action;
- (void)setTouchDownTarget:(id)target action:(SEL)action;
- (void)setTouchUpTarget:(id)target action:(SEL)action;

@end

SKButton.m:

#import "SKButton.h"
#import <objc/message.h>


@implementation SKButton

#pragma mark Texture Initializer

/**
 * Override the super-classes designated initializer, to get a properly set SKButton in every case
 */
- (id)initWithTexture:(SKTexture *)texture color:(UIColor *)color size:(CGSize)size {
    return [self initWithTextureNormal:texture selected:nil disabled:nil];
}

- (id)initWithTextureNormal:(SKTexture *)normal selected:(SKTexture *)selected {
    return [self initWithTextureNormal:normal selected:selected disabled:nil];
}

/**
 * This is the designated Initializer
 */
- (id)initWithTextureNormal:(SKTexture *)normal selected:(SKTexture *)selected disabled:(SKTexture *)disabled {
    self = [super initWithTexture:normal color:[UIColor whiteColor] size:normal.size];
    if (self) {
        [self setNormalTexture:normal];
        [self setSelectedTexture:selected];
        [self setDisabledTexture:disabled];
        [self setIsEnabled:YES];
        [self setIsSelected:NO];

        _title = [SKLabelNode labelNodeWithFontNamed:@"Arial"];
        [_title setVerticalAlignmentMode:SKLabelVerticalAlignmentModeCenter];
        [_title setHorizontalAlignmentMode:SKLabelHorizontalAlignmentModeCenter];

        [self addChild:_title];
        [self setUserInteractionEnabled:YES];
    }
    return self;
}

#pragma mark Image Initializer

- (id)initWithImageNamedNormal:(NSString *)normal selected:(NSString *)selected {
    return [self initWithImageNamedNormal:normal selected:selected disabled:nil];
}

- (id)initWithImageNamedNormal:(NSString *)normal selected:(NSString *)selected disabled:(NSString *)disabled {
    SKTexture *textureNormal = nil;
    if (normal) {
        textureNormal = [SKTexture textureWithImageNamed:normal];
    }

    SKTexture *textureSelected = nil;
    if (selected) {
        textureSelected = [SKTexture textureWithImageNamed:selected];
    }

    SKTexture *textureDisabled = nil;
    if (disabled) {
        textureDisabled = [SKTexture textureWithImageNamed:disabled];
    }

    return [self initWithTextureNormal:textureNormal selected:textureSelected disabled:textureDisabled];
}




#pragma -
#pragma mark Setting Target-Action pairs

- (void)setTouchUpInsideTarget:(id)target action:(SEL)action {
    _targetTouchUpInside = target;
    _actionTouchUpInside = action;
}

- (void)setTouchDownTarget:(id)target action:(SEL)action {
    _targetTouchDown = target;
    _actionTouchDown = action;
}

- (void)setTouchUpTarget:(id)target action:(SEL)action {
    _targetTouchUp = target;
    _actionTouchUp = action;
}

#pragma -
#pragma mark Setter overrides

- (void)setIsEnabled:(BOOL)isEnabled {
    _isEnabled = isEnabled;
    if ([self disabledTexture]) {
        if (!_isEnabled) {
            [self setTexture:_disabledTexture];
        } else {
            [self setTexture:_normalTexture];
        }
    }
}

- (void)setIsSelected:(BOOL)isSelected {
    _isSelected = isSelected;
    if ([self selectedTexture] && [self isEnabled]) {
        if (_isSelected) {
            [self setTexture:_selectedTexture];
        } else {
            [self setTexture:_normalTexture];
        }
    }
}

#pragma -
#pragma mark Touch Handling

/**
 * This method only occurs, if the touch was inside this node. Furthermore if 
 * the Button is enabled, the texture should change to "selectedTexture".
 */
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if ([self isEnabled]) {
        objc_msgSend(_targetTouchDown, _actionTouchDown);
        [self setIsSelected:YES];
    }
}

/**
 * If the Button is enabled: This method looks, where the touch was moved to.
 * If the touch moves outside of the button, the isSelected property is restored
 * to NO and the texture changes to "normalTexture".
 */
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    if ([self isEnabled]) {
        UITouch *touch = [touches anyObject];
        CGPoint touchPoint = [touch locationInNode:self.parent];

        if (CGRectContainsPoint(self.frame, touchPoint)) {
            [self setIsSelected:YES];
        } else {
            [self setIsSelected:NO];
        }
    }
}

/**
 * If the Button is enabled AND the touch ended in the buttons frame, the
 * selector of the target is run.
 */
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint touchPoint = [touch locationInNode:self.parent];

    if ([self isEnabled] && CGRectContainsPoint(self.frame, touchPoint)) {
        objc_msgSend(_targetTouchUpInside, _actionTouchUpInside);
    }
    [self setIsSelected:NO];
    objc_msgSend(_targetTouchUp, _actionTouchUp);
}

Ein Beispiel: Um eine Schaltfläche zu initialisieren, schreiben Sie die folgenden Zeilen:

    SKButton *backButton = [[SKButton alloc] initWithImageNamedNormal:@"buttonNormal" selected:@"buttonSelected"];
    [backButton setPosition:CGPointMake(100, 100)];
    [backButton.title setText:@"Button"];
    [backButton.title setFontName:@"Chalkduster"];
    [backButton.title setFontSize:20.0];
    [backButton setTouchUpInsideTarget:self action:@selector(buttonAction)];
    [self addChild:backButton];

Außerdem benötigen Sie die 'buttonAction'-Methode in Ihrer Klasse. * Keine Garantie, dass diese Klasse in jedem Fall richtig funktioniert. Ich bin noch ziemlich neu in Ziel-c. * *

Wenn Sie mit denken , dies zu tun ist ärgerlich und sinnlos Sie die Prüfung in den Build - Einstellungen deaktivieren und ‚Enable strenge Überprüfung der objc_msgSend Calls'auf‘ No'

Dennis-Tra
quelle
Danke für das Teilen. Gibt es einen Grund, warum Sie objc_msgSendanstelle von verwenden [target performSelector:selector]?
Jeffrey W.
2
Ah ja, verdammter ARC. Ich habe diese Warnung vergessen: | Hier ist eine gute Lösung,
Jeffrey W.
Der obige Code ist großartig, aber ich erhalte Fehler, wenn ich versuche, - (void) changeToScene: (SKButtonNode *) sender {} als @selector zu verwenden. Ich würde lieber eine einzige Methode verwenden, um Szenen mithilfe von sender.name zu wechseln, wenn ich könnte.
Beau Nouvelle
1
Danke, dass du das geteilt hast! Ich füge es in meinen Code ein. Wir werden sehen, ob es funktioniert. Ein Vorschlag: Ändern Sie den Namen der Klasse von SKButton in einen für Sie einzigartigeren Namen, z. B. GRFButton. Irgendwann führt Apple möglicherweise einen SKButton ein, und Sie möchten den Namespace nicht verwirren und Ihren Code später beschädigen.
James Paul Mason
1
@BeauYoung - Es funktioniert, wenn Sie das selfam Ende wie folgt hinzufügen :objc_msgSend(_targetTouchUpInside, _actionTouchUpInside, self)
Genki
19

Für Leute, die ihre Spiele in Swift schreiben! Ich habe die wesentlichen Teile von Grafs Lösung in eine schnelle Klasse umgeschrieben. Ich hoffe es hilft:

import Foundation
import SpriteKit

class FTButtonNode: SKSpriteNode {

    enum FTButtonActionType: Int {
        case TouchUpInside = 1,
        TouchDown, TouchUp
    }

    var isEnabled: Bool = true {
    didSet {
        if (disabledTexture != nil) {
            texture = isEnabled ? defaultTexture : disabledTexture
        }
    }
    }
    var isSelected: Bool = false {
    didSet {
        texture = isSelected ? selectedTexture : defaultTexture
    }
    }
    var defaultTexture: SKTexture
    var selectedTexture: SKTexture

    required init(coder: NSCoder) {
        fatalError("NSCoding not supported")
    }

    init(normalTexture defaultTexture: SKTexture!, selectedTexture:SKTexture!, disabledTexture: SKTexture?) {

        self.defaultTexture = defaultTexture
        self.selectedTexture = selectedTexture
        self.disabledTexture = disabledTexture

        super.init(texture: defaultTexture, color: UIColor.whiteColor(), size: defaultTexture.size())

        userInteractionEnabled = true

        // Adding this node as an empty layer. Without it the touch functions are not being called
        // The reason for this is unknown when this was implemented...?
        let bugFixLayerNode = SKSpriteNode(texture: nil, color: nil, size: defaultTexture.size())
        bugFixLayerNode.position = self.position
        addChild(bugFixLayerNode)

    }

    /**
    * Taking a target object and adding an action that is triggered by a button event.
    */
    func setButtonAction(target: AnyObject, triggerEvent event:FTButtonActionType, action:Selector) {

        switch (event) {
        case .TouchUpInside:
            targetTouchUpInside = target
            actionTouchUpInside = action
        case .TouchDown:
            targetTouchDown = target
            actionTouchDown = action
        case .TouchUp:
            targetTouchUp = target
            actionTouchUp = action
        }

    }

    var disabledTexture: SKTexture?
    var actionTouchUpInside: Selector?
    var actionTouchUp: Selector?
    var actionTouchDown: Selector?
    weak var targetTouchUpInside: AnyObject?
    weak var targetTouchUp: AnyObject?
    weak var targetTouchDown: AnyObject?

    override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!)  {
        let touch: AnyObject! = touches.anyObject()
        let touchLocation = touch.locationInNode(parent)

        if (!isEnabled) {
            return
        }
        isSelected = true
        if (targetTouchDown != nil && targetTouchDown!.respondsToSelector(actionTouchDown!)) {
            UIApplication.sharedApplication().sendAction(actionTouchDown!, to: targetTouchDown, from: self, forEvent: nil)
        }


    }

    override func touchesMoved(touches: NSSet!, withEvent event: UIEvent!)  {

        if (!isEnabled) {
            return
        }

        let touch: AnyObject! = touches.anyObject()
        let touchLocation = touch.locationInNode(parent)

        if (CGRectContainsPoint(frame, touchLocation)) {
            isSelected = true
        } else {
            isSelected = false
        }

    }

    override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) {

        if (!isEnabled) {
            return
        }

        isSelected = false

        if (targetTouchUpInside != nil && targetTouchUpInside!.respondsToSelector(actionTouchUpInside!)) {
            let touch: AnyObject! = touches.anyObject()
            let touchLocation = touch.locationInNode(parent)

            if (CGRectContainsPoint(frame, touchLocation) ) {
                UIApplication.sharedApplication().sendAction(actionTouchUpInside!, to: targetTouchUpInside, from: self, forEvent: nil)
            }

        }

        if (targetTouchUp != nil && targetTouchUp!.respondsToSelector(actionTouchUp!)) {
            UIApplication.sharedApplication().sendAction(actionTouchUp!, to: targetTouchUp, from: self, forEvent: nil)
        }
    }

}
Groot
quelle
5

Wenn Sie es wünschen, Sie können UIButton (oder einem anderen UIView) verwenden.

Wenn ein SKSceneerstellt wird, existiert es noch nicht in einem SKView. Sie sollten didMoveToView:in Ihrer SKSceneUnterklasse implementieren . Zu diesem Zeitpunkt haben Sie Zugriff auf die SKViewSzene, in der sich die Szene befindet, und können UIKitObjekte hinzufügen . Aus Gründen der Schönheit habe ich sie eingeblendet ...

- (void)didMoveToView:(SKView *)view {
  UIView *b = [self _createButton];  // <-- performs [self.view addSubview:button]
  // create other UI elements, also add them to the list to remove …
  self.customSubviews = @[b];

  b.alpha = 0;

  [UIView animateWithDuration:0.4
                        delay:2.4
                      options:UIViewAnimationOptionCurveEaseIn
                   animations:^{
                     b.alpha = 1;
                   } completion:^(BOOL finished) {
                     ;
                   }];
}

Sie müssen sie absichtlich aus der Szene entfernen, wenn Sie weggehen, es sei denn, es ist natürlich absolut sinnvoll, dass sie dort bleiben.

- (void)removeCustomSubviews {
  for (UIView *v in self.customSubviews) {
    [UIView animateWithDuration:0.2
                          delay:0
                        options:UIViewAnimationOptionCurveEaseIn
                     animations:^{
                       v.alpha = 0;
                   } completion:^(BOOL finished) {
                       [v removeFromSuperview];
                 }];
  }
}

Für diejenigen, die nicht mit dem programmgesteuerten Erstellen eines vertraut sind UIButton, hier ein Beispiel (Sie könnten hier 100 Dinge anders machen)…

- (UIButton *)_createButton {
  UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom];
  [b setTitle:@"Continue" forState:UIControlStateNormal];
  [b setBackgroundImage:[UIImage imageNamed:@"GreenButton"] forState:UIControlStateNormal];
  [b setBackgroundImage:[UIImage imageNamed:@"GreenButtonSelected"] forState:UIControlStateHighlighted];
  b.titleLabel.adjustsFontSizeToFitWidth = YES;
  b.titleLabel.font = [UIFont fontWithName:@"HelveticaNeue-Bold" size:36];
  b.frame = CGRectMake(self.size.width * .7, self.size.height * .2, self.size.width * .2, self.size.height * .1);
  [b addTarget:self action:@selector(continuePlay) forControlEvents:UIControlEventTouchUpInside];
  [self.view addSubview:b];

  return b;
}

Erinnerung: UIViewUrsprung ist oben links, SKSceneUrsprung ist unten links.

bshirley
quelle
3

Ich habe die SKButton-Klasse von Graf verwendet .

Ich benutze den SKButton für die Szenennavigation. dh eine andere Szene präsentieren, wenn der Benutzer den SKButton drückt. Ich bekomme EXC_BAD_ACCESSFehler beitouchesEnded->[self setIsSelected:NO] . Dies passiert besonders häufig auf dem neuesten iPad mit schneller CPU.

Nach Überprüfung und Fehlerbehebung wurde mir klar, dass das SKButton-Objekt bereits "freigegeben" ist, wenn das setIsSelected Funktion . Dies liegt daran, dass ich den SKButton verwende, um zur nächsten Szene zu navigieren, und dies bedeutet auch, dass die aktuelle Szene jederzeit freigegeben werden kann.

Ich habe eine kleine Änderung vorgenommen, indem ich setIsSelected wie folgt in den Abschnitt "else" eingefügt habe.

Hoffe, dies hilft anderen Entwicklern, die den gleichen Fehler sehen.

(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint touchPoint = [touch locationInNode:self.parent];

    if ([self isEnabled] && CGRectContainsPoint(self.frame, touchPoint)) {
        objc_msgSend(_targetTouchUpInside, _actionTouchUpInside);
    } else {
       [self setIsSelected:NO];
    }
    objc_msgSend(_targetTouchUp, _actionTouchUp);
}
user3204765
quelle
Bitte formatieren Sie Ihren Beitrag und Quellcode, es ist sehr schwer, auf diese Weise zu lesen!
Uli Köhler
2

Hier ist eine andere Version, die auf Filip's Swift-Code basiert. Ich habe es nur ein wenig vereinfacht und zugelassen, dass es Blöcke und nicht nur Selektoren enthält:

import Foundation
import SpriteKit

enum FTButtonTarget {
    case aSelector(Selector, AnyObject)
    case aBlock(() -> Void)
}

class FTButtonNode: SKSpriteNode {

    var actionTouchUp : FTButtonTarget?
    var actionTouchUpInside : FTButtonTarget?
    var actionTouchDown : FTButtonTarget?

    var isEnabled: Bool = true {
        didSet {
            if (disabledTexture != nil) {
                texture = isEnabled ? defaultTexture : disabledTexture
            }
        }
    }
    var isSelected: Bool = false {
        didSet {
            texture = isSelected ? selectedTexture : defaultTexture
        }
    }

    var defaultTexture: SKTexture
    var selectedTexture: SKTexture

    required init(coder: NSCoder) {
        fatalError("NSCoding not supported")
    }

init(normalTexture defaultTexture: SKTexture!, selectedTexture:SKTexture!, disabledTexture: SKTexture?) {

    self.defaultTexture = defaultTexture
    self.selectedTexture = selectedTexture
    self.disabledTexture = disabledTexture

    super.init(texture: defaultTexture, color: UIColor.whiteColor(), size: defaultTexture.size())

    userInteractionEnabled = true

    // Adding this node as an empty layer. Without it the touch functions are not being called
    // The reason for this is unknown when this was implemented...?
    let bugFixLayerNode = SKSpriteNode(texture: nil, color: nil, size: defaultTexture.size())
    bugFixLayerNode.position = self.position
    addChild(bugFixLayerNode)

}

var disabledTexture: SKTexture?

func callTarget(buttonTarget:FTButtonTarget) {

    switch buttonTarget {
    case let .aSelector(selector, target):
        if target.respondsToSelector(selector) {
            UIApplication.sharedApplication().sendAction(selector, to: target, from: self, forEvent: nil)
        }
    case let .aBlock(block):
        block()
    }

}

override func touchesBegan(touches: NSSet, withEvent event: UIEvent)  {
    let touch: AnyObject! = touches.anyObject()
    let touchLocation = touch.locationInNode(parent)

    if (!isEnabled) {
        return
    }
    isSelected = true

    if let act = actionTouchDown {
        callTarget(act)
    }

}

override func touchesMoved(touches: NSSet, withEvent event: UIEvent)  {

    if (!isEnabled) {
        return
    }

    let touch: AnyObject! = touches.anyObject()
    let touchLocation = touch.locationInNode(parent)

    if (CGRectContainsPoint(frame, touchLocation)) {
        isSelected = true
    } else {
        isSelected = false
    }

}

 override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {

     if (!isEnabled) {
         return
     }

     isSelected = false

     let touch: AnyObject! = touches.anyObject()
     let touchLocation = touch.locationInNode(parent)

     if (CGRectContainsPoint(frame, touchLocation) ) {

         if let act = actionTouchUpInside {
             callTarget(act)
         }
     }

     if let act = actionTouchUp {
         callTarget(act)
     }
 }
}

Verwenden Sie es so:

       aFTButton.actionTouchUpInside = FTButtonTarget.aBlock({ () -> Void in
        println("button touched")
    })

Hoffe das hilft.

Guillaume Laurent
quelle
2

Bearbeiten: Ich habe ein Github-Repo für meinen SKButtonNode erstellt, das ich hoffentlich auf dem neuesten Stand halten und aktualisieren werde, wenn sich schnell etwas entwickelt!

SKButtonNode


Leider kann ich Filip's schnelle Implementierung von SKButton in Swift noch nicht kommentieren. Super glücklich, dass er das in Swift gemacht hat! Ich bemerkte jedoch, dass er keine Funktion zum Hinzufügen von Text zur Schaltfläche enthielt. Dies ist eine große Funktion für mich, sodass Sie nicht für jede einzelne Schaltfläche separate Assets erstellen müssen, sondern nur den Hintergrund und dynamischen Text hinzufügen müssen.

Ich habe eine einfache Funktion hinzugefügt, um SKButton eine Textbezeichnung hinzuzufügen. Es ist wahrscheinlich nicht perfekt - ich bin neu bei Swift, genau wie alle anderen! Fühlen Sie sich frei zu kommentieren und helfen Sie mir, dies so gut wie möglich zu aktualisieren. Hoffe euch gefällt!

 //Define label with the textures
 var defaultTexture: SKTexture
 var selectedTexture: SKTexture

 //New defining of label
 var label: SKLabelNode

 //Updated init() function:

 init(normalTexture defaultTexture: SKTexture!, selectedTexture:SKTexture!, disabledTexture: SKTexture?) {

    self.defaultTexture = defaultTexture
    self.selectedTexture = selectedTexture
    self.disabledTexture = disabledTexture

    //New initialization of label
    self.label = SKLabelNode(fontNamed: "Helvetica");

    super.init(texture: defaultTexture, color: UIColor.whiteColor(), size: defaultTexture.size())
    userInteractionEnabled = true

    //Creating and adding a blank label, centered on the button
    self.label.verticalAlignmentMode = SKLabelVerticalAlignmentMode.Center;
    self.label.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Center;
    addChild(self.label)

    // Adding this node as an empty layer. Without it the touch functions are not being called
    // The reason for this is unknown when this was implemented...?
    let bugFixLayerNode = SKSpriteNode(texture: nil, color: nil, size: defaultTexture.size())
    bugFixLayerNode.position = self.position
    addChild(bugFixLayerNode)

  }




    /*
      New function for setting text. Calling function multiple times does 
      not create a ton of new labels, just updates existing label.
      You can set the title, font type and font size with this function
    */

    func setButtonLabel(#title: NSString, font: String, fontSize: CGFloat) {
        var title = title
        var font = font
        var fontSize = fontSize

        self.label.text = title
        self.label.fontSize = fontSize
        self.label.fontName = font        
     } 

Beispiel für die Erstellung einer Schaltfläche:

    var buttonTexture = SKTexture(imageNamed: "Button");
    var buttonPressedTexture = SKTexture(imageNamed: "Button Pressed");
    var button = SKButton(normalTexture:buttonTexture, selectedTexture:buttonPressedTexture, disabledTexture:buttonPressedTexture);
    button.setButtonLabel(title: "Play",font: "Helvetica",fontSize: 40);
    button.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
    self.addChild(button);

Vollständige Klasse unten aufgeführt:

import Foundation
import SpriteKit


class SKButton: SKSpriteNode {




enum FTButtonActionType: Int {
    case TouchUpInside = 1,
    TouchDown, TouchUp
}

var isEnabled: Bool = true {
    didSet {
        if (disabledTexture != nil) {
            texture = isEnabled ? defaultTexture : disabledTexture
        }
    }
}
var isSelected: Bool = false {
    didSet {
        texture = isSelected ? selectedTexture : defaultTexture
    }
}
var defaultTexture: SKTexture
var selectedTexture: SKTexture
var label: SKLabelNode


required init(coder: NSCoder) {
    fatalError("NSCoding not supported")
}

init(normalTexture defaultTexture: SKTexture!, selectedTexture:SKTexture!, disabledTexture: SKTexture?) {

    self.defaultTexture = defaultTexture
    self.selectedTexture = selectedTexture
    self.disabledTexture = disabledTexture
    self.label = SKLabelNode(fontNamed: "Helvetica");
    super.init(texture: defaultTexture, color: UIColor.whiteColor(), size: defaultTexture.size())
    userInteractionEnabled = true


    self.label.verticalAlignmentMode = SKLabelVerticalAlignmentMode.Center;
    self.label.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Center;
    addChild(self.label)

    // Adding this node as an empty layer. Without it the touch functions are not being called
    // The reason for this is unknown when this was implemented...?
    let bugFixLayerNode = SKSpriteNode(texture: nil, color: nil, size: defaultTexture.size())
    bugFixLayerNode.position = self.position
    addChild(bugFixLayerNode)

}

/**
* Taking a target object and adding an action that is triggered by a button event.
*/
func setButtonAction(target: AnyObject, triggerEvent event:FTButtonActionType, action:Selector) {

    switch (event) {
    case .TouchUpInside:
        targetTouchUpInside = target
        actionTouchUpInside = action
    case .TouchDown:
        targetTouchDown = target
        actionTouchDown = action
    case .TouchUp:
        targetTouchUp = target
        actionTouchUp = action
    }

}


func setButtonLabel(#title: NSString, font: String, fontSize: CGFloat) {
    var title = title;
    var font = font;
    var fontSize = fontSize;

    self.label.text = title;
    self.label.fontSize = fontSize;
    self.label.fontName = font;

}

var disabledTexture: SKTexture?
var actionTouchUpInside: Selector?
var actionTouchUp: Selector?
var actionTouchDown: Selector?
weak var targetTouchUpInside: AnyObject?
weak var targetTouchUp: AnyObject?
weak var targetTouchDown: AnyObject?

override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!)  {
    let touch: AnyObject! = touches.anyObject()
    let touchLocation = touch.locationInNode(parent)

    if (!isEnabled) {
        return
    }
    isSelected = true
    if (targetTouchDown != nil && targetTouchDown!.respondsToSelector(actionTouchDown!)) {
        UIApplication.sharedApplication().sendAction(actionTouchDown!, to: targetTouchDown, from: self, forEvent: nil)
    }


}

override func touchesMoved(touches: NSSet!, withEvent event: UIEvent!)  {

    if (!isEnabled) {
        return
    }

    let touch: AnyObject! = touches.anyObject()
    let touchLocation = touch.locationInNode(parent)

    if (CGRectContainsPoint(frame, touchLocation)) {
        isSelected = true
    } else {
        isSelected = false
    }

}

override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) {

    if (!isEnabled) {
        return
    }

    isSelected = false

    if (targetTouchUpInside != nil && targetTouchUpInside!.respondsToSelector(actionTouchUpInside!)) {
        let touch: AnyObject! = touches.anyObject()
        let touchLocation = touch.locationInNode(parent)

        if (CGRectContainsPoint(frame, touchLocation) ) {
            UIApplication.sharedApplication().sendAction(actionTouchUpInside!, to: targetTouchUpInside, from: self, forEvent: nil)
        }

    }

    if (targetTouchUp != nil && targetTouchUp!.respondsToSelector(actionTouchUp!)) {
        UIApplication.sharedApplication().sendAction(actionTouchUp!, to: targetTouchUp, from: self, forEvent: nil)
    }
}

}}

Morgan Wesemann
quelle
Der Code für Swift 2.1 wurde hier aktualisiert: gist.github.com/richy486/5d408c442ac1c0c2891f
Richy
..und ich habe hier auf Swift 3 aktualisiert: github.com/jglasse/SKButtonSwift3
jglasse
2

Was für viele großartige Lösungen für dieses Problem! Für die Hardcore-Scroller, die es bis hierher schaffen, gibt es ein Vergnügen! Ich habe eine Unterklasse erstellt SKScene, und es ist EIN Funktionsaufruf erforderlich, um einen beliebigen Knoten zu registrieren, der sich wie ein UIButton! Hier ist die Klasse:

class KCScene : SKScene {
//------------------------------------------------------------------------------------
//This function is the only thing you use in this class!!!
func addButton(_ node:SKNode, withCompletionHandler handler: @escaping ()->()) {
    let data = ButtonData(button: node, actionToPerform: handler)
    eligibleButtons.append(data)
}
//------------------------------------------------------------------------------------
private struct ButtonData {
    //TODO: make a dictionary with ()->() as the value and SKNode as the key.
    //Then refactor this class!
    let button:SKNode
    let actionToPerform:()->()
}

private struct TouchTrackingData {
    //this will be in a dictionary with a UITouch object as the key
    let button:SKNode
    let originalButtonFrame:CGRect
}

private var eligibleButtons = [ButtonData]()
private var trackedTouches = [UITouch:TouchTrackingData]()
//------------------------------------------------------------------------------------
//TODO: make these functions customizable,
//with these implementations as defaults.
private func applyTouchedDownEffectToNode(node:SKNode) {
    node.alpha  = 0.5
    node.xScale = 0.8
    node.yScale = 0.8
}
private func applyTouchedUpEffectToNode(node:SKNode)   {
    node.alpha  = 1
    node.xScale = 1
    node.yScale = 1
}
//------------------------------------------------------------------------------------
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    for touch in touches {
        let touchLocation = touch.location(in: self)
        let touchedNode = atPoint(touchLocation)

        for buttonData in eligibleButtons {
            if touchedNode === buttonData.button {
                //then this touch needs to be tracked, as it touched down on an eligible button!
                for (t, bD) in trackedTouches {
                    if bD.button === buttonData.button {
                        //then this button was already being tracked by a previous touch, disable the previous touch
                        trackedTouches[t] = nil
                    }
                }
                //start tracking this touch
                trackedTouches[touch] = TouchTrackingData(button: touchedNode, originalButtonFrame: touchedNode.frameInScene)
                applyTouchedDownEffectToNode(node: buttonData.button)
            }
        }
    }
}
//------------------------------------------------------------------------------------
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    for touch in touches {
        if trackedTouches[touch] == nil {continue}
        //Now we know this touch is being tracked...
        let touchLocation = touch.location(in: self)
        //TODO: implement an isBeingTouched property on TouchTrackingData, so 
        //applyTouchedDown(Up)Effect doesn't have to be called EVERY move the touch makes
        if trackedTouches[touch]!.originalButtonFrame.contains(touchLocation) {
            //if this tracked touch is touching its button
            applyTouchedDownEffectToNode(node: trackedTouches[touch]!.button)
        } else {
            applyTouchedUpEffectToNode(node: trackedTouches[touch]!.button)
        }

    }
}
//------------------------------------------------------------------------------------
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    for touch in touches {
        if trackedTouches[touch] == nil {continue}
        //Now we know this touch is being tracked...
        let touchLocation = touch.location(in: self)

        if trackedTouches[touch]!.originalButtonFrame.contains(touchLocation) {
            applyTouchedUpEffectToNode(node: trackedTouches[touch]!.button)

            for buttonData in eligibleButtons {
                if buttonData.button === trackedTouches[touch]!.button {
                    buttonData.actionToPerform()
                }
            }
        }
        trackedTouches[touch] = nil
    }
}
//------------------------------------------------------------------------------------
override func touchesCancelled(_ touches: Set<UITouch>?, with event: UIEvent?) {
    for touch in touches! {
        if trackedTouches[touch] == nil {continue}
        //Now we know this touch is being tracked...
        //Since this touch was cancelled, it will not be activating a button,
        //and it is not worth checking where the touch was
        //we will simply apply the touched up effect regardless and remove the touch from being tracked
        applyTouchedUpEffectToNode(node: trackedTouches[touch]!.button)
        trackedTouches[touch] = nil
    }
}
//------------------------------------------------------------------------------------

}}

Es enthält viele Ideen, die ich noch nicht implementiert habe, und einige Erklärungen zum Code, aber kopieren Sie ihn einfach und fügen Sie ihn in Ihr Projekt ein, und Sie können ihn unverändert in Ihrer eigenen Szene verwenden. Hier ist ein vollständiges Beispiel für die Verwendung:

class GameScene : KCScene {
var playButton:SKSpriteNode
override init(size:CGSize) {
    playButton = SKSpriteNode(color: SKColor.red, size: CGSize(width:200,height:200))
    playButton.position.x = size.width/2
    playButton.position.y = size.height*0.75
    super.init(size: size)
}
override func didMove(to view: SKView) {
    addChild(playButton)
    addButton(playButton, withCompletionHandler: playButtonPushed)
}
func playButtonPushed() {
    let scene = GameScene(size: CGSize(width: 768, height: 1024))
    scene.scaleMode = .aspectFill
    view!.presentScene(scene)
}
}

Die einzige Einschränkung ist , wenn Sie implementieren touchesBegan, touchesMoved, touchesEnded, und / odertouchesCancelled Sie MÜSSEN SUPER AUFRUF! Sonst wird es nicht funktionieren.

Und bitte beachten Sie, dass es in diesem Beispiel wirklich nur EINE CODE-LINIE gibt, die Sie benötigen, um JEDEN NODE- UIButtonEigenschaften zuzuweisen ! Es war diese Zeile:

addButton(playButton, withCompletionHandler: playButtonPushed)

Ich bin immer offen für Ideen und Vorschläge. Lass sie in den Kommentaren und Happy Coding !!

Hoppla, ich habe vergessen zu erwähnen, dass ich diese raffinierte Erweiterung verwende. Sie können es aus einer Erweiterung herausnehmen (da Sie es wahrscheinlich nicht in jedem Knoten benötigen) und es in meine Klasse einfügen. Ich benutze es nur an einem Ort.

extension SKNode {
var frameInScene:CGRect {
    if let scene = scene, let parent = parent {
        let rectOriginInScene = scene.convert(frame.origin, from: parent)
        return CGRect(origin: rectOriginInScene, size: frame.size)
    }
    return frame
}

}}

Mogelbuster
quelle
Wie stellt dies sicher, dass auf die Abschlussfunktion playButtonPushed zugegriffen werden kann? Oder wo platziere ich die Funktion playButtonPushed, um sicherzustellen, dass sie von der KScene-Instanz erreicht werden kann, von der ich annehme, dass sie die Schaltfläche ist?
Verwirrt
@Confused Sie würden Ihre eigene Szene zu einer Unterklasse von KCScene anstelle von SKScene machen : class ConfusedScene : KCScene {. Dann ConfusedScenemachen Sie einfach eine Funktion, um zu tun, was Sie wollen, wenn die Taste gedrückt wird. Ich habe das gemacht : func playButtonPushed() { /*do whatever happens when play button is pushed*/}. Warum dies funktioniert, ist zu kompliziert, um es hier zu erklären, aber Sie können hier über Schließungen lesen .
Mogelbuster
1

Meine Lösung zur Lösung dieses Problems wurde vollständig in SWIFT unter Verwendung von Verschlüssen geschrieben.

Es ist ziemlich einfach zu bedienen! https://github.com/txaidw/TWControls

class Test {
    var testProperty = "Default String"

    init() {
        let control = TWButton(normalColor: SKColor.blueColor(), highlightedColor: SKColor.redColor(), size: CGSize(width: 160, height: 80))
        control.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
        control.position.allStatesLabelText = "PLAY"
        control.addClosureFor(.TouchUpInside, target: self, closure: { (scene, sender) -> () in
            scene.testProperty = "Changed Property"
        })
    }

    deinit { println("Class Released..") }
}
txaidw
quelle
0

Ich hatte vor einiger Zeit eine Klasse für die Verwendung von SKSpriteNode als Schaltfläche erstellt. Sie finden es auf GitHub hier.

AGSpriteButton

Die Implementierung basiert auf UIButton. Wenn Sie also bereits mit iOS vertraut sind, sollten Sie es einfach finden, damit zu arbeiten.

Es kann auch ein Block oder eine SKAction zugewiesen werden, die ausgeführt werden soll, wenn die Taste gedrückt wird.

Es enthält auch eine Methode zum Einrichten eines Etiketts.

Eine Schaltfläche wird normalerweise wie folgt deklariert:

AGSpriteButton *button = [AGSpriteButton buttonWithColor:[UIColor redColor] andSize:CGSizeMake(300, 100)];
[button setLabelWithText:@"Button Text" andFont:nil withColor:nil];
button.position = CGPointMake(self.size.width / 2, self.size.height / 3);
[button addTarget:self selector:@selector(someSelector) withObject:nil forControlEvent:AGButtonControlEventTouchUpInside];
[self addChild:button];

Und das ist es. Du bist gut zu gehen.

ZeMoon
quelle
Gibt es einen Grund, warum wir SKColor nicht anstelle von UIColor verwenden könnten? Wenn wir UIColor verwenden, stecken wir unter iOS fest.
Maury Markowitz
Sie können genauso einfach SKColor anstelle von UIColor
ZeMoon
0

Und da wir alle nicht auf iOS abzielen, ist hier der Beginn eines Codes, den ich geschrieben habe, um die Mausinteraktion auf dem Mac zu handhaben.

Frage an die Gurus: Bietet MacOS Touch-Events bei Verwendung eines Trackpads an? Oder werden diese als Mausereignisse an SpriteKit gesendet?

Eine andere Frage für die Gurus: Sollte diese Klasse nicht richtig SKButton Node heißen ?

Wie auch immer, versuchen Sie das ...

#if os(iOS)
    override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!)  {
        let touch: AnyObject! = touches.anyObject()
        let touchLocation = touch.locationInNode(parent)

        if (!isEnabled) { return }

        isSelected = true
        if (targetTouchDown != nil && targetTouchDown!.respondsToSelector(actionTouchDown!)) {
            UIApplication.sharedApplication().sendAction(actionTouchDown!, to: targetTouchDown, from: self, forEvent: nil)
        }
    }

    override func touchesMoved(touches: NSSet!, withEvent event: UIEvent!)  {
        if (!isEnabled) { return }

        let touch: AnyObject! = touches.anyObject()
        let touchLocation = touch.locationInNode(parent)

        if (CGRectContainsPoint(frame, touchLocation)) {
            isSelected = true
        } else {
            isSelected = false
        }
    }

    override func touchesEnded(touches: NSSet!, withEvent event: UIEvent!) {
        if (!isEnabled) { return }

        isSelected = false

        if (targetTouchUpInside != nil && targetTouchUpInside!.respondsToSelector(actionTouchUpInside!)) {
            let touch: AnyObject! = touches.anyObject()
            let touchLocation = touch.locationInNode(parent)

            if (CGRectContainsPoint(frame, touchLocation) ) {
                UIApplication.sharedApplication().sendAction(actionTouchUpInside!, to: targetTouchUpInside, from: self, forEvent: nil)
            }
        }

        if (targetTouchUp != nil && targetTouchUp!.respondsToSelector(actionTouchUp!)) {
            UIApplication.sharedApplication().sendAction(actionTouchUp!, to: targetTouchUp, from: self, forEvent: nil)
        }
    }
#else

    // FIXME: needs support for mouse enter and leave, turning on and off selection

    override func mouseDown(event: NSEvent) {
        if (!isEnabled) { return }

        if (targetTouchDown != nil && targetTouchDown!.respondsToSelector(actionTouchDown!)) {
            NSApplication.sharedApplication().sendAction(actionTouchDown!, to: targetTouchDown, from: self)
        }
    }

    override func mouseUp(event: NSEvent) {
        if (!isEnabled) { return }

        if (targetTouchUpInside != nil && targetTouchUpInside!.respondsToSelector(actionTouchUpInside!)) {
            let touchLocation = event.locationInNode(parent)

            if (CGRectContainsPoint(frame, touchLocation) ) {
                NSApplication.sharedApplication().sendAction(actionTouchUpInside!, to: targetTouchUpInside, from: self)
            }
        }

        if (targetTouchUp != nil && targetTouchUp!.respondsToSelector(actionTouchUp!)) {
            NSApplication.sharedApplication().sendAction(actionTouchUp!, to: targetTouchUp, from: self)
        }
    }
#endif
Maury Markowitz
quelle
Soweit ich weiß, beobachtet Spritekit für OSX nur Mausmaterial: / Und ja, es sollte wahrscheinlich das Wort Node am Ende haben. Wie SKLabelNode.
CodyMace
0

Ich habe untergeordnet SKScene Klasse und das Problem des Lösen von Tastenanschlägen in diesem Projekt erreicht.

https://github.com/Prasad9/SpriteKitButton

Darin sollten alle Knoten benannt werden, die beim Tippen bekannt sein müssen.

In diesem Projekt können Sie nicht nur das Tippen auf eine Schaltfläche erkennen, sondern auch feststellen, ob die Berührung eines bestimmten Knotens begonnen oder beendet wurde.

Überschreiben Sie die folgende Methode in Ihrer Szenendatei, um eine Tap-Aktion zu erhalten.

- (void)touchUpInsideOnNodeName:(NSString *)nodeName atPoint:(CGPoint)touchPoint {
    // Your code here.
 }

Überschreiben Sie die folgende Methode in Ihrer Szenendatei, um den Beginn der Berührung eines bestimmten Körpers kennenzulernen.

 - (void)touchBeginOnNodeName:(NSString *)nodeName {
    // Your code here.
 }

Überschreiben Sie die folgende Methode in Ihrer Szenendatei, um das Ende der Berührung eines bestimmten Körpers kennenzulernen.

 - (void)touchEndedOnNodeName:(NSString *)nodeName {
    // Your code here.
 }
Prasad
quelle
0

Grafs Lösung hat ein Problem. Beispielsweise:

self.pauseButton = [[AGSKBButtonNode alloc] initWithImageNamed:@"ButtonPause"];
self.pauseButton.position = CGPointMake(0, 0);
[self.pauseButton setTouchUpInsideTarget:self action:@selector(pauseButtonPressed)];

[_hudLayer addChild:_pauseButton];

_hudLayer ist ein SKNode, eine Eigenschaft meiner Szene. Sie erhalten also eine Ausnahme, da die Methode touchEnded in SKButton ausgeführt wird. Es wird [SKSpriteNode pauseButtonPressed] aufgerufen, nicht mit Szene.

Die Lösung, um self.parent zu ändern, um das Ziel zu berühren:

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInNode:self.parent];

if ([self isEnabled] && CGRectContainsPoint(self.frame, touchPoint)) {
    if (_actionTouchUpInside){
        [_targetTouchUpInside performSelectorOnMainThread:_actionTouchUpInside withObject:_targetTouchUpInside waitUntilDone:YES];
    }
}
[self setIsSelected:NO];
if (_actionTouchUp){
    [_targetTouchUp performSelectorOnMainThread:_actionTouchUp withObject:_targetTouchUp waitUntilDone:YES];
}}
Kartpick
quelle
0

Eigentlich funktioniert das gut unter Swift 2.2 unter Xcode 7.3

Ich mag FTButtonNode ( richy486 / FTButtonNode.swift ), aber es ist nicht möglich, direkt während der Initialisierung eine andere Größe (statt der Standardtexturgröße) anzugeben. habe ich diese einfache Methode hinzugefügt:

Sie müssen dies unter der offiziellen benutzerdefinierten Init-Methode (ähnlich dieser) kopieren, damit Sie eine andere Init-Methode verwenden können:

init(normalTexture defaultTexture: SKTexture!, selectedTexture:SKTexture!, disabledTexture: SKTexture?, size:CGSize) {

        self.defaultTexture = defaultTexture
        self.selectedTexture = selectedTexture
        self.disabledTexture = disabledTexture
        self.label = SKLabelNode(fontNamed: "Helvetica");

        super.init(texture: defaultTexture, color: UIColor.whiteColor(), size: size)
        userInteractionEnabled = true

        //Creating and adding a blank label, centered on the button
        self.label.verticalAlignmentMode = SKLabelVerticalAlignmentMode.Center;
        self.label.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Center;
        addChild(self.label)

        // Adding this node as an empty layer. Without it the touch functions are not being called
        // The reason for this is unknown when this was implemented...?
        let bugFixLayerNode = SKSpriteNode(texture: nil, color: UIColor.clearColor(), size: size)
        bugFixLayerNode.position = self.position
        addChild(bugFixLayerNode)

    }

Eine andere wichtige Sache ist die "Auswahlzeit", ich habe gesehen, dass in den neuen Geräten (iPhone 6) irgendwann die Zeit zwischen touchesBeganund touchesEndedzu schnell ist und Sie die Änderungen zwischen defaultTextureund nicht sehen selectedTexture.

Mit dieser Funktion:

func dispatchDelay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

Sie können die touchesEndedMethode neu schreiben , um die Texturvariation korrekt anzuzeigen:

override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        if (!isEnabled) {
            return
        }

        dispatchDelay(0.2) {
            self.isSelected = false
        }

        if (targetTouchUpInside != nil && targetTouchUpInside!.respondsToSelector(actionTouchUpInside!)) {
            let touch: AnyObject! = touches.first
            let touchLocation = touch.locationInNode(parent!)

            if (CGRectContainsPoint(frame, touchLocation) ) {
                UIApplication.sharedApplication().sendAction(actionTouchUpInside!, to: targetTouchUpInside, from: self, forEvent: nil)
            }

        }

        if (targetTouchUp != nil && targetTouchUp!.respondsToSelector(actionTouchUp!)) {
            UIApplication.sharedApplication().sendAction(actionTouchUp!, to: targetTouchUp, from: self, forEvent: nil)
        }
}
Alessandro Ornano
quelle
0

Leider hat SpriteKit keinen Schaltflächenknoten, ich weiß nicht warum, weil es eine sehr nützliche Steuerung ist. Also habe ich beschlossen, meine eigenen zu erstellen und über CocoaPods zu teilen, bitte benutze es OOButtonNode . Schaltflächen können Text / Hintergrund oder Bilder verwenden, die in Swift 4 geschrieben wurden.

Oleg O.
quelle
0

Hier ist eine einfache Schaltfläche, die mit dem modernen Swift (4.1.2) geschrieben wurde.

Eigenschaften

  • Es werden 2 Bildnamen akzeptiert, 1 für den Standardstatus und einer für den aktiven Status
  • Der Entwickler kann die Verschlüsse touchBeganCallbackund festlegen touchEndedCallback, um benutzerdefiniertes Verhalten hinzuzufügen

Code

import SpriteKit

class SpriteKitButton: SKSpriteNode {

    private let textureDefault: SKTexture
    private let textureActive: SKTexture

    init(defaultImageNamed: String, activeImageNamed:String) {
        textureDefault = SKTexture(imageNamed: defaultImageNamed)
        textureActive = SKTexture(imageNamed: activeImageNamed)
        super.init(texture: textureDefault, color: .clear, size: textureDefault.size())
        self.isUserInteractionEnabled = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("Not implemented")
    }

    var touchBeganCallback: (() -> Void)?
    var touchEndedCallback: (() -> Void)?

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.texture = textureActive
        touchBeganCallback?()
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        self.texture = textureDefault
        touchEndedCallback?()
    }
}

Wie man es benutzt

class GameScene: SKScene {

    override func didMove(to view: SKView) {

        // 1. create the button
        let button = SpriteKitButton(defaultImageNamed: "default", activeImageNamed: "active")

        // 2. write what should happen when the button is tapped
        button.touchBeganCallback = {
            print("Touch began")
        }

        // 3. write what should happen when the button is released
        button.touchEndedCallback = {
            print("Touch ended")
        }

        // 4. add the button to the scene
        addChild(button)

    }
}
Luca Angeletti
quelle