Version: Unity 6.6 (6000.6)
Language : English
Avoiding asset duplication
Distribute assets as packages

Temporary asset changes at runtime

When a script holds a reference to an asset, that reference points to the asset itself. Any change made through the reference affects every object that uses the asset, and in the Unity Editor the change can persist in the asset file. To change an asset without affecting other objects, you can modify a copy of the asset instead.

You can make a copy of an asset with Object.Instantiate and assign the copy to the object. Alternatively, materials, meshes, and Physics materials have shortcut properties which make the copy for you.

Shortcut properties can be useful if a single object needs its own version of an asset, and your script only needs to change it once or a few times. However, the first time a script accesses a shortcut property on a component, Unity creates a copy even if the script only reads from it, which means that you might create more copies than you intend on. Creating a copy of an object with Object.Instantiate gives you better control over when a copy is created, and it allows you to share a modified copy with several objects.

Make a temporary copy of an asset

To make a temporary copy of an asset, create a copy with Object.Instantiate, assign the copy to the component, and then modify the copy. The original asset and every other object that uses it stays unchanged.

The following example gives an enemy a low-friction copy of its Physics material. The enemy slides across surfaces more easily, but other enemies that use the same Physics material asset aren’t affected. The example requires a non-kinematic Rigidbody, and a single collider that has a Physics material assigned:

using UnityEngine;

[RequireComponent(typeof(Collider))]
public class FreezeEnemy : MonoBehaviour
{
    PhysicsMaterial m_FrozenMaterial;

    public void Freeze()
    {
        // Only make one copy. The shortcut property Collider.material does this check for you.
        if (m_FrozenMaterial != null)
            return;

        Collider enemyCollider = GetComponent<Collider>();

        // Copy the shared asset and assign the copy to this collider only.
        m_FrozenMaterial = Instantiate(enemyCollider.sharedMaterial);
        enemyCollider.sharedMaterial = m_FrozenMaterial;

        m_FrozenMaterial.dynamicFriction = 0f;
        m_FrozenMaterial.staticFriction = 0f;
    }

    void OnDestroy()
    {
        // Unity doesn't destroy runtime copies automatically.
        if (m_FrozenMaterial != null)
            Destroy(m_FrozenMaterial);
    }
}

Use shortcut properties to modify assets

The following asset types have shortcut properties that make the copy for you, as follows:

Each of these components also has a shared property, such as Renderer.sharedMaterial, that returns the asset currently assigned to the component. Before a copy is assigned, this is the original asset. After a shortcut property or a script assigns a copy, the shared property returns the copy. To restore the original asset later, store a reference to it before you make a copy.

The first time a script accesses a shortcut property, Unity copies the shared asset, assigns the copy to that component, and returns the copy. Later calls on the same component return the same copy. Unity makes the copy even if the script only reads a value from it. The result is the same as making the copy yourself with Object.Instantiate.

The following is an example of using shortcut properties to achieve the same outcome as the previous example:

using UnityEngine;

[RequireComponent(typeof(Collider))]
public class FreezeEnemyShortcut : MonoBehaviour
{
    PhysicsMaterial m_FrozenMaterial;

    public void Freeze()
    {
        // The first access to Collider.material copies the shared asset
        // and assigns the copy to this collider.
        m_FrozenMaterial = GetComponent<Collider>().material;
        m_FrozenMaterial.dynamicFriction = 0f;
        m_FrozenMaterial.staticFriction = 0f;
    }

    void OnDestroy()
    {
        // Destroy the cached copy. Accessing Collider.material here
        // creates a new copy if one doesn't exist yet.
        if (m_FrozenMaterial != null)
            Destroy(m_FrozenMaterial);
    }
}

Impact on performance

Each copy is a separate object in memory. If many objects each make their own copy the project can end up with thousands of copies of the same asset.

Unity doesn’t destroy copies when the GameObject that uses them is destroyed. You must destroy each copy with Object.Destroy when it’s no longer needed. Resources.UnloadUnusedAssets also destroys unused copies, but Unity only usually calls it when loading a new scene.

If you access Renderer.material or MeshFilter.mesh in Edit mode, Unity logs an error because the copy leaks into the scene. Use the shared properties in Editor code instead. Changes made through a shared property affect the asset assigned to the component, so to change a single object, create a copy with Object.Instantiate, assign the copy through the shared property, and then modify the copy.

Additional resources

Avoiding asset duplication
Distribute assets as packages