1

当事件发生时,SKNode 子类应该如何通知父场景?

我有一个 SKNode 子类,它根据游戏逻辑通过适当地重绘或删除节点来处理触摸事件。通知父场景(或其他同级 SKNode 实例)事件发生的 iOS 最佳实践是什么?我应该使用 NSNotification 中心吗?某种形式的委托?我想避免紧耦合。我可以想到几种方法来完成它,但想知道是否有标准做法,因为似乎有非游戏 MCV iOS 设计(例如用于模型之间通信的 NSNotification,或模型 -> 控制器,以及委托或目标 -视图操作 -> 控制器等)

从概念上讲,我喜欢 SKNode 子类本质上充当“视图”角色的想法,并解释触摸事件,然后将“语义”游戏信息传达给包含场景或其他节点。

// SampleScene.m

#include "SampleScene.h"
#include "SampleBallNode.h"

@implementation SampleScene
- (void)didMoveToView:(SKView *)view {
  if (!self.contentCreated) {
    [self createSceneContents];
    self.contentCreated = YES;
  }
}

- (void)createSceneContents {
  self.backgroundColor = [SKColor whiteColor];
  self.scaleMode = SKSceneScaleModeAspectFit;
  [self addBall];
}

- (void)addBall {
  SampleBallNode *ball = [[SampleBallNode alloc] init];
  ball.position = CGPointMake(100, 100);
  [self addChild:ball];
}

@end

// SampleBallNode.m

#include "SampleBallNode.h"

- (id)init {
  if ([super init]) {
    self.name = @"ball";
    self.userInteractionEnabled = YES;
    [self drawSelf];
  }
  return self;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  [self removeFromParent];

  // TELL SCENE THAT I'VE DIED HERE... TO UPDATE SCORE OR SOMETHING...
}

- (void)drawSelf {
  UIBezierPath* ovalPath = [UIBezierPath bezierPathWithOvalInRect ...

  SKShapeNode *tokenBall = [[SKShapeNode alloc] init];
  tokenBall.path = ovalPath.CGPath;
  [self addChild:tokenBall];
}

@end
4

3 回答 3

7

对于一般的触摸和输入之类的东西,我会避免在每个单独的实例中处理它们。不仅因为它效率低下,而且您也没有简单的方法来决定谁接收触摸以及触摸应该如何传播到其他节点。

简单的情况:两个重叠的节点——你只想删除最上面的节点,还是只删除任何被触摸的节点,或者全部在触摸的位置?在这种情况下,最好在中心位置接收输入,然后遍历“可触摸”节点。

除此之外:

  • 使用委托,否则您将通过直接向另一个对象发送消息来实现紧密耦合
  • 如果您有多个可能想要对事件做出反应的潜在代表,并且该事件不会经常发生(即绝对不是每一帧),请使用通知
  • 当您完全懒惰并且不在乎时使用单例

从概念上讲,您将通过行为在Kobold Kit中找到很好的示例和解决方案,行为是游戏组件的一种形式。还包括一个每个节点的模型存储类 KKModel,它允许您在任何节点中存储诸如分数之类的东西,而不必仅为变量添加子类。

例如:

[self.scene.model setDouble:score forKey:@"score"];

然后,您可以只使用标签类中的更新方法来获取分数:

double score = [self.scene.model doubleForKey:@"score"];

避免了消息传递、委托、通知的需要,同时牺牲了(一点)效率。

于 2013-10-18T17:29:17.283 回答
1

我不知道文档中的任何内容,但是我个人使用 NSNotification 中心,因为它具有非常松散的耦合,并且我将游戏逻辑和视图分开。这对我来说效果很好,因为我最初是在 cocos2d 中开始我的项目并将其移至 spritekit。(我将离开这里的原因)。但是由于我使用它,过渡非常容易。

我让我的节点“监视”来自游戏模型的某些已发布通知,并让它们做出相应的响应或更新。对于使用交互,我会将其回发,模型将监视相应的通知。

于 2013-10-18T17:30:04.383 回答
0

这是一个使用 SKSpriteNode 子类的示例。它的委托 BallDelegate 有 4 个可选方法。如果你想要一些必需的方法,你可以添加没有可选的方法

@objc protocol BallDelegate {
    @objc optional func ball(ball:Ball, touchesBegan touches: Set<UITouch>, withEvent event: UIEvent?)
    @objc optional func ball(ball:Ball, touchesMoved touches: Set<UITouch>, withEvent event: UIEvent?)
    @objc optional func ball(ball:Ball, touchesEnded touches: Set<UITouch>, withEvent event: UIEvent?)
    @objc optional func ball(ball:Ball, touchesCancelled touches: Set<UITouch>?, withEvent event: UIEvent?)
}

class Ball: SKSpriteNode {

   var delegate:BallDelegate?


   override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?){
        self.delegate?.ball?(self, touchesBegan: touches, withEvent: event)
   }

   override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
        self.delegate?.ball?(self, touchesMoved: touches, withEvent: event)
    }

   override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        self.delegate?.ball?(self, touchesEnded: touches, withEvent: event)
   }

   override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
       self.delegate?.ball?(self, touchesCancelled: touches, withEvent: event)
   }
}

如果您想了解更多信息,可以查看 Swift 的文档:

Swift 编程语言 (Swift 2.2)

于 2016-06-21T19:57:51.177 回答