Trailing transparent snapshots are 3D model clones of the jet at variable intervals.
Use SCNView allowsCameraControl to zoom and rotate the entire scene, including models, for different points of view.
* The 3D Jet ship mesh is part of the default Xcode Scene Kit project.



@import SceneKit;
@import GLKit;
@interface ESJet: NSObject
@property (nonatomic) SCNNode * node;
@property (nonatomic, assign) CGFloat velocity;
@property (nonatomic, assign) BOOL roll;
@property (nonatomic, assign) CGFloat rollRate;
-(void)update:(NSTimeInterval)aTime isVisible:(BOOL)aVisible;
-(instancetype)init;
-(instancetype)initWithNode:(SCNNode *)aNode zPosition:(int)aZPosition;
@end
@implementation ESJet
-(void)update:(NSTimeInterval)aTime isVisible:(BOOL)aVisible;
{
CGFloat nextZPosition = self.velocity;
// NSLog(@"jet position: %@", NSStringFromGLKVector3(SCNVector3ToGLKVector3(self.node.position)));
if (aVisible)
{
NSLog(@"jet visible");
}
else
{
//when jet flys off right-side of screen, reposition on left
NSLog(@"...");
nextZPosition = -(self.node.position.x * 1.95);
}
GLKVector3 nextFramePosition = GLKVector3Make(0, 0, nextZPosition);
GLKMatrix4 transform = SCNMatrix4ToGLKMatrix4(self.node.transform);
self.node.transform = SCNMatrix4FromGLKMatrix4(GLKMatrix4TranslateWithVector3(transform, nextFramePosition));
if (self.roll)
{
//rotate jet ~ 1 degree per frame on X axis
GLKQuaternion rot = GLKQuaternionMakeWithAngleAndAxis((M_1_PI * self.rollRate), 1, 0, 0);
GLKQuaternion orientation = GLKQuaternionMultiply(rot, GLKQuaternionMake(self.node.orientation.x,
self.node.orientation.y,
self.node.orientation.z,
self.node.orientation.w));
self.node.orientation = SCNVector4Make(orientation.x, orientation.y, orientation.z, orientation.w);
}
}
-(instancetype)init
{
if (self = [super init])
{
self.velocity = 0.33f;
self.rollRate = (self.velocity * 0.364);
}
return self;
}
-(instancetype)initWithNode:(SCNNode *)aNode zPosition:(int)aZPosition
{
if ([self init])
{
self.node = aNode;
[self.node removeAllActions];
//rotate jet 90 degrees on Y axis, facing right edge of screen
GLKQuaternion rot = GLKQuaternionMakeWithAngleAndAxis(M_PI_2, 0, 1, 0);
self.node.orientation = SCNVector4Make(rot.x, rot.y, rot.z, rot.w);
self.node.position = SCNVector3Make(0, 0, aZPosition);
}
return self;
}
@end
@interface GameViewController: UIViewController<SCNSceneRendererDelegate>
@end
@implementation GameViewController
{
SCNScene * skScene;
SCNView * skView;
SCNNode * cameraNode;
NSMutableArray * nodeSnapshots;
NSDate * frameTimer;
ESJet * jet;
}
-(void)viewDidLoad
{
//default project code here...
skScene = scene;
skView = scnView;
skView.delegate = self;
skView.playing = YES;
nodeSnapshots = [NSMutableArray array];
jet = [[ESJet alloc] initWithNode:ship zPosition:-20];
jet.roll = YES;
}
//SCNSceneRendererDelegate
-(void)renderer:(id<SCNSceneRenderer>)aRenderer updateAtTime:(NSTimeInterval)time
{
void (^cloneNode)() = ^
{
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(q, ^
{
SCNNode * clone = [jet.node clone];
clone.opacity = 0.20f;
[nodeSnapshots addObject:clone];
[skScene.rootNode addChildNode:clone];
});
};
void (^cloneNodeOnTimer)(float) = ^(float interval)
{
if ((frameTimer == nil) || (frameTimer.timeIntervalSinceNow <= -interval))
{
frameTimer = [NSDate date];
cloneNode();
}
};
//SCNSceneRenderer protocol
BOOL nodeIsVisible = [aRenderer isNodeInsideFrustum:jet.node withPointOfView:cameraNode];
if (nodeIsVisible)
{
cloneNodeOnTimer(0.6f);
}
else
{
skView.playing = NO;
jet.node.hidden = YES;
if (nodeSnapshots.count > 0)
{
//remove transparency from last cloned node
((SCNNode *)[nodeSnapshots lastObject]).opacity = 1.0;
}
}
[jet update:time isVisible:nodeIsVisible];
}
//tap any node to render new scene
-(void)handleTap:(UIGestureRecognizer*)gestureRecognize
{
CGPoint tp = [gestureRecognize locationInView:skView];
NSArray * hitTestResults = [skView hitTest:tp options:nil];
if (hitTestResults.count > 0)
{
[nodeSnapshots enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop)
{
[(SCNNode *)obj removeFromParentNode];
}];
[nodeSnapshots removeAllObjects];
jet.node.hidden = NO;
skView.playing = YES;
}
}
No comments:
Post a Comment