我很难找到这个问题的答案,我认为这并不难。
如何从 SKScene 引用在视图控制器上定义的方法和属性?
并以此为基础:如何从另一个 SKScene 中加载的 SKScene 引用 ViewController?
我很难找到这个问题的答案,我认为这并不难。
如何从 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
获取一个引用的 ivar UIViewController
:
var gameManager:GameManager?
4)当GameManager
需要调用时UIViewController
,它是这样的:
gameManager.loadHighScoreScene()
我们现在有一个明确定义的合同 aGameManager
是什么。在这个项目上工作的任何其他开发人员(以及当前开发人员未来的自己)现在都知道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 的众神会很高兴。
缺点:要编写更多代码(但比通知少得多)。
您应该避免引用UIViewController
from SKScene
,因为它破坏了 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 方法将精灵套件场景退出到调用视图控制器。这是跳回 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 中的 touchesBegan 函数以返回视图控制器
else if nodeTouched.name == "quit"
{
NSNotificationCenter.defaultCenter().postNotificationName("quitToLevelID", object: nil)
}
现在,当我点击“退出”标签节点时,它会让我在呈现我的场景的视图控制器中运行一个名为 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 及其子类已经有了 a.parent
和 a.scene
属性,所以这种方法并非闻所未闻。是的,我们正在对依赖项进行硬编码并更紧密地耦合我们的代码,但我们编写的代码更少,所以它不会全是坏事。