Unity missing warning when Button has missing OnCl

2020-07-13 07:54发布

is there a way to get some kind of info or warning, when any Button component has a missing method?

What I mean by that is that when you implement a method for a Button, assign that method in the scene and then e.g. rename the method, the rename won't update the method call in the scene and so it says "< Missing ScriptName.OldMethodName >".

When this happens I'd like to get notified about that - at least when pressing play, or at the very least when deploying the application.

标签: c# unity3d
2条回答
Bombasti
2楼-- · 2020-07-13 08:21

you can extend the button class and check for event counts + method names, if the name contains missing, fire a error

using UnityEngine.UI;

public class SafeButton : Button
{
    override protected void Awake()
    {
        for (int i = 0; i < onClick.GetPersistentEventCount(); i++)
        {
            var methodName = onClick.GetPersistentMethodName(i);
            // if method name contains "missing"
                // -> Log Error
        }
    }
}
查看更多
够拽才男人
3楼-- · 2020-07-13 08:24

Scott's answer is very close to what you are doing and led to this answer. Although it is missing so many things. You need to do more work to get that script working.

1.You need to get all the Buttons in the scene (including inactive/disabled ones) with Resources.FindObjectsOfTypeAll.

2.Loop through the buttons and check if the class/script that holds that function exist with reflection. You do this because sometimes, we rename scripts. This could cause problems. Show message of the script does not exist.

You can do this by simply checking if Type.GetType(className); is null.

If it is null then don't even both the test below because the component that holds that Button's onClick function has been re-named. Display an error message that says that this script has been deleted or re-named.

3.If the class exist, now check if the function exist with reflection in that class that is registered to the Button's onClick event.

This can be done by simply checking if type.GetMethod(functionName); is null.

If the function exist, that Button is fine. You don't have to show a message if the function exist. Stop here.

If that returns null then continue to #4.

4.Check if the function exist, but this time, check if the function is declared with a private access modifier.

This is a typical mistake people make. They declare function as public, assign it through the Editor, then mistakenly change it from public to private. This should work but can cause problem in the future.

This can be done by simply checking if type.GetMethod(functionName, BindingFlags.Instance | BindingFlags.NonPublic); is null.

If the function exist, show a message that warns you that this function's access modifier has been changed from public to private and should be changed back to public.

If the function does not exist, show a message that warns you that this function does no longer exist or has been re-named.

Below is a script that performs everything I mentioned above. Attach it to an empty GameObject and it will do its job anytime you run the game in the Editor.

using System;
using System.Reflection;
using UnityEngine;
using UnityEngine.UI;

public class MissingOnClickDetector : MonoBehaviour
{
    void Awake()
    {
        //Debug.Log("Class exist? " + classExist("ok.ButtonCallBackTest"));
        searchForMissingOnClickFunctions();
    }

    void searchForMissingOnClickFunctions()
    {
        //Find all Buttons in the scene including hiding ones
        Button[] allButtonScriptsInScene = Resources.FindObjectsOfTypeAll<Button>() as Button[];
        for (int i = 0; i < allButtonScriptsInScene.Length; i++)
        {
            detectButtonError(allButtonScriptsInScene[i]);
        }
    }

    //Searches each registered onClick function in each class
    void detectButtonError(Button button)
    {
        for (int i = 0; i < button.onClick.GetPersistentEventCount(); i++)
        {
            //Get the target class name
            UnityEngine.Object objectName = button.onClick.GetPersistentTarget(i);

            //Get the function name
            string methodName = button.onClick.GetPersistentMethodName(i); ;

            //////////////////////////////////////////////////////CHECK CLASS/SCRIPT EXISTANCE/////////////////////////////////////////

            //Check if the class that holds the function is null then exit if it is 
            if (objectName == null)
            {
                Debug.Log("<color=blue>Button \"" + button.gameObject.name +
                    "\" is missing the script that has the supposed button callback function. " +
                    "Please check if this script still exist or has been renamed</color>", button.gameObject);
                continue; //Don't run code below
            }

            //Get full target class name(including namespace)
            string objectFullNameWithNamespace = objectName.GetType().FullName;

            //Check if the class that holds the function exist then exit if it does not
            if (!classExist(objectFullNameWithNamespace))
            {
                Debug.Log("<color=blue>Button \"" + button.gameObject.name +
                     "\" is missing the script that has the supposed button callback function. " +
                     "Please check if this script still exist or has been renamed</color>", button.gameObject);
                continue; //Don't run code below
            }

            //////////////////////////////////////////////////////CHECK FUNCTION EXISTANCE/////////////////////////////////////////

            //Check if function Exist as public (the registered onClick function is ok if this returns true)
            if (functionExistAsPublicInTarget(objectName, methodName))
            {
                //No Need to Log if function exist
                //Debug.Log("<color=green>Function Exist</color>");
            }

            //Check if function Exist as private 
            else if (functionExistAsPrivateInTarget(objectName, methodName))
            {
                Debug.Log("<color=yellow>The registered Function \"" + methodName + "\" Exist as a private function. Please change \"" + methodName +
                    "\" function from the \"" + objectFullNameWithNamespace + "\" script to a public Access Modifier</color>", button.gameObject);
            }

            //Function does not even exist at-all
            else
            {
                Debug.Log("<color=red>The \"" + methodName + "\" function Does NOT Exist in the \"" + objectFullNameWithNamespace + "\" script</color>", button.gameObject);
            }
        }
    }

    //Checks if class exit or has been renamed
    bool classExist(string className)
    {
        Type myType = Type.GetType(className);
        return myType != null;
    }

    //Checks if functions exist as public function
    bool functionExistAsPublicInTarget(UnityEngine.Object target, string functionName)
    {
        Type type = target.GetType();
        MethodInfo targetinfo = type.GetMethod(functionName);
        return targetinfo != null;
    }

    //Checks if functions exist as private function
    bool functionExistAsPrivateInTarget(UnityEngine.Object target, string functionName)
    {
        Type type = target.GetType();
        MethodInfo targetinfo = type.GetMethod(functionName, BindingFlags.Instance | BindingFlags.NonPublic);
        return targetinfo != null;
    }
}
查看更多
登录 后发表回答