Can I create a script from an hidden template

I would like to make a package that contains editable scripts (for example an Enum that would be modifiable by the user) so I’d need these scripts to be created once and then never override them after a package update.

I found the way by creating script templates and then calling ProjectWindowUtil.CreateScriptAssetFromTemplateFile as explained here .

However, since it is a once per project life operation, it would be very confusing for the user to have these templates around, specially because I have other templates that I plan to make available.

So, the best for me would be to have these templates available by code but not by user interaction, i.e. disable the create menu entries for these templates. I guess another way would be to put an ugly “DO NOT USE” as template category name, but that would be as last resort and I prefer asking first.

Packages being readonly is kind of fundamental to the idea. Also, I as a user it feels pretty unclean to edit scripts to configure user-specific data. In most cases, you can just change your implementation to be data-driven instead. Why hardcode enum values, if users provide them? Use ScriptableObject assets instead. For example, each ScriptableObject instance of a particular type defines a unique value, or a lookup table contains strings.

As for creating unique scripts from templates: you can only attempt to catch most cases the user can abuse or break this system, but you won’t make it waterproof. You could enfore a unique path or file name, maybe add a marker to the custom userData property on the meta file or build your own database of known files (like a search index) and store that at a fixed location. It just doesn’t feel like a clean solution.

Why do you need so many templates or code snippets to be configured? Usually, if you develop a plugin, you provide special extension points to your API that users can use to implement their own things following their own workflow. Some examples are: find all sub-classes of a special type which you define (and users just implement the base class or interface to extend it). Provide static events to which users can subscribe in InitializeOnLoad, etc.

They do not just edit scripts, they make the scripts themselves, I just provide the base classes to help them. Some classes are required beforehand and rely on an enum that can be extended, so I need the user to be able to edit this enum. For example you create an InputController for your game, I provide the InputControllerBase with some benefits, nothing else. The InputControllerBase requires an enum to adjust depending on the user interaction (menu, main scene, settings, etc…).

But why do you need the template? I wouldn’t want you to provide a template for me, because you don’t know how I or my organizations needs to setup scripts. We have certain headers and a namespace and formatting rules, there’s no need for you to setup any of that for users. I would simply derive my own script file from InputControllerBase.

Extending the enum is more difficult, that’s true, but still I wouldn’t force users to edit existing code. You can always change your design to use plain old data. Most of the time, if you switch on enum values, you also need behaviour; if this is the case, you can also change the implementation to something like custom classes + attributes (like the CustomEditor system in Unity).

1 Like

The main reason I need templates is to extend the base classes. The second reason is to create a package that does not break because the enum file does not exist. If I provided the enum in a .cs file, then it would be overwritten at next update. Also the enum needs to evolve during the project life, for example if I add a map to my game, then the input will be different and I need to add Map to the InputMode enum to reflect that.

I don’t see how attributes or scriptable objects would help with this.

As for not using the templates it’s up to you, you can ignore them and inherit directly as you said.

@Xarbrough_1 Thanks for mentioning CustomAttributes, I probably can do something using them, however I rather keep it simple for the first version and see what needs to be improved from there. I am extracting the code from one of my projects and try to make a package out of it, which is not so simple actually.

As for my issue you are right, I don’t need to use ScriptTemplates, for it is too high level, I just need to put the files as text in another Templates folder and load them with AssetDatabase.LoadAssetAtPath().

Let’s not dwell on the implementation for your general purpose plugin, but in general, that use case is exactly why ScriptableObject asset enums are a great implementation and why e.g. Rewired and the newer Unity Input System both use assets to define their action maps in a data-driven way. “Map”, “Combat”, “Menu” are only identifiers, they only need to be compared to one another or listed or used as a lookup key for localization etc. So these things can be enums, of course, but also just numbers, strings or object references. If you use strings, you can let users define them in a simple serialized list. If you use ScriptableObject asset instances, it can be more powerful, because you can find all instances in the AssetDatabase, no matter where they are located. Or simply put them in the Resources folder to load at runtime. Most of the time you only need to compare them with one another (map == map) instead of switching on the enum. But now you also have another option: you can add custom data to the asset instance.

Just a rough example for future inspiration:

// Instances: Menu, Combat, Driving, Map.
[CreateAssetMenu]
public class InputContext : ScriptableObject
{
    public virtual void Process(InputHandler handler) { }
}

[CreateAssetMenu]
public class MapInputContext : InputContext
{
    public float zoomSpeed = 1f;

    public override void Process(InputHandler handler)
    {
        handler.DoSomething(zoomSpeed);
    }
}

public class InputHandler : MonoBehaviour
{
    public InputContext defaultContext;

    private InputContext currentContext;

    private void Start()
    {
        currentContext = defaultContext;
    }

    private void Update()
    {
        currentContext.Process(this);
    }

    public void DoSomething(float zoomSpeed)
    {
        // Do something here or forward access to camera earlier etc.
    }
}

You can still simply compare InputContext instances the same way you use a switch on the enum value, but now you can improve your design in an object-oriented way by separating concerns into multiple classes. Now, if you add a new context (inventory navigation), you add a new ScriptableObject and also the required implementation, all together nicely grouped. This is usually better design that switching on identifiers in multiple locations.

1 Like

OK, you are right. I can even add methods and the input controller only has to invoke them rather than using a switch. Thanks for your input.