Hello Guest

Author Topic: Permanently changing sprite material in collection data via code  (Read 4821 times)

twicecircled

  • 2D Toolkit
  • Newbie
  • *
  • Posts: 4
    • View Profile
Permanently changing sprite material in collection data via code
« on: September 05, 2014, 02:07:09 pm »
Hi there, I'm looking for a way to permanently change the material associated with a given sprite in a given collection via code.

I understand that there is a GUI-based solution here http://www.unikronsoftware.com/2dtoolkit/docs/latest/tutorial/multiple_materials_in_a_sprite_collection.html however I have large sprite collections and this method requires multiple rebuilds of the entire atlas which is very time-consuming.

I've set up the following code, attached to a prefab and run from an editor script, which seems to do the job nicely and instantaneously without any rebuilding of the collection. The updates are effective in the player.

Code: [Select]
public class MaterialChanger : MonoBehaviour {

public tk2dSpriteCollectionData data;
public string spriteContains;
public Material newMaterial;

public void ChangeMaterial () {
// Add material if necessary
List<Material> materials = new List<Material>(data.materials);
if (!materials.Contains(newMaterial)){
materials.Add(newMaterial);
data.materials = materials.ToArray();
}

// Get material index
int mIndex = 0;
for(int i =0; i<materials.Count;i++){
if (materials[i]==newMaterial){
mIndex = i;
break;
}
}

foreach (tk2dSpriteDefinition def in data.spriteDefinitions) {
if (def.name.Contains(spriteContains)){
def.material = newMaterial;
def.materialId = mIndex;
}
}
}
}

// EDITOR:

[CustomEditor(typeof(MaterialChanger))]
public class MaterialChangerEditor : Editor {

public override void OnInspectorGUI() {
MaterialChanger materialChanger = (MaterialChanger) target;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Sprite collection data");
materialChanger.data = (tk2dSpriteCollectionData) EditorGUILayout.ObjectField(materialChanger.data,typeof(tk2dSpriteCollectionData),false);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Identifier");
materialChanger.spriteContains = EditorGUILayout.TextField(materialChanger.spriteContains);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Material to change to");
materialChanger.newMaterial = (Material)EditorGUILayout.ObjectField(materialChanger.newMaterial,typeof(Material),false);
EditorGUILayout.EndHorizontal();

EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Do change", GUILayout.MaxWidth(120))){
materialChanger.ChangeMaterial();
}
EditorGUILayout.EndHorizontal();
}
}

However, when I restart Unity my changes are overwritten and I'm forced to rerun this script again every time I restart. Is there a way to change the material references via code permanently, either on the tk2dspritecollectiondata or tk2dspritecollection prefab?

I'm quite happy going in and changing stuff directly on the tk2d prefabs and I understand the consequences of any changes I make. I'm not looking for a neat solution, just one that works. I am also absolutely fine with the changes being reversed every time I rebuild the sprite collection. My only requirement is that is does not reverse the changes on restart and, of course, that it does not required a Commit() of the collection.

PS Just noticed Private Support forum. Apologies if this should have gone in there. Feel free to move topic if so.
« Last Edit: September 05, 2014, 03:00:23 pm by twicecircled »

unikronsoftware

  • Administrator
  • Hero Member
  • *****
  • Posts: 9709
    • View Profile
Re: Permanently changing sprite material in collection data via code
« Reply #1 on: September 07, 2014, 11:58:04 am »
You probably need to call EditorUtiliy.SetDirty on the changed objects to get Unity to save the changes?

twicecircled

  • 2D Toolkit
  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: Permanently changing sprite material in collection data via code
« Reply #2 on: September 07, 2014, 03:58:08 pm »
Hi Unikron,

Thanks so much for the quick reply, that's solved the issue nicely! Please except my apologies, I'm new to editor scripts and didn't realise about this SetDirty() method.

In case anybody else is reading this, the entire call required to solve the issue in this case is:

Code: [Select]
UnityEditor.EditorUtility.SetDirty(tk2dSpriteCollectionData);
This tells Unity that the prefab object has changed and it should be saved to the disc. Then when you reload the editor the changes are kept. The requirement is nothing to do with tk2d, you need to do this whenever you change any prefab in Unity.

In case anybody wants to use this code, I've reposted it below with this line added. Just attach the MaterialChanger.cs script to any gameobject and it will work as a little tool for switching the material attached to a sprite.

Code: [Select]
// MaterialChanger.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MaterialChanger : MonoBehaviour {

public tk2dSpriteCollectionData data;
public string spriteContains;
public Material newMaterial;

public void ChangeMaterial () {
// Add material if necessary
List<Material> materials = new List<Material>(data.materials);
if (!materials.Contains(newMaterial)){
materials.Add(newMaterial);
data.materials = materials.ToArray();
}

// Get material index
int mIndex = 0;
for(int i =0; i<materials.Count;i++){
if (materials[i]==newMaterial){
mIndex = i;
break;
}
}

foreach (tk2dSpriteDefinition def in data.spriteDefinitions) {
if (def.name.Contains(spriteContains)){
def.material = newMaterial;
def.materialId = mIndex;
}
}
UnityEditor.EditorUtility.SetDirty(data);
}
}

// MaterialChangerEditor.cs - needs to be placed in a folder called "Editor"
using UnityEngine;
using UnityEditor;
using System.Collections;

[CustomEditor(typeof(MaterialChanger))]
public class MaterialChangerEditor : Editor {

public override void OnInspectorGUI() {
MaterialChanger materialChanger = (MaterialChanger) target;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Sprite collection data");
materialChanger.data = (tk2dSpriteCollectionData) EditorGUILayout.ObjectField(materialChanger.data,typeof(tk2dSpriteCollectionData),false);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Identifier");
materialChanger.spriteContains = EditorGUILayout.TextField(materialChanger.spriteContains);
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel("Material to change to");
materialChanger.newMaterial = (Material)EditorGUILayout.ObjectField(materialChanger.newMaterial,typeof(Material),false);
EditorGUILayout.EndHorizontal();

EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Do change", GUILayout.MaxWidth(120))){
materialChanger.ChangeMaterial();
}
EditorGUILayout.EndHorizontal();
}
}