Search code examples
c#unity-game-enginepaint

Paint-like a pp on Unity too slow


I've been trying to create a Paint-like app through Unity for some kids and it's almost fine. The issue is that if you drag the finger too fast it will lag a bit and the lines will be straight and jagged. Any suggestions on how to improve it?

Basically, it works in the following way: the player can choose which color he wants to use and he can switch through a pencil and an eraser. When the player will drag his mouse/finger across the screen, the code will change the pixels of the image to which its assigned.

public class PaintColor : MonoBehaviour, IPointerDownHandler, IDragHandler
{

[SerializeField] ColorManager manager; //manages whether the player can draw and with what color

Vector2 prevPos;
Image sr;
[SerializeField] int brushWidth = 10;
[SerializeField] float maxDistance = 5; //when dragging, max possible distance between previous stored position and current

void Start()
{

    sr = GetComponent<Image>();
    ClearPanel();
}

/*get pixel coordinates from mouse position*/
Vector2 PixelPos(Vector2 v)
{


    var rt = GetComponent<RectTransform>();
    int px = Mathf.Clamp(0, (int)(((v.x - rt.rect.x) * sr.sprite.texture.width) / rt.rect.width), sr.sprite.texture.width);
    int py = Mathf.Clamp(0, (int)(((v.y - rt.rect.y) * sr.sprite.texture.height) / rt.rect.height), sr.sprite.texture.height);

    return new Vector2(px, py);
}

public void OnPointerDown(PointerEventData eventData)
{

    if (manager.CanColor())
    {

        Vector2 pixelPos;
        if (RectTransformUtility.ScreenPointToLocalPointInRectangle(GetComponent<RectTransform>(), eventData.position, eventData.pressEventCamera, out pixelPos))
            Draw(PixelPos(pixelPos));
        prevPos = pixelPos;
    }
}

public void OnDrag(PointerEventData eventData)
{

    if (manager.CanColor())
    {

        Vector2 pixelPos;
        if (RectTransformUtility.ScreenPointToLocalPointInRectangle(GetComponent<RectTransform>(), eventData.position, eventData.pressEventCamera, out pixelPos))
        {

            var distance = Mathf.Max(Vector2.Distance(prevPos, pixelPos), maxDistance);
            for (float lerp = 0; lerp <= 1; lerp += 1 / distance)    //color all pixels in between last recorded position and current
            {

                Draw(PixelPos(Vector2.Lerp(prevPos, pixelPos, lerp)));
            }
        }
        prevPos = pixelPos;
    }
}

/*clear out drawing panel*/
public void ClearPanel()
{

    var clear = new Color[(int)sr.sprite.rect.width * (int)sr.sprite.rect.height];
    for (int x = 0; x < clear.Length; x++)
        clear[x] = Color.clear;
    sr.sprite.texture.SetPixels(clear);
    sr.sprite.texture.Apply();
}

void Draw(Vector2 pixelPos)
{

    /*color pixels in area around pointer position*/
    for (int i = (int)pixelPos.x - brushWidth / 2; i < (int)pixelPos.x + brushWidth / 2; i++)
    {

        for (int j = (int)pixelPos.y - brushWidth / 2; j < (int)pixelPos.y + brushWidth / 2; j++)
        {

            sr.sprite.texture.SetPixel(i, j, manager.GetSelectedColor());
        }
    }
    sr.sprite.texture.Apply();
}

}


Solution

  • This is likely a result of poor performance. You should run a profiler to identify the reason for the poor performance, but I'm going to make a guess:

    sr.sprite.texture.SetPixel(i, j, manager.GetSelectedColor());
    ...
    sr.sprite.texture.Apply();
    

    Setting individual pixels is likely a slow operation, and may involve getting locks, etc. And you run "Apply" for each pixel, uploading the entire texture to the GPU for each modified pixel.

    I have not used Unity enough to suggest the most appropriate solution. In most regular UI frameworks you would have a simple Draw Line method you could call. But I would at the very least fix the code to only call Apply() once per drag event, and you may consider using GetPixelData to get direct access to the texture memory. But I would try to find some more suitable methods for drawing lines, and if nothing more suitable exist, maybe consider some other framework?