この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
先日、iOS オールスターズ勉強会に参加しました。すごく良かったです!どれも勉強になりました。
そのなかで、まだiOSでリッチな演出に疲弊してるの?という発表があったのですが、そこでUIKitにSpriteKitのを入れられることを知りました。(´-`).。oO(しかも、思ったより簡単そう・・・)
だがしかし、パーティクルの使いドコロって、ゲームじゃないアプリだと使いドコロがが難しいです。
パーティクル上級者が実装すれば素敵な感じになると思いますが、自分のような初心者が通常の画面で多用すると、ユーザビリティの低下を起こしそうなのです。
でも、使ってみたい!\\\\(۶•̀ᴗ•́)۶////

はい。前置きが長くなりましたが、今回はSpriteKitとUIKitを組み合わせてウォークスルー画面作りたいと思います。 Swiftのデモは素敵なのが *1あるので、時代を逆行している感はありますがObjectice-Cで今回はやっていきます。
サンプルプロジェクトの作成
サンプルでは、ようこそ画面における温泉の湯けむりを演出します。(´-`).。oO(Smokeパーティクル使いたい・・・)
環境
- Xcode 6.1.1
- iOS SDK 8.1
演出を入れる前の画面を作成する
演出を入れる前のウォークスルー画面を作っていきます。 プロジェクトテンプレートよりSingle View Applicationを選択します。
次にMain.storyboard上にウォークスルーの画面を作成します。ベースはUIViewControllerです。
そして、UIImagerView、UIScrollView、UIPageControlの順に置いていきます。UIImagerViewには背景画像を表示させます。
ウォークスルー画面のクラスを作成します。今回はWalkThroughViewControllerというクラスを作成しました。
WalkThroughViewController.m
#import "WalkThroughViewController.h"
#import "WalkThroughPageView.h"
@interface WalkThroughViewController () <UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property (weak, nonatomic) IBOutlet UIPageControl *pageControl;
@end
@implementation WalkThroughViewController
#pragma mark - Lifecycle Methods
- (void)viewDidLoad
{
[super viewDidLoad];
[self setupScrollView];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - scrollView Delegate method
- (void)scrollViewDidScroll:(UIScrollView*)scrollView
{
// 縦方向のスクロールをキャンセルする
CGPoint point = scrollView.contentOffset;
point.y = 0;
self.scrollView.contentOffset = point;
// ページャ設定
CGFloat pageWidth = scrollView.frame.size.width;
float fractionalPage = scrollView.contentOffset.x / pageWidth;
NSInteger page = lround(fractionalPage);
if (self.pageControl.currentPage != page) {
self.pageControl.currentPage = page;
}
}
#pragma mark - Private methods
- (void)setupScrollView
{
// スクローラーの設定
self.scrollView.pagingEnabled = YES;
self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.showsVerticalScrollIndicator = NO;
self.scrollView.delegate = self;
self.scrollView.userInteractionEnabled = YES;
// ページの中身を入れる処理(コード省略)
・
・
・
// pageControl設定
self.pageControl.numberOfPages = titles.count;
self.pageControl.currentPage = 0;
self.pageControl.userInteractionEnabled = NO; // タップを無効
}
@end
※ページの中身を作成しているコードは省略しています
サンプルアプリなので、実行時に問答無用で呼び出すようにします。
ViewController.m
#import "ViewController.h"
#import "WalkThroughViewController.h"
@interface ViewController ()
@end
@implementation ViewController
#pragma mark - Lifecycle Methods
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
WalkThroughViewController *walkThroughViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"WalkThroughViewController"];
[self presentViewController:walkThroughViewController animated:YES completion:nil];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
@end
説明を色々と端折りましたが、アプリ起動時にウォークスルー画面が表示されれば準備は完了です。
パーティクル作成
パーティクルの作成については以下のページを参考に作成しました。
今回は湯けむりなので、Smokeパーティクルを選択します。 そしてSmokeParticle.sksファイルを作成しました。
それっぽく見えるように調整します。パーティクルのパラメータの解説については、下記サイトにまとめられていたので参考になりました。
SpriteKitの準備
パーティクルを呼び出すには、SpriteKitSceneが必要になるので作成します。 ResourceからSpriteKitSceneを選択して作成します。
また、SpriteKitScene用のクラスファイルを作成します。サブクラスはSKSceneとします。
以下、3ファイルを作りました。
- SmokeScene.sks
- SmokeScene.h
- SmokeScene.m
SmokeScene.hですが、デフォルトではSpriteKitをインポートしてませんでした。
SmokeScene.h
#import <UIKit/UIKit.h>
を
#import <SpriteKit/SpriteKit.h>
に変更します。
また、SmokeScene.mにパーティクルを呼び出すコードを追加します。
SmokeScene.m
#import "SmokeScene.h"
@implementation SmokeScene
- (void)didMoveToView:(SKView *)view
{
[self setBackgroundColor:[UIColor clearColor]];
if (self.children.count == 0) {
NSString *path = [[NSBundle mainBundle] pathForResource:@"SmokeParticle" ofType:@"sks"];
SKEmitterNode *particle = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
particle.position = CGPointMake(CGRectGetMidX(self.frame), 0.0f);
[self addChild:particle];
}
}
@end
今回は任意のタイミングではなく、このシーンが表示されたらパーティクルを表示するようにしてます。
particle.positionで呼び出したい場所を指定します。今回は画面下から出てくるイメージなので、y座標を0にします。(左下が(0,0)になるので・・・)
これでSpriteKitの準備は完了です。
UIKitに入れる
先ほど作成したMain.storyboardのウォークスルー画面にSpriteKit用のViewを追加します。種類はUIView、位置は背景画像とScrollViewの間に入れます。
ここでポイントなのが、追加したUIViewのクラスをSKViewにします。またBackgroundを透明にしておきます。
また、パーティクルを入れたいViewController(ここではWalkThroughViewController)にSKViewを呼び出すコードを入れます。追加したのは色を付けた部分です。Storyboardに追加したSKViewもアウトレット化しておきます。
WalkThroughViewController.m
#import "WalkThroughViewController.h"
#import "WalkThroughPageView.h"
#import "SmokeScene.h"
@implementation SKScene (Unarchive)
+ (instancetype)unarchiveFromFile:(NSString *)file
{
// アプリケーションバンドルからシーンファイルのパスを取得
NSString *nodePath = [[NSBundle mainBundle] pathForResource:file ofType:@"sks"];
NSData *data = [NSData dataWithContentsOfFile:nodePath
options:NSDataReadingMappedIfSafe
error:nil];
NSKeyedUnarchiver *arch = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
[arch setClass:self forClassName:@"SKScene"];
SKScene *scene = [arch decodeObjectForKey:NSKeyedArchiveRootObjectKey];
[arch finishDecoding];
return scene;
}
@end
@interface WalkThroughViewController () <UIScrollViewDelegate>
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property (weak, nonatomic) IBOutlet UIPageControl *pageControl;
@property (weak, nonatomic) IBOutlet SKView *skView;
@end
@implementation WalkThroughViewController
#pragma mark - Lifecycle Methods
- (void)viewDidLoad
{
[super viewDidLoad];
[self setupScrollView];
[self setupParticle];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - scrollView Delegate method
- (void)scrollViewDidScroll:(UIScrollView*)scrollView
{
// 縦方向のスクロールをキャンセルする
CGPoint point = scrollView.contentOffset;
point.y = 0;
self.scrollView.contentOffset = point;
// ページャ設定
CGFloat pageWidth = scrollView.frame.size.width;
float fractionalPage = scrollView.contentOffset.x / pageWidth;
NSInteger page = lround(fractionalPage);
if (self.pageControl.currentPage != page) {
self.pageControl.currentPage = page;
}
}
#pragma mark - Private methods
- (void)setupScrollView
{
// スクローラーの設定
self.scrollView.pagingEnabled = YES;
self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.showsVerticalScrollIndicator = NO;
self.scrollView.delegate = self;
self.scrollView.userInteractionEnabled = YES;
// ページの中身を入れる処理(コード省略)
・
・
・
// pageControl設定
self.pageControl.numberOfPages = titles.count;
self.pageControl.currentPage = 0;
self.pageControl.userInteractionEnabled = NO; // タップを無効
}
- (void)setupParticle
{
self.skView.userInteractionEnabled = NO;
self.skView.allowsTransparency = YES;
SmokeScene *scene = [SmokeScene unarchiveFromFile:@"SmokeScene"];
scene.scaleMode = SKSceneScaleModeAspectFill;
[self.skView presentScene:scene];
}
@end
出来上がったもの
実行してみました。 GIFにしたら画質が悪くなってしまいましたが、下記のような湯けむりを演出することが出来ました。
さいごに
SpriteKitを扱うのが初めてだったので、手探りつつやってみましたが、思っていたよりも手軽に出来るなと思いました。実際の案件ではあまり使う事は無さそうな気もしますが、こういった要望がきた時には、さっと実装できるとカッコ良いです。(´◡`人)
今回の記事の中で使用している画像は下記からお借りしています。
脚注
- https://github.com/ryusukefuda/SpriteKit-Demo ↩