1
votes

Problem :

I want to make prototype for cleaning windows (I mean cleaning dirty windows) in unity.

I was searching about this subject and finding that I can change pixel by Texture2D.SetPixel().

I try to do it by this method, First I enabled read/write of texture and try this method but nothing happened on my sprite.

So I want to ask it if it's possible to change alpha of the sprite that is clicked by mouse or touched to show the below sprite of original one !?

My Code :

    private RaycastHit2D hitInfo;
    private SpriteRenderer spriteRendererComponent;
    private Color zeroAlpha;

    // Use this for initialization
    void Start ()
    {
        spriteRendererComponent = transform.GetComponent<SpriteRenderer>();
        zeroAlpha = Color.blue;
    }

    // Update is called once per frame
    void Update () {
        if (Input.GetMouseButton(0))
        {
            MouseClick();
        }
    }

    public void MouseClick()
    {
        Vector2 mousePosition = Vector2.zero;
        mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        hitInfo = Physics2D.Raycast(mousePosition, Vector2.zero);
        if (hitInfo)
        {
            spriteRendererComponent.sprite.texture.SetPixel((int)hitInfo.point.x, (int)hitInfo.point.y, zeroAlpha);
            spriteRendererComponent.sprite.texture.Apply();
        }
    }

Answer :

You can use this thread for Optimizing of changing pixels of sprite


I've found answer about changing pixels of sprite (Painting)

public float radius;
public Color InitialColor;

private RaycastHit2D hitInfo;

// Use this for initialization
void Start()
{

}

// Update is called once per frame
void Update()
{
    if (CustomInput.ControlStay())
    {
        hitInfo = CustomInput.ClickednTouched().hitInfo;
        if (hitInfo)
        {
            UpdateTexture();
        }
    }
}

public Texture2D CopyTexture2D(Texture2D copiedTexture2D)
{
    float differenceX;
    float differenceY;

    //Create a new Texture2D, which will be the copy
    Texture2D texture = new Texture2D(copiedTexture2D.width, copiedTexture2D.height);

    //Choose your filtermode and wrapmode
    texture.filterMode = FilterMode.Bilinear;
    texture.wrapMode = TextureWrapMode.Clamp;

    //Center of hit point circle 
    int m1 = (int)((hitInfo.point.x + 2.5f) / 5 * copiedTexture2D.width);
    int m2 = (int)((hitInfo.point.y + 2.5f) / 5 * copiedTexture2D.height);

    for (int x = 0; x < texture.width; x++)
    {
        for (int y = 0; y < texture.height; y++)
        {
            differenceX = x - m1;
            differenceY = y - m2;

            //INSERT YOUR LOGIC HERE
            if (differenceX * differenceX + differenceY * differenceY <= radius * radius)
            {
                //This line of code and if statement, turn all texture pixels within radius to zero alpha
                texture.SetPixel(x, y, InitialColor);
            }
            else
            {
                //This line of code is REQUIRED. Do NOT delete it. This is what copies the image as it was, without any change
                texture.SetPixel(x, y, copiedTexture2D.GetPixel(x, y));
            }
        }
    }

    //This finalizes it. If you want to edit it still, do it before you finish with Apply(). Do NOT expect to edit the image after you have applied.
    texture.Apply();

    return texture;
}

public void UpdateTexture()
{
    SpriteRenderer mySpriteRenderer = gameObject.GetComponent<SpriteRenderer>();
    Texture2D newTexture2D = CopyTexture2D(mySpriteRenderer.sprite.texture);

    //Get the name of the old sprite
    string tempName = mySpriteRenderer.sprite.name;
    //Create a new sprite
    mySpriteRenderer.sprite = Sprite.Create(newTexture2D, mySpriteRenderer.sprite.rect, new Vector2(0.5f, 0.5f));
    //Name the sprite, the old name
    mySpriteRenderer.sprite.name = tempName;

    //Update the material
    //If you have multiple sprites, you will want to do this in a loop
    //mySpriteRenderer.material.mainTexture = newTexture2D;
    //mySpriteRenderer.material.shader = Shader.Find("Unlit/Transparent");

}

Another problem :

Finding pixel on sprite :

In Unity3d we have RaycastHit.textureCoord but it doesn't exist anymore in 2D. I was searching about this problem, a lot but I didn't find anything useful.

So I want to know the solution for this problem and I'm wondering why method like textureCoord in 3D doesn't exist in 2D.

Answer :

I've found answer again as you see in the previous code for finding pixel on sprite.

Thread : Finding pixel on sprite in Unity

2

2 Answers

1
votes

Check this out!

I have fixed your script. Works on different texture sizes. Different texture places and camera size. Require box collider 2d.

    using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour
{
    public float radius;
    public Color InitialColor;

    private RaycastHit2D hitInfo;

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButton(0))
        {
            hitInfo = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
            if (hitInfo)
            {
                UpdateTexture();
            }
        }
        if (Input.GetMouseButtonUp(0))
        {
            Resources.UnloadUnusedAssets();
        }
    }

    public Texture2D CopyTexture2D(Texture2D copiedTexture2D)
    {
        float differenceX;
        float differenceY;

        //Create a new Texture2D, which will be the copy
        Texture2D texture = new Texture2D(copiedTexture2D.width, copiedTexture2D.height);

        //Choose your filtermode and wrapmode
        texture.filterMode = FilterMode.Bilinear;
        texture.wrapMode = TextureWrapMode.Clamp;

        //Center of hit point circle 
        int m1 = (int)((hitInfo.point.x - hitInfo.collider.bounds.min.x) * (copiedTexture2D.width / hitInfo.collider.bounds.size.x));
        int m2 = (int)((hitInfo.point.y - hitInfo.collider.bounds.min.y) * (copiedTexture2D.height / hitInfo.collider.bounds.size.y));

        //Vector2 extremeScreenPoint = Camera.main.ScreenToWorldPoint(new Vector2(0, 0));
        //Debug.Log("extremeScreenPoint= " + extremeScreenPoint.x
        //                 + " hitInfo.point.x =" +  hitInfo.point.x 

        //    //+ "  mousePosition =" + Camera.main.ScreenToWorldPoint(Input.mousePosition).x
        //                + "  bounds.min =" +  hitInfo.collider.bounds.min .x
        //                + "  bounds.max =" +  hitInfo.collider.bounds.max .x
        //                                        + "  size =" + hitInfo.collider.bounds.size.x
        //                                        + "  hit =" + (hitInfo.point.x - hitInfo.collider.bounds.min.x)
        //                                        + "  pixels =" + (hitInfo.point.x - hitInfo.collider.bounds.min.x) * (copiedTexture2D.width / hitInfo.collider.bounds.size.x)
        //    );



        for (int x = 0; x < texture.width; x++)
        {
            for (int y = 0; y < texture.height; y++)
            {
                differenceX = x - m1;
                differenceY = y - m2;



                //INSERT YOUR LOGIC HERE
                if (differenceX * differenceX + differenceY * differenceY <= radius * radius)
                {
                    //This line of code and if statement, turn all texture pixels within radius to zero alpha
                    texture.SetPixel(x, y, InitialColor);
                }
                else
                {
                    //This line of code is REQUIRED. Do NOT delete it. This is what copies the image as it was, without any change
                    texture.SetPixel(x, y, copiedTexture2D.GetPixel(x, y));
                }
            }
        }

        //This finalizes it. If you want to edit it still, do it before you finish with Apply(). Do NOT expect to edit the image after you have applied.
        texture.Apply();
        //DestroyImmediate(copiedTexture2D, true);
        return texture;
    }

    public void UpdateTexture()
    {
        SpriteRenderer mySpriteRenderer = gameObject.GetComponent<SpriteRenderer>();
        Texture2D newTexture2D = CopyTexture2D(mySpriteRenderer.sprite.texture);

        //Get the name of the old sprite
        string tempName = mySpriteRenderer.sprite.name;
        //Create a new sprite
        mySpriteRenderer.sprite = Sprite.Create(newTexture2D, mySpriteRenderer.sprite.rect, new Vector2(0.5f, 0.5f));
        //Name the sprite, the old name
        mySpriteRenderer.sprite.name = tempName;

        //Update the material
        //If you have multiple sprites, you will want to do this in a loop
        //mySpriteRenderer.material.mainTexture = newTexture2D;
        //mySpriteRenderer.material.shader = Shader.Find("Unlit/Transparent");

    }
}
0
votes

I worked with Writing and Reading a Texture once (for a scratching card). You have to consider that when you change the Pixels of the Sprite, you are changing the pixels for the entire texture. So let's say that i change the pixel 1x1, most likely it will not change the pixel 1x1 in my sprite if i have a bunch of sprites in the same texture. So you have to consider the offsets of the sprite and reposition the pixel that you want to change. Try to do something like this:

public void MouseClick()
{
    Vector2 offset = new Vector2(XXX, YYY);
    Vector2 mousePosition = Vector2.zero;
    mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
    hitInfo = Physics2D.Raycast(mousePosition, Vector2.zero);
    if (hitInfo)
    {
        spriteRendererComponent.sprite.texture.SetPixel((int)hitInfo.point.x + offset.x, (int)hitInfo.point.y + offset.y, zeroAlpha);
        spriteRendererComponent.sprite.texture.Apply();
    }
}