What is the correct way to instance a sprite or texture2D?
Hi,
i am trying to edit a sprite at run-time, which works really well until i have multiple instances of the sprite at the same time.
I have tried to instance the sprite as below (because the Texture2D appears to be read only), but if i set the pixels to a specific color on one instance, it is still applied to the other instances.
public SpriteRenderer sr;
Sprite sprite;
void Start()
{
sr = GetComponent<SpriteRenderer>();
Sprite instancesprite = Instantiate(sr.sprite);
sr.sprite = instancesprite;
sprite = sr.sprite;
}
I am using the below method to set the colour:
void SetColourPixel(Color colour, int pixelX, int pixelY)
{
Color pixelColor = sprite.texture.GetPixel(pixelX, pixelY);
if (pixelColor.a != 0)
{
sprite.texture.SetPixel(pixelX, pixelY, colour);
}
}
Any help would be greatly appreciated.
Thanks!
an update, i tried adding the below also (unsuccessfully). Both instances of the texture2D still end up with the same instanceID value.
Texture2D texture2D;
// Use this for initialization
void Start()
{
sr = GetComponent<SpriteRenderer>();
sprite = sr.sprite;
texture2D = CreateANewTexture2DInstance(sr.sprite.texture);
sr.sprite.texture.UpdateExternalTexture(texture2D.GetNativeTexturePtr());
sr.sprite.texture.Apply();
Debug.Log(sr.sprite.texture.GetInstanceID());
}
public Texture2D CreateANewTexture2DInstance(Texture2D tex, bool use$$anonymous$$ip$$anonymous$$ap = false)
{
Texture2D result = new Texture2D(tex.width, tex.height, tex.format, use$$anonymous$$ip$$anonymous$$ap);
result.SetPixels(tex.GetPixels());
result.Apply();
return result;
}
I also tried:
void Start()
{
sr = GetComponent<SpriteRenderer>();
texture2D = new Texture2D(sr.sprite.texture.width, sr.sprite.texture.height, sr.sprite.texture.format, false);
texture2D.SetPixels(sr.sprite.texture.GetPixels());
texture2D.Apply();
$$anonymous$$aterial material = new $$anonymous$$aterial(sr.material.shader);
material.enableInstancing = true;
sr.material = material;
sr.material.mainTexture = texture2D;
Debug.Log(sr.sprite.texture.GetInstanceID());
}
I am finally making progress with this:
void Start()
{
sr = GetComponent<SpriteRenderer>();
texture2D = new Texture2D(sr.sprite.texture.width, sr.sprite.texture.height, sr.sprite.texture.format, false);
texture2D.SetPixels(sr.sprite.texture.GetPixels());
texture2D.Apply();
Debug.Log("Rect: " + sr.sprite.rect + ", pivot: " + sr.sprite.pivot + ", ppu: " + sr.sprite.pixelsPerUnit + " offset: " + sr.sprite.textureRectOffset);
Sprite sprite = Sprite.Create(texture2D, sr.sprite.rect, sr.sprite.pivot, sr.sprite.pixelsPerUnit);
Debug.Log("new sprite offset: " + sprite.textureRectOffset);
sr.sprite = sprite;
$$anonymous$$aterial material = new $$anonymous$$aterial(sr.material.shader);
material.enableInstancing = true;
sr.material = material;
sr.material.mainTexture = texture2D;
Debug.Log(sr.sprite.texture.GetInstanceID());
}
Now i just need to work out what is introducing a bizarre offset between the objects location and where the sprite is displayed.
Answer by Matt-Face · Jan 02, 2018 at 01:53 PM
Pretty sure there is a bug here because the new sprite.bounds seems to be incorrect. It also looks like there is not much i can do about it given how the bounds are read only and there does not appear to be any control over this in the create method.
public SpriteRenderer sr;
void Start()
{
sr = GetComponent<SpriteRenderer>();
Texture2D texture2D = new Texture2D(sr.sprite.texture.width, sr.sprite.texture.height, sr.sprite.texture.format, false);
texture2D.filterMode = FilterMode.Point;
texture2D.alphaIsTransparency = true;
texture2D.alphaIsTransparency = sr.sprite.texture.alphaIsTransparency;
texture2D.anisoLevel = sr.sprite.texture.anisoLevel;
texture2D.wrapMode = sr.sprite.texture.wrapMode;
texture2D.wrapModeU = sr.sprite.texture.wrapModeU;
texture2D.wrapModeV = sr.sprite.texture.wrapModeV;
texture2D.wrapModeW = sr.sprite.texture.wrapModeW;
texture2D.SetPixels(sr.sprite.texture.GetPixels());
texture2D.Apply();
Rect rect = sr.sprite.rect;
Sprite oldSprite = sr.sprite;
//Sprite sprite = Sprite.Instantiate(sr.sprite);
Sprite sprite = Sprite.Create(texture2D, rect, sr.sprite.pivot, sr.sprite.pixelsPerUnit);
Debug.Log("OLD bounds: " + oldSprite.bounds + ", NEW Bounds: " + sprite.bounds);
sr.sprite = sprite;
Material material = new Material(sr.material.shader);
Debug.Log(material.mainTextureOffset + ", " + sr.material.mainTextureOffset);
material.enableInstancing = true;
sr.material = material;
sr.material.mainTexture = texture2D;
Debug.Log(sr.sprite.texture.GetInstanceID());
}
Just in case anyone else is having the same issue, I have put in a bug report regarding this which has now been passed on to the Dev $$anonymous$$m by Unity-QA, as they could reproduce the issue. Hopefully a fix will be available soon.
This offset issue was due to an error in the documentation which has now been fixed; The pivot point is not in pixels but is a value between 0 & 1.
The correct approach is as follows:
public SpriteRenderer sr;
void Start()
{
sr = GetComponent<SpriteRenderer>();
Texture2D texture2D = new Texture2D(sr.sprite.texture.width, sr.sprite.texture.height, sr.sprite.texture.format, false);
texture2D.filter$$anonymous$$ode = Filter$$anonymous$$ode.Point;
texture2D.alphaIsTransparency = sr.sprite.texture.alphaIsTransparency;
texture2D.anisoLevel = sr.sprite.texture.anisoLevel;
texture2D.wrap$$anonymous$$ode = sr.sprite.texture.wrap$$anonymous$$ode;
texture2D.wrap$$anonymous$$odeU = sr.sprite.texture.wrap$$anonymous$$odeU;
texture2D.wrap$$anonymous$$odeV = sr.sprite.texture.wrap$$anonymous$$odeV;
texture2D.wrap$$anonymous$$odeW = sr.sprite.texture.wrap$$anonymous$$odeW;
texture2D.SetPixels(sr.sprite.texture.GetPixels());
texture2D.Apply();
Rect rect = sr.sprite.rect;
Sprite oldSprite = sr.sprite;
Sprite sprite = Sprite.Create(texture2D, rect, new Vector2((sr.sprite.pivot.x / rect.width), (sr.sprite.pivot.y / rect.height)), sr.sprite.pixelsPerUnit);
sr.sprite = sprite;
$$anonymous$$aterial material = new $$anonymous$$aterial(sr.material.shader);
Debug.Log(material.mainTextureOffset + ", " + sr.material.mainTextureOffset);
material.enableInstancing = true;
sr.material = material;
sr.material.mainTexture = texture2D;
Debug.Log("sprite instance: " + sr.sprite.texture.GetInstanceID());
}