うにのトゲは刺さると痛い(´・ω・`)-8

なんちゃってDialogが一応出来た気がする(´・ω・`)

Dialogを実装しようとぐぐってみると、色々と実現方法はあったものの、ほとんどがCanvasを使ったりプラグインを追加してネイティブのDialogを呼び出すものであった為に

俺氏、全てを拒絶する(`・ω・´)イヤヤ

今考えてみると、素直にどっちかの方式に従っておけば、さっさと次の機能実装に取り掛かれたのであったが、俺氏は何故か

IMGUIで実装したかった(`・ω・´)
だっておら、onGUI()で実装出来たら楽だなって思ってたから(´・ω・`)ジツハギャクニメンドウクサカッタ

結果的に調べたり試行錯誤を繰り返したりで一日以上を費やしてしまった…_| ̄|○


IMGUIを使用したなんちゃってDialog
事の始まりはこちらのエントリ。
Unityゲームを終了する

この方法で取り敢えずBackButtonからのDialogっぽいモノを表示、GUI.Buttonをクリックしたらアプリケーションが終了するという形までは出来た。

ここで問題になったのは、ボタンクリック後、下のカードのTouchCard()が呼ばれてしまうこと(´・ω・`)
最初はクリックイベントが透過しちゃうのが原因なのかなと思って、EventSystem.IsPointerOverGameObjectでも使えばいいのかと考えたが、イマイチ使い方が解らない(´・ω・`)

参考にしたエントリのコード同様にGameManagerにbool型のisOpenedを作成して、TouchCard()内で

        if (GameManager.isOpened)
        {
            return;
        }

みたいな感じにしてるのに、なんか上手く行かない(´・ω・`)
凄く速くタッチすると下がめくれないことがあったりしたので、もしかしたらisOpenedがfalseになった瞬間にまだ指がタッチしていてそれが拾われちゃうのかとも思ったが、どうも必ずしもそうでもないような…(ヽ'ω`)ワカラナイ

で、まぁ色々悩んだ末に考えついたことは、
Delay入れれば、弾けるんじゃね(・∀・)?
といういい加減極まりない手法w


コルーチンによるボタンクリックアニメ

というわけでStartCoroutine+WaitForSecondsをクリック後に追加した。結果、isOpenedがfalseになるのが遅延して余計なクリックイベントを弾くようになった模様…実際にこの対処法が正しいのかは不明(´・ω・`)

参考:
Unityで一定時間待つ処理の実装方法
【Unity】スクリプトの処理の実行タイミングを操作する

で、ここでふと思ったのが、

WaitForSecondsを二回呼んで、その間にボタンのテクスチャを描き換えれば、ボタンクリックアニメっぽくなるんじゃね(・∀・)?
ということ。

ちょろっとやってみた。
それっぽくなった(・∀・) オレシカコイイ ダイテ(・∀・)


else節を利用したボタン押下アニメ
で、まぁ人の欲望とは際限なきもので、ボタンクリックアニメが出来るようになると今度はボタン押下状態のアニメというか画像変更がしたくなってくる(´・ω・`)タルコトヲシラヌ ヨクブカモノ

色々とぐぐっても、今ひとつGUI.Buttonの実体というか押下イベント状態を掴む方法が解らない(ヽ'ω`)

UnityのマニュアルでImmediate Mode GUI (IMGUI)の項を読む……

IMGUI システムは通常、プレイヤーが操作する普通のゲーム内ユーザーインターフェイスに使う事は意図されていません。それらにはゲームオブジェクトベースで考えられた Unity のメイン UI system を使ってください。そちらの方が UI 要素の編集と位置決めにゲームオブジェクトベースでアプローチでき、 UI のレイアウトと視覚デザインをするのにはより便利なツールです。

ガ━━(゚Д゚;)━━━ン!!

_| ̄|○

ぐぐってみても、そういうボタン押下状態の画像差し替えのコードはない…(ヽ'ω`)

実体もしくは押下イベント状態を掴む方法がないとなぁ…(´・ω・`)と半ば諦めかけていた時にふと思う。
GUI.Buttonって

if (GUI.Button (Rect (25, 25, 100, 30), "Button")){}

みたいな感じで書いて、クリックされている時は{}内のコードが実行される…
じゃあ、elseの時はどうなんの……

あ、ここでクリックではない時の実体(?)がつかめんじゃね(・∀・)?
と思う。

でまぁ、elseの部分でこのボタンのテクスチャやフォントサイズを変えればいいということに気付く。
具体的にはbool型のisPressedみたいな変数を追加し、

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            isPressed = true;
        }
        if (Input.GetMouseButtonUp(0))
        {
            isPressed = false;
        }
    }

で状態を格納するようにして、かつ、Input.mousePositionがボタンのRect内であるならばボタン押下状態とみなすという形でチェックし、trueならPressed状態、falseならNormal状態のGUIStyleをセットすることによって実現した(`・ω・´)

これが上手く動いた時は
俺氏天才(`・ω・´)
って思った。←こうやって自画自賛しないとやってられない人


面倒くさいので、サンプル用に削ったコードを作成せずそのまま載せるw

  1. Resourceフォルダの下にimagesフォルダを掘って、そこに以下の画像を突っ込む。押下状態の画像は通常状態の画像を反転しただけw Rect用のテクスチャ同様にコード内で生成してしまえば、完全にスクリプトだけで完結するなんちゃってダイアログが作れる。ちなみにアクアなボタン台 フリー素材のボタンをかっぱらってきて、ちょっと加工した(・∀・)


  2. GameManager側では
     
        void OnGUI()
        {
            float sw = Screen.width;
            float sh = Screen.height;
    
            if (GUI.Button(new Rect(0, 0, sw * 3 / 12, sh / 12), "menu") && !isOpened)
            {
                Debug.Log("push!");
                this.gameObject.AddComponent<MenuDialog>();
                isOpened = true;
                return;
            }
        }
    

    という感じでGUIボタンで呼び出す様にしたり、Androidの実機のBackボタンで試すなら

    #if UNITY_ANDROID
            if (Input.GetKeyDown(KeyCode.Escape))
            {
                this.gameObject.AddComponent<MenuDialog>();
                isOpened = true;
                return;
            }
    #endif
    

    をupdate()に追加する。isOpened自体はこのサンプルでは本当は必要ないのだけれども、そこら辺を修正するのが面倒なのでどっかで宣言しておく。

  3. よく解ってないのだがY座標は画面下から上に増加していく座標のようなので、ボタン領域内のチェックはScreenheightから引いて逆転させている(´・ω・`)ココハジブンデモリカイシテナイw
    using System.Collections;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    public class MenuDialog : MonoBehaviour
    {
        private GUIStyle rectStyle = new GUIStyle();
        private GUIStyle quitStyle = new GUIStyle();
        private GUIStyle returnStyle = new GUIStyle();
        private GUIStyle cancelStyle = new GUIStyle();
    
        private float dialogWidth = 500;
        private float dialogHeight = 800;
        private float dialogLeft;
        private float dialogTop;
        private float buttonLeft;
    
        private float buttonWidth = 368;
        private float buttonHeight = 83;
        
        bool isPressed;
    
        void Start()
        {
            GUIStyleState rectStyleState = new GUIStyleState();
            rectStyleState.textColor = Color.white;
            rectStyleState.background = MakeRectTexture();
            rectStyle.normal = rectStyleState;
    
            SetTextConfig(rectStyle, 50, TextAnchor.MiddleCenter);
            SetTextConfig(quitStyle, 50, TextAnchor.MiddleCenter);
            SetTextConfig(cancelStyle, 50, TextAnchor.MiddleCenter);
            SetTextConfig(returnStyle, 50, TextAnchor.MiddleCenter);
        }
    
        Texture2D MakeRectTexture()
        {
            Texture2D rectTexture = new Texture2D(16, 16);
            for (int y = 0; y < rectTexture.height; y++)
            {
                for (int x = 0; x < rectTexture.width; x++)
                {
                    if (x == 0)
                    {
                        rectTexture.SetPixel(x, y, Color.white);
                    }
                    else if (x == rectTexture.width - 1)
                    {
                        rectTexture.SetPixel(x, y, Color.gray);
                    }
                    else
                    {
                        if (y == 0)
                        {
                            rectTexture.SetPixel(x, y, Color.gray);
                        }
                        else if (y == rectTexture.height - 1)
                        {
                            rectTexture.SetPixel(x, y, Color.white);
                        }
                        else
                        {
                            rectTexture.SetPixel(x, y, new Color(1f, 0f, 0f, 0.5f));
                        }
                    }
                }
            }
            rectTexture.Apply();
            return rectTexture;
        }
    
        void SetTextConfig(GUIStyle targetStyle, int fontSize, TextAnchor textAnchor)
        {
            targetStyle.fontSize = fontSize;
            targetStyle.alignment = textAnchor;
        }
    
        void MakeStyleNormal(GUIStyle targetStyle, Color color,Texture2D texture,int fontsize, TextAnchor textAnchor)
        {
            GUIStyleState tempStyleState = new GUIStyleState();
            tempStyleState.textColor = color;
            tempStyleState.background = texture;
            targetStyle.fontSize = fontsize;
            targetStyle.alignment = textAnchor;
            targetStyle.normal = tempStyleState;
        }
        
        bool CheckIsInButton(int y)
        {
            if (isPressed &&
                buttonLeft < Input.mousePosition.x &&
                buttonLeft + buttonWidth > Input.mousePosition.x &&
                Screen.height - (dialogTop + buttonHeight * y) > Input.mousePosition.y &&
                Screen.height - (dialogTop + buttonHeight * y + buttonHeight) < Input.mousePosition.y)
            {
                return true;
            }
            return false;
        }
    
    
        void OnGUI()
        {
            dialogLeft = Screen.width / 2 - dialogWidth / 2;
            dialogTop = Screen.height / 2 - dialogHeight / 2;
            buttonLeft = dialogLeft + dialogWidth / 2 - buttonWidth / 2;
    
            GUI.Box(new Rect(dialogLeft, dialogTop, dialogWidth, dialogHeight), "TestDialog", rectStyle);
    
            if (GUI.Button(new Rect(buttonLeft,
                dialogTop + buttonHeight * 2, buttonWidth, buttonHeight), "quit", quitStyle))
            {
                ChangeButtonStyleState(quitStyle, null, "images/quit2");
                StartCoroutine(DoDelayedMethod(quitStyle, Color.white, "images/quit", (0.1f)));
                Application.Quit();
            }
            else
            {
                if (CheckIsInButton(2))
                {
                    ChangeButtonStyleState(quitStyle, null, "images/quit2");
                }
                else
                {
                    ChangeButtonStyleState(quitStyle, Color.white, "images/quit");
                }
            }
    
            if (GUI.Button(new Rect(buttonLeft,
                dialogTop + buttonHeight * 4, buttonWidth, buttonHeight), "cancel", cancelStyle))
            {
                ChangeButtonStyleState(cancelStyle, null, "images/cancel2");
                StartCoroutine(DoDelayedMethod(cancelStyle, Color.white, "images/cancel", (0.1f)));
            }
            else
            {      
                if (CheckIsInButton(4))
                {
                    ChangeButtonStyleState(cancelStyle, null, "images/cancel2");
                }
                else
                {
                    ChangeButtonStyleState(cancelStyle, Color.white, "images/cancel");
                }
    
            }
    
            if (GUI.Button(new Rect(buttonLeft,
                dialogTop + buttonHeight * 6, buttonWidth, buttonHeight), "return", returnStyle))
            {
                ChangeButtonStyleState(returnStyle, null, "images/return2");
                StartCoroutine(DoDelayedMethod(returnStyle, Color.white, "images/return", (0.1f)));
                SceneManager.LoadScene("TitleScreen");
            }
            else
            {
                if (CheckIsInButton(6))
                {
                    ChangeButtonStyleState(returnStyle, null, "images/return2");
                }
                else
                {
                    ChangeButtonStyleState(returnStyle, Color.white, "images/return");
                }
            }
        }
    
        void ChangeButtonStyleState(GUIStyle targetStyle, Color? pressedFontColor, string FilePath)
        {
            GUIStyleState pressedButtonStyleState = new GUIStyleState();
            if (pressedFontColor != null)
            {
                pressedButtonStyleState.textColor = (Color)pressedFontColor;
            }
    
            pressedButtonStyleState.background = (Texture2D)Resources.Load<Texture>(FilePath);
            targetStyle.normal = pressedButtonStyleState;
        }
    
        void Update()
        {
            if (Input.GetMouseButtonDown(0))
            {
                isPressed = true;
            }
            if (Input.GetMouseButtonUp(0))
            {
                isPressed = false;
            }
        }
    
        private IEnumerator DoDelayedMethod(GUIStyle targetStyle, Color? tempFontColor, string FilePath, float waitTime)
        {
            yield return new WaitForSeconds(waitTime);
            GUIStyleState tempButtonStyleState = new GUIStyleState();
            
            if (tempButtonStyleState != null)
            {
                ChangeButtonStyleState(targetStyle, tempFontColor, FilePath);
            }
            yield return new WaitForSeconds(waitTime);
            GameManager.isOpened = false;
            Destroy(this);
        }
    }
    

    参考:

    テクスチャの作成
    Textureを変更する


テクスチャの作り方を完全には理解していないので、その辺は参考サイトのコードを適当にいじって背景Rect用テクスチャを作った(ノ∀`)
背景Rect用テクスチャをきちんとした画像を用意して読み込んだり、setPixel()で頑張るかすれば、もっとちゃんとした外観になるはず。

テクスチャの透過はシェーダーをどうにかしないといけないとか面倒臭そうだったので四角いボタンにしたが、テクスチャを角丸画像にして背景色を背景Rect用テクスチャのものと同一にして多少のクリック判定の誤差を諦めるか、ボタン押下状態の背景Rect用テクスチャを用意してクリック時に同時に背景Rect用テクスチャも切り替えれば角丸ボタンも実装出来るんじゃないかなぁ( ゜σ・゚)ホジホジ ←今のところ、実装する気がない。

※2018/7/3追記
結局テクスチャをプログラムで生成する形にしたが、SetPixelでColor.clearをセットすると透過みたいになっている……ような気がする……がイマイチ状況を把握出来ていない(ノ∀`)

多分デリゲートとかをちゃんと理解している人ならもっとスマートな書き方が出来るのだろうが、俺氏には無理なので旧態依然とした因数分解的リファクタリングしか出来なかった(ノ∀`)


えらく長くなったが、これで今後このコードの意味がわからなくなっても、この経緯を読み直せば理解出来るはず( ・´ω・`)

何はともあれIMGUIとの戦いは勝ったわけでもないが

敗けなかった(`・ω・´) と思いたい。