HLSpriteKit
SpriteKit 场景和节点子类,以及各种实用工具。
功能
HLLayoutManager
布局管理器提供单个方法(layout
)来布局节点。它可以使用 SKNode+HLLayoutManager
类别附加到任何 SKNode
。
目前提供的布局管理器
-
HLTableLayoutManager
用于类似表格的布局; -
HLGridLayoutManager
用于类似网格的布局; -
HLStackLayoutManager
用于一维布局; -
HLRingLayoutManager
用于类似环的极坐标布局; -
HLOutlineLayoutManager
用于垂直列表(特别是文本)和有缩进级别的。 -
HLParallaxLayoutManager
用于以不同速度移动的节点层。
将布局代码放在第三方对象中(而不是放在 SKScene
或 SKNode
子类中)可以使常用布局数学更易于重用。
自定义 SKNode 子类
HLSpriteKit
包含了许多自定义 SKNode
子类。
-
HLGridNode
。以相同大小的正方形网格组织内容,包含视觉格式化和交互选项。 -
HLLabelButtonNode
。在SKSpriteNode
上显示一个简单的SKLabelNode
,但具有额外的尺寸和居中对齐选项。特别是,它可以调整精灵节点的尺寸以适应文本,并且可以实现基线对齐,使得整个字体大小(包括下降部分)垂直居中在背景中;对于类别SKLabelNode+HLLabelNodeAdditions
中的所有SKLabelNode
,都提供了计算数学。 -
HLMenuNode
。按钮层次菜单的接口和模型。接口是一个简单的垂直堆叠按钮,但目前它提供了一些布局和动画功能。 -
HLMessageNode
。在固体或纹理背景上显示一条文本消息,并包含一些动画选项。 -
HLMultilineLabelNode
。一个可以显示多行文本的标签节点。 -
HLRingNode
。在中心点周围排列一系列项目(通常是按钮)。 -
HLScrollNode
。支持通过缩放手势和滚动手势进行滚动和缩放内容。接口故意与UIScrollView
类似。 -
HLTiledNode
。像SKSpriteNode
一样平铺其纹理以适应指定的尺寸。 -
HLToolbarNode
。具有各种视觉格式化、尺寸和动画选项的水平工具栏,由正方形组成。
HLGestureTarget
手势目标处理来自手势识别器的手势(在 iOS 下为 UIGestureRecognizer
,在 macOS 下为 NSGestureRecognizer
)。它可以使用类分类 SKNode+HLGestureTarget
附加到任何 SKNode
上。
使用模式是这样的:SKScene
了解其视图,因此场景是手势识别器的委托。它管理共享手势识别器的集合,根据需要将其附加到视图并将其从视图中分离。当手势识别器识别出某个手势时,场景会了解哪个节点或哪些节点是手势的目标,并使用 HLGestureTarget
接口将这些手势转发给这些节点。
关键是:场景可以有效地使用手势识别器(而不是响应者接口 touchesBegan:withEvent:
或 mouseUp:
),并且手势处理代码可以封装在节点子类中(而不是将它们放入臃肿的场景中)。
HLScene
HLScene
包含许多场景常用的功能,包括但不限于:
- 在后台线程中加载场景资源
- 一个共享的手势识别系统和
HLGestureTarget
感知的手势代理实现 - 在场景上方呈现模态节点
- 注册节点以实现常见的场景相关行为(例如:场景缩放时调整大小;编码时不编码等)
HLAction
HLAction
提供了针对 SKAction
系统的一种状态化的替代方案。HLAction
的主要使用场景是在编码期间持久化动画状态并在解码时恢复。
以下是一个示例来说明这一点。
假设在游戏中每当一只兽人死亡时,都会运行一个 SKAction
序列:首先,兽人节点因为纹理动画而颤抖并跌倒;然后,兽人节点在 3 秒内缓慢淡出;最后,将兽人节点从场景中移除。
假设在这样的死亡淡入过程中,用户将应用置于后台(或保存游戏),并对其进行编码。
当游戏重新开始(并解码)时,最好是如果兽人尸体继续淡出。更好的是,它应该与 iOS 在应用状态保存期间捕获的截图像素级匹配。
使用 SKAction
系统难以达到这样的精确度。以下是一些常见可能性:
-
如果使用
NSCoding
对整个兽人节点进行编码,那么正在进行的动画序列将能够成功编码、解码并恢复。不过,遗憾的是,它并没有编码其进度或其原始状态,因此在没有重置 alpha 的情况下从序列的开始重新开始。在示例中,这意味着部分淡出的兽人将再次跳跃,重新开始颤抖和跌倒。 -
如果不对兽人节点进行编码,而是在恢复时重新创建,那么
SKAction
动画序列将无法恢复。兽人节点要么会消失,要么会无限期地保持原地不动,或者应用必须找出如何保留和恢复动画序列的状态。
HLAction
通过只将所有动画的状态表示为与特定节点松散耦合的可编码对象,来解决此问题。
HLAction
的主要缺点是您无法在 SKNode
中使用 SKAction
运行循环,并且所提供的行为功能不如完整。
正在根据需要添加新行为。如果您需要尚未添加的行为,请告诉我。
HLHacktion
HLHacktion
为各种目的提供了 SKAction
的替代方案。
名称中的 hack 避免了与 HLAction
的命名冲突,并且承认这些解决方案是在现有的 SKAction
系统之上构建。有关 SKAction
的独立于它的替代系统,请参阅 HLAction
。
HLHacktion 的一个例子:[HLHacktion performSelector:onWeakTarget:]
返回一个保留弱目标的 SKAction
,这样可以避免由 [SKAction performSelector:onTarget:]
导致的保留循环。
HLHacktion
还提供了可编码的块运行 SKAction
行为的替代方案。问题是这样的:当对 SKScene
节点层次结构进行编码时,这在应用程序状态保存或“游戏保存”期间很常见,必须特别处理运行 SKAction
行为并带有代码块的节点,因为这些代码块不能进行编码。特别是,尝试编码 runBlock:
或 customActionWithDuration:actionBlock:
将导致运行时警告消息
SKAction:运行块行为不能正确编码,Objective-C 块不支持 NSCoding。
HLHacktion
行为提供的解决方案是使用选择器回调(带额外功能)而不是代码块。
手势识别常见问题及示例
UIGestureRecognizer
或 NSGestureRecognizer
来识别手势。
我想在我的场景中使用 这里是在 HLSpriteKit
中使用的模式:
-
您的场景拥有与场景相关的所有手势识别器对象。作为在
SKView
上显示的,它会将手势识别器添加到视图中。 -
您的场景是手势识别器的代理;即
UIGestureRecognizerDelegate
或NSGestureRecognizerDelegate
。 -
在手势识别器开始识别之前,您的场景将手势识别器的目标(对象和选择器)设置为最相关的接收节点。随着手势的识别,该节点将获得调用。
考虑一些替代设计方案。特别是,如果你的场景包含多个按钮节点,它们应该响应触摸事件。这些设计方案 不包括 在 HLSpriteKit
中使用的模式。
-
每个按钮都可以向
SKView
添加自己的UITapGestureRecognizer
。 -
按钮可以共享一个单个的
UITapGestureRecognizer
,该手势识别器在场景中有固定的目标方法;称之为handleTap:
。当识别到触摸手势时,handleTap:
会确定哪个按钮被触摸,并执行相应的代码。 -
按钮可以共享单个的
UITapGestureRecognizer
,并且每个都向其添加一个单独的目标。当识别到触摸手势时,每个目标都会决定是否被触摸,如果是,则执行相应的代码。
HLSpriteKit
方式在我的场景中使用手势识别器。
我想以 将您的场景创建为 HLScene
的子类。
HLToolbarNode
。
我想在我的场景中使用您的一些手势目标组件,例如 HLSpriteKit
中的组件都是手势目标,但默认情况下该机制是禁用的。只需几行代码即可启用。
首先,确保您是 HLScene
的子类
#import "HLSpriteKit/HLSpriteKit.h"
@interface MyScene : HLScene
接下来,创建您的 HLToolbarNode
并将其添加到场景中
HLToolbarNode *toolbarNode = ...;
toolbarNode.delegate = self;
[self addChild:toolbarNode];
最后,将工具栏的手势目标设置为自身,并通知场景它需要创建一些适当的的手势识别器
[toolbarNode hlSetGestureTarget:toolbarNode];
[self needsSharedGestureRecognizersForNode:toolbarNode];
这样将为工具栏工具的触摸或点击提供委托回调。
请参阅示例项目(项目中的 HLSpriteKit/Example/HLSpriteKit/HLCatalogScene.m
或 GitHub 上的示例)以获得使用多个手势目标的场景的运行示例。
我想在我的场景中创建自己的文果target节点。
好的!
这里有一些选项
-
创建一个可以自己作为文果target的自定义节点。
-
将一个通用文果target附加到现有节点。
-
将一个自定义文果target附加到现有节点。
-
在场景中处理。
创建一个可以自己作为文果target的自定义节点。
遵循HLSpriteKit
中组件的模式,并在自定义节点类中遵守HLGestureTarget
协议。通过HLGestureTarget
接口,您的节点将告诉其场景它期望的文果识别器,以及当这些文果识别器被触发时要做什么。
然后,您可以像包含HLSpriteKit
中的文果target组件一样包含您的节点。
将一个通用文果target附加到现有节点。
有时创建一个新的节点类可能有些过度。有时甚至实现代理接口也显得有些过度。这里有一些例子
-
您在场景中有一个红色的正方形精灵节点,您想让它在被点击时摆动。
-
您想弹出一个带有一些文本的标签节点,并且点击它时消失。
或者这里有个不同的问题:比如说您从一个第三方库中获得了一个现成的节点类,它没有任何交互程序,而您想让它对点击做出反应。
对于所有这些问题,您可以无需创建任何新类,将一个通用文果target附加到现有节点。
以下是在假设您的场景是HLScene
的子类的情况下,使点击时红色的正方形精灵节点摆动的代码
SKSpriteNode *redSquareNode = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(20.0f, 20.0f)];
[self addChild:redSquareNode];
HLTapGestureTarget *tapGestureTarget = [[HLTapGestureTarget alloc] init];
tapGestureTarget.handleGestureBlock = ^(UIGestureRecognizer *gestureRecognizer){
// wiggle red square node
};
[redSquareNode hlSetGestureTarget:tapGestureTarget];
[self needsSharedGestureRecognizersForNode:redSquareNode];
HLTapGestureTarget
是一个简单的手势目标实现,它只认识点击手势(而非滑动或长按)。因为点击手势非常直接,所以很容易将同一个手势目标重复用于任何节点。
弹出示例
HLLabelButtonNode *labelButtonNode = [[HLLabelButtonNode alloc] initWithColor:[SKColor blackColor] size:CGSizeZero];
labelButtonNode.automaticWidth = YES;
labelButtonNode.automaticHeight = YES;
labelButtonNode.text = @"Tap to dismiss";
[self addChild:labelButtonNode];
__weak HLLabelButtonNode *labelButtonNodeWeak = labelButtonNode;
[labelButtonNode hlSetGestureTarget:[HLTapGestureTarget tapGestureTargetWithHandleGestureBlock:^(UIGestureRecognizer *gestureRecognizer){
[labelButtonNodeWeak removeFromParent];
}]];
[self needsSharedGestureRecognizersForNode:labelButtonNode];
HLLabelButtonNode
甚至没有实现自己的手势目标,因为通用的HLTapGestureTarget
通常是所有者想要的。因此,这段代码也可以作为将通用手势目标附加到第三方节点的示例。
将自定义手势目标附加到现有节点。
HLToolbarNode
可以识别点击,但不能识别长按。你能处理长按吗?也许还能处理滑动呢?
你可以编写自己的自定义HLGestureTarget
,并使用熟悉的SKNode
类别扩展hlSetGestureTarget
将其附加到节点。
我这样做是一个练习,发现它不太愉快。练习的结果是HLToolbarNode.h
中声明的类HLToolbarNodeMultiGestureTarget
。一旦编写,启用代码就变得熟悉
HLToolbarNode *toolbarNode = ...;
toolbarNode.delegate = self;
[self addChild:toolbarNode];
HLToolbarNodeMultiGestureTarget *multiGestureTarget = [[HLToolbarNodeMultiGestureTarget alloc] initWithToolbarNode:toolbarNode];
multiGestureTarget.delegate = self;
[toolbarNode hlSetGestureTarget:multiGestureTarget];
[self needsSharedGestureRecognizersForNode:toolbarNode];
你可以使用HLToolbarNodeMultiGestureTarget
类作为编写自定义手势目标的模板。
在设计阶段,为任何节点编写自定义手势目标的能力似乎是HLGestureTarget
系统的优势之一。例如,它可以防止默认的HLToolbarNode
手势目标出现臃肿。但在实践中,似乎要花费很多精力才能从场景中移除手势处理代码,然后再将其委托回场景。
可能更好的设计选择是子类化HLToolbarNode
以覆盖默认的手势处理。或者直接在场景中处理手势,而不是在手势目标中。
在场景中处理。
HLGestureTarget
的一个目标是将手势处理代码从场景中提取出来,以便它可以在场景之间更容易地重用。
但在这里我们遇到了。你希望在场景中处理一些手势。
你可以在Flippy的一个场景中看到一个这种混合模型示例gestureRecognizer:shouldReceiveTouch:
。该场景大多数手势是在行内处理的,但有时会调用[super]
让HLScene
处理真正的HLGestureTarget
组件。这里有一个摘录
// Modal overlay layer (handled by HLScene).
if ([self modalNodePresented]) {
return [super gestureRecognizer:gestureRecognizer shouldReceiveTouch:touch];
}
// Construction toolbar.
if (_constructionToolbarState.toolbarNode
&& _constructionToolbarState.toolbarNode.parent
&& [_constructionToolbarState.toolbarNode containsPoint:sceneLocation]) {
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
[gestureRecognizer removeTarget:nil action:NULL];
[gestureRecognizer addTarget:self action:@selector(handleConstructionToolbarPan:)];
return YES;
}
if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]
&& [(UITapGestureRecognizer *)gestureRecognizer numberOfTapsRequired] == 1) {
[gestureRecognizer removeTarget:nil action:NULL];
[gestureRecognizer addTarget:self action:@selector(handleConstructionToolbarTap:)];
return YES;
}
if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) {
[gestureRecognizer removeTarget:nil action:NULL];
[gestureRecognizer addTarget:self action:@selector(handleConstructionToolbarLongPress:)];
return YES;
}
return NO;
}
...
注意重写的方式与 HLScene
相同的模式:如果某个组件应该获取手势,则清除旧的对手势的目标并设置一个新的目标。
HLToolbarNode
,但我不想使用您的手势处理系统。
我想在我的场景中使用您的一些手势目标组件,例如 HLGestureTarget
轻量级,可选,不应为 HLSpriteKit
组件引入额外的开销。
我已经为一些组件编写了非常简单的 UIResponder
和 NSResponder
用户交互实现作为概念证明,但我自己并没有使用很多。我很乐意进一步工作,或接受拉取请求。告诉我你需要什么!
开发
HLSpriteKit
正在进行开发,因此包含了其他足够通用的实验性类和函数,适用于重用。例如,包括了一些 SKEmitterNode
存储和一些图像处理函数,但它们是否有用尚不清楚。
安装
# CocoaPods
pod "HLSpriteKit", "~> 2.0"
# Carthage
github "hilogames/HLSpriteKit" ~> 2.0
作者
Karl Voskuil (karl * hilogames dot com)
许可证
HLSpriteKit
目前在 MIT 许可下可用。请参阅 LICENSE 文件了解详细信息。