2013-09-29 88 views
73

我發現UIButtonsSKScene不能很好地工作,所以我試圖在SKNode的子類中創建一個按鈕SpriteKit在SKScene中設置按鈕

我希望它的工作方式是,如果我在SKScene中初始化一個按鈕並啓用了觸摸事件,那麼當按下按鈕時,該按鈕將調用我的SKScene中的方法。

我會很感激任何意見,會導致我找到解決這個問題。謝謝。

+2

我正在尋找更多的學習經驗以及解決方案。我認爲正確的解決方案是將SKScene設置爲按鈕的代理,但我不確定如何執行此操作。我可以將SKScene設置爲按鈕的實例變量並調用它的方法嗎? – AlexHeuman

+0

您可以做很多事情,委託或更靈活地使用NSNotification,以便任何節點都可以響應。如果您使用委託,請確保將委託屬性設置爲弱。 – LearnCocos2D

+0

我發現[此代碼](https://github.com/sgerhardt/SpriteKitButton)有助於創建精靈套件按鈕。它擴展了SKSpriteKitNode,並允許您輕鬆地將文本添加到按鈕。 – sager89

回答

101

你可以使用一個SKSpriteNode作爲你的按鈕,然後當用戶觸摸時,檢查它是否是觸摸的節點。使用SKSpriteNode的名稱屬性標識節點:

//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; 
} 

添加節點到場景:

[self addChild: [self fireButtonNode]]; 

手柄亮點:

//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... 
    } 
} 
+8

如果你添加一個iVar作爲按鈕,你可以刪除名稱檢查,並使用if([_fireNode containsPoint:location])做同樣的事情,只是不同而已。 – DogCoffee

+3

比較字符串是一個骯髒的解決方案。儘管@Smick的解決方案更好,但是實現這一目標是否還有其他更簡潔的方法? –

+0

嘿,我們不能像SpLabelNode那樣在SpriteKit中添加按鈕? –

51

我做了我自己的鈕釦類我正在合作。 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); 
} 

一個例子:要初始化按鈕,你寫了下面幾行:

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]; 

而且你所需要的「buttonAction '你班上的方法。 *本課程無論在任何情況下均無任何擔保。我對objective-c仍然很陌生。 *

+0

感謝您的分享。是否有你使用'objc_msgSend'而不是'[target performSelector:selector]'的原因? –

+2

啊,是的,補ARC。我忘記了這個警告:|這裏有一個很好的工作,如果你有興趣http://stackoverflow.com/questions/11895287/performselector-arc-warning –

+0

上述代碼是偉大的,但我在嘗試使用時出現錯誤 - (void)changeToScene :(SKButtonNode *)sender {}作爲@selector。如果可以的話,我寧願使用單一方法通過使用sender.name來切換場景。 –

3

我使用了SKButton類Graf

我使用SKButton做場景導航。即當用戶按下SKButton時呈現另一場景。我在touchesEnded->[self setIsSelected:NO]處得到了EXC_BAD_ACCESS錯誤。這種情況在具有快速CPU的最新iPad上尤其頻繁發生。

檢查並排除故障後,我意識到當調用setIsSelected函數時,SKButton對象已被「釋放」。這是因爲我使用SKButton導航到下一個場景,這也意味着當前場景可以隨時解除分配。

我做了一個小的改變,把setIsSelected放在「else」部分如下。

希望這有助於其他開發者也看到相同的錯誤。

(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); 
} 
+0

請格式化您的文章和源代碼,這是非常難以閱讀這條路! –

0

我不久前創建了一個使用SKSpriteNode作爲按鈕的類。你可以在GitHub上找到它。

AGSpriteButton

它的實現是基於UIButton的,所以如果你已經熟悉了iOS上,你會發現它易於使用。

當按鈕被按下時,它也可以被分配一個塊或一個SKAction來執行。

它還包括一種設置標籤的方法。

A鍵通常會宣稱,像這樣:

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]; 

就是這樣。你很好走。

+0

是否有任何原因,我們不能使用SKColor而不是UIColor?如果我們使用UIColor,我們就停留在iOS上。 –

+0

您可以輕鬆使用SKColor而不是UIColor – ZeMoon

18

對於在Swift中編寫遊戲的人來說! 我已經將Graf的解決方案的基本部分改寫成了一個快速類。希望它能幫助:

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) 
     } 
    } 

} 
5

如果需要,還可以使用的UIButton(或任何其他的UIView)。

創建SKScene時,它尚不存在於SKView中。您應該在SKScene子類中實施didMoveToView:。此時,您可以訪問SKView場景,您可以添加UIKit對象。對於美貌,我褪色他們...

- (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) { 
        ; 
        }]; 
} 

您需要特意從場景中刪除他們,當你離開過渡,當然,除非它非常有意義他們留在那裏。

- (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]; 
       }]; 
    } 
} 

對於那些不熟悉編程方式創建UIButton,這裏一個例子(你可以做一個100分不同的事情在這裏)...

- (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; 
} 

提醒:UIView原點在左上角, SKScene原點在左下角。

2

編輯:我做了一個github回購我的SKButtonNode,我希望保持當前和更新迅速發展!

SKButtonNode


不幸的是我還不能對菲利普在斯威夫特迅速落實SKButton的評論。超級快樂,他在Swift中做到了這一點!但是,我注意到他沒有包含向按鈕添加文本的功能。這對我來說是一個巨大的功能,所以您不必爲每個按鈕創建單獨的資源,而只需創建背景並添加動態文本。

我添加了一個簡單的函數來添加一個文本標籤到SKButton。它可能並不完美 - 我和其他人一樣,對Swift很陌生!隨時發表評論,並幫助我將其更新到最佳狀態。希望你們喜歡!

//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   
    } 

樣品創建按鈕:

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); 

全部類別如下:

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) 
    } 
} 

}

+0

更新了swift 2.1的代碼:https://gist.github.com/richy486/5d408c442ac1c0c2891f – richy

+0

..我更新到Swift 3:https:// github。com/jglasse/SKButtonSwift3 – jglasse

2

下面是根據菲利普的SWIFT代碼另一個版本。我只是簡化了一點,並允許其採取塊,而不是隻選擇:

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) 
    } 
} 
} 

使用方法如下:

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

希望這有助於。

0

因爲我們所有人都沒有針對iOS,所以我開始編寫一些代碼來處理Mac上的鼠標交互。

大師們的問題:使用觸控板時MacOS是否提供觸摸事件?或者這些被作爲鼠標事件發送到SpriteKit?

對於大師的另一個問題,不應該被稱爲SKButton的這個類節點

不管怎樣,試試這個...

#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 
+0

據我所知Spritekit for OSX只觀察鼠標的東西:/是的,它可能應該有結束字Node。像SKLabelNode一樣。 – CodyMace

1

我的解決方案來解決這個問題,SWIFT完全寫入,使用閉。

它的使用非常簡單! 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..") } 
} 
0

我有子類SKScene類實現在這個項目解決按鈕水龍頭的問題。

https://github.com/Prasad9/SpriteKitButton

在這裏面,所有這一切都需要在挖掘被稱爲節點應該被命名。

除了檢測按鈕敲擊之外,該項目還使您能夠檢測特定節點上的觸摸是否已啓動或結束。

要獲取點按操作,請在場景文件中覆蓋以下方法。

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

要了解特定主體的觸摸開始情況,請在場景文件中覆蓋以下方法。

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

要了解特定主體上的觸摸結束,請在場景文件中覆蓋以下方法。

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

格拉夫的解決方案有一個問題。 例如:

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

[_hudLayer addChild:_pauseButton]; 

_hudLayer是SKNode,我的場景的屬性。所以,你會得到異常,因爲SKButton中的方法touchesEnded。它會調用[SKSpriteNode pauseButtonPressed],而不是場景。

的解決方案來改變self.parent觸及目標:

- (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]; 
}} 
0

其實這項工作做得很好的雨燕2.2的Xcode 7.3

我喜歡FTButtonNoderichy486/FTButtonNode.swift ),但它無法在初始化過程中直接指定其他尺寸(而不是默認紋理尺寸),因此我添加了這個簡單方法:

您必須複製,官方定製的init方法(類似於此)下讓你擁有另一種init方法來使用:

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) 

    } 

另一個重要的事情是「選擇時間」,我已經看到了,在新設備(iPhone 6)在touchesBegantouchesEnded之間的某個時間段過快,您沒有看到defaultTextureselectedTexture之間的更改。

有了這個功能:

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

你可以重新寫touchesEnded方法來正確顯示紋理變化:

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) 
     } 
} 
2

什麼了很多對這一問題的大型解決方案的!對於那些讓它變得如此糟糕的硬核滾輪來說,你一定會喜歡!我已經子類SKScene,它需要一個函數調用註冊任何節點行事像一個UIButton!這裏是類:

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 
    } 
} 
//------------------------------------------------------------------------------------ 

}

它包括很多的想法我還沒有實現和代碼的一些解釋,而只是將其複製並粘貼到你的項目,你可以使用它在你自己的場景中。下面是一個完整的例子用法:

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) 
} 
} 

的一個警告,如果實現touchesBegantouchesMovedtouchesEnded是,和/或touchesCancelled你必須調用SUPER!否則它將無法工作。

請注意,在這個例子中,真的只有一行代碼,你需要給任何NODE UIButton特徵!這是這條線:

addButton(playButton, withCompletionHandler: playButtonPushed) 

我總是打開想法和建議。留下他們的評論和快樂編碼!

糟糕,我忘了提及我使用這個漂亮的擴展。你可以把它從一個擴展中取出(因爲你可能不需要它在每個節點中)並將它放在我的課程中。我只在一個地方使用它。

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 
} 

}

+0

這是如何確保playButtonPushed完成功能可用的?或者我把playButtonPushed函數放在哪裏,以確保KScene實例可以訪問它,我假設它是按鈕? – Confused

+0

@Confused你會讓自己的場景成爲KCScene的一個子類而不是SKScene:'class ConfusedScene:KCScene {'。然後在'ConfusedScene'裏面就可以創建一個函數來按下你想要的按鈕。我做到了這一點:'func playButtonPushed(){/ *當按下播放按鈕* /}時做任何事情。爲什麼這個作品太牽扯到這裏解釋,但你可以閱讀關於[關閉] [這裏](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html#//apple_ref/ DOC/UID/TP40014097-CH11-ID94)。 – mogelbuster

0

不幸的是SpriteKit沒有按鈕節點,我不知道爲什麼,因爲它是非常有用的控件。所以我決定創建我自己的,並通過CocoaPods共享,請使用它OOButtonNode。 按鈕可以使用文字/背景或圖像,用Swift 4編寫。