16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

アプリ道場Advent Calendar 2015

Day 18

【SpriteKit】複数のParticle FileをSKActionを使って組み合わせて、変化に富んだエフェクトを作る

Posted at

遅くなりまして、すみません! 「アプリ道場Advent calendar 2015」 18日目分の投稿です。
前回のParticle File設定編【SpriteKit】爆発や雪などの表現ができる「Particle File」についてを経て、後編になる今回はオリジナルのエフェクトを作ってみます。

【本題】複数のParticle FileをSKActionを使って組み合わせて、変化に富んだエフェクトを作る
【おまけ】MacBookにあるツール(テキストエディットとプレビュー)だけでParticle用のTextureを作る(雪の結晶などの特徴的な形のもの)

以上の2本立ててでお送りします。

#複数のParticle FileをSKActionを使って組み合わせて、変化に富んだエフェクトを作る

前回で1つのParticle Fileでも、炎や火花などかなり綺麗なエフェクトがゲームに組み込める事が分かりました。

そうなると、日本のゲームによくある魔法や技のような、魔法陣が浮かび上がってから炎が広がるとか、飛び散る光の粒と炎が同時に放たれるとか、そんな変化に富んだエフェクトがXcode1つの中で完結して出来たなら………嬉しいかもしれない!

という事で、今回は複数のParticle FileとSKActionを使用して、
「画面をタップすると、その場所に4種類の形の雪の結晶が違う動きで次々出てくる、氷の魔法のようなエフェクトを出す」
を試作してみました。

出来あがったものを先に載せると、このようなエフェクトが作れました。

動画GIF.gif
沢山タップしてエフェクトを発生させた動画もあります。YouTubeに飛びます)

##Particle Textureに自作の画像を使用したParticle Fileを作る

まずは、エフェクトの中身となる4種類のParticle Fileを作ります。
4つともParticle Textureに自作の画像を使いました。

・effect1用のParticle File:「rain」テンプレートを改良
・effect2用のParticle File:「fire」テンプレートを改良
・effect3用のParticle File:「bokeh」テンプレートを改良
・effect4用のParticle File:「spark」テンプレートを改良

使用したTexture画像と、それぞれの数値は以下の画像の通りです。

画像_1_エフェクトの設定.jpg
(Texture画像は分かりやすいように拡大しているので、ぼやけています)

これで1つのエフェクトに使いたい素材のParticleが4種類できました!

##SKActionで各Particle Fileの表示・非表示とその時間を設定して組み合わせる

あとは、これらをタップした時に、画面にイメージ通りの順番に表示させて1つのエフェクトを作ってみます。

これの作り方については、きっともっとプログラムに詳しい方ならばもっとスマートな方法が出来るのだと思いますが、今回は限られたことしかまだ扱えない初心者なりに出来る事の範囲の中で、やりたい事を作ってみました。

SKActionを使って、各パーティクル(SKEmitterNode)の表示・非表示、それらの時間、徐々に消えていく・徐々に出てくるを設定し、動かしてみました。
今回、パーティクルの演出のコントロールのために使用したSKActionは次のものです。

・hide:Nodeを非表示にする
・unhide:Nodeを表示させる
・waitForDuration:Nodeを待たせる
・fadeInWithDuration:Nodeをフェードインさせる
・fadeOutWithDuration:Nodeをフェードアウトさせる

最後にNodeを削除するremoveFromParentを入れて、これらをsequenceで順番に行うようにして、runActionで各パーティクル(SKEmitterNode)に実行させるようにしました。

###(1)タップした位置を持っておく見えないNodeを作っておく
まずタップした時にその場所に、目には見えないただのNodeを作りました。

 for touch in touches {
            let location = touch.locationInNode(self)
            
            //タップした場所に見えないNodeを作っておく
            let node = SKNode()
            node.position = location
            self.addChild(node)
            
            //この中にeffect1〜effect4を設定する
            //1番目のeffect(effect1)
            //2番目のeffect(effect2)
            //3番目のeffect(effect3)
            //4番目のeffect(effect4)
            
            //最後に、最初に作った見えないNodeを削除する
            node.removeFromParent()
            
            }         

そのNodeと同じ場所(=タップした場所)にeffect1〜effect4までのSKEmitterNodeを置くように設定し、行わせたいActionを作りました。

###(2)1番目に表示させたいeffect1のSKActionの内容
最初に表示させるものなので、表示している状態から徐々に消えていくようにフェードアウトさせる、fadeOutWithDurationを使用したのちに、removeFromParentで削除します。

            //1番目のeffect(effect1)
            if let effect = SKEmitterNode(fileNamed: "snowCrystal01"){
                //タップした場所(見えないNodeの場所に置く)
                effect.position = node.position 
                self.addChild(effect)
                
                //effect1のSKAction(表示とその時間)についての指定
                let fadeOut = SKAction.fadeOutWithDuration(1)
                let remove = SKAction.removeFromParent()
                let actionEffect1 = SKAction.sequence([fadeOut,remove])
                effect.runAction(actionEffect1)
            }

###(3)2番目に表示させたいeffect2のSKActionの内容
effect1の後に表示させたいので、最初にhideを使って隠しておきます。
Nodeを待機させておくwaitForDurationで、隠した状態の時間を指定します。
その後でunhideで今度は表示させて、同じく表示状態の時間をwaitForDurationで指定して、1番目のeffectと同じくフェードアウトで徐々に消えるようにして、最後に削除しておきます。

            //2番目のeffect(effect2)
            if let effect2 = SKEmitterNode(fileNamed: "snowCrystal02"){
                effect2.position = node.position
                self.addChild(effect2)
                
                let off = SKAction.hide()
                let waitOff = SKAction.waitForDuration(0.5)
                let on = SKAction.unhide()
                let waitOn = SKAction.waitForDuration(0.5)
                let fadeOut = SKAction.fadeOutWithDuration(0.4)
                let remove = SKAction.removeFromParent()
                let actionEffect2 = SKAction.sequence([off,waitOff,on,waitOn,fadeOut,remove])
                
                effect2.runAction(actionEffect2)
            }

###(4)3番目に表示させたいeffect3のSKActionの内容
以下、3番目と4番目に表示させたいeffect3とeffect4は、2番目と同じ内容で、秒数の指定を変えているだけです。
前のeffectの表示にかけている秒数を見て、微妙に表示タイミングが重なるように、あるいは重ならないように、非表示で待機させている秒数を設定していきます。

途中で表示を確認してみたら、急に3番目のeffectが表示されると唐突な感じがしたので、unhide(Nodeの表示)を指定したあとで、ほんの数秒ですが、徐々に出てくるようにフェードイン(fadeInWithDuration)を加えています。

           //3番目のeffect(effect3)
            if let effect3 = SKEmitterNode(fileNamed: "snowCrystal03"){
                effect3.position = node.position
                self.addChild(effect3)

                let off = SKAction.hide()
                let waitOff = SKAction.waitForDuration(1)
                let on = SKAction.unhide()
                let fadeIn = SKAction.fadeInWithDuration(0.5)
                let fadeOut = SKAction.fadeOutWithDuration(1)
                let remove = SKAction.removeFromParent()
                let actionEffect3 = SKAction.sequence([off,waitOff,on,fadeIn,fadeOut,remove])

                effect3.runAction(actionEffect3)
            }

(5)4番目に表示さたいeffect4のSKActionの内容
これまでの流れと一緒です。SKActionの構成はeffect2と同じで、指定している秒数が違うだけです。

            //4番目のeffect
            if let effect4 = SKEmitterNode(fileNamed: "snowCrystal04"){
                effect4.position = node.position
                self.addChild(effect4)

                let off = SKAction.hide()
                let waitOff = SKAction.waitForDuration(2)
                let on = SKAction.unhide()
                let waitOn = SKAction.waitForDuration(0.2)
                let fadeOut = SKAction.fadeOutWithDuration(0.8)
                let remove = SKAction.removeFromParent()
                let actionEffect4 = SKAction.sequence([off,waitOff,on,waitOn,fadeOut,remove])

                effect4.runAction(actionEffect4)
            }

最後にタップした時に作った見えないノードを削除するようにします。

こうして出来たのが、冒頭のGIFアニメ画像のエフェクトです。
タップした場所に次々と形と動きの変わるエフェクトが現れては消えます。

今回は次々と表示が変わるようにしてみましたが、同寺に複数のパーティクルを表示させることで豪華なエフェクトにもなります。

サンプルとしてサッと作ったので、カッコよさはイマイチかもしれませんが、良いエフェクトを参考資料に、各パーティクルの設定をして、表示時間などを調整すれば、Xcode1つででもゲームに良くあるスキルや魔法のエフェクトのようなものが作れそうです。

今回はパーティクルの動きをコード上ではいじってませんが、こちらも組み合わせれば更に1つのパーティクルの中でも複雑な動きをつけていく事が出来ると思います。

同じParticle Fileでも、particleTextureでコード上でテクスチャ画像の差し替えも出来るので、同じ動きで次々形の変わるものなどバリエーションも作れます。

さらに動きをつけたSpriteNodeなどを組み合わせることで、かなり凝った演出も作れると思います。

参考:「SpriteKit Framework Reference(SKAction Class Reference)」


「アプリ道場Advent calender 2015」15日目の記事「【SpriteKit】初心者がSpriteKitでクリスマスカードアプリを作って考えたこと」 にて、niggさんがParticle(SKEmitterNode)とSpriteNodeを使用して素敵なクリスマスカードのアプリを作成されています。
こちらも見てみますと、あれこれ演出の想像が広がって楽しいと思います。

#【おまけ】MacBookにあるツール(テキストエディットとプレビュー)だけでParticle用のTextureを作る

おまけの小ネタです。
今回、エフェクトを作るのに使用した雪の結晶や小さい結晶のようなテクスチャ画像ですが、実は画像製作系のソフトで描画したものではなくて、テキストから作ったものです。

制限はありますが、MacBookにある**「テキストエディット」「プレビュー」**を使えば、フォントから雪の結晶や星やハートなどの形のテクスチャを手軽に作る事が出来ます。

フォントについては、アプリなどに使う場合ライセンスには注意です。
今回のように画像として素材として使うについても各フォントの使用範囲が問題ないかどうか、フリーフォントを使う場合には、その点は要確認です。

iOSに元から入っているものについては、すでにアプリで組み込みで使えるものだから恐らく画像素材としてアプリ内で使う分にも特に別途契約などはしなくても大丈夫かなというところで、標準で入っているフォントの中から、使えそうな図形をPNG画像として書き出してみました。

##テキストエディットからプレビューで透過PNG画像を作る手順

ちょっとしたポイント(コツのようなもの)も書いていますので説明が長いですが、方法は簡単です。

###(1)テキストエディットで使いたい図形にあたるものを入力する

色の変更などを使いたいので「フォーマット」は「リッチテキスト」にしておきます。

・テキストエディットの「編集」>「絵文字と記号」から探すか、
・Macの入力の切り替えのところから「文字ビューアを表示」
で、探すと一覧が見られるので便利です。

使いたい図形を入力したら、Texture画像にあらかじめ色をつけて使いたい場合には、使いたい色に変えておきます。
白いPNG画像を使って、Particle FileのColor Rampで色を変化させたい場合は、ここでのテキスト色は一旦グレーにしておきます(明るすぎず暗すぎずのところ(真ん中あたり)ならどこでも可)

<ポイント>
コツとしては、最終的に小さい画像を使いたいとしても、ある程度おおきめのフォントサイズ(60、70以上くらい〜)で出しておくと、このあとのプレビューでの作業がやりやすくなり、綺麗なエッジの切り抜き画像が作れます。

###(2)プレビューでPNG画像に変換する
テキストエディットの「ファイル」>「プリント」でプリントのダイアログが出て来たら、下の左端、「?」のとなりにある「PDF」のところで「プレビューでPDFを開く」を選択します。

画像_2_プレビューの説明.jpg

すると、プレビューに移ります。
今度はここでツールボックスのようなボタンをクリックして、マークアップツールバーを表示さて、左から2番目の「選択ツール」で使いたい画像の部分だけを切り抜きます。

画像_3_プレビューの操作.jpg

まずはだいたい大雑把に不要な部分が削除できればOKです。
途中にでてくる「PDF 書類を切り取っても、選択範囲外の内容は削除されません。」や「もとの書類を変更できないため〜〜」などの警告は、気にしないで進めます。

だいたい必要な部分だけ切り抜いたファイルを「ファイル」>「書き出す…」で「フォーマット」の部分を「PNG」を選択して、分かりやすい名前をつけて保存します。
(解像度はそのままだと150と出て来ていると思いますが、いったんそのままで保存します)

プレビューで編集中の時は、背景が透過に見えてましたが、ここで背景が白のPNG画像に書き出されます。
(なので白い画像をつくりたい時にテキストを白い色にすると、背景と同化してしまいます。)

###(3)透過PNGを作成する(画像の色やサイズを調整する)

透過PNGにするには、引き続きプレビューで「ファイル」>「開く…」で先ほど保存した画像を開きます。

ツールボックスのようなボタンをクリックして、マークアップツールバー(画像編集のツール類)を表示させます。
左から2番目の「インスタントアルファ」を選択します。

切り抜きたい色の部分に十字のカーソルを合わせてクリックからのドラッグで選択範囲を指定します。
ドラッグ加減で、エッジの微妙な色の境目の範囲を調整できます。
範囲が指定できたら、「切り取り」というボタンが表示されるので、そこをクリックで選択した部分をカットできます。

※不要な背景の白い部分を選択した場合には、そのまま切り抜きすると残したい図形が消えてしまうので、「編集」>「選択部分を反転」してから「切り抜き」します。

複雑な形(透明にしたい不要な白い部分がいくつかに区切られている)の場合は、図形の色の部分を選択して調整すると1度で白い部分を削除できます。

図形の色そのままを使いたい場合には、これで保存すれば終了ですが、白い図形の画像を使いたい場合は、最後に色の調整をプレビューで行います。

「ツール」>「カラーを調整…」を選択します。
(マークアップツールバーの、三角が途中で切られているようなマークのアイコンも「カラーを調整」です)
・「露出」の部分を一番右(白いマークの方)
・「コントラスト」の部分を一番左(黒い丸のマークの方)
に調整すると真っ白の画像になります。

画像_4_プレビューの操作.jpg

※もとの文字色を黒にしてた場合、白にはならないので、最終的に白い画像をつくりたい時にはグレーにしておく必要があります。

(2)のPDFからPNGへの変換で、画像の切り抜き範囲が、図形が上下左右のどちらかに寄りすぎてるのが気になる場合は、背景を透過する前に「選択ツール」を使い不要な余白をカットして調整します。

また、この時点で画像のサイズを小さくしたい場合には、「ツール」>「サイズを調整」で任意のサイズに調整します。
Particle Fileの設定でもScaleでTextureのサイズは調整できるので、だいたいのサイズでOKにしておきます。

これで背景が透過のPNG画像ができました!

##資料画像と後書き
最後にフォントで出せる使えそうな図形をいくつか画像として並べてみます。

図形の文字.png

図形としてみるとアスタリスク(*)も結構使えます。
色をつければ花っぽい感じになりますし、輝く黄色っぽい色にすれば光の粒のような表現にもなるかと思います。

ひし形の辺を少し凹ませたような、光の効果に使えそうな図形に、六芒星や桜のような形もあるので、使い方や組み合わせ次第で多彩なエフェクトが作れそうです。

「プレビュー」の機能が、ちょっとした画像編集も可能なので、フォントで作った図形に、プレビューでさらに図形などを足せば、形の組み合わせも出来ます。


SpriteKitの登場で、自分のゲームを作ってみるという敷居は下がってきたと思います。
「よし!自分もiPhone用のゲームを作ってみるぞ!」と思ってMacBookを買ってきたら、あとはXcodeを入れれば、その環境だけでも多彩なエフェクトも作る事ができます。

個人や少人数での自作ゲーム制作に、ちょっとかっこいい演出を作ってみたいなという場合に役立ったり、あるいは制作のモチベーションアップに繋がったりする事があれば幸いです。

16
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?