行列を実感する
エフェクトは、移動や回転で作ることができます。
ページ全体に対しても、アイコン1つ1つに対しても、エフェクトを掛けることができますが、少し凝ったものを作ろうとすると、どうしても数学の知識が必要になってきます。
しかし、単なる移動エフェクトも、複雑なエフェクトも、内部では「行列」による演算が行われています。
3次元の移動や回転もそうですが、拡大も行列で表すことができます。
この際、3次元だからと言って、3行3列の行列で演算する訳ではありません。
どうしても限界にぶち当たってしまうため、4行4列で変換行列を用意します。
1次元増やすことで、計算がとてもシンプルになるのです。
一般的には、こんな計算式です。
実際にCylinder用のLUAプログラムを組むには、以下のようにします。
transform[12] = -1/PERSPECTIVE_DISTANCE
page.transform = transform
行列指定の例
行列を読み込んで、変更して、書き換えるという流れです。
しかし、エラーが出なくなるまでは出来たのですが、ここで直接行列を操作して紹介できるようなサンプルが完成しません。
行列操作ができるのか???と、疑心暗鬼になっているくらいです。
ですので、方向性を変えようと思います。
これまで紹介したサンプルを、行列で表現するとこんな感じになります!というような紹介が出来そうにありませんので、行列で演算してるのね!ということが感じられるサンプルを紹介しようと思います。
一言で言うと、「軸変換」です。
ページもアイコンも、回転軸はその中央です。
その回転軸をズラすことで、これまで出来なかったようなエフェクトが実現できるようになります。
ページの左端で回転させる
ページの回転軸は、ページ中央です。
この回転軸を左端にすることで、新たな回転エフェクトを作ってみましょう。
手順は以下の通りです。
1:軸が合うように移動する
2:回転させる
3:戻す
ザックリですが、これが全てです。
画像では以下の通りです。
今回は、左端を軸にして回そうとしていますので、回転軸を左端まで移動します。
その後、回転して、移動した分だけ戻します。
実際のプログラミングは、以下のようになります。
local percent = offset / page.width
local angle = percent * math.pi / 2
local dx = page.width / 2
page:translate( offset )
page:translate( -dx, 0, 0)
page:rotate(angle, 0, 1, 0)
page:translate( dx, 0, 0)
end
effect30_1
percentとangleは、すでに定番ですね。
今回は、90°の回転です。
移動量は、画面幅の半分ですが、これをdxに格納します。
page:translate(offset)
左端が回転軸ですので、ページを固定して確認します。
page:translate( -dx, 0, 0)
ページを半分左に移動して、軸を左端に合わせます。
page:rotate(angle, 0, 1, 0)
何も構わず回します。
page:translate( dx, 0, 0)
軸を元に戻します。
実際の動作は、以下の通りです。
左端軸回転エフェクト
左端が回転軸になっていることがわかります。
行列を感じる
サンプルで紹介したエフェクトを行列で考えてみましょう。
移動と回転で4つのページエフェクトがありましたので、それぞれの行列から紹介します。
page:translate(offset)
この移動エフェクトを行列で表すと、このように表現されます。
page:translate( -dx, 0, 0)
この移動エフェクトを行列で表すと、このように表現されます。
page:rotate(angle, 0, 1, 0)
この移動エフェクトを行列で表すと、このように表現されます。
page:translate( dx, 0, 0)
この移動エフェクトを行列で表すと、このように表現されます。
これを、サンプル通りの順番で行列を並べると、以下のようになります。
行列ですので、エフェクトを掛ける順番に、右から並べていきます。
これを計算すると、以下の行列になります。
この行列が何なのか?ということなのです。
このページを作り始めた理由が、この行列なのでございます。
プログラムで幾つかのエフェクトを並べることと、エフェクトの行列を計算しておき、プログラムにおいて行列でエフェクトを与えることで、同じことができることを紹介したかったのです。
ただ、この行列でエフェクトを与えるサンプルが完成しないのです。
何もエラーが出ないのですが、何もエフェクトが掛けられてもいないのです。
サンプルが見当外れなのか、Cylinderが対応していないのか、それも分かりません。
ですので、行列の雰囲気を感じてもらうしかないのです・・・
では、先ほどのサンプルにおいて、ページエフェクトの順番を変えてみましょう。
local percent = offset / page.width
local angle = percent * math.pi / 2
local dx = page.width / 2
page:translate( -dx, 0, 0)
page:rotate(angle, 0, 1, 0)
page:translate( dx, 0, 0)
page:translate( offset )
end
effect30_2
ページを固定するtranslate(offset)を最後にしただけです。
このエフェクトは、以下のように動作します。
順番を入れ替えたエフェクト
全然違うエフェクトになってしまったのが分かりますが、これはこれで使えそうです。
「思いがけない副産物」に出会えた感じです。
では、入れ替えたエフェクトの順番で行列を並べると、以下のようになります。
行列ですので、エフェクトを掛ける順番に、右から並べていきます。
これを計算すると、以下の行列になります。
先ほどの行列と違うことがお分かりでしょうか?
これが、行列の性質であり、醍醐味でもあります。
エフェクトの順番を変えただけで、全く違うエフェクトになってしまう所に行列を感じて頂けるかと思います。
この行列を眺めてみると、回転は変更がないのですが、4行目の平行移動に変更があることが分かります。
Cylinderの指定順によって、思いがけないエフェクトが誕生するのは、行列が絡んでいたことがお分かり頂けましたでしょうか?
仕上げる
順番を入れ替えることで、良い感じのエフェクトになりました。
ただ、入れ替える前のエフェクトが置き去りになったままでしたので、仕上げておこうと思います。
動作としては、出て行くページと同じ動きで、反対側から入ってきてもらおうと思います。
coverflowのような動きですね。
入ってくるページを意識して考えると、符号変換のみで完了することが分かります。
論理演算子で符号変換してみましたので、追加するのは1行で済みます。
local percent = offset / page.width
local angle = percent * math.pi / 2
local dx = page.width / 2
dx = ( angle < 0 ) and -dx or dx
page:translate( offset )
page:translate( -dx, 0, 0)
page:rotate(angle, 0, 1, 0)
page:translate( dx, 0, 0)
end
effect30_3
angleが負の時、つまり入ってくるページの動作中の時は、dxを-dxにします。
このエフェクトは、以下のように動作します。
左端軸回転エフェクト
この手直しにより、紹介できるエフェクトに仕上がったと思います。
アイコンでもやってみる
これまでは、ページ全体に対して軸変換をしてきましたが、アイコンでもやってみます。
やることは、特に変わりありませんので、いきなりサンプルを紹介します。
local percent = offset / page.width
local angle = percent*math.pi/2 * 0.95
local r = page[1].width / 2 * 1.05
page:translate( offset )
for i, icon in subviews(page) do
icon:translate(0, 0, -r)
icon:rotate(-angle, 0, 1, 0)
icon:translate(0, 0, r)
end
end
effect30_4
angleとrに関して、多少の手を加えています。
これは、動作確認後の修正です。
iPhone6Plusによる確認ですので、修正値はこの限りではないかもしれません。
3次元を扱うとき、「視点」も考えなければならなくなってきます。
「perspective」で検索していただくと、何となくでも理解していただけると思いますが、このperspectiveによって、表示される大きさなどが変わってきます。
このサンプルでは、このperspectiveが悪戯していると思うのですが、行列でperspectiveの指定が上手くできなかったため、直接調整しました。
実際の動作は、こんな感じです。
アイコンキューブエフェクト
アイコン1つ1つがキューブとなり、回転して切り替わるエフェクトです。
当サイトの更新状況を、アラートで表示するかどうかの設定をします。
保存する