Moving Mesh Instance UVs in Editor? (code provided)

Hey,

So I made this script that edits the UV coordinates of a model to slide it along a texture atlas for a semi 2D game. This function in itself works, but in the editor, I used MeshFilter.sharedMesh (like the docs said to) to prevent leaking; as an unintended side-effect, all meshes in the scene with this script change their uvs to the selected coords in the script since they all share a model. I don’t want that to happen because I can’t tell what type of sprite is where without running the game.

TL;DR: How can I make this script ONLY affect the mesh it’s attached to?

*feel free to use this if you like

    #pragma strict
    @script ExecuteInEditMode()
    
    var myMesh:MeshFilter;
    	var myTex:Texture2D;
    
    var uvs:Vector2[] =new Vector2[4];
    
    //Use these like coordinates to access the UVs. 
    //These are controlled via editor script
    var rowNum:int = 0; //On which row is the sprite located?
    var columnNum:int = 0;//In which column is the sprite located;
    var isAnimated:boolean;
    	var framerate:int = 10;
    	var animCoords : Vector2[] = new Vector2[10];	
    	
    private var padding:float=0.00390625;//Whatever padding space you use between sprites
    private var width:float = 0.0625;//either direction, because I only use square textures.
    
    private var _myMesh:Mesh;
    	
    function getObjectMesh (){
    		var mf:MeshFilter= GetComponent(MeshFilter);
    		var mesh:Mesh;
    		if (Application.isEditor){
    			mesh=mf.sharedMesh;
    		} else {
    			mesh=mf.mesh;	
    		}
    		return mesh;
    }
    	
    function Offset(){
    		//Get the current mesh to edit 
    	myMesh=GetComponent(MeshFilter);
    	_myMesh = myMesh.sharedMesh;
    	
    	var mesh:Mesh = getObjectMesh();
    	
    	//Current texture, in case you need it;
    		myTex =renderer.sharedMaterial.mainTexture;//don't want to generate an instance
    	
    	//Assign uvs, maintain square aspect ratio
    		//bottom left
    	uvs[0] = Vector2(/*x value*/	(padding*(columnNum))+(((columnNum)-1)*width),
    		/*y value*/ 	(1-(  (padding*(rowNum))+ (width*(rowNum-1)))  ));
    		
    		//bottom right
    	uvs[1] = Vector2(uvs[0].x+width, uvs[0].y);
    	
    		//top left
    	uvs[2] = Vector2(uvs[1].x,1-(    (padding*(rowNum)) + (width*(rowNum)) )  );
    	
    		//top right. 
    	uvs[3] = Vector2(uvs[0].x, uvs[2].y); // pretty easy, huh?
    	
    	mesh.uv = uvs;
    	
    	return;
    }
    	
    function Start () {
    		rowNum = Mathf.Clamp (rowNum,1,15);
    		columnNum = Mathf.Clamp (columnNum,1,15);
    		Offset();
    }
    
    function Update () {
    	getObjectMesh ();
    	
    	if(!Application.isPlaying)
    		Offset();
    }

You have to use MeshFilter.mesh for this, as opposed to MeshFilter.sharedMesh. It does mean you’ll have many instances of the mesh, but when the mesh is just a quad that cost isn’t prohibitive.

Like invicticide said you have to use individual assets. Everything at edit time has to be an asset, otherwise it’s lost when you reload Unity / open another scene / …

You can use the AssetDatabase to save a clone of your mesh as new asset. However it’s up to you how you trigger the cloning & saving process since it only has to be done once. That’s usually a good example for an EditorWindow or wizard.

However, beside modifying the mesh, wouldn’t be enough in your case to just adjust the Materials uv-offset and scaling?

Appreciate all the answers guys.

I scripted a pretty good solution to this over the past couple of days. Feel free to use/modify (I grant no warranties/assume no liabilities/etc.)

WHAT IT DOES:

-Slide quad uvs in the editor along an atlas

-Linearly Animate uv placements via an array of Row/column pairs

-Batched Draw Calls (for the most part)

-You can remove when done sliding, and changes hold.

-Prefab-able!

KNOWN PROBLEMS:

-QUADS ONLY. I hard-coded the the uv sliding to work with vertex indexes, so you may need to tweak the vertex numbers in the script.

-I used this on a 15x15 sprite atlas on a 256 X 256 image, so you’ll have to adjust rowPadding etc. for your needs

-I used .mesh for this, so meshes DO leak. Sorry, no better way it seems :\ Since you’ll be using quads, though, this isn’t that bad a problem :slight_smile:

PoorMan’s SpriteManager:

#pragma strict
@script ExecuteInEditMode()

var myMesh:MeshFilter;
	var myTex:Texture2D;

var uvs:Vector2[] =new Vector2[4];

//Use these like coordinates to access the UVs. 
//These are controlled via editor script
var rowNum:int = 0; //On which row is the sprite located?
var columnNum:int = 0;//In which column is the sprite located;
var isAnimated:boolean;
	var framerate:int = 10;
	var animCoords : Vector2[] = new Vector2[10];	
	
private var padding:float=0.00390625;//Whatever padding space you use between sprites
private var width:float = 0.0625;//either direction, because I only use square textures.
	

	var index:int = 0;
	var waitTime:float = 0.01;
function Animate():IEnumerator{
	rowNum=animCoords[index].x;
	columnNum =animCoords[index].y;
	
	//ready next frame
	if(index<(animCoords.length-1))
		index++;
	else{
		index = 0;
	}
	
	yield WaitForSeconds(waitTime);
	Animate();
	return;
}
	
function Offset(){
		//Get the current mesh to edit 
	myMesh=gameObject.GetComponent(MeshFilter);	


	
	//var mesh:Mesh = getObjectMesh();
	
	//Current texture, in case you need it;
		myTex =renderer.sharedMaterial.mainTexture;//don't want to generate an instance
	
	//Assign uvs, maintain square aspect ratio
		//bottom left
	uvs[0] = Vector2(/*x value*/	(padding*(columnNum))+(((columnNum)-1)*width),
		/*y value*/ 	(1-(  (padding*(rowNum))+ (width*(rowNum-1)))  ));
		
		//bottom right
	uvs[1] = Vector2(uvs[0].x+width, uvs[0].y);
	
		//top left
	uvs[2] = Vector2(uvs[1].x,1-(    (padding*(rowNum)) + (width*(rowNum)) )  );
	
		//top right. 
	uvs[3] = Vector2(uvs[0].x, uvs[2].y); // pretty easy, huh?
	
	myMesh.mesh.uv = uvs;
	return;
}
	
function CreateNewMesh(){
		var newMesh:Mesh;
		var temp:GameObject = (Instantiate(Resources.Load("Prefabs/Blocks/basePlane"))as GameObject);
		var tempF:MeshFilter = temp.GetComponent(MeshFilter);
		
		newMesh = tempF.sharedMesh;	
		
		DestroyImmediate(temp);
		DestroyImmediate(tempF);
		
		return newMesh;
}	

function Start () {
		if(!Application.isPlaying)
			myMesh.mesh = CreateNewMesh();
		
		rowNum = Mathf.Clamp (rowNum,1,15);
		columnNum = Mathf.Clamp (columnNum,1,15);
		Offset();
		if(isAnimated){
			Animate();
		}
}

function Update () {
	if(!Application.isPlaying||isAnimated)
		Offset();
}