Omitting script component attached to GameObject in build

I have a “Readme” script I attach to gameobjects to make notes and TODO stuff on the gameobject.

public class _README : MonoBehaviour
{
    [TextAreaAttribute (5, 500)]
    public string comment = "[COMMENT]";
}

I don’t want this component to be included in the build, so I put the script in a folder called Editor. (Assets/Scripts/Editor/Readme.cs".

But, now this results in it the “The associated script could not be loaded” shown in the inspector on the gameobject.

How do I configure this so the _README script component is not included in the build?

It’s strange because when I click on the script field on the component, Unity points to the file, but yet the error message still appears.

3344478--261280--scriptissue.jpg
.

Stop putting your _README class in an editor folder (move it out to wherever your regular scripts are). Then include this script inside of an Editor folder in your project:

using UnityEditor.Build;
using UnityEngine;
using UnityEngine.SceneManagement;

public class ReadmeSceneStripper : IProcessScene
{
    public int callbackOrder { get { return 0; } }
   
    public void OnProcessScene(Scene scene)
    {
        foreach (var readme in Object.FindObjectsOfType<_README>())
        {
            Object.DestroyImmediate(readme, true);
        }
    }
}

Now whenever you build, those will be removed from all objects in all scenes. They will also be temporarily removed every time you enter play mode (then restored when you exit play mode).

3 Likes

Thanks I’ll try that. (At first glance it looks a bit goofy.)

I took my script and added “#if UNITY_EDITOR” at the top of the class as described here https://support.unity3d.com/hc/en-us/articles/208456906-Excluding-Scripts-and-Assets-from-builds.
I haven’t tried a build yet. Do you think that should work?

It would, but Claytonious’ solution is far more elegant. Slightly better would be to use a class attribute and collect all MonoBehaviour-derived classes that possess that attribute in the assemblies into one list, then iterate over that list doing the same stripping Claytonious suggests. It would take a bit longer, but it wouldn’t require manually adding additional class types to check against in the function. Just exclude the common assemblies from the attribute check to speed it up, like mscorlib, System, etc… Here’s a quick example:

// do not put this in an Editor folder
using System;

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class DoNotIncludeInBuildsAttribute : Attribute
{

}
// put this in an Editor folder
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor.Build;
using UnityEngine;
using UnityEngine.SceneManagement;

public class DoNotIncludeInBuildAttributeSceneStripper : IProcessScene
{
    private readonly string[] excludedAssemblies = new string[]
    {
        "System",
        "UnityEditor",
        "Mono.Cecil",
        "UnityScript",
        "Boo.Lan",
        "mscorlib",
        "JetBrains",
        "nunit",
        "NUnit",
        "I18N"
    };

    public int callbackOrder { get { return 0; } }

    public void OnProcessScene(Scene scene)
    {
        var types = GetAllTypesWithAttribute<DoNotIncludeInBuildsAttribute>();
        types = types.Where(t => t.IsSubclassOf(typeof(MonoBehaviour)));

        foreach (var type in types)
        {
            foreach (var obj in UnityEngine.Object.FindObjectsOfType(type))
            {
                UnityEngine.Object.DestroyImmediate(obj, true);
            }
        }
    }

    private IEnumerable<Type> GetAllTypesWithAttribute<T>() where T : Attribute
    {
        var assemblyList = new List<Assembly>();
        foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
        {
            bool exclude = false;
            foreach (var excludedAssembly in excludedAssemblies)
            {
                if (assembly.FullName.StartsWith(excludedAssembly))
                {
                    exclude = true;
                    break;
                }
            }

            if (!exclude)
                assemblyList.Add(assembly);
        }

        var types = assemblyList.SelectMany(assembly => assembly.GetTypes());
        types = types.Where(type =>
            !type.IsAbstract
            && !type.IsInterface
            && type.GetCustomAttributes(typeof(T), true).Length > 0);

        return types.ToArray();
    }
}
using System.Collections;
using UnityEngine;

[DoNotIncludeInBuilds]
public class ReadMe : MonoBehaviour
{

}
3 Likes

That will cause you to have a “Missing Reference” error on your GameObject at runtime, because it is being told to include a component that it can no longer find. It’s ugly and wrong.

That’s really nice. We do something similar. We also support knocking things out based on tags, too, optionally.

It’s extremely helpful when building multiple variants of a game, such as for Vive, Daydream, Oculus, desktop, mobile, etc.

Yeah, you’re right. The following appeared in the game output log:

The referenced script on this Behaviour (Game Object ‘README’) is missing!
A script behaviour (probably _README?) has a different serialization layout when loading. (Read 32 bytes but expected 48 bytes)
Did you #ifdef UNITY_EDITOR a section of your serialized properties in any of your scripts?

(It doesn’t appear to be a fatal error, as the game can continue executing normally.)

I tried your script and it seems to work nicely for my situation.
I only want to remove script during build, not when entering playmode , so I added the following at the beginning of the function:

      //Only remove script when creating a build, not when playing in editor
        if (Application.isPlaying) {         
            return;
        }