おもちゃラボ

Unityで遊びを作ってます

【Unityシェーダ入門】シェーダでワイプエフェクトを作る

 昔のマリオやペルソナ5でも使われている、だんだん視界が狭くなるワイプエフェクトをUnityのシェーダで作ってみます。このエフェクト、ワイプエフェクトとかアイリスエフェクトとか、色々な呼ばれ方をしているようで正式名称がわかりませんが、下のようなやつです。

f:id:nn_hokuson:20161128200019p:plain

今回の記事のコンテンツは次の通りです。

カメラのメソッドをフックする

今回はポストエフェクトとしてワイプエフェクトをかけるシェーダで実装します。ポストエフェクトについては↓の記事で詳しく説明しています。

nn-hokuson.hatenablog.com

今回も雰囲気を出すために、ユニティちゃんを配置しておきました。

f:id:nn_hokuson:20161128201032p:plain

プロジェクトビューで「PostEffect.cs」というファイルを作成し、次のプログラムを入力してください。このスクリプトの説明は上の記事で説明しています。

using UnityEngine;
using System.Collections;

public class PostEffect : MonoBehaviour {

	public Material wipeCircle;

	void OnRenderImage(RenderTexture src, RenderTexture dest)
	{
		Graphics.Blit (src, dest, wipeCircle);
	}
}

スクリプトが保存できたら、PostEffect.csをMainCameraにドラッグ&ドロップしてアタッチしておきます。

f:id:nn_hokuson:20161128201313p:plain

Unityシェーダとマテリアルを作る

つづいて、ワイプエフェクトを表示するためのシェーダを作ります。プロジェクトビューで「右クリック」→「Create」→「Shader」→「Standard Surface Shader」を選択し、作成したファイル名をwipeCircleに変更します。続いてこのシェーダファイルをアタッチするマテリアルも作成しましょう。wipeCircleのシェーダをを選択した状態で「右クリック」→「Create」→「Material」を選択すると、wipeCircleというマテリアルが作成されます。

f:id:nn_hokuson:20161128203726p:plain

wipeCircle.shaderを開いて次のスクリプトを入力してください。

Shader "Custom/wipeCircle" {
	Properties{
		_Radius("Radius", Range(0,2))=2
	}
    SubShader {
        Pass {
            CGPROGRAM

            #include "UnityCG.cginc"

            #pragma vertex vert_img
            #pragma fragment frag

            float _Radius;
            fixed4 frag(v2f_img i) : COLOR {
        	i.uv -= fixed2(0.5, 0.5);
           	i.uv.x *= 16.0/9.0;
           	if( distance(i.uv, fixed2(0,0)) < _Radius ){
           		discard;
           	}
                return fixed4(0.0,0.0,0.0,1.0);
            }
            ENDCG
        }
    }
}

 今回作るワイプエフェクトはポストエフェクトなので、フラグメントシェーダで処理しています。やっていることは、原点からの値を見て黒く塗りつぶすかを決めているだけです。ただ、次の2点を補正するための処理が必要になります。

  • 原点が左下にある
  • 画面のアスペクトが影響する

画面の左下が原点になるため、画面の中央からの距離を見たい場合には次のようにu方向、v方向ともに0.5ずつ座標をずらす必要があります。

i.uv -= fixed2(0.5, 0.5);

f:id:nn_hokuson:20161128203455j:plain

また、入力される座標系は、画面の左下が(0, 0)、右上が(1, 1)になり、アスペクト比は考慮されていません。そこで正円をかくために、次のようにu方向にアスペクト比の補正をかけています。

i.uv.x *= 16.0/9.0;

f:id:nn_hokuson:20161128203956j:plain

最後に、原点からの距離が_Radius以下の場合にはワイプエフェクトをかけずに元の色を出力します。それ以外の場合には黒色で塗りつぶしています。_Radiusはインスペクタから変更できるように、Propertiesブロックで宣言しています。

シェーダで円を書く方法はこちらの記事でも紹介しています。

nn-hokuson.hatenablog.com

カメラにマテリアルをセットして実行する

作成したマテリアルをPostEffectスクリプトにセットします。ヒエラルキービューから「Main Camera」を選択し、インスペクタのPostEffectスクリプトのwipeCircle欄に、プロジェクトビューのwipeCircleマテリアルをドラッグ&ドロップします。

f:id:nn_hokuson:20161128204743p:plain

プロジェクトビューにあるwipeCircleのマテリアルを選択して、インスペクタからスライダを移動させるとワイプエフェクトが表示されます。

f:id:nn_hokuson:20161128205009p:plain

作成したワイプエフェクトはの実行結果は次のようになります。

f:id:nn_hokuson:20161128205425g:plain

InputFieldでテキストハイライティングをしてみる

UnityのuGUIにテキストを打ち込むためのInputFieldという部品があります。このInputFieldを使ってテキストハイライティングする方法を紹介します。

ちょっとトリッキーな方法なので、実際に使えるかは状況によります(笑)

今回の記事は次の内容です。

InputFieldを配置する

まずは、InputFieldをシーンビューに配置しましょう。ヒエラルキービューの「Create」→「UI」→「Input Field」を選択します。InputFieldが作成できたら、適当な位置に移動させておきましょう。

f:id:nn_hokuson:20161121231117p:plain

リッチテキストを使う

Input Fieldはリッチテキストを記述できるようになっていますが、Textの欄の上に「Using Rich Text with Input is unsupported」と表示されているように、基本的にはInput Fieldでリッチテキストを使うことは想定されていません。

f:id:nn_hokuson:20161121230748p:plain

例えば、リッチテキストを使って表示していても、バックスペースを使って消していくと、いきなりマークアップのタグが出現してしまいます。

f:id:nn_hokuson:20161121230603g:plain

このように、ハイライトしたい単語を直接マークアップしてしまうと、バックスペースなどで編集した時に不都合が起こってしまいます。

TextをInputFieldの上に配置する

そこで、InputFieldに表示する文字は透明にしておき、別のTextに文字を表示するようにします。InputFieldの文字をTextにコピーする際にマークアップの記述を追加します。

f:id:nn_hokuson:20161122203343j:plain

InputFieldの文字を透明にする

まずは、InputFieldの文字を透明にします。ヒエラルキービューから「InputField」→「Text」を選択し、インスペクタからColorのアルファ値を0にします。

f:id:nn_hokuson:20161122203806p:plain

このままではカーソルまで透明になってしまいます。カーソルは黒色にするため、ヒエラルキービューから「InputField」を選択し、インスペクタから「Custom Caret Color」のチェックを入れ、「Caret Color」を黒色に設定します。

f:id:nn_hokuson:20161122204109p:plain

Textを追加する

続いて、InputFieldの上に配置するTextを追加します。ヒエラルキービューの「Create」→「UI」→「Text」を選択し、InputFieldの上にかぶるように配置します。Textの名前は「ColorText」に変更しておきましょう。

f:id:nn_hokuson:20161122205446p:plain

InputFieldの文字をTextにコピーするスクリプトを作る

最後に、InputFieldに入力された文字を、Textにコピーするプログラムを作ります。InputFieldからコピーする際に、マークアップを追加してシンタックスハイライティング出来るようにします。

プロジェクトビューで右クリックし、「Create」→「C# Script」を選択して「TextHighlightController」を作成してから次のプログラムを入力して下さい。

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class TextHighlightController : MonoBehaviour {

	public Text colorText;

	void Update () {
		string script = GetComponent<InputField> ().text;
		script = script.Replace ("red", "<color=#c93a40>red</color>");
		colorText.text = script;
	}
}


作成したスクリプトをシーンビューのInputFieldにドラッグ&ドロップしてアタッチし、スクリプト中のcolorText変数にcolorTextをドラッグ&ドロップしてセットします。

f:id:nn_hokuson:20161122212344p:plain

実行してみる

実行するとちゃんと「red」という文字だけが赤色にハイライトされているのがわかります。また、バックスペースで消したときもマークアップが表示されることはありません。

f:id:nn_hokuson:20161122212056g:plain

文章の書き方

本を一冊出してしまった後に読む本じゃないでしょう・・・という気もしますが(笑)買って読んでいなかったので、今更ながら読んでみました。

わかりやすい文章の例として福沢諭吉の福翁自伝が引用されているのですが、このお話が物凄く面白い。

汽車にゆられながら『福翁自伝』を読み、読んでいるうちにそれが明治時代の書であることを忘れました。前の席に座っているおじいさんが、ゴットンゴットンという音に合わせて昔話を語っってくれているのを聞いているような、くつろいだ、快い調子がありました。

福沢諭吉の文章が分かりやすいのには、次のような理由があるようです。

  • 身の丈にあった言葉を使っている
  • 書かれたことが絵になって思い浮かべられる
  • 比喩がうまい

ただ、良い文章を書くためにはこれらのテクニックよりも「相手になんとしても伝えたいという情熱」が一番大切です、と書かれていました。

ブログでも「毎週3記事書くんだー」と気張ると、伝えることよりも書くことが目的になってしまうという罠にはまりますね。そうすると、だいたい面白くない記事になり、しかも自分も書いていて楽しくないという一石二鳥の逆、二兎追うものは一兎も得ず(?)になってしまいます。

色々と身につまされる話もあり、別に本を書く予定なんてないよという方でも、読み物としてとても面白かったので、ぜひどうぞ!

最後に印象に残った文を引用しておきます。

山肌に染み込んだ水がやがて、泉となってあふれでてくるように、好奇心によって心に染み込んだ水はやがて、あふれでてきます。
(中略)
あふれる泉を人に飲んでもらうために、文章という器をつくります。