Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Modifying UV coordinates in the editor problem

Discussion in 'Scripting' started by ThunderMusic, Dec 14, 2015.

  1. ThunderMusic

    ThunderMusic

    Joined:
    Sep 19, 2013
    Posts:
    43
    Hi,
    I've developped a script to be able to modify the UV coordinates of my plane "models" (simple plains with uv coordinates inside a texture alias) based on the scale of the model and an offset.

    I've managed to make it work correctly for runtime, but I can't see it in realtime in the editor unless I use the ExecuteInEditMode attribute on my script. The problem is, if I do that, I get errors from the editor (in the console) saying that using MeshFilter.mesh makes the editor leak meshes and I should use MeshFilter.sharedMesh. The thing is, my needs are instance by instance, not for an entire model. And when I use the ExecuteInEditMode, wether I use mesh or sharedMesh, the UV coordinates of my "models" are modified (and kept as the original UVs) each time I hit "Play" and "Stop" and the operations add up to something really out of hand.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using System.Linq;
    5.  
    6. public class UvAligner : MonoBehaviour {
    7.  
    8.     List<Vector2> OriginalUvs = new List<Vector2>();
    9.  
    10.     List<Vector2> alignedUvs = new List<Vector2>();
    11.  
    12.     Vector3 lastScale = new Vector3();
    13.  
    14.     public Vector2 UvOffset = new Vector2 ();
    15.  
    16.     Vector2 LastUvOffset = new Vector2();
    17.  
    18.     Vector2 minValues = new Vector2();
    19.  
    20.     // Use this for initialization
    21.     void Start () {
    22.         OriginalUvs = new List<Vector2> (GetComponent<MeshFilter> ().sharedMesh.uv);
    23.         alignedUvs = new List<Vector2>(OriginalUvs.ToArray());
    24.         lastScale = new Vector3(1,1,1);
    25.         var minX = OriginalUvs.Min (vector => vector.x);
    26.         var minY = OriginalUvs.Min (vector => vector.y);
    27.         minValues = new Vector2 (minX, minY);
    28.     }
    29.    
    30.     // Update is called once per frame
    31.     void Update () {
    32.         AlignUvs ();
    33.     }
    34.  
    35.     private void AlignUvs() {
    36.         var currentScale = this.transform.localScale;
    37.         if (currentScale.x == lastScale.x && currentScale.y == lastScale.y && currentScale.z == lastScale.z &&
    38.             LastUvOffset.x == UvOffset.x && LastUvOffset.y == UvOffset.y) {
    39.             return;
    40.         }
    41.         lastScale = currentScale;
    42.         LastUvOffset = UvOffset;
    43.         for (var i=0; i<OriginalUvs.Count; i++) {
    44.             var uv = OriginalUvs [i];
    45.             var x = ((uv.x - minValues.x) * lastScale.x) + minValues.x + UvOffset.x;
    46.             var y = ((uv.y - minValues.y) * lastScale.z) + minValues.y + UvOffset.y;
    47.             alignedUvs [i] = new Vector2 (x, y);
    48.         }
    49.  
    50.         GetComponent<MeshFilter> ().mesh.uv = alignedUvs.ToArray();
    51.     }
    52. }
    53.  
    In Start(), I get the sharedMesh UVs to always base on the original ones (or so I thought), the original scale and the original offsets.

    On Update, I check if anything has changed, if not, I return. Otherwise, I process the new UVs from the original ones and the current scale and offsets. I set the new UVs to GetComponent<MeshFilter>.mesh.uv.

    For some reason, when using prefabs (because my planes must have colliders), it creates meshes instances and I must always use the "reset to prefab" function in the editor. Is there an equivalent in code so I can always reset? Am I doing something wrong?

    Thanks a lot.
     
    Last edited: Dec 14, 2015
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,797
    I am not sure, but this sounds like perhaps before you set the new mesh (when running in editor mode), you should Destroy() the old mesh first. This might also be a cause to use DestroyImmediately(), which is a dangerous little function specifically designed (mostly) for use in-editor.
     
    ThunderMusic likes this.
  3. ThunderMusic

    ThunderMusic

    Joined:
    Sep 19, 2013
    Posts:
    43
    Thanks for the answer. I have updated my original post to include the code I'm using. I'm not really sure how I should use the Destroy() and DestroyImmediately() methods. I've tried to destroy the mesh in different parts of this script, but the mesh only disappears and I have to "reset to prefab" again. Using the code in my original post, where should I use Destroy or DestroyImmediate?

    Thanks again.

    [Edit]
    I have uploaded my test project so you can see what I'm trying to achieve and how the script works. Here is the link : https://drive.google.com/file/d/0BwbWwcCjL9EJdmV3YmxtX3RaZ0E/view?usp=sharing

    I have commented the line with the ExecuteInEditMode attribute. If you want to see the behavior I am trying to achieve, uncomment it and rebuild (if it's not done automatically), then move the offset or the scale of the planes (grey brick).
    [/Edit]
     
    Last edited: Dec 14, 2015
  4. ThunderMusic

    ThunderMusic

    Joined:
    Sep 19, 2013
    Posts:
    43
    So... does anyone have any idea? Thx