【Unity】uGUIでimageに穴を開けたい!(くり抜く)
ルパン三世的な…?
チュートリアルなどでゲーム画面の説明をしたい。
よくある画面を暗くして見てほしい箇所にライトを当てるような演出をしたい。
こんなんただ塗って穴開けてるだけやん。
それだけなのに、パッと実装が分からない。
Maskじゃない、なんというかMaskの逆?
uGUIだけで出来る?どうやるんだ?
- すでにゲームは作ってあるので、そこはなるべく触りたくない
- uGUIベースのゲームなのでuGUIでやりたい。
- 実装はなるべくシンプルで簡単に
どうも既存機能だけでは厳しそうな感触。
なんらかの工夫が必要か。
くりぬく?
nn-hokuson.hatenablog.com
なるほど、ColorMask 0!
これでも行けそう?だけど
裏のQueueもいじらないと駄目か…。うーむ。
あ、そうか別Layerにして、サブカメラにすればいけそうかな?
ワイプ、トランジション?
裏に影響なく画面全体を被せるのはこれが近いのかな?
tsubakit1.hateblo.jp
これを応用すればやりたいことが出来そう?
おお!これか!?これじゃん!
nn-hokuson.hatenablog.com
これを応用してみよう。
実装してみる
もうほとんどそのままだけどパラメータを外から設定出来るように。
Shader "Custom/HoleViewShader" { Properties{ _BackColor("background color", Color) = (0, 0, 0, 0) // 背景色 _HoleX("Hole Position X", float) = 0 // 穴の位置X _HoleY("Hole Position Y", float) = 0 // 穴の位置Y _Radius("hole radius", float) = 0 // 穴の大きさ _ScreenW("Screen Width" , float) = 960.0 // 画面の幅 _ScreenH("Screen Height" , float) = 640.0 // 画面の高さ } SubShader{ Tags{ "Queue" = "Transparent" } // alphaに対応するのに必要 Blend SrcAlpha OneMinusSrcAlpha // alphaに対応するために必要 Pass{ CGPROGRAM #include "UnityCG.cginc" #pragma vertex vert_img #pragma fragment frag // Propertiesの値をShaderに渡す float _HoleX; float _HoleY; fixed4 _BackColor; float _Radius; float _ScreenW; float _ScreenH; fixed4 frag(v2f_img i) : COLOR{ i.uv.x *= _ScreenW / _ScreenH; // アスペクト比を計算してあげる if (distance(i.uv, fixed2(_HoleX, _HoleY)) < _Radius){ discard; // 指定位置より一定距離以内だったら処理を飛ばすだけ } return _BackColor; } ENDCG } } }
値を渡すスクリプト側はこんな感じ。
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class HoleView : MonoBehaviour { void Start() { // 外部から設定する SetHole(new Vector2(480, 320), 0.2f); } public void SetHole(Vector2 pos, float radius) { // 画面との比率を出す var pinPos = new Vector2(pos.x / Screen.width, pos.y / Screen.height); // アスペクト比の問題なのか、位置的にはこのあたりで調整すると思った位置になるので計算してあげる // x range 0.45 - 1.05 この値が画面端から画面端でいい感じの所 // y range 0.3 - 0.7 // 比率と範囲値を計算して値を出す pinPos.x *= (1.05f - 0.45f); pinPos.y *= (0.7f - 0.3f); // 最下値を足して範囲に入れる pinPos.x += 0.45f; pinPos.y += 0.3f; // shaderに値を渡す var img = GetComponent<Image>(); img.material.SetFloat("_HoleX", pinPos.x); img.material.SetFloat("_HoleY", pinPos.y); img.material.SetFloat("_Radius", radius); img.material.SetFloat("_ScreenW", Screen.width); img.material.SetFloat("_ScreenH", Screen.height); } }
githubにもさくっと。
github.com
Inspectorはこれだけ!
Image一個あればOK!
これで超簡単!後からちょい載せで使えるヤーツの完成です。
uGUI同士ならCanvasのSortOrderとかで順番をいい感じにしてあげてください。