我发现UIButtons
与SKScene
不能很好地工作,所以我试图在SKNode
的子类中创建一个按钮SpriteKit
。在SKScene中设置按钮
我希望它的工作方式是,如果我在SKScene
中初始化一个按钮并启用了触摸事件,那么当按下按钮时,该按钮将调用我的SKScene
中的方法。
我会很感激任何意见,会导致我找到解决这个问题。谢谢。
我发现UIButtons
与SKScene
不能很好地工作,所以我试图在SKNode
的子类中创建一个按钮SpriteKit
。在SKScene中设置按钮
我希望它的工作方式是,如果我在SKScene
中初始化一个按钮并启用了触摸事件,那么当按下按钮时,该按钮将调用我的SKScene
中的方法。
我会很感激任何意见,会导致我找到解决这个问题。谢谢。
你可以使用一个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...
}
}
如果你添加一个iVar作为按钮,你可以删除名称检查,并使用if([_fireNode containsPoint:location])做同样的事情,只是不同而已。 – DogCoffee
比较字符串是一个肮脏的解决方案。尽管@Smick的解决方案更好,但是实现这一目标是否还有其他更简洁的方法? –
嘿,我们不能像SpLabelNode那样在SpriteKit中添加按钮? –
我做了我自己的纽扣类我正在合作。 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仍然很陌生。 *
感谢您的分享。是否有你使用'objc_msgSend'而不是'[target performSelector:selector]'的原因? –
啊,是的,补ARC。我忘记了这个警告:|这里有一个很好的工作,如果你有兴趣http://stackoverflow.com/questions/11895287/performselector-arc-warning –
上述代码是伟大的,但我在尝试使用时出现错误 - (void)changeToScene :(SKButtonNode *)sender {}作为@selector。如果可以的话,我宁愿使用单一方法通过使用sender.name来切换场景。 –
我使用了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);
}
请格式化您的文章和源代码,这是非常难以阅读这条路! –
我不久前创建了一个使用SKSpriteNode作为按钮的类。你可以在GitHub上找到它。
它的实现是基于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];
就是这样。你很好走。
是否有任何原因,我们不能使用SKColor而不是UIColor?如果我们使用UIColor,我们就停留在iOS上。 –
您可以轻松使用SKColor而不是UIColor – ZeMoon
对于在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)
}
}
}
如果需要,还可以使用的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
原点在左下角。
编辑:我做了一个github回购我的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)
}
}
}
下面是根据菲利普的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")
})
希望这有助于。
因为我们所有人都没有针对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
据我所知Spritekit for OSX只观察鼠标的东西:/是的,它可能应该有结束字Node。像SKLabelNode一样。 – CodyMace
我的解决方案来解决这个问题,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..") }
}
我有子类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.
}
格拉夫的解决方案有一个问题。 例如:
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];
}}
其实这项工作做得很好的雨燕2.2上的Xcode 7.3
我喜欢FTButtonNode(richy486/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)在touchesBegan
和touchesEnded
之间的某个时间段过快,您没有看到defaultTexture
和selectedTexture
之间的更改。
有了这个功能:
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)
}
}
什么了很多对这一问题的大型解决方案的!对于那些让它变得如此糟糕的硬核滚轮来说,你一定会喜欢!我已经子类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)
}
}
的一个警告,如果实现touchesBegan
,touchesMoved
,touchesEnded
是,和/或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
}
}
这是如何确保playButtonPushed完成功能可用的?或者我把playButtonPushed函数放在哪里,以确保KScene实例可以访问它,我假设它是按钮? – Confused
@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
我不相信任何上述选项,所以根据我创建my own solution最新Swift4。
不幸的是SpriteKit没有按钮节点,我不知道为什么,因为它是非常有用的控件。所以我决定创建我自己的,并通过CocoaPods共享,请使用它OOButtonNode。 按钮可以使用文字/背景或图像,用Swift 4编写。
我正在寻找更多的学习经验以及解决方案。我认为正确的解决方案是将SKScene设置为按钮的代理,但我不确定如何执行此操作。我可以将SKScene设置为按钮的实例变量并调用它的方法吗? – AlexHeuman
您可以做很多事情,委托或更灵活地使用NSNotification,以便任何节点都可以响应。如果您使用委托,请确保将委托属性设置为弱。 – LearnCocos2D
我发现[此代码](https://github.com/sgerhardt/SpriteKitButton)有助于创建精灵套件按钮。它扩展了SKSpriteKitNode,并允许您轻松地将文本添加到按钮。 – sager89