- Home /
Erasing a part of an image using SetPixel without gaps
I am creating a project in which a part of an image can be erased with a mouse click/drag. I have already made the mouse click. However, when the mouse is dragged quickly, there are gaps between the erased part like this. Is there a solution for this? Below is my code which was taken from here.
void Update () {
hit = Physics2D.Raycast (Camera.main.ScreenToWorldPoint (Input.mousePosition), Vector2.zero);
if (Input.GetMouseButton (0) && hit) {
UpdateTexture ();
}
}
public void UpdateTexture() {
spriteRend = gameObject.GetComponent<SpriteRenderer> ();
tex = CopyTexture (spriteRend.sprite.texture);
string tempName = spriteRend.sprite.name;
spriteRend.sprite = Sprite.Create (tex, spriteRend.sprite.rect, new Vector2 (0.5f, 0.5f));
spriteRend.sprite.name = tempName;
}
public Texture2D CopyTexture(Texture2D copiedTexture) {
float dX, dY;
Texture2D newTex = new Texture2D (copiedTexture.width, copiedTexture.height);
newTex.filterMode = FilterMode.Bilinear;
newTex.wrapMode = TextureWrapMode.Clamp;
int mX = (int)((hit.point.x - hit.collider.bounds.min.x) * (copiedTexture.width / hit.collider.bounds.size.x));
int mY = (int)((hit.point.y - hit.collider.bounds.min.y) * (copiedTexture.height / hit.collider.bounds.size.y));
for (int x = 0; x < newTex.width; x++) {
for (int y = 0; y < newTex.height; y++) {
dX = x - mX;
dY = y - mY;
if (dX * dX + dY * dY <= erSize * erSize) {
newTex.SetPixel (x, y, zeroAlpha);
} else {
newTex.SetPixel (x, y, copiedTexture.GetPixel (x, y));
}
}
}
newTex.Apply ();
return newTex;
}
Answer by Bunny83 · Aug 27, 2018 at 11:09 AM
Don't do a check againt a point but against a line segment. The only difference is that before you do the distance check you have to project the coordinate of the current pixel you're testing onto the line segment . Of course this projected point need to be clamped between the start and end points of the line. Once you have that point you can simply subtract the projected point from x and y instead of just the new point. This will naturally draw a "line" with a width of twice your radius. It's basically the shadow of a capsule.
So all you need is to save the last position while dragging so you have that line segment you need. Note that currently you iterate over all pixels in the image. This could be optimised by just iterating over the bounding rectangle of your line segment. So just figure out the min and max values of your start and end coordinates and add / subract the radius and finally clamp the result to the image resolution. Finally you shouldn't use SetPixel in a loop. It's much more efficient to use GetPixels once in Start, modify the array and use SetPixels after your processing.
Also you shouldn't create a new texture each time. Unused Textures need to be destroyed or you will run out of memory. You may also want to create a single texture which you are going to modify in start and reuse that texture..
edit
I just created a script based on your approach but heavily modified it with the changes i've mentioned.
Here's an example of the script in action. The first radius is 10, the second is 40
Gif: https://www.dropbox.com/s/qlanau5rm1y7xdl/TextureLineDrawing.gif
Note the image used here is "1024x240" and works smoothly. Though if you have a larger image you may run into performance issues as updating such large images is always a problem as it has to be uploaded to the GPU each time. You might get better performance when creating an actual mask mesh and only apply the drawn lines every now and then. Though for relatively small images the current approach seems to work fine.
Thank you so much for your answer. I never thought you would modify the script. I had a hard time understanding your explanation because I am new in Unity and it is advanced for me. But based on your script I kinda understand it now. It worked perfectly. Again, thank you so much.
Amazing answer, but could you please explain for real newbie: 1) Is this script should be attached to Image that have Sprite Renderer? 2) What type of collider i have to use?
I create sprite renderer object, assign to sprite my texture, in texture setting made it readable and type of Sprite then add your script and looks likes it needs collider 2d? Because "Drawing" bool variable never true without it. After adding Box 2d collider i can only cut center part, but can't drag or something like you show.
Could you help please?
Yes, a BoxCollider2d should work just fine as long as it covers the whole image. Please keep in $$anonymous$$d that this is a 2d solution. Also check what value you have in your "erSize" variable. It defines the drawing / "erasing" radius.
Please note that this script was meant as an example for the described technique. It can and might need to be adjusted to your specific usecase. You are free to use the script as it is but this was never meant to be a universally and robust solution.
Hi man i am trying to do same thing with aegis.
I couldn't reach ur script i think that link is broken, can you share it with me? or you can tell me ur way to do this.
@Bunny83 thank you so much. It is working perfectly. I have one request. Would you please tell us here — how to calculate the pixel amount which have been erased (how much portion is erased). For my game I want to show LEVEL COMPLETE after fully erased the sprite. Sorry for my bad English. Btw I am very new in unity. Thanks.
Answer by KrazyTeam · Nov 05, 2020 at 03:09 PM
@Bunny83 brother your code is very clean and good in working thanks for that but, this work fine in a single sprite when I try this with sprite sheet it work something wrong could you please help.
,your code is very clean and good in working thanks for that but, this work fine in a single sprite when I try this with sprite sheet it work something wrong could you please help
Your answer
Follow this Question
Related Questions
SetPixel on a sprite texture without changing it globally 1 Answer
Painting sprite in unity 1 Answer
Texture2D Power of 2 0 Answers
Image is crushed --- Sprite 2 Answers
Clearing pixel from texture changes the asset as well? 2 Answers