SKNode子类应该如何与父场景和兄弟节点进行通信?

时间:2013-10-18 17:05:40

标签: ios objective-c sprite-kit

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

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

从概念上讲,我喜欢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

3 个答案:

答案 0 :(得分:7)

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

简单案例:两个重叠的节点 - 您是否只想要移除最顶层的节点,或者只是想要触摸的任何一个节点,还是所有触摸位置?在这种情况下,最好在中心位置接收输入,然后迭代“可触摸”节点。

除此之外:

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

从概念上讲,您可以通过行为找到Kobold Kit中的好示例和解决方案,这些行为是game components的一种形式。还包括一个每节点模型存储类KKModel,它允许您在任何节点中存储得分等内容,而无需仅为变量添加子类。

例如:

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

然后,您只需在标签类中使用update方法即可获得分数:

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

在影响(一点点)效率的同时,避免了消息传递,委派和通知的需要。

答案 1 :(得分:1)

我没有意识到文档中的任何内容,但我个人使用NSNotification中心,因为它有一个非常松散的耦合,我将游戏逻辑和视图分开。这对我来说非常好,因为我最初在cocos2d中开始了我的项目,并将其移动到spritekit。(我将留下原因离开这里)。但由于我使用它,过渡很容易。

我让我的节点'观察'来自游戏模型的某些发布的通知,并让他们相应地响应或更新。对于使用交互,我会回发它,模型会监视相应的通知。

答案 2 :(得分: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的文档:

The Swift Programming Language (Swift 2.2)