重新分配SKScene委托协议

时间:2014-11-24 17:35:59

标签: ios sprite-kit protocols skscene

所以我在场景中创建了一个委托协议和相应的属性。在我的视图控制器中保存我的所有场景,我将场景委托给自己,这样我就可以在菜单中调用全屏广告(来自视图控制器),这是第一个被调用的游戏场景。这很好用,我可以调用这个方法。但是在我转换到级别场景然后回到菜单场景调用我的方法来显示全屏广告什么也没做。我假设因为委托人被设置为自己已经丢失。如何在游戏场景中将委托设置回自身?这就是我在View controller.M

中的做法
myScene = [GameScene unarchiveFromFile:@"GameScene"];
((GameScene *)myScene).mySceneDelegate = self; // i need to be able to do this within the scene
myScene.scaleMode = SKSceneScaleModeResizeFill;

// Present the scene.
[skView presentScene:myScene];

如何从菜单场景中调用VC中的fullScreen方法...

[self.mySceneDelegate showFullScreen:self];

...所以在我的代码中,当我从我的LevelOne场景转换时,我尝试使用以下代码重新分配代理,但没有任何反应

-(void)goBackToMenu{

    GameScene *newscene = [GameScene sceneWithSize:self.size];
    [self.view presentScene:newscene transition:[SKTransition doorsCloseHorizontalWithDuration:1]];
    ((GameScene *)newscene).mySceneDelegate = self;
}

我也尝试将属性设置为Strong而不是弱而没有运气

**********************所有代码*********************** ***************

更多代码..我的Viewcontroller.h

@protocol TCAMySceneDelegate;

@interface GameViewController : UIViewController<TCAMySceneDelegate>

@property(nonatomic, weak) id<TCAMySceneDelegate> mySceneDelegate;

@end

我的观点controller.m

- (void)viewDidLoad
{
    [super viewDidLoad];

    SKView * skView = (SKView *)self.view;

    myScene = [GameScene unarchiveFromFile:@"GameScene"];

    ((GameScene *)myScene).mySceneDelegate = self;

    // Present the scene.
    [skView presentScene:myScene];
}


-(void)showFullScreen:(GameScene *)gameScene{

    NSLog(@"Show Full Screen");

    [revmobFS loadWithSuccessHandler:^(RevMobFullscreen *fs) {
        [fs showAd];
        NSLog(@"Ad loaded");
    } andLoadFailHandler:^(RevMobFullscreen *fs, NSError *error) {
        NSLog(@"Ad error: %@",error);
    } onClickHandler:^{
        NSLog(@"Ad clicked");
    } onCloseHandler:^{
        NSLog(@"Ad closed");
    }];
    [RevMobAds startSessionWithAppID:@"547251235f2a043608a66f1a"
                  withSuccessHandler:^{
                      NSLog(@"Session started with block");
                      // Here, you should call the desired ad unit
                      revmobFS = [[RevMobAds session] fullscreen];
                      [revmobFS showAd];
                      // [RevMobAds session].testingMode = RevMobAdsTestingModeWithAds;
                      revmobFS.delegate = self;
                      [revmobFS loadAd];

                  } andFailHandler:^(NSError *error) {
                      NSLog(@"Session failed to start with block");
                  }];
}

我的游戏scene.h文件..

@protocol TCAMySceneDelegate;

@interface GameScene : SKScene

@property (nonatomic, weak) id<TCAMySceneDelegate> mySceneDelegate;

@end

@protocol TCAMySceneDelegate <NSObject>

-(void)showFullScreen:(GameScene *)gameScene;
@end

我的游戏scene.m文件..

-(void)didMoveToView:(SKView *)view {

    //Show Ad

    [self.mySceneDelegate showFullScreen:self];

}

现在上面的showFullScreen:在第一次出现gameScene(我的游戏菜单)时被调用但是在线...转换到游戏中的其他场景,比如我的等级然后回到我的菜单(gamescene)它永远不会被叫。希望现在更清楚了

我如何从我的游戏场景切换到另一个场景.m

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInNode:self];
    SKNode *node = [self nodeAtPoint:location];
    NSLog(@"Level Selected: %@", node.name);
    int x = [node.name intValue];

    switch (x) {
        case 1:{
                LevelOne *newscene = [LevelOne sceneWithSize:self.size];
                [self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
            break;
        }
        case 2:{

                LevelTwo *newscene = [LevelTwo sceneWithSize:self.size];
                [self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
            }
            break;
        }
        case 3:{
                LevelThree *newscene = [LevelThree sceneWithSize:self.size];
                [self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
            }
            break;
        }
//..........

        default:
            break;
    }
}

LevelOne.h代码......

#import <SpriteKit/SpriteKit.h>
#import "DrawCanvas.h"
#import "GameScene.h"

@interface LevelOne : SKScene<SKPhysicsContactDelegate>

@end

1 个答案:

答案 0 :(得分:1)

就个人而言,我发现你的描述以及你发布的代码有点缺乏细节而且有点令人困惑。提供代码时,如果您提供更多详细信息,您可能会获得更好/更快的帮助。例如,goBackToMenu属于哪个文件?

如何/在什么情况下

[self.mySceneDelegate showFullScreen:self];

叫什么?我认为它来自GameScene中的某个触发点。 showFullScreen的方法签名是什么?

既然你没有说明mySceneDelegate的类型,那么你就会让读者猜出你的意思。

所以试图解读你的要求......

假设您有一些名为GameSceneDelegate的委托。我也会假设你已经知道SKSceneDelegate已经存在并且已经使用它,但只是没有发布该代码或者不需要使用它。

您的GameViewController将其界面定义为:

@interface GameViewController : UIViewController<GameSceneDelegate&GT;

你的GameScene将有一个如下定义的属性:

@property (nonatomic, weak) id<GameSceneDelegate> mySceneDelegate;

注意你应该在这里使用弱。因为如果您的GameViewController对您的GameScene有强烈的引用,那么您只是创建了一个保留周期。

您可以选择何时设置mySceneDelegate。如果我知道我必须定义一个委托,我经常做的是将它作为参数添加到初始化器。这样很明显它必须有一个委托。因此,假设您使用类方法生成实例,那么您可以执行以下操作:

@interface GameScene : SKScene

@property (nonatomic, weak) id<GameSceneDelegate> mySceneDelegate;

+ (instancetype)unarchiveFromFile:(NSString *)file mySceneDelegate:(id<GameSceneDelegate>)mySceneDelegate;

+ (instancetype)sceneWithSize:(CGSize)size mySceneDelegate:(id<GameSceneDelegate>)mySceneDelegate;

@end

那些方法看起来像是:

+ (instancetype)unarchiveFromFile:(NSString *)file mySceneDelegate:(id<GameSceneDelegate>)mySceneDelegate
{
    GameScene *scene = [super unarchiveFromFile:file];

    scene.mySceneDelegate = mySceneDelegate;

    return scene;
}

+ (instancetype)sceneWithSize:(CGSize)size mySceneDelegate:(id<GameSceneDelegate>)mySceneDelegate
{
    GameScene *scene = [super sceneWithSize:size];

    scene.mySceneDelegate = mySceneDelegate;

    return scene;
}

注意要使其工作,您需要在GameViewController.h中定义它(如果您愿意,还可以在该类别的单独文件中)

@interface SKScene (Unarchive)

+ (instancetype)unarchiveFromFile:(NSString *)file;

@end

另一种方法是在创建场景实例后立即设置mySceneDelegate。

实际上,如果你这样做,它应该有效,你的代表应该保持机智。 FWIW,我实际上是在游戏中这样做的。所以我知道它有效。

好的,所以这就是你问题中朦胧的地方。您表明代表是&#34;丢失&#34;。代表可能失败的唯一方式&#34;如果有人明确地将其设置为零,那将是。或者我想的另一种可能性是你的GameScene正在被解除and并且你没有意识到它。

我设置了两件事来帮助调试它。

在GameScene中添加

- (void)dealloc
{
    NSLog(@"I'm being dealloc'd");
}

- (void)setMySceneDelegate:(id<GameSceneDelegate>)mySceneDelegate
{
    _mySceneDelegate = mySceneDelegate;

    NSLog(@"Setting mySceneDelegate");
}

所以现在你可以尝试确定它在哪里&#34;迷路了#34;如果其中任何一个在丢失时打印到控制台,只需在这些方法中设置一个断点,以查看谁在调用它。

其他一些有用的信息

  • 你是什么意思指派代表给自己?我不知道是什么 这意味着。

  • 您提到您认为代理人丢失了。为什么?你有没有 证据还是纯粹的猜测?

如果你能提供一些答案和更好的细节,我们可以找出出错的地方。

好的,现在查看您的代码,我认为您的问题出现在GameScene.m中:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInNode:self];
    SKNode *node = [self nodeAtPoint:location];
    NSLog(@"Level Selected: %@", node.name);
    int x = [node.name intValue];

    switch (x) {
        case 1:{
                LevelOne *newscene = [LevelOne sceneWithSize:self.size];
                newScene.mySceneDelegate = self.mySceneDelegate;
                // Or, if you can make it work, newscene = [LevelOne sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
                [self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
            break;
        }
        case 2:{

                LevelTwo *newscene = [LevelTwo sceneWithSize:self.size];
                newScene.mySceneDelegate = self.mySceneDelegate;
                // Or, if you can make it work, newscene = [LevelOne sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
                [self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
            }
            break;
        }
        case 3:{
                LevelThree *newscene = [LevelThree sceneWithSize:self.size];
                newScene.mySceneDelegate = self.mySceneDelegate;
                // Or, if you can make it work, newscene = [LevelOne sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
                [self.view presentScene:newscene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
            }
            break;
        }
//..........

        default:
            break;
    }
}

我在这里做的是将新场景的mySceneDelegate分配给正在使用的现有场景。

您可以清除此代码以使其更具可读性。

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInNode:self];
    SKNode *node = [self nodeAtPoint:location];
    NSLog(@"Level Selected: %@", node.name);
    int x = [node.name intValue];
    GameScene *newScene = nil;

    switch (x) {
        case 1:
            newScene = [LevelOne sceneWithSize:self.size];
            // Or, if you can make it work, newScene = [LevelOne sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
            break;
        case 2:
            newScene = [LevelTwo sceneWithSize:self.size];
            // Or, if you can make it work, newScene = [LevelTwo sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
            break;
        case 3:
            newScene = [LevelThree sceneWithSize:self.size];
            // Or, if you can make it work, newScene = [LevelThree sceneWithSize:size mySceneDelegate:self.mySceneDelegate;
            break;

        // other cases..........

        default:
            break;
    }
    if (newScene) {
        newScene.mySceneDelegate = self.mySceneDelegate;
        [self.view presentScene:newScene transition:[SKTransition doorsOpenVerticalWithDuration:1]];
    }
}

对于您的关卡,您需要执行以下操作:

#import <SpriteKit/SpriteKit.h>
#import "DrawCanvas.h"
#import "GameScene.h"

@interface LevelXYZ : GameScene<SKPhysicsContactDelegate>

@end

XYZ是等级(例如一个)。通过这样做,您从GameScene继承mySceneDelegate。如果由于某种原因你坚持认为级别场景不是从GameScene继承的,那么添加一个包含委托的基类。所以这就像是:

@interface MyBaseScene : SKScene

@property(nonatomic, weak) id<TCAMySceneDelegate> mySceneDelegate;

@end

然后GameScene就是这样:

@interface GameScene : MyBaseScene

你的等级场景将是

@interface LevelXYZ : MyBaseScene<SKPhysicsContactDelegate>

请注意我做过的几件事。

  • 将newscene更改为newScene。在iOS中,您通常使用来自案例。 你是为mySceneDelegate做的。一致性是关键。

  • 最初将newScene声明为nil并指定为需要。然后在切换结束时,如果存在newScene,则分配委托和 当下。这样,你只在一个地方做。它使生活 更轻松。例如,如果您想更改每个的持续时间 一个,那又怎样?你必须改变任何案例。注意有一个 垮台。如果你想在关卡上有特殊的持续时间, 然后它并没有起作用。但你可以通过改变它来改变它 持续时间和过渡类型变量。

顺便说一句,我强烈建议不要让你的场景类包含进行实际转换的代码。我要么让它委托代表做或者找另一种方式(比如使用需要改变场景的通知)。例如,在我当前的游戏中,我的视图控制器是所有场景变化的控制器。这样做的好处是你也可以从其他外力调用场景转换(而不是让你的场景成为管理场景的场景)。

最后,您可以希望看到问题可被发现的唯一方法是提供正确的代码。我同意很难知道要呈现什么代码,因为如果代码太多,那么人们可能不会阅读它。这是一条很好的路线,但需要一些思考。