2013-09-29 68 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编写。