【问题标题】:How should SKNode subclasses communicate with parent Scenes and sibling nodes?SKNode 子类应该如何与父场景和兄弟节点通信?
【发布时间】:2023-04-08 22:06:01
【问题描述】:

当事件发生时,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

【问题讨论】:

    标签: ios objective-c sprite-kit


    【解决方案1】:

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

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

    除此之外:

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

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

    例如:

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

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

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

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

    【讨论】:

    • 谢谢。有趣且非常有帮助。后续问题:在遍历列表时检测是否/哪个可触摸节点被触摸的最简单方法是什么?这真的可能在计算上更有效率吗?非常感谢
    • CGRectContainsPoint 与可触摸节点点的转换为世界空间。更有效率的是,您可以按照节点可能被按下的可能性顺序处理节点(如果有偏好),并且绝对可以在找到一个节点后尽早退出,否则所有节点都必须执行检查.
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      这是一个使用 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)

      【讨论】:

        猜你喜欢
        • 2021-11-18
        • 2015-02-17
        • 1970-01-01
        • 2014-02-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多