如何从精灵套件场景引用当前视图控制器



我很难找到这个问题的答案,我认为这个问题没有那么难。

如何从SKScene引用在视图控制器上定义的方法和特性?

在此基础上:如何从另一个SKScene中加载的SKScene引用ViewController?

比我的"反向引用"答案更好的选择是使用协议显式定义SKScene子类希望UIViewController子类实现的方法。以下是您需要经历的步骤。

1) 定义UIViewController需要采用并遵守的协议(合同):

protocol GameManager{
func loadHomeScene()
func loadGameScene(level:Int)
func loadHighScoreScene()
}

2)UIViewController子类现在采用协议:

class GameViewController: UIViewController,GameManager{

3)GameScene获得引用UIViewController:的ivar

var gameManager:GameManager?

4) 当GameManager需要调用UIViewController时,它会这样做:

gameManager.loadHighScoreScene()

我们现在对GameManager有了一个定义明确的契约。任何其他从事该项目的开发人员(以及当前开发人员未来的自己)现在都知道GameManager应该实现哪些方法。这意味着我们可以很容易地将此代码移植到不同的项目(从而移植到一个不同的UIViewController),并且只要新的UIViewController采用GameManager协议,它仍然可以工作。

类似地,如果我们决定将场景导航代码移出视图控制器,而将其放置在一个单例类中,则该单例类只需要采用GameManager协议。

使用哪种方法

这个线程提供了4种解决问题的方法,我个人已经使用了所有4种方法。以下是我对它们的看法:

1)对UIViewController子类的调用进行硬编码:

(self.view!.window!.rootViewController as! GameViewController).loadGameScene(1)

优势:1行代码!

缺点:两个类的紧密耦合,没有显式契约。

2)使用指向UIViewController:的指针

优点:只需要很少的代码,不需要任何协议。

缺点:2类紧耦合。没有明确的合同。

3)使用NSNotificationCenter:

优点:松耦合类。

缺点:需要更多的代码来设置广播程序、接收器和回调方法。更难跟踪和调试代码。没有明确的合同。通知的"一对多"功能很好,但对我们没有帮助。

4)创建协议:

优点:明确的合同。MVC之神会很高兴的。

缺点:要写的代码更多(但比通知少得多)。

您应该避免从SKScene引用UIViewController,因为它破坏了MVC模式。

作为一种替代方式,您可以使用NSNotificationCenter通知UIViewController高分:

ViewController:中

- (void)awakeFromNib {
[[NSNotificationCenter defaultCenter] 
addObserver:self
selector:@selector(reportScore:)
name:@"ReportScore"
object:nil];
}

-(void)reportScore:(NSNotification *) notification {
NSDictionary *userInfo = [notification userInfo];
NSNumber *scores = (NSNumber *)[userInfo objectForKey:@"scores"];
// do your stuff with GameCenter....
}

- (void) dealloc
{
// If you don't remove yourself as an observer, the Notification Center
// will continue to try and send notification objects to the deallocated
// object.
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

SKScene:中

- (void)gameOver {
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:
[NSNumber numberWithInt:self.scores forKey:@"scores"];
[[NSNotificationCenter defaultCenter]
postNotificationName:@"ReportScore" object:self userInfo:userInfo];
}

理论上你不应该这样做,但在实践中。。。

self.view.window.rootViewController

感谢您的NSNotification方法将sprite工具包场景退出到调用视图控制器。这是一个简单的方法跳回到UIKit&将发布我的代码。我工作很快;刚刚为Swift的工作人员添加了4行代码。此代码已添加到viewDidLoad函数中的视图控制器中。

override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameLevels.unarchiveFromFile("GameLevels") as? GameLevels{
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .AspectFill
//set up notification so scene can get back to this view controller
NSNotificationCenter.defaultCenter().addObserver(self, selector: "quitToLevel:", name: "quitToLevelID", object: nil)
skView.presentScene(scene)
}
}
// Function to pop this view controller and go back to my Levels screen
func quitToLevel(notification: NSNotification) {
self.navigationController!.popViewControllerAnimated(true)
}

我把这个代码添加到我的触摸中。在我的SKscene中开始使用函数,返回到视图控制器

else if nodeTouched.name == "quit"
{
NSNotificationCenter.defaultCenter().postNotificationName("quitToLevelID", object: nil)
}

现在,当我点击"退出"labelNode时,它将允许我在显示场景的视图控制器中运行一个名为quitToLevel的函数。这个功能会让我回到主屏幕,因为我使用的是导航控制器,但可以用于任何目的。

我尝试了其他几种退出场景的方法,但如果我在多个场景中跳来跳去,失去了对视图控制器的引用,就会出现问题。我可以在任何场景中使用这种方法

这不是最优雅的解决方案,但从SKScene子类到GameViewController的"反向引用"如何?类似这样的东西:

// call from GameViewController
let scene = HomeScene(size:screenSize, scaleMode:SKSceneScaleMode.AspectFill, viewController: self)

您的SKScene子类如下:

class HomeScene: SKScene {
var viewController:GameViewController
init(size:CGSize, scaleMode:SKSceneScaleMode, viewController:GameViewController) {
self.viewController = viewController
super.init(size:size)
self.scaleMode = scaleMode
}
}

以及更高版本:

func gameOver(){
self.viewController.loadHighScoreScene()
}

SKNode及其子类已经有了.parent.scene属性,所以这种方法并非闻所未闻。是的,我们正在对依赖项进行硬编码,并更紧密地耦合我们的代码,但我们正在编写更少的代码,所以这不会很糟糕。

最新更新