适用于 iOS 7+ SpriteKit 框架的组件模型。向您的节点添加执行特定行为的组件。使用基于组件的模型的好处包括
您的基场景必须继承自 SKComponentScene。SKComponentScene 是组件模型的主机,确保所有 SKComponentNodes 都被找到并注册。
您的场景图应基于 SKComponentNodes,它们应将您的图形/渲染节点作为其子节点。SKComponentNodes 可以在场景的任何位置添加。
使用 [node addComponent:[MyComponent new]];
将行为添加到您的 SKComponentNodes
您的组件必须实现以下协议。所有方法都是可选的,但是 enabled 属性是必需的。
@protocol SKComponent <NSObject>
@property (nonatomic, readwrite) BOOL enabled;
@optional
@property (nonatomic, weak) SKNode *node;
@optional
// triggered when the component is added to a component node
- (void)awake;
// when the node is added to the scene
- (void)onEnter;
// when the node is removed from the scene
- (void)onExit;
// called every frame. dt = time since last frame
- (void)update:(CFTimeInterval)dt;
// SpriteKit - forwarded from SKScene
- (void)onSceneSizeChanged:(CGSize)oldSize;
// SpriteKit - forwarded from SKScene
- (void)didEvaluateActions;
#pragma mark -- Physics Handlers --
// SpriteKit - forwarded from SKScene
- (void)didSimulatePhysics;
// SpriteKit - forwarded from SKScene when this node is one of the nodes in contact
- (void)didBeginContact:(SKPhysicsContact *)contact;
- (void)didEndContact:(SKPhysicsContact *)contact;
#pragma mark -- Touch Handlers --
// all touch handlers are only triggered if the tough down is inside the node content area
// called once a touch moves beyond the SKComponentNode dragThreshold (defaults to 4 units)
- (void)dragStart:(SKCTouchState*)touchState;
// called every time a touch moves after dragging has started
- (void)dragMoved:(SKCTouchState*)touchState;
// called on touch up after dragging has started
- (void)dragDropped:(SKCTouchState*)touchState;
// called if the touch is canceled after dragging has started
- (void)dragCancelled:(SKCTouchState*)touchState;
-
// called on Touch Up if UITouch tap count >= 1 and touch is not classified as dragging or a long touch
- (void)onTap:(SKCTouchState*)touchState;
// called if touch is held for SKComponentNode longPressTime (defaults to 1 second)
// AND touch has not moved beyond dragThreshold
- (void)onLongPress:(SKCTouchState*)touchState;
// equivalent to iOS Touch Up Inside. Typically used for menu items rather than tap
- (void)onSelect:(SKCTouchState*)touchState;
// standard touchesBegan event, called prior to touchState based events
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
// standard touchesMoved event, called prior to touchState based events
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
// standard touchesEnded event, called prior to touchState based events
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
// standard touchesCancelled event, called prior to touchState based events
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
@end
SKComponentNodes 自动将其 alpha 值应用到其直接子节点,但假设您想将此 alpha 值应用到您的节点子节点的子节点,依此类推。
SKCDeepAlpha.h
@interface SKCDeepAlpha : NSObject<SKComponent> {
float previousAlpha;
}
@end
SKCDeepAlpha.m
#import "SKCDeepAlpha.h"
@implementation SKCDeepAlpha
@synthesize node,enabled;
- (void)onEnter {
recursivelyApplyAlpha(node, node.alpha);
}
- (void)didEvaluateActions {
if (previousAlpha != node.alpha) {
recursivelyApplyAlpha(node, node.alpha);
previousAlpha = node.alpha;
}
}
void recursivelyApplyAlpha(SKNode* node, float alpha) {
for (SKNode *child in node.children) {
child.alpha = alpha;
if (child.children.count > 0)
recursivelyApplyAlpha(child, alpha);
}
}
@end
当您将此组件添加到场景中的节点时,onEnter 和 didEvaluateActions 将自动被调用。将此组件添加到您的 SKComponentNodes 之一,并且每次您更改组件节点上的 alpha,它都会设置每个后代的 alpha。
要使用此组件,只需像这样将其添加到任何 SKComponentNode:
SKNode* node = [SKComponentNode node];
[node addComponent:[SKCDeepAlpha new]];
// add sprites or shapes as children of your node, then add it to the scene
[scene addChild:node];
首先,不要忘记在你的SKComponentNode上启用用户交互功能,使用代码node.userInteractionEnabled = YES;
。在组件的唤醒(awake)方法中做这个操作是个好方法。
每个应用都有按钮。让我们创建一个组件,它可以响应触摸内部的手势。
@implementation SKCSelectTest
@synthesize node, enabled;
- (void)awake {
node.userInteractionEnabled = YES;
}
- (void)onSelect:(SKCTouchState*)touchState {
// do something
}
@end
哎呀,这太简单了。让我们创建一个组件,可以让你在屏幕上拖动节点。
@implementation SKCDraggable
@synthesize node, enabled;
@synthesize startPosition;
- (void)awake {
node.userInteractionEnabled = YES;
}
- (void)dragStart:(SKCTouchState*)touchState {
// we could do something here to clue the user in on the fact that we started dragging
startPosition = node.position;
}
- (void)dragMoved:(SKCTouchState*)touchState {
// check out the skHelper.m for a couple shorthand functions/methods for vector` math
node.position = skpAdd(node.position, touchState.touchLocation);
}
- (void)dragDropped:(SKCTouchState*)touchState {
// we could show the user we dropped successfully here
}
- (void)dragCancelled:(SKCTouchState*)touchState {
node.position = startPosition;
}
@end
假设你给节点添加了一些组件,现在你需要更改其中某个组件的属性。你可以保留对每个组件的引用,但这会很烦人。相反,你只需请求组件节点即可。
// Disable the component of type MyComponent (callbacks will immediately stop)
[node getComponent:[MyComponent class]].enabled = NO;
// or better yet, get the component casted to the proper type
SKGetComponent(node, MyComponent).customProperty = 42;
通常情况下,节点上的每个组件类型不同,但如果你想添加两个相同类型的组件,你需要命名它们,而不是依靠类名。
[node addComponent:[SpeedDoubler new] withName:@"2xSpeed"];
[node addComponent:[SpeedDoubler new] withName:@"4xSpeed"];
[node getComponentWithName:@"4xSpeed"].enabled = NO;
如果你是SKComponentNode的子类,并且想使用组件回调而不创建额外的组件,只需实现SKComponent
协议。现在你的节点也获得所有组件回调。请确保调用[super onEnter/onExit/update:]
,这样组件节点就可以执行它背后的魔法。
查看我们CI在https://travis-ci.org/xr1337/SpriteKit-Components
此软件受MIT许可证(MIT)许可。有关详细信息,请参阅LICENSE文件。