用UGUI開(kāi)發(fā)強(qiáng)制引導(dǎo)功能

強(qiáng)制引導(dǎo)需要的內(nèi)容

開(kāi)發(fā)強(qiáng)制引導(dǎo)腻暮,則要求玩家必須點(diǎn)擊指定的地方睦柴、按鈕,否則不予受理捕犬。那么基于這個(gè)通用需求,可以分析出我們核心需要的內(nèi)容主要有兩部分:

  1. 置灰圖或遮罩圖:我們需要通過(guò)這張圖片酵镜,將屏幕置灰碉碉,并突出需要玩家點(diǎn)擊的按鈕或區(qū)域;
  2. 非強(qiáng)制引導(dǎo)交互屏蔽:強(qiáng)制引導(dǎo)階段需要屏蔽任何非引導(dǎo)按鈕的點(diǎn)擊等交互操作笋婿。

方案1誉裆,Canvas

使用一張Mask圖片實(shí)現(xiàn)屏幕置灰功能,并屏蔽所有UI交互缸濒。同時(shí)足丢,將需要高亮或突出的按鈕添加Canvas組件,以確保該UI元素不會(huì)被Mask圖片屏蔽庇配。

網(wǎng)上有看到過(guò)這樣的方案斩跌,但是需要手動(dòng)維護(hù)Canvas組件的添加刪除,如果游戲架構(gòu)開(kāi)發(fā)不完善則很可能會(huì)因遺漏導(dǎo)致后續(xù)出現(xiàn)bug捞慌,所以沒(méi)有選用此方案耀鸦。

方案2,事件穿透

使用一張Mask圖片實(shí)現(xiàn)屏幕置灰功能,并屏蔽所有UI交互袖订。使用事件穿透實(shí)現(xiàn)對(duì)指定按鈕交互不受屏蔽影響氮帐。在此基礎(chǔ)上,Mask圖片如果沒(méi)有額外的顯示需求洛姑,則可以直接生成鏤空?qǐng)D片上沐,以實(shí)現(xiàn)置灰其他UI而不置灰指定UI的功能。


后續(xù)開(kāi)發(fā)說(shuō)明以方案2為基準(zhǔn)楞艾。

事件穿透

UGUI的事件穿透参咙,借助EventSystem就可以實(shí)現(xiàn)。

EventPermeate代碼如下:

public class EventPermeate : MonoBehaviour, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler
{
    /// <summary>
    /// 事件穿透對(duì)象
    /// </summary>
    [HideInInspector] public GameObject target;

    /// <summary>
    /// 監(jiān)聽(tīng)點(diǎn)擊
    /// </summary>
    public void OnPointerClick(PointerEventData eventData)
    {
        PassEvent(eventData, ExecuteEvents.submitHandler);
        PassEvent(eventData, ExecuteEvents.pointerClickHandler);
    }

    /// <summary>
    /// 監(jiān)聽(tīng)按下
    /// </summary>
    public void OnPointerDown(PointerEventData eventData)
    {
        PassEvent(eventData, ExecuteEvents.pointerDownHandler);
    }

    /// <summary>
    /// 監(jiān)聽(tīng)抬起
    /// </summary>
    public void OnPointerUp(PointerEventData eventData)
    {
        PassEvent(eventData, ExecuteEvents.pointerUpHandler);
    }

    /// <summary>
    /// 將事件透下去
    /// </summary>
    public void PassEvent<T>(PointerEventData data, ExecuteEvents.EventFunction<T> function) where T : IEventSystemHandler
    {
        List<RaycastResult> results = new List<RaycastResult>();
        EventSystem.current.RaycastAll(data, results);
        for (int i = 0, imax = results.Count; i < imax; i++)
        {
            if (target == results[i].gameObject)
            {
                ExecuteEvents.Execute(results[i].gameObject, data, function);
                break;
            }
        }
    }
}

EventPermeate添加到Mask圖片硫眯,并指定可以穿透的目標(biāo)蕴侧,就可以實(shí)現(xiàn)事件穿透功能。代碼很好理解此處就不再贅述两入。

遮罩圖片的生成

思路

遮罩圖片的生成我使用Image配合自定義Shader實(shí)現(xiàn)净宵。思路如下:

  1. 繼承Image,重寫(xiě)OnPopulateMesh方法谆刨,在這里面去生成我們所需鏤空的形狀塘娶,并對(duì)所有頂點(diǎn)的顏色進(jìn)行賦值
    • 非鏤空形狀上的頂點(diǎn),正常賦予顏色值
    • 鏤空形狀上的頂點(diǎn)痊夭,賦予顏色值,但是alpha值設(shè)為0
  2. 實(shí)現(xiàn)一個(gè)UGUI的Shader脏里,頂點(diǎn)計(jì)算照舊她我,片元計(jì)算處需要特殊處理:
    • 若片元的alpha值為0,則該片元的alpha值就返回0
    • 若片元的alpha值不為0迫横,則使用從Image顏色傳遞來(lái)的alpha值

Shader說(shuō)明

下面給出Shader片元算法:

float _MaskAlpha; // 由Image傳入的alpha值

fixed4 frag(v2f i) : SV_Target
{
    fixed4 col = i.color;
    return fixed4(col.rgb, col.a > 0 ? _MaskAlpha : 0);
}

此處所針對(duì)的需求番舆,是圍繞Mask圖片僅僅是一層半透明遮罩展開(kāi)。如果需要的是一張圖片矾踱,則還需要通過(guò)UV進(jìn)行采樣恨狈,如此一來(lái)在繼承的Image中,每個(gè)頂點(diǎn)需要賦予正確的UV值才可以(計(jì)算很簡(jiǎn)單呛讲,但會(huì)額外增加一些計(jì)算量)禾怠。

Image圖片生成說(shuō)明

覆寫(xiě)OnPopulateMesh方法生成我們所需要的鏤空?qǐng)D片,核心思路如下(所有生成的三角形保持順時(shí)針):

  1. 計(jì)算出鏤空部分的AABB贝搁,然后將Image以內(nèi)吗氏、AABB以外的部分按照固定格式生成三角形數(shù)據(jù)
  2. 限制鏤空部分為凸多邊形,這樣又可以按照以下規(guī)則將AABB內(nèi)的三角形生成出來(lái):
    1. 尋找凸多邊形的最左點(diǎn)雷逆、最高點(diǎn)弦讽、最右點(diǎn)、最低點(diǎn)
      1. 從最左點(diǎn)遍歷至最高點(diǎn)膀哲,與AABB左上角依次生成三角形
      2. 從最高點(diǎn)遍歷至最右點(diǎn)往产,與AABB右上角依次生成三角形
      3. 從最右點(diǎn)遍歷至最低點(diǎn)被碗,與AABB右下角依次生成三角形
      4. 從最低點(diǎn)遍歷至最左點(diǎn),與AABB左下角依次生成三角形
    2. 凸多邊形內(nèi)部以一個(gè)點(diǎn)為起始點(diǎn)仿村,遍歷剩余的點(diǎn)生成這個(gè)凸多邊形的所有三角形

核心算法思路展示如下:

// Image生成核心算法蛮放,rect是Image的Rect,innerPos是需要鏤空的凸多邊形的頂點(diǎn)坐標(biāo)數(shù)據(jù)
private void _PickOutConvex(VertexHelper toFill, Rect rect, List<Vector2> innerPos)
{
    toFill.Clear();
    
    // 凸多邊形獲取邊界四點(diǎn)
    int left, top, right, bottom;
    ...

    // 頂點(diǎn)數(shù)量
    int innerCount = innerPos.Count;

    Vector2 min = new Vector2(innerPos[left].x, innerPos[bottom].y);
    Vector2 max = new Vector2(innerPos[right].x, innerPos[top].y);

    Color maskColor = new Color(color.r, color.g, color.b, 0);

    // 凸多邊形AABB的四個(gè)點(diǎn)的計(jì)算與添加
    int nA = toFill.currentVertCount;
    toFill.AddVert(new UIVertex() { position = new Vector2(min.x, max.y), color = color });
    toFill.AddVert(new UIVertex() { position = max, color = color });
    toFill.AddVert(new UIVertex() { position = new Vector2(max.x, min.y), color = color });
    toFill.AddVert(new UIVertex() { position = min, color = color });

    // 凸多邊形頂點(diǎn)數(shù)據(jù)添加
    int ni = toFill.currentVertCount;
    for (int i = 0; i < innerCount; i++) toFill.AddVert(innerPos[i], maskColor, Vector4.zero);

    // 從最左點(diǎn)遍歷至最高點(diǎn)奠宜,與AABB左上角依次生成三角形
    for (int i = left, imax = left > top ? top + innerCount : top; i < imax; i++)
    {
        int index1 = i % innerCount + ni;
        int index2 = (i + 1) % innerCount + ni;
        toFill.AddTriangle(nA, index2, index1);
    }

    // 從最高點(diǎn)遍歷至最右點(diǎn)包颁,與AABB右上角依次生成三角形
    ...
    // 從最右點(diǎn)遍歷至最低點(diǎn),與AABB右下角依次生成三角形
    ...
    // 從最低點(diǎn)遍歷至最左點(diǎn)压真,與AABB左下角依次生成三角形
    ...

    // 凸多邊形內(nèi)部三角形的構(gòu)建
    for (int i = 1, imax = innerCount - 1; i < imax; i++) toFill.AddTriangle(ni, ni + i, ni + i + 1);

    // Image的四個(gè)頂點(diǎn)的添加
    int nX = toFill.currentVertCount;
    toFill.AddVert(new UIVertex() { position = new Vector2(rect.xMin, rect.yMax), color = color }); // 0
    toFill.AddVert(new UIVertex() { position = rect.max, color = color }); // 1
    toFill.AddVert(new UIVertex() { position = new Vector2(rect.xMax, rect.yMin), color = color }); // 2
    toFill.AddVert(new UIVertex() { position = rect.min, color = color }); // 3

    // Image到凸多邊形AABB空間的三角形生成
    toFill.AddTriangle(nX + 0, nX + 1, nA + 1);
    toFill.AddTriangle(nA + 1, nA + 0, nX + 0);
    ...
}

有了上述方法后娩嚼,剩下的就是在OnPopulateMesh方法內(nèi),根據(jù)指定的幾種固定形狀滴肿,生成他們的頂點(diǎn)數(shù)據(jù)岳悟,再傳入_PickOutConvex方法,即可獲得所需的Mask圖片泼差。

可優(yōu)化

這套實(shí)現(xiàn)贵少,已經(jīng)完成了這套方案下所有的核心點(diǎn)。根據(jù)不同的應(yīng)用場(chǎng)景堆缘,可以擴(kuò)展shader或是Image的算法滔灶,以實(shí)現(xiàn)效果、性能上的需求吼肥。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末录平,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子缀皱,更是在濱河造成了極大的恐慌斗这,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啤斗,死亡現(xiàn)場(chǎng)離奇詭異表箭,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)钮莲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)免钻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人臂痕,你說(shuō)我怎么就攤上這事伯襟。” “怎么了握童?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵姆怪,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)稽揭,這世上最難降的妖魔是什么俺附? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮溪掀,結(jié)果婚禮上事镣,老公的妹妹穿的比我還像新娘。我一直安慰自己揪胃,他們只是感情好璃哟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著喊递,像睡著了一般随闪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上骚勘,一...
    開(kāi)封第一講書(shū)人閱讀 51,208評(píng)論 1 299
  • 那天铐伴,我揣著相機(jī)與錄音,去河邊找鬼俏讹。 笑死当宴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的泽疆。 我是一名探鬼主播户矢,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼于微!你這毒婦竟也來(lái)了逗嫡?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤株依,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后延窜,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體恋腕,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年逆瑞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了荠藤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡获高,死狀恐怖哈肖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情念秧,我是刑警寧澤淤井,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響币狠,放射性物質(zhì)發(fā)生泄漏游两。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一漩绵、第九天 我趴在偏房一處隱蔽的房頂上張望贱案。 院中可真熱鬧,春花似錦止吐、人聲如沸宝踪。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至掉房,卻和暖如春蕴忆,著一層夾襖步出監(jiān)牢的瞬間颤芬,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工套鹅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留站蝠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓卓鹿,卻偏偏與公主長(zhǎng)得像菱魔,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子吟孙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 前言 上一篇文章我們認(rèn)識(shí)并編寫(xiě)了一個(gè)最簡(jiǎn)單的Shader澜倦,這一篇文章我們將會(huì)把第一個(gè)圖形真正的畫(huà)出來(lái),本文是系列最...
    theAnswer_閱讀 364評(píng)論 0 1
  • 一.CPU 與 GPU 對(duì)于現(xiàn)代計(jì)算機(jī)系統(tǒng)杰妓,簡(jiǎn)單來(lái)說(shuō)可以大概視作三層架構(gòu):硬件藻治、操作系統(tǒng)與進(jìn)程。對(duì)于移動(dòng)端來(lái)說(shuō)巷挥,進(jìn)...
    螞蟻安然閱讀 1,239評(píng)論 1 0
  • 聲明: opengl是一個(gè)跨平臺(tái)的圖形桩卵,廣泛采用的3D api;opengl es 是一個(gè)為手持和嵌入設(shè)備為目標(biāo),...
    眾少成多積小致巨閱讀 420評(píng)論 0 2
  • 目錄 OpenGL ES的簡(jiǎn)介 OpenGL ES的基本流程和概念 篇外話:本來(lái)這篇要寫(xiě)SurfaceView和T...
    yabin小站閱讀 1,694評(píng)論 0 5
  • 光柵化渲染管線是學(xué)習(xí)圖形學(xué)的基礎(chǔ)倍宾,學(xué)習(xí)渲染管線流程時(shí)雏节,如果對(duì)其中的各個(gè)關(guān)鍵步驟理解不夠深入,可能會(huì)看得一頭霧水高职。這...
    太刀閱讀 1,699評(píng)論 0 2