スターウォーズのオープニングクロールを作る

「2015年は映画の年」と言われるくらい、注目作品が上映予定となっております。
その中でも、スターウォーズの存在は大きいと思います。

ここでは、スターウォーズのオープニングクロールを作ってみようと思います。
スターウォーズを見たことがなくても、知ってる人が多いあれです。

動きのイメージを固める

スターウォーズのオープニングクロールは、誰でも知っているくらい有名ですが、ホーム画面とどのように繋げばいいのでしょうか?
画面の外から入ってきて、奥へと消えていくのですが、もともと画面にあるアイコンで表現するには、工夫や妥協が必要になります。

そんな訳で、以下のような動作にしようと思います。
・現在のページを奥へ傾ける。
・現在のページを縮小しながら遠ざける。
・次のページを画面外から入り込ませる。
・傾いた次のページを起こす。
こんな感じですが、スターウォーズのオープニングクロールをイメージすると、現在のページと次のページは、同時に表示させるべきですね。

では、1つ1つイメージに近づけていきましょう。

現在のページを傾ける

スターウォーズのオープニングクロールは、ご存知の通り、手前から奥へと消えて行きます。
そのイメージを実現させるには、もともと平面であるページを傾けて、奥行き感を演出する必要があります。
とりあえず、次のページは後回しにして、現在のページを傾けるところまで作ってみましょう。

ここでは、以下のサンプルを流用します。
・動きを止める(effect1_1)
・ページ回転のいろいろ(effect4_3)
この2つで十分かと思います。

最初に、エフェクト的にページ移動を無効にしなければなりませんので、以下の1行を入れておきます。

page:translate( offset )

2ページ目は、とりあえず消えていてもらうので、以下を入れておきます。

if( percent < 0 ) then page:scale(0)

ページを傾けるのですが、どれくらい傾けるのが妥当なのでしょうか???
しばらく決まりそうにないので、変数に登場してもらいましょう。
それでも初期値は必要ですので、0.9とします。
これは、90°に対してどれくらい傾けるのかというものですが、ほとんど寝てるくらい傾けるイメージが強いので、0.9としました。

では、この初期値でどれくらい傾くのか確認してみましょう。
プログラムは以下の通りです。

return ( page, offset, screen_width, screen_height)
 local percent = offset/page.width
 local angle = percent*math.pi/2
 local rr = 0.9

 page:translate( offset )

 if( percent < 0 ) then
  page:scale(0)
 else
  page:rotate(rr * angle, 1, 0, 0)
 end
end

effect34_1

変数rrが、ページの傾き具合を表し、90°に対する割合を格納します。
実際に動かしてみると、こんな感じです。

画面を傾ける

いきなり問題発生ですね。
画面を傾けるだけのはずが、妙なひねりも入ってしまいました。

これが、「3Dエフェクトにおける予想外の動作」と言われるもので、予想外だからこそ面白いということで使われています。
しかし、プログラミング的には「バグ」扱いになりますので、対応しなければなりません。

これまでと同様のやりかたで横移動を止めたはずなのですが、どうしてこんな動きになってしまったのでしょうか?
ページの移動を止めるために、translateを使ったのですが、translateは行列変換ですので、この「行列」がイタズラしているものと考えられます。
では、行列(translate)を使わずに、横方向の移動を止めてみましょう。
やり方は単純で、横方向への移動を打ち消すだけです。
横方向は、page.layer.xで取得&指定できますので、以下のように書き換えます。

return ( page, offset, screen_width, screen_height)
 local percent = offset/page.width
 local angle = percent*math.pi/2
 local rr = 0.9

 page.layer.x = page.layer.x + offset

 if( percent < 0 ) then
  page:scale(0)
 else
  page:rotate(rr * angle, 1, 0, 0)
 end
end

effect34_1_2

このやり方が、本来のやり方なのでしょうね。
このやり方で動かしてみると、こんな感じです。

画面を傾ける(対策後)

イメージ通りの動きになりました。
これで、スタートできますね。

動きを2段階にする

ページを傾けることができましたので、傾いたページを奥へ移動させるのですが、その前に、動きを2段階にしておきます。

ここでは、以下のサンプルを追加流用します。
・エフェクトを段階的にする(effect26_1)

動作をどのタイミングで変更するかを表す変数をerとし、その初期値を0.5とします。
動きに関しては、動作全体の真ん中で動作を止めることにします。

では、実際にプログラムを記述してみましょう。

return ( page, offset, screen_width, screen_height)
 local percent = offset/page.width
 local angle = percent*math.pi/2
 local rr = 0.9
 local er = 0.5

 page.layer.x = page.layer.x + offset

 if( percent < 0 ) then
  page:scale(0)
 else
  if( percent < er ) then
   page:rotate(percent*rr*math.pi/2, 1, 0, 0)
  else
   page:rotate(er*rr*math.pi/2, 1, 0, 0)
  end
 end
end

effect34_2

erの前後で動作を変えています。
erまではpercentに応じて動作し、その後はerで固定するという内容です。
変数angleの宣言が残っていますが、angleの式の中にpercentがあるため、angle自体使っていません。

実際に動かしてみると、こんな感じです。

途中で止める

ページ移動の5割の段階で、90°の9割傾くというのが、ここで目的としている動作なのですが、見た感じ45°くらいで止まってしまっていますね。
少し眺めていたら分かったのですが、5割と9割を掛け算しているので、45°も傾いていないんですね。

ページ移動の5割の段階で、90°の9割傾かせるには、「エフェクトを段階的にする」の「修正2」のように、工夫しなければなりません。

簡単ですが、以下のように対策しました。

return ( page, offset, screen_width, screen_height)
 local percent = offset/page.width
 local angle = percent*math.pi/2
 local rr = 0.9
 local er = 0.5

 page.layer.x = page.layer.x + offset

 if( percent < 0 ) then
  page:scale(0)
 else
  if( percent < er ) then
   page:rotate(percent*rr*math.pi/2/er, 1, 0, 0)
  else
   page:rotate(er*rr*math.pi/2/er, 1, 0, 0)
  end
 end
end

effect34_2_2

erで割っただけです。
プログラム的には無駄が残ったままですが、以下のような動きになります。

途中で止める2

きとんと、ページ移動の5割の段階で、90°の9割傾きました。
9割傾いたからこそ分かったのですが、傾き過ぎていますね。
ここで、傾きを8割にして、様子を見てみましょう。
ついでに、プログラムの無駄を削いで仕上げます。

変数epを導入し、if文内のページ回転を1つにまとめることにします。

return ( page, offset, screen_width, screen_height)
 local percent = offset/page.width
 local rr = 0.8
 local er = 0.5
 local ep

 page.layer.x = page.layer.x + offset

 if( percent < 0 ) then
  page:scale(0)
 else
  if( percent < er ) then
   ep = percent
  else
   ep = er
  end
  page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
 end
end

effect34_2_3

プログラムも、少しはシンプルになりましたね。
このタイミングで、angleの行も削除しました。

途中で止める3

きとんと、ページ移動の5割の段階で、90°の8割傾きました。
良い感じの傾きかと思います。

画面奥へ消えるようにする

画面を傾けることができましたが、これはまだまだ「お膳立て」の段階です。
スターウォーズのオープニングクロールは、ここから奥へと消えていくので、奥へ消えてもらいましょう。

では、どうすれば奥へ消えていく感じを演出できるのでしょうか?
ここが今回のメインなので、じっくり時間をかけて考えたほうが良さそうなのですが、問題発生を待っている自分もいるので、単純に縮小するところから始めていきましょう。

ホントに縮小しただけのサンプルは以下の通りです。

return ( page, offset, screen_width, screen_height)
 local percent = offset/page.width
 local rr = 0.8
 local er = 0.5
 local ep

 page.layer.x = page.layer.x + offset

 if( percent < 0 ) then
  page:scale(0)
 else
  if( percent < er ) then
   ep = percent
  else
   do
    ep = er
    page:scale(1-((percent-er)/(1-er)))
   end
  end
  page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
 end
end

effect34_3

実際に動かしてみると、こんな感じです。

画面縮小

想定内ではありますが、考えれば考えるほど、大きな問題に感じます。
傾きの方向に消えて行くって・・・

まぁ、解決策を見出してからだと面白くないので、とりあえず、縮小しながら上に移動させてみましょう。
これで、それっぽく見えたら良いのですが・・・

縮小しながら上に移動ということは、縮小率と移動量に何らかの関係性を持たせた方が良さそうですね。
とりあえず、縮小率と移動量を変数にし、その関係性に重点を置くことにします。
まずは変数ですが、縮小率をsp、移動量をdyとしましょう。
移動量は、画面のどこを目指すかが問題になってきますが、デバイスによって違うので、page.heightを使って、画面高さと縮小率で決定すべきかと思います。
とりあえず、縮小率に画面高さを掛け合わせて、様子を見てみようと思います。

以下が、そのサンプルです。

return ( page, offset, screen_width, screen_height)
 local percent = offset/page.width
 local rr = 0.8
 local er = 0.5
 local ep, sp, dy

 page.layer.x = page.layer.x + offset

 if( percent < 0 ) then
  page:scale(0)
 else
  if( percent < er ) then
   ep = percent
  else
   do
    ep = er
    sp = 1-((percent-er)/(1-er))
    dy = (1-sp) * page.height / 4
    page:scale( sp )
    page:translate(0, -dy, 0)
   end
  end
  page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
 end
end

effect34_3_2

画面高さ分だけ移動すると、さすがに移動し過ぎですので、画面高さの1/4にしてみました。

実際に動かしてみると、こんな感じです。

画面縮小 & 縦移動

おっと・・・
良い感じに移動したかと思いきや、下がってしまいました。
失速して落ちていく感じが出て面白いのですが、望んだものと違うので、これは「バグ」と判断します。
単なる平行移動のはずなのに、どうしてこんな動きになってしまうのでしょうか?
これも行列の影響なのでしょうか?
行列!?!?
ということは・・・
page:translateによる移動をやめれば良いのかもしれませんね。
そうとなれば、早速試してみましょう。。

書き換えたサンプルが、以下になります。

return ( page, offset, screen_width, screen_height)
 local percent = offset/page.width
 local rr = 0.8
 local er = 0.5
 local ep, sp, dy

 page.layer.x = page.layer.x + offset

 if( percent < 0 ) then
  page:scale(0)
 else
  if( percent < er ) then
   ep = percent
  else
   do
    ep = er
    sp = 1-((percent-er)/(1-er))
    dy = (1-sp) * page.height / 4
    page:scale( sp )
    page.layer.y = page.layer.y - dy
   end
  end
  page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
 end
end

effect34_3_3

実際に動かしてみると、こんな感じです。

画面縮小 & 縦移動2

良い感じぢゃないですか〜!?
微調整も要らないほどに感じます。

やはり、行列が原因だったようですね。
予期しない動作があるので、少し変更したら即確認という流れは必要な作業ですね。

次のページを出現させる

ここからが、今回の醍醐味の部分となります。
現在のページが消えていく様ができましたので、次のページも同じようにやれば良さそうです。
もちろん、かなり大きい状態で出現し、小さくなりながら進んで行くという流れになるかと思います。

手始めとして、お膳立てとなる画面の傾きにかかる時間を短くしましょう。
時間という言い方も変なのですが、ページ移動の5割も使って画面を傾けていたので、これを1割にします。

手始めのサンプルが、以下になります。

return ( page, offset, screen_width, screen_height)
 local percent = offset/page.width
 local rr = 0.8
 local er = 0.1
 local ep, sp, dy

 page.layer.x = page.layer.x + offset

 if( percent < 0 ) then
  page:scale(0)
 else
  if( percent < er ) then
   ep = percent
  else
   do
    ep = er
    sp = 1-((percent-er)/(1-er))
    dy = (1-sp) * page.height / 4
    page:scale( sp )
    page.layer.y = page.layer.y - dy
   end
  end
  page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
 end
end

effect34_4

ホントに1割にしただけです。

こんな感じの動作になります。

動作全体の1割で傾く

5割で見慣れていたので、1割になると急に速く感じます。
まぁ、ざっと5倍のスピードですので、速いのでしょうね。
速すぎかとも思うのですが、画面を傾けることがメインではないので、このまま進みます。

では、次のページに出てきてもらいましょう。
その登場方法は、もちろん言うまでもありませんが、同じような処理で代用するところから始めます。
代用したサンプルが、以下になります。

return ( page, offset, screen_width, screen_height)
 local percent = offset/page.width
 local rr = 0.8
 local er = 0.1
 local ep, sp, dy

 page.layer.x = page.layer.x + offset

 if( percent < 0 ) then
  do
   ep = er
   sp = 1-((percent+er)/(1-er))
   dy = (1-sp) * page.height / 4
   page:scale( sp )
   page.layer.y = page.layer.y - dy
   page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
  end

 else
  if( percent < er ) then
   ep = percent
  else
   do
    ep = er
    sp = 1-((percent-er)/(1-er))
    dy = (1-sp) * page.height / 4
    page:scale( sp )
    page.layer.y = page.layer.y - dy
   end
  end
  page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
 end
end

effect34_4_2

縮小率spの内容が違う程度で、あとは全く同じ内容の処理で代用しました。

こんな感じの動作になります。

次ページの出現

いきなり出現したので、これには私もビックリしました。
縮小率が、1より大きい数字から始まっているはずなので、大きい状態で始まることは想定していたのですが、大きくなった分だけ画面の外にはみ出すと思っていたため、「想定外」となってしまいました。

はみ出させるならdyを大きくすれば良いと思ったのですが、dyはspに依存しているため、spを変更して様子を見ようと思います。
dyでは4で割っているため、spでは4を掛けて見ようと思います。

4倍お試しのサンプルは、以下の通りです。

return ( page, offset, screen_width, screen_height)
 local percent = offset/page.width
 local rr = 0.8
 local er = 0.1
 local ep, sp, dy

 page.layer.x = page.layer.x + offset

 if( percent < 0 ) then
  do
   ep = er
   sp = 1-((percent+er)/(1-er)) * 4
   dy = (1-sp) * page.height / 4
   page:scale( sp )
   page.layer.y = page.layer.y - dy
   page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
  end

 else
  if( percent < er ) then
   ep = percent
  else
   do
    ep = er
    sp = 1-((percent-er)/(1-er))
    dy = (1-sp) * page.height / 4
    page:scale( sp )
    page.layer.y = page.layer.y - dy
   end
  end
  page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
 end
end

effect34_4_3

全体を4倍すると都合が悪いので、部分的に4倍しています。

こんな感じの動作になります。

4倍お試し

いい感じではないでしょうか?
イメージに近づいているのが実感できると思います。
しかし、実際には「もうちょい」といった感じですので、6倍で試してみます。

6倍お試しのサンプルは、以下の通りです。

return ( page, offset, screen_width, screen_height)
 local percent = offset/page.width
 local rr = 0.8
 local er = 0.1
 local ep, sp, dy

 page.layer.x = page.layer.x + offset

 if( percent < 0 ) then
  do
   ep = er
   sp = 1-((percent+er)/(1-er)) * 6
   dy = (1-sp) * page.height / 4
   page:scale( sp )
   page.layer.y = page.layer.y - dy
   page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
  end

 else
  if( percent < er ) then
   ep = percent
  else
   do
    ep = er
    sp = 1-((percent-er)/(1-er))
    dy = (1-sp) * page.height / 4
    page:scale( sp )
    page.layer.y = page.layer.y - dy
   end
  end
  page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
 end
end

effect34_4_4

全体を6倍すると都合が悪いので、部分的に6倍しています。

こんな感じの動作になります。

6倍お試し

これこれ!!
これぞ!!って感じの動きになったではありませんか!?

良い感じの動きになったところで、区切りとします。

次のページも2段階にする

次のページが良い感じで登場したので、次のページも2段階にして、傾きを元に戻しましょう。

では、どのタイミングで動きを変えれば良いのでしょうか?
これは、1ページ目と同様に、erの値を流用することにします。

2ページ目も2段階にしたサンプルが、以下になります。

return ( page, offset, screen_width, screen_height)
 local percent = offset/page.width
 local rr = 0.8
 local er = 0.1
 local ep, sp, dy

 page.layer.x = page.layer.x + offset

 if( percent < 0 ) then
  if( percent < -er ) then
   do
    ep = er
    sp = 1-((percent+er)/(1-er)) * 6
    dy = (1-sp) * page.height / 4
    page:scale( sp )
    page.layer.y = page.layer.y - dy
    page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
   end
  else
   ep = percent
   page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)

  end

 else
  if( percent < er ) then
   ep = percent
  else
   do
    ep = er
    sp = 1-((percent-er)/(1-er))
    dy = (1-sp) * page.height / 4
    page:scale( sp )
    page.layer.y = page.layer.y - dy
   end
  end
  page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
 end
end

effect34_5

2ページ目の部分にif文を追加し、動作終了側のerに傾きを戻すためのep=percentを入れています。

2ページ目も2段階にしたサンプルは、こんな感じです。

2ページ目も2段階

2ページ目も2段階になりましたが、傾きを戻す段階で、傾きが逆になっていますね。

傾きを逆にするには、page:rotate内の該当する回転方向を-1にすれば良いのですが、最終的に仕上げる段階で、page:rotateも1つにまとめたいので、ここではep=-percentとして対応しておきます。

対応後のサンプルは、以下の通りです。

return ( page, offset, screen_width, screen_height)
 local percent = offset/page.width
 local rr = 0.8
 local er = 0.1
 local ep, sp, dy

 page.layer.x = page.layer.x + offset

 if( percent < 0 ) then
  if( percent < -er ) then
   do
    ep = er
    sp = 1-((percent+er)/(1-er)) * 6
    dy = (1-sp) * page.height / 4
    page:scale( sp )
    page.layer.y = page.layer.y - dy
    page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
   end
  else
   ep = - percent
   page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
  end

 else
  if( percent < er ) then
   ep = percent
  else
   do
    ep = er
    sp = 1-((percent-er)/(1-er))
    dy = (1-sp) * page.height / 4
    page:scale( sp )
    page.layer.y = page.layer.y - dy
   end
  end
  page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
 end
end

effect34_5_2

2ページ目の部分にif文を追加し、動作終了側のerに傾きを戻すためのep=percentを入れています。

2ページ目も2段階にしたサンプルは、こんな感じです。

傾き修正

できてる?
できてますよね?

思いっきり完成ですね!?

仕上げる

動きは出来上がりましたので、プログラムを仕上げましょう。
ここで言う「仕上げ」は、無駄を省くという作業を指します。
具体的には、if文の中にあるpageの動作をif文の外にひとまとめにし、if文には、ep, sp, dyを求める式だけにすることにします。
そうすることで、とてもスッキリするはずです。

無駄を省いたサンプルが、以下になります。

return ( page, offset, screen_width, screen_height)
 local percent = offset/page.width
 local rr = 0.8
 local er = 0.1
 local ep, sp, dy

 page.layer.x = page.layer.x + offset

 if( percent < 0 ) then
  if( percent < -er ) then
   do
    ep = er
    sp = 1-((percent+er)/(1-er)) * 6
    dy = (1-sp) * page.height / 4
   end
  else
   do
    ep = - percent
    sp = 1
    dy = 0
   end
  end
 else
  if( percent < er ) then
   do
    ep = percent
    sp = 1
    dy = 0
   end
  else
   do
    ep = er
    sp = 1-((percent-er)/(1-er))
    dy = (1-sp) * page.height / 4
   end
  end
 end
 page:scale( sp )
 page.layer.y = page.layer.y - dy
 page:rotate(ep*rr*math.pi/2/er, 1, 0, 0)
end

effect34_6

ep, sp, dyは3つで1セットとしますので、抜けている部分に追加しています。

動作は、こんな感じです。

完成エフェクト

書き換えただけなので、同じ動きかどうかの確認ですね。



当サイトの更新状況を、アラートで表示するかどうかの設定をします。


保存する

その機能で月額1500円もするのですか??
その機能なら年額1500円で手に入りますよ!

当サイトもこちらのレンタルサーバーを利用しています。

Copyright (C) 2007 Bokechans.net All Rights Reserved.