[iOS] SpriteKitとUIKitを組み合わせて、ちょっとリッチなウォークスルー画面 を作りたい
はじめに
先日、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というクラスを作成しました。
#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
※ページの中身を作成しているコードは省略しています
サンプルアプリなので、実行時に問答無用で呼び出すようにします。
#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をインポートしてませんでした。
#import <UIKit/UIKit.h>
を
#import <SpriteKit/SpriteKit.h>
に変更します。
また、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もアウトレット化しておきます。
#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 ↩