ContextMenu在座標投影時的小問題

ContextMenu確實替Debug的過程帶來巨大的便利性,但太過耀眼的光茫卻讓旅人忽視了其底下隱藏的陷阱……

 

前言

對Unity的Inspector有點研究的人都會知道一個叫作ContexMenu的標籤
只要把它加到無參數且宣告為public的Method上

public class Foo : MonoBehaviour
{
    [ContextMenu("hello world")]
    public void Hello()
    {
        Debug.Log("Hello world");
    }
}

ContextMenu-50560
就能直接在Inspector的右鍵選單上看到它,點兩下就能出現結果,簡直就是懶人Debug及測試的神兵利器。
BUT! 前幾天就被這好用到不行的東西給攞了一道。

且聽我細細說來

且說當時做的是一個經營遊戲的建造模式,打開建造模式就會在每個建地上彈出建造按鈕。由於建地是在Wold座標下,所以必需計算出其投影在螢幕上的座標方能生成按鈕,當時的代碼如下。

public class BuildSystem : MonoBehaviour
{
    public Transform target;//建地
    public GameObject buildBtnPrefab;//按鈕的預置物
    public Transform canvas;

    [ContextMenu("Open Build Mode")]
    public void OpenBuildMode()
    {
        var screenPos= Camera.main.WorldToScreenPoint(target.position);
        Instantiate(buildBtnPrefab, screenPos, Quaternion.identity, canvas);
    }
}

ContextMenu-aedc9
把腳本拖到Inspector上然後把target指定為Cube
結果實際執行後卻得到了下面的結果。
ContextMenu-a1db8
為什麼按鈕跑到了奇怪的地方?這時我就開時思考了,或許是投影的公式錯了?亦或是RectTransform座標不能直接在Instantiate內指定?但最後檢查結果沒問題。

見鬼了

最後我突然萌生了一個想法﹐如果把Code改成下面這樣呢?

private void Start()
{
    OpenBuildMode();
}

public void OpenBuildMode()
{
    Debug.Log(Camera.main);
    var screenPos= Camera.main.WorldToScreenPoint(target.position);
    Instantiate(buildBtnPrefab, screenPos, Quaternion.identity, canvas);
}

ContextMenu-460db

嗯~一點問題都沒有呢~

真TMD見鬼了

好的,那麼罪魁禍首100%出在ContexMenu上沒跑了。
經過仔細的測試後,結論是如果在編輯器暫停時呼叫ContexMenu,座標計算就不會出錯。

結論

如果有使用到Camera.main.WorldToScreenPoint或是其它會牽涉到座標投影函式的情況下,切記不要使用ContextMenu來做測試,自己用OnGUI或是用Canvas做個按鈕吧。
不過OnGUI在部份平台上會導致記憶體無法釋放要小心唷~(笑)