NSNotificationCenter 与 SpriteKit
NSNotificationCenter with SpriteKit
我正在使用 NSNotificationCenter 在键盘上按下按键时发出通知。在场景之间移动时,如果在按下导致场景转换的键后过快地按下另一个键,应用程序会崩溃。不知道是不是前一个场景不再接收通知,还是下一个场景的通知观察者没有设置。我能做些什么来阻止这种情况发生?这是两个不同场景的代码和处理通知的自定义视图。本质上,我在 CustomSKView
中发布按键通知,然后我在此处未列出的名为 keyPressed:
的方法中处理各个场景中的按键。
LevelSelectScene.m
@implementation LevelSelectScene
-(void)didMoveToView:(SKView *)view {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyPressed:) name:@"KeyPressedNotificationKey" object:nil];
//perform scene setup here
...
}
-(void)willMoveFromView:(SKView *)view {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:@"KeyPressedNotificationKey"
object:nil];
//perform additional cleanup before moving to next scene
...
}
Menu.m
-(void) didMoveToView:(SKView *)view {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyPressed:) name:@"KeyPressedNotificationKey" object:nil];
//perform menu setup here
...
}
-(void) willMoveFromView:(SKView *)view {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:@"KeyPressedNotificationKey"
object:nil];
//perform additional cleanup before moving to next scene
...
}
CustomSKView.m
#import "CustomSKView.h"
@implementation CustomSKView:SKView {
}
- (id) initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
return self;
}
- (void) keyDown:(NSEvent *)theEvent {
[[NSNotificationCenter defaultCenter] postNotificationName:@"KeyPressedNotificationKey"
object:nil
userInfo:@{@"keyCode" : @(theEvent.keyCode)}];
}
@end
编辑:堆栈跟踪
2015-08-15 05:47:08.199 PianoKeyboardTest[21854:4643404] -[NSPathStore2 keyPressed:]: unrecognized selector sent to instance 0x10050d110
2015-08-15 05:47:08.199 PianoKeyboardTest[21854:4643404] -[NSPathStore2 keyPressed:]: unrecognized selector sent to instance 0x10050d110
2015-08-15 05:47:08.200 PianoKeyboardTest[21854:4643404] (
0 CoreFoundation 0x00007fff8575803c __exceptionPreprocess + 172
1 libobjc.A.dylib 0x00007fff9227376e objc_exception_throw + 43
2 CoreFoundation 0x00007fff8575b0ad -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
3 CoreFoundation 0x00007fff856a0e24 ___forwarding___ + 1028
4 CoreFoundation 0x00007fff856a0998 _CF_forwarding_prep_0 + 120
5 CoreFoundation 0x00007fff8571445c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
6 CoreFoundation 0x00007fff85604634 _CFXNotificationPost + 3140
7 Foundation 0x00007fff83e8e9d1 -[NSNotificationCenter postNotificationName:object:userInfo:] + 66
8 PianoKeyboardTest 0x000000010001d50e -[CustomSKView keyDown:] + 270
9 AppKit 0x00007fff8ba1c11b -[NSWindow _reallySendEvent:isDelayedEvent:] + 5452
10 AppKit 0x00007fff8b3add76 -[NSWindow sendEvent:] + 470
11 AppKit 0x00007fff8b3aa9b1 -[NSApplication sendEvent:] + 4199
12 AppKit 0x00007fff8b2d3c68 -[NSApplication run] + 711
13 AppKit 0x00007fff8b250354 NSApplicationMain + 1832
14 PianoKeyboardTest 0x0000000100005322 main + 34
15 libdyld.dylib 0x00007fff8f1ee5c9 start + 1
)
编辑:解决方案
以下是我对 CustomSKView 所做的更改。
#import "CustomSKView.h"
@implementation CustomSKView:SKView {
// Add instance variables here
}
- (id) initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
// Allocate and initialize your instance variables here
}
return self;
}
- (void) keyDown:(NSEvent *)theEvent {
[[NSNotificationCenter defaultCenter] postNotificationName:@"KeyPressedNotificationKey"
object:nil
userInfo:@{@"keyCode" : @(theEvent.keyCode)}];
}
//overridden version of SKScene's presentScene: transition: method
-(void) presentScene:(SKScene *)scene transition:(SKTransition *)transition {
[[NSNotificationCenter defaultCenter] removeObserver:self.scene
name:@"KeyPressedNotificationKey"
object:nil];
[super presentScene:scene transition:transition];
}
@end
问题似乎是时机问题。您需要在转换开始之前删除通知,但是 SKView
没有为此提供方便的挂钩。
一种可能的管理方法是继承 SKView
以提供添加和删除观察者的机制,并有可能使用多个通知;一个用于击键,另一个用于转换 start/ends。当触发 transition start 通知时,该子类将删除击键观察器。当转换完成时,可以通知它重新观察击键。然而这听起来确实很复杂。
要在游戏转换到新场景时从当前 SKScene
中移除观察者,请覆盖自定义视图 class 中的 presentScene
方法,移除观察者,然后调用超级 class 的 presentScene
:
- (void) presentScene:(SKScene *)scene transition:(SKTransition *)transition {
[[NSNotificationCenter defaultCenter] removeObserver:self.scene
name:@"KeyPressedNotificationKey"
object:nil];
[super presentScene:scene transition:transition];
}
我正在使用 NSNotificationCenter 在键盘上按下按键时发出通知。在场景之间移动时,如果在按下导致场景转换的键后过快地按下另一个键,应用程序会崩溃。不知道是不是前一个场景不再接收通知,还是下一个场景的通知观察者没有设置。我能做些什么来阻止这种情况发生?这是两个不同场景的代码和处理通知的自定义视图。本质上,我在 CustomSKView
中发布按键通知,然后我在此处未列出的名为 keyPressed:
的方法中处理各个场景中的按键。
LevelSelectScene.m
@implementation LevelSelectScene
-(void)didMoveToView:(SKView *)view {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyPressed:) name:@"KeyPressedNotificationKey" object:nil];
//perform scene setup here
...
}
-(void)willMoveFromView:(SKView *)view {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:@"KeyPressedNotificationKey"
object:nil];
//perform additional cleanup before moving to next scene
...
}
Menu.m
-(void) didMoveToView:(SKView *)view {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyPressed:) name:@"KeyPressedNotificationKey" object:nil];
//perform menu setup here
...
}
-(void) willMoveFromView:(SKView *)view {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:@"KeyPressedNotificationKey"
object:nil];
//perform additional cleanup before moving to next scene
...
}
CustomSKView.m
#import "CustomSKView.h"
@implementation CustomSKView:SKView {
}
- (id) initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
return self;
}
- (void) keyDown:(NSEvent *)theEvent {
[[NSNotificationCenter defaultCenter] postNotificationName:@"KeyPressedNotificationKey"
object:nil
userInfo:@{@"keyCode" : @(theEvent.keyCode)}];
}
@end
编辑:堆栈跟踪
2015-08-15 05:47:08.199 PianoKeyboardTest[21854:4643404] -[NSPathStore2 keyPressed:]: unrecognized selector sent to instance 0x10050d110
2015-08-15 05:47:08.199 PianoKeyboardTest[21854:4643404] -[NSPathStore2 keyPressed:]: unrecognized selector sent to instance 0x10050d110
2015-08-15 05:47:08.200 PianoKeyboardTest[21854:4643404] (
0 CoreFoundation 0x00007fff8575803c __exceptionPreprocess + 172
1 libobjc.A.dylib 0x00007fff9227376e objc_exception_throw + 43
2 CoreFoundation 0x00007fff8575b0ad -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
3 CoreFoundation 0x00007fff856a0e24 ___forwarding___ + 1028
4 CoreFoundation 0x00007fff856a0998 _CF_forwarding_prep_0 + 120
5 CoreFoundation 0x00007fff8571445c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
6 CoreFoundation 0x00007fff85604634 _CFXNotificationPost + 3140
7 Foundation 0x00007fff83e8e9d1 -[NSNotificationCenter postNotificationName:object:userInfo:] + 66
8 PianoKeyboardTest 0x000000010001d50e -[CustomSKView keyDown:] + 270
9 AppKit 0x00007fff8ba1c11b -[NSWindow _reallySendEvent:isDelayedEvent:] + 5452
10 AppKit 0x00007fff8b3add76 -[NSWindow sendEvent:] + 470
11 AppKit 0x00007fff8b3aa9b1 -[NSApplication sendEvent:] + 4199
12 AppKit 0x00007fff8b2d3c68 -[NSApplication run] + 711
13 AppKit 0x00007fff8b250354 NSApplicationMain + 1832
14 PianoKeyboardTest 0x0000000100005322 main + 34
15 libdyld.dylib 0x00007fff8f1ee5c9 start + 1
)
编辑:解决方案
以下是我对 CustomSKView 所做的更改。
#import "CustomSKView.h"
@implementation CustomSKView:SKView {
// Add instance variables here
}
- (id) initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
// Allocate and initialize your instance variables here
}
return self;
}
- (void) keyDown:(NSEvent *)theEvent {
[[NSNotificationCenter defaultCenter] postNotificationName:@"KeyPressedNotificationKey"
object:nil
userInfo:@{@"keyCode" : @(theEvent.keyCode)}];
}
//overridden version of SKScene's presentScene: transition: method
-(void) presentScene:(SKScene *)scene transition:(SKTransition *)transition {
[[NSNotificationCenter defaultCenter] removeObserver:self.scene
name:@"KeyPressedNotificationKey"
object:nil];
[super presentScene:scene transition:transition];
}
@end
问题似乎是时机问题。您需要在转换开始之前删除通知,但是 SKView
没有为此提供方便的挂钩。
一种可能的管理方法是继承 SKView
以提供添加和删除观察者的机制,并有可能使用多个通知;一个用于击键,另一个用于转换 start/ends。当触发 transition start 通知时,该子类将删除击键观察器。当转换完成时,可以通知它重新观察击键。然而这听起来确实很复杂。
要在游戏转换到新场景时从当前 SKScene
中移除观察者,请覆盖自定义视图 class 中的 presentScene
方法,移除观察者,然后调用超级 class 的 presentScene
:
- (void) presentScene:(SKScene *)scene transition:(SKTransition *)transition {
[[NSNotificationCenter defaultCenter] removeObserver:self.scene
name:@"KeyPressedNotificationKey"
object:nil];
[super presentScene:scene transition:transition];
}