Useful Unity scripts

PlatformerUtils (WIP)

This is a script I actively use in the development of Dinosaur Island. For example, I use it to make sure all of my pickup objects start at the same height from the ground without having to calculate that at runtime.

using System.Collections;
using System.Collections.Generic;
#if (UNITY_EDITOR)
using UnityEngine;
using UnityEditor;

public class PlatformerUtils : EditorWindow
{

    int layerOffsetLayer;
    Vector3 layerOffsetVector;
    bool layerOffsetGroundCheck;

    string tagOffsetTag;
    Vector3 tagOffsetVector;
    bool tagOffsetGroundCheck;

    string setRotationTag;
    Vector3 setRotationVector;
    RotationType rotationType;

    [MenuItem("Tools/PlatformerUtils")]
    private static void OpenUtils() {
        new PlatformerUtils().Show();
    }

    public enum RotationType {
        Global,
        Local
    }

    void OnGUI() {
        LoadPrefs();

        EditorGUILayout.LabelField("Offset Objects in Layer", EditorStyles.boldLabel);
        layerOffsetLayer = EditorGUILayout.LayerField("Layer for Offsetting:", layerOffsetLayer);
        layerOffsetVector = EditorGUILayout.Vector3Field("Offset Vector", layerOffsetVector);
        layerOffsetGroundCheck = EditorGUILayout.Toggle("Check for Ground", layerOffsetGroundCheck);

        if (GUILayout.Button("Offset by Layer")) {
            GameObject[] toOffset = FindObjectsByLayer(layerOffsetLayer);
            foreach(GameObject obj in toOffset){
                OffsetObject(obj, layerOffsetVector, layerOffsetGroundCheck);
            }
        }

        EditorGUILayout.Space();

        EditorGUILayout.LabelField("Offset Objects with Tag", EditorStyles.boldLabel);
        tagOffsetTag = EditorGUILayout.TagField("Tag for Offsetting", tagOffsetTag);
        tagOffsetVector = EditorGUILayout.Vector3Field("Offset Vector", tagOffsetVector);
        tagOffsetGroundCheck = EditorGUILayout.Toggle("Check for Ground", tagOffsetGroundCheck);

        if (GUILayout.Button("Offset by Tag")) {
            GameObject[] toOffset = GameObject.FindGameObjectsWithTag(tagOffsetTag);
            foreach(GameObject obj in toOffset){
                OffsetObject(obj, tagOffsetVector, tagOffsetGroundCheck);
            }
        }

        EditorGUILayout.Space();

        EditorGUILayout.LabelField("Set Rotation by Tag", EditorStyles.boldLabel);
        setRotationTag = EditorGUILayout.TagField("Tag for Rotation", setRotationTag);
        setRotationVector = EditorGUILayout.Vector3Field("Rotation Vector", setRotationVector);
        rotationType = (RotationType)EditorGUILayout.EnumPopup("Locality", rotationType);

        if (GUILayout.Button("Rotate by Tag")) {
            GameObject[] toOffset = GameObject.FindGameObjectsWithTag(setRotationTag);
            foreach(GameObject obj in toOffset){
                switch(rotationType){
                    case RotationType.Global:
                        obj.transform.eulerAngles = setRotationVector;
                        break;
                    case RotationType.Local:
                        obj.transform.localRotation = Quaternion.Euler(setRotationVector.x, setRotationVector.y, setRotationVector.z);
                        break;
                }
            }
        }

        SavePrefs();
    }

    GameObject[] FindObjectsByLayer(int layer)
    {
        List validTransforms = new List();
        Transform[] objs = Resources.FindObjectsOfTypeAll() as Transform[];
        for (int i = 0; i < objs.Length; i++)
        {
            if (objs[i].hideFlags == HideFlags.None)
            {
                if (objs[i].gameObject.layer == layer)
                {
                    validTransforms.Add(objs[i].gameObject);
                }
            }
        }
        return validTransforms.ToArray();
    }

    void OffsetObject(GameObject obj, Vector3 offsetVector, bool checkGround){
        if(checkGround){
            RaycastHit hit;
            if(Physics.Raycast(obj.transform.position,
                               -obj.transform.up,
                               out hit,
                               Mathf.Infinity,
                               CONSTANTS.GROUND_MASK)){
                obj.transform.position = obj.transform.position + offsetVector;
                obj.transform.position = new Vector3(obj.transform.position.x,
                                                     obj.transform.position.y - hit.distance,
                                                     obj.transform.position.z);
                Debug.Log("Moved object " + obj.name);
            } else {
                Debug.Log("No ground detected underneath " + obj.name);
            }
        } else {
            obj.transform.position += offsetVector;
            Debug.Log("Moved object " + obj.name + " without checking for ground");
        }
    }

    void LoadPrefs(){
        layerOffsetLayer = EditorPrefs.GetInt("PlatformerUtils_layerOffsetLayer");
        layerOffsetVector = new Vector3(EditorPrefs.GetFloat("PlatformerUtils_layerOffsetVectorX"),
                                        EditorPrefs.GetFloat("PlatformerUtils_layerOffsetVectorY"),
                                        EditorPrefs.GetFloat("PlatformerUtils_layerOffsetVectorZ"));
    }

    void SavePrefs(){
        EditorPrefs.SetInt("PlatformerUtils_layerOffsetLayer", layerOffsetLayer);

        EditorPrefs.SetFloat("PlatformerUtils_layerOffsetVectorX", layerOffsetVector.x);
        EditorPrefs.SetFloat("PlatformerUtils_layerOffsetVectorY", layerOffsetVector.y);
        EditorPrefs.SetFloat("PlatformerUtils_layerOffsetVectorZ", layerOffsetVector.z);
    }
}
#endif
BuildPlayer

I really hate the Unity build menu. It's got too many options and it isn't easily navigatable via keyboard. This is a script I wrote to build multiple platform standalones in quick succession, and I use it all the time

using UnityEngine;
using UnityEditor;
#if (UNITY_EDITOR)
using UnityEditor.Build.Reporting;
using System.Collections.Generic;

public class BuildPlayer : EditorWindow {

    enum BuildVersion{
        Windows,
        OSX,
        Linux
    }

    int nScenes;
    static string[] sceneNames;

    bool showScenes;

    bool buildWin;
    bool buildMac;
    bool buildLin;

    static string buildPath;

    [MenuItem("Tools/BuildPlayer")]
    private static void OpenBuilder() {
        buildPath = Application.dataPath.Substring(0, Application.dataPath.Length - 6) + "/";
        new BuildPlayer().Show();
    }

    BuildPlayerOptions getBuildOptions(BuildVersion bv){
        BuildPlayerOptions options = new BuildPlayerOptions();

        string[] formattedSceneNames = new string[nScenes];
        for(int i = 0; i < nScenes; i++){
            formattedSceneNames[i] = "Assets/Scenes/" + sceneNames[i] + ".unity";
        }

        options.scenes = formattedSceneNames;
        options.options = BuildOptions.None;
        switch(bv){
            case BuildVersion.Windows:
                options.locationPathName = buildPath + "Builds/Windows/DinosaurIsland.exe";
                options.target = BuildTarget.StandaloneWindows64;
                break;
            case BuildVersion.OSX:
                options.locationPathName = buildPath + "Builds/Mac/DinosaurIsland.app";
                options.target = BuildTarget.StandaloneOSX;
                break;
            case BuildVersion.Linux:
                options.locationPathName = buildPath + "Builds/Linux/DinosaurIsland.x86_64";
                options.target = BuildTarget.StandaloneLinux64;
                break;
            default:
                Debug.Log("BuildVersion " + bv + " not recognized");
                break;
        }

        return options;
    }

    void OnGUI() {
        LoadPrefs();

        EditorGUILayout.BeginHorizontal();

        nScenes = EditorGUILayout.DelayedIntField("Number of scenes in build:", nScenes);
        showScenes = EditorGUILayout.Toggle("Show scenes:", showScenes);

        EditorGUILayout.EndHorizontal();

        if(sceneNames != null){
            List sceneNamesList = new List(sceneNames);
            int difference = nScenes - sceneNames.Length;

            if(difference > 0){
                sceneNamesList.AddRange(new string[difference]);
            } else if(difference < 0){
                sceneNamesList.RemoveRange(sceneNamesList.Count + difference, -difference);
            }

            sceneNames = sceneNamesList.ToArray();
        }

        for(int i = 0; showScenes && i < nScenes; i++){
            sceneNames[i] = EditorGUILayout.TextField("Scene " + i + ":", sceneNames[i]);
        }

        buildWin = EditorGUILayout.Toggle("Build Windows", buildWin);
        buildMac = EditorGUILayout.Toggle("Build OSX", buildMac);
        buildLin = EditorGUILayout.Toggle("Build Linux", buildLin);

        if (GUILayout.Button("Build")) {
            if(buildWin){
                BuildReport winReport = BuildPipeline.BuildPlayer(getBuildOptions(BuildVersion.Windows));
                BuildSummary winSummary = winReport.summary;
            }
            if(buildMac){
                BuildReport macReport = BuildPipeline.BuildPlayer(getBuildOptions(BuildVersion.OSX));
                BuildSummary macSummary = macReport.summary;
            }
            if(buildLin){
                BuildReport linReport = BuildPipeline.BuildPlayer(getBuildOptions(BuildVersion.Linux));
                BuildSummary linSummary = linReport.summary;
            }
        }

        SavePrefs();
    }

    void LoadPrefs(){
        nScenes = EditorPrefs.GetInt("BuildPlayer_nScenes");
        sceneNames = new string[nScenes];

        for(int i = 0; i < nScenes; i++){
            sceneNames[i] = EditorPrefs.GetString("BuildPlayer_Scene" + (i + 1));
        }

        showScenes = EditorPrefs.GetBool("BuildPlayer_showScenes");

        buildWin = EditorPrefs.GetBool("BuildPlayer_buildWin");
        buildMac = EditorPrefs.GetBool("BuildPlayer_buildMac");
        buildLin = EditorPrefs.GetBool("BuildPlayer_buildLin");
    }

    void SavePrefs(){
        EditorPrefs.SetInt("BuildPlayer_nScenes", nScenes);

        for(int i = 0; i < nScenes; i++){
            EditorPrefs.SetString("BuildPlayer_Scene" + (i + 1), sceneNames[i]);
        }

        EditorPrefs.SetBool("BuildPlayer_showScenes", showScenes);

        EditorPrefs.SetBool("BuildPlayer_buildWin", buildWin);
        EditorPrefs.SetBool("BuildPlayer_buildMac", buildMac);
        EditorPrefs.SetBool("BuildPlayer_buildLin", buildLin);
    }
}
#endif
IntroLoop

I've used this script in most of my Unity games. It provides a utility for letting music loop with an intro bit. E.g. if a song has an "introduction" section, it will play the introduction section, and when it loops it'll skip that intro.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(AudioSource))]
public class IntroLoop : MonoBehaviour
{

    public float loop_start_seconds;
    public float loop_end_seconds;

    AudioSource source;

    // Start is called before the first frame update
    void Start()
    {
        source = GetComponent<AudioSource>();
    }

    // Update is called once per frame
    void Update()
    {
        if(source.time >= loop_end_seconds){
            source.time = loop_start_seconds;
        }
    }
}