在应用程序状态保存期间,有哪些好方法可以解决 SKAction 代码块的编码限制?

2023-12-07

Problem

当节点层次结构被编码时(这在应用程序状态保存或“游戏保存”期间很常见),运行的节点SKAction带有代码块的动作必须特殊处理,因为代码块不能被编码。

示例1:动画后延迟回调

在这里,一名兽人被杀。它以动画方式淡出,然后将其自身从节点层次结构中删除:

SKAction *fadeAction = [SKAction fadeOutWithDuration:3.0];
SKAction *removeAction = [SKAction removeFromParent];
[orcNode runAction:[SKAction sequence:@[ fadeAction, removeAction ]]];

如果对orc节点进行编码然后解码,动画将正确恢复并按预期完成。

但现在该示例已修改为使用淡入淡出后运行的代码块。也许一旦兽人(最终)死了,代码就会清理一些游戏状态。

SKAction *fadeAction = [SKAction fadeOutWithDuration:3.0];
SKAction *removeAction = [SKAction removeFromParent];
SKAction *cleanupAction = [SKAction runBlock:^{
  [self orcDidFinishDying:orcNode];
}];
[orcNode runAction:[SKAction sequence:@[ fadeAction, removeAction, cleanupAction ]]];

不幸的是,代码块不会编码。在应用程序状态保存(或游戏保存)期间,如果此序列正在运行,则会发出警告:

SKAction:运行块动作无法正确编码, Objective-C 块不支持 NSCoding。

解码后,兽人会消失并从父级中移除,但清理方法orcDidFinishDying:不会被调用。

解决此限制的最佳方法是什么?

示例 2:补间

The SKAction customActionWithDuration:actionBlock:看起来非常适合补间。我的此类事情的样板代码是这样的:

SKAction *slideInAction = [SKAction customActionWithDuration:2.0 actionBlock:^(SKNode *node, CGFloat elapsedTime){
  CGFloat normalTime = (CGFloat)(elapsedTime / 2.0);
  CGFloat normalValue = BackStandardEaseInOut(normalTime);
  node.position = CGPointMake(node.position.x, slideStartPositionY * (1.0f - normalValue) + slideFinalPositionY * normalValue);
}];

很遗憾,customActionWithDuration:actionBlock:无法编码。如果在动画播放期间保存游戏,则在游戏加载时将无法正确恢复。

再说一遍,解决此限制的最佳方法是什么?

不完美的解决方案

以下是我考虑过但不喜欢的解决方案。 (也就是说,我很想阅读成功支持其中一项的答案。)

  • 不完美的解决方案:使用performSelector:onTarget:而不是runBlock:在动画中。这个解决方案并不完美,因为参数无法传递给调用的选择器;调用的上下文只能由目标和选择器的名称来表达。不是很好。

  • 不完美的解决方案:编码时,去掉SKAction从任何相关节点进行序列并推进程序状态,就像序列已完成一样。在第一个示例中,这意味着设置节点alpha立即到0.0,从父节点中删除 orc 节点,然后调用orcDidFinishDying:。这是一个不幸的解决方案,至少有两个原因:1)在编码过程中需要特殊的处理代码; 2)从视觉上看,节点将没有机会完成其动画。

  • 不完美的解决方案:编码时,去掉SKAction来自任何相关节点的代码块,并在解码期间重新创建它们。这一点很重要。

  • 不完美的解决方案:永远不要使用SKAction代码块,尤其是在延迟之后。切勿依赖动画的完成来恢复良好的应用状态。 (如果您需要以可编码的方式安排未来的事件,请不使用代码块构建您自己的事件队列。)此解决方案并不完美,因为runBlock and customActionWithDuration:actionBlock:实在是太有用了,如果认为它们是邪恶的,那将是一种耻辱(对于新手来说也是一个反复出现的陷阱)。


可编码的轻量级对象可以对以下类型进行建模SKAction我们想要使用(但不能)的代码块。

以下想法的代码是here.

替代runBlock

第一个可编码的轻量级对象取代runBlock。它可以使用一个或两个参数进行任意回调。

  • 调用者实例化轻量级对象并设置其属性:目标、选择器和参数。

  • 轻量级对象被触发runAction标准无参动画[SKAction performSelector:onTarget:]。对于这个触发动作,目标是轻量级对象,选择器是指定的“执行”方法。

  • 轻量级物体符合NSCoding.

  • 作为奖励,触发SKAction保留对轻量级对象的强引用,因此两者都将与运行操作的节点一起编码。

  • 可以制作这个轻量级对象的版本来弱保留目标,这可能很好和/或必要。

以下是可能的界面草稿:

@interface HLPerformSelector : NSObject <NSCoding>

- (instancetype)initWithTarget:(id)target selector:(SEL)selector argument:(id)argument;

@property (nonatomic, strong) id target;

@property (nonatomic, assign) SEL selector;

@property (nonatomic, strong) id argument;

- (void)execute;

@end

以及附带的实现:

@implementation HLPerformSelector

- (instancetype)initWithTarget:(id)target selector:(SEL)selector argument:(id)argument
{
  self = [super init];
  if (self) {
    _target = target;
    _selector = selector;
    _argument = argument;
  }
  return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
  self = [super init];
  if (self) {
    _target = [aDecoder decodeObjectForKey:@"target"];
    _selector = NSSelectorFromString([aDecoder decodeObjectForKey:@"selector"]);
    _argument = [aDecoder decodeObjectForKey:@"argument"];
  }
  return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
  [aCoder encodeObject:_target forKey:@"target"];
  [aCoder encodeObject:NSStringFromSelector(_selector) forKey:@"selector"];
  [aCoder encodeObject:_argument forKey:@"argument"];
}

- (void)execute
{
  if (!_target) {
    return;
  }
  IMP imp = [_target methodForSelector:_selector];
  void (*func)(id, SEL, id) = (void (*)(id, SEL, id))imp;
  func(_target, _selector, _argument);
}

@end

以及使用它的示例:

SKAction *fadeAction = [SKAction fadeOutWithDuration:3.0];
SKAction *removeAction = [SKAction removeFromParent];
HLPerformSelector *cleanupCaller = [[HLPerformSelector alloc] initWithTarget:self selector:@selector(orcDidFinishDying:) argument:orcNode];
SKAction *cleanupAction = [SKAction performSelector:@selector(execute) onTarget:cleanupCaller];
[orcNode runAction:[SKAction sequence:@[ fadeAction, removeAction, cleanupAction ]]];

替代customActionWithDuration:actionBlock:

第二个可编码的轻量级对象取代customActionWithDuration:actionBlock:。然而,这件事并不那么简单。

  • 再次,它是由无争论触发的[SKAction performSelector:onTarget:],调用指定的execute method.

  • A customActionWithDuration:actionBlock:有持续时间。但触发的performSelector:onTarget:才不是。调用者必须插入一个同伴waitForDuration:行动进入她的序列,如果它取决于持续时间。

  • 轻量级对象使用目标、选择器、节点和持续时间进行初始化。

  • 当它被触发时,轻量级对象会跟踪自己的运行时间,并定期调用目标上的选择器,将节点和运行时间传递给它。

  • 轻量级物体符合NSCoding。解码时,如果已经触发,它将在其配置的剩余时间内恢复调用选择器。

局限性

我已经实施了这些建议类的一个版本。通过少量使用,我已经发现了一个重要的限制:使用运行编码的节点SKAction序列 解码后从头开始重新启动序列.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在应用程序状态保存期间,有哪些好方法可以解决 SKAction 代码块的编码限制? 的相关文章

  • Swift AVCaptureSession 关闭打开按钮错误:当前不支持多个音频/视频 AVCaptureInputs

    我有一个可用的条形码扫描仪代码 当我点击openCamera按钮 第一次一切都很好 当我点击closeCamera按钮 很好 但是如果我再次点击openCamera按钮给出致命错误 代码和错误如下 事实上 是否可以一键切换相机视图 Barc
  • iOS 10 bug:UICollectionView 收到索引路径不存在的单元格的布局属性

    在 iOS 10 设备上运行我的应用程序时出现以下错误 UICollectionView 收到索引路径不存在的单元格的布局属性 在 iOS 8 和 9 中工作正常 我一直在研究 发现这与使集合视图布局无效有关 我尝试实施该解决方案但没有成功
  • 如何以编程方式检查 UILabel 是否具有属性文本或普通文本?

    有什么方法可以判断 UILabel 是否使用其文本设置label attributedText or label text财产 问题是当你设置attributedText text也会更新 反之亦然 因此无法检查这些属性是否为 nil 受
  • 应用内购买导致偶尔崩溃

    我在互联网上搜索了这方面的帮助 但没有结果 我的应用程序已在应用程序商店中上线 少数用户报告应用程序在进行应用内购买后冻结并崩溃 我的游戏中唯一的 IAP 它基本上解锁了完整版本 即使他们重新启动设备并尝试继续 设备也会再次崩溃 我无法重现
  • 与桌面浏览器相比,移动浏览器有多强大? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 获取保存到照片库的图像的 PHAsset/localIdentifier

    我在用着UIImageWriteToSavedPhotosAlbum https www hackingwithswift com example code media uiimagewritetosavedphotosalbum how
  • 如何在 Xcode 8 中更改应用程序显示名称以添加空格

    我试图在 Xcode 8 中的应用程序名称 独立贴纸包 中添加一个空格 我在这里看到的解决方案是更改产品名称 在包装中 或更改 捆绑显示名称 我更改了产品名称 但没有起作用 我清理 重建 重置模拟器中的内容和设置 并注销 xcode 重新登
  • iOS-如何使用 AVAsset 或 AVURLAsset 获取 .mp4 文件的持续时间

    我知道视频类型问题的持续时间之前已得到解答 但我在获取视频持续时间方面遇到了真正的麻烦 mp4文件通过使用AVAsset and by AVURLAsset 我正在使用以下代码 NSString itemPathString NSSearc
  • 为什么一个崩溃日志具有象征意义,而不是另一个?

    我在 XCode Organizer 中有两个来自 iPhone 应用程序的崩溃日志 两者相隔 20 分钟内发生 在同一台设备上运行相同版本的应用程序 其中一个已被象征化 而另一个则没有 无符号日志具有类型和代码 Exception Typ
  • iOS 版本中的 Twitter 个人资料屏幕是如何构建的?

    我想知道 Twitter 是如何实现其个人资料屏幕的 起初我以为它是一个带有标题 个人资料信息 和部分标题 用于选择推文 媒体 收藏夹的分段控件 的表格视图 这对我来说是有意义的 因为当用户向下滚动时 配置文件信息消失 但分段控件保留 而这
  • 以编程方式设置 UITabBarItem 标题?

    我的应用程序基于标签栏架构 为了有一个简单的国际化机制 我不在 XIB 文件中写入任何字符串 viewdidload 允许以编程方式更改视图中的字符串 当我的应用程序启动时 我可以看到 4 个选项卡 但实际上只有第一个加载其视图控制器 其他
  • 影响 UINavigationBar 的后退按钮方法 (iOS)

    我有一个推送到详细视图控制器的表视图 从详细视图控制器中 当我按下 后退 按钮时 我希望更改一个整数值 如何以编程方式编辑导航栏后退按钮的操作 后退按钮会自动放置在我的应用程序中 因为我使用的是表视图 所以我实际上没有创建按钮 所以我不知道
  • 如何分发我的应用程序的多个版本?

    我想分发我的应用程序的两个版本 稳定分支以及当前的开发主干 使用试飞 http testflightapp com 而且 如果可能的话 我只想邀请测试人员一次 我可以在一个 TestFlight 团队中拥有一个应用程序的两个版本吗 或者也许
  • WatchOS 应用程序未检测到配套的 iOS 应用程序

    我正在制作一个独立的Apple Watch应用程序 但有一个配套的iPhone应用程序 这对于手表应用程序的运行来说不是必需的 该应用程序使用手表连接如果安装了 iPhone 配套应用程序 则可以在设备之间同步数据 我可以打电话sessio
  • 如何使用社交框架呈现 SKscene 中的 UIViewController?

    我正在制作一款类似 Flappy Bird 的游戏 如何从 SKScene 呈现 UIViewController 首先 我告诉我的环境 Mac OS X 10 9 Xcode 5 0 2 Sprite Kit framework soci
  • 如何在 iOS 模拟器上打开开发者工具?

    我想打开开发者工具在 iOS 模拟器上 我在 2016 款 MacBook 上执行了以下步骤macOS 塞拉利昂 10 12 1并且不知道如何进一步 我已经安装了 Xcode 我创建了一个新的游乐场 右键单击 Xcode 停靠栏图标 然后单
  • iOS 11.x 系统颜色

    我读过很多关于如何自定义视图颜色的文章 但没有任何关于检索标准控件 如 iOS 11 x 或以前版本中的导航栏 状态栏和选项卡栏 的系统颜色的文章 UIColor 类有 3 种系统颜色 但它们几乎没有用 例如 调用 UINavigation
  • Apple IOS 上的 C# 应用程序 [已关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我有基于 C Net 的应用程序 有什么方法可以在 Apple IOS 上运行这些应用程序吗 我没有资
  • 当现有的转换或演示正在进行时;导航堆栈不会更新

    我遇到过这个警告 PushViewController animated 在现有转换或演示文稿运行时调用 发生 导航堆栈不会更新 当试图打电话时navigationController popViewControllerAnimated f
  • 标签栏不显示图标?

    我读了一堆相关的问题 我尝试了他们所说的 但没有任何效果 不知道为什么 所以 我有3个不同的UIStoryboards 第一个是处理登录 注册的 Auth Storyboard 并且有一个对第二个 Storyboard 选项卡栏 Story

随机推荐