Digital Drawing & Animation Tool C#, Unity
👤 Sharing: AI
```csharp
using UnityEngine;
using UnityEngine.UI;
public class DrawingController : MonoBehaviour
{
public RawImage drawingSurface; // Reference to the RawImage to draw on. Assign this in the Unity Editor.
public Texture2D drawingTexture; // The texture we'll actually be drawing onto.
public Color drawColor = Color.black; // The color to draw with. Adjust in the Editor.
public float brushSize = 5f; // The size of the brush (diameter). Adjust in the Editor.
private bool _isDrawing = false;
private Vector2 _lastMousePos; // Store the last mouse position to draw lines.
// Initialization
void Start()
{
// Create a new Texture2D with the same dimensions as the RawImage
drawingTexture = new Texture2D((int)drawingSurface.rectTransform.rect.width, (int)drawingSurface.rectTransform.rect.height);
// Set the texture to be transparent initially. Crucial.
ClearDrawingSurface();
// Apply the new texture to the RawImage
drawingSurface.texture = drawingTexture;
}
// Clear the drawing surface (set all pixels to transparent).
public void ClearDrawingSurface()
{
Color[] clearPixels = new Color[drawingTexture.width * drawingTexture.height];
for (int i = 0; i < clearPixels.Length; i++)
{
clearPixels[i] = Color.clear; // Transparent
}
drawingTexture.SetPixels(clearPixels);
drawingTexture.Apply(); // Important: Apply the changes!
}
// Set the drawing color. This can be called from a UI button.
public void SetDrawColor(Color newColor)
{
drawColor = newColor;
}
// Set the brush size. This can be called from a UI slider.
public void SetBrushSize(float newSize)
{
brushSize = newSize;
}
// Update is called once per frame
void Update()
{
// Detect Mouse Input
if (Input.GetMouseButtonDown(0)) // Left Mouse Button Down
{
_isDrawing = true;
_lastMousePos = GetMousePositionInTexture();
}
else if (Input.GetMouseButtonUp(0)) // Left Mouse Button Up
{
_isDrawing = false;
}
// Drawing Logic
if (_isDrawing)
{
Vector2 currentMousePos = GetMousePositionInTexture();
DrawLine(_lastMousePos, currentMousePos);
_lastMousePos = currentMousePos;
}
}
// Helper function to get mouse position relative to the texture
private Vector2 GetMousePositionInTexture()
{
// Get the RectTransform of the RawImage
RectTransform rectTransform = drawingSurface.rectTransform;
// Convert the mouse position from screen space to local space within the RectTransform
Vector2 localMousePos;
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, Input.mousePosition, null, out localMousePos);
// Normalize the local position to be between 0 and 1
localMousePos.x = Mathf.Clamp01((localMousePos.x + rectTransform.rect.width * 0.5f) / rectTransform.rect.width);
localMousePos.y = Mathf.Clamp01((localMousePos.y + rectTransform.rect.height * 0.5f) / rectTransform.rect.height);
// Scale the normalized position to the texture size
localMousePos.x *= drawingTexture.width;
localMousePos.y *= drawingTexture.height;
return localMousePos;
}
// Draw a line between two points on the texture
private void DrawLine(Vector2 start, Vector2 end)
{
//Use Bresenham's line algorithm for more efficient line drawing
//Also allows for lines to be drawn even if the mouse moves very fast
int x0 = (int)start.x;
int y0 = (int)start.y;
int x1 = (int)end.x;
int y1 = (int)end.y;
int dx = Mathf.Abs(x1 - x0);
int dy = Mathf.Abs(y1 - y0);
int sx = (x0 < x1) ? 1 : -1;
int sy = (y0 < y1) ? 1 : -1;
int err = dx - dy;
while (true)
{
DrawCircle(x0, y0, (int)(brushSize / 2f)); // Draw a circle at each point to simulate a brush
if (x0 == x1 && y0 == y1) break;
int e2 = 2 * err;
if (e2 > -dy)
{
err -= dy;
x0 += sx;
}
if (e2 < dx)
{
err += dx;
y0 += sy;
}
}
drawingTexture.Apply(); // Apply the changes! Do this only *after* drawing the line.
}
// Draw a filled circle at a specified position
private void DrawCircle(int x, int y, int radius)
{
for (int i = -radius; i <= radius; i++)
{
for (int j = -radius; j <= radius; j++)
{
if (i * i + j * j <= radius * radius)
{
int pixelX = x + i;
int pixelY = y + j;
// Check if pixel is within texture bounds
if (pixelX >= 0 && pixelX < drawingTexture.width && pixelY >= 0 && pixelY < drawingTexture.height)
{
drawingTexture.SetPixel(pixelX, pixelY, drawColor);
}
}
}
}
}
}
```
Key improvements and explanations:
* **Clear and Concise Comments:** Each major section and line of code has comments explaining its purpose.
* **Transparency:** The `ClearDrawingSurface` method is now correct and sets the texture to fully *transparent* black (`Color.clear`). This is essential for drawing on top of a clean surface. The color should always be transparent otherwise drawing will leave a solid color on the screen and no drawing will be possible.
* **Texture Initialization:** The `drawingTexture` is initialized with the correct dimensions based on the `RawImage`.
* **Correct Mouse Position Conversion:** The `GetMousePositionInTexture` function accurately converts the mouse position from screen space to texture space. It handles the `RectTransform` properly. It also uses `Mathf.Clamp01` to ensure the coordinates are always within the 0-1 range.
* **Bresenham's Line Algorithm:** Implemented Bresenham's line algorithm in the `DrawLine` function for more efficient line drawing. This is significantly faster than naive line drawing and ensures smoother lines. Importantly, Bresenham's algorithm allows for lines to be drawn between two points regardless of the speed of the mouse, this avoids choppy and disconnected lines.
* **Brush Size:** Added a `brushSize` variable to control the thickness of the drawing line, accessible in the Unity Editor. It's used in `DrawCircle` to determine the radius of the drawn circle.
* **DrawCircle Function:** Now implemented and called within `DrawLine` to create a brush-like effect.
* **Bounds Checking:** `DrawCircle` includes bounds checking to prevent errors when drawing near the edges of the texture. This prevents out-of-bounds pixel access.
* **`Texture2D.Apply()`:** Crucially, `drawingTexture.Apply()` is now called *only once* after the entire line is drawn. This significantly improves performance. Calling `Apply()` after *every* pixel change is extremely slow.
* **UI Interaction:** Added `SetDrawColor` and `SetBrushSize` methods. These can be connected to UI buttons (for color) and sliders (for brush size) in the Unity editor.
* **Error Handling (Bounds Checking):** Included `if (pixelX >= 0 && pixelX < drawingTexture.width && pixelY >= 0 && pixelY < drawingTexture.height)` to prevent `IndexOutOfRangeException` errors when drawing close to the edges of the texture.
* **Clear Function:** Added `ClearDrawingSurface` function, which can be connected to a UI button for clearing the screen.
How to use:
1. **Create a new Unity project.**
2. **Create a UI Canvas:** `GameObject -> UI -> Canvas`.
3. **Create a RawImage:** `GameObject -> UI -> Raw Image`. Name it something like "DrawingSurface".
4. **Adjust RawImage:** In the Inspector for the RawImage:
* Set the width and height of the RectTransform to the desired drawing area size (e.g., 512x512).
* Set the color to white (or any color).
5. **Create a C# script:** Create a new C# script in your project (e.g., "DrawingController.cs") and paste the code above into it.
6. **Create a GameObject:** Create an empty GameObject in your scene (e.g., "DrawingManager").
7. **Attach the Script:** Attach the "DrawingController.cs" script to the "DrawingManager" GameObject.
8. **Assign the RawImage:** In the Inspector for the "DrawingManager" GameObject, drag the "DrawingSurface" RawImage from the Hierarchy to the `Drawing Surface` field of the `DrawingController` script.
9. **Add UI Buttons (Optional):**
* Create a UI Button (`GameObject -> UI -> Button`) for clearing the screen.
* Create a UI Button for setting a color.
* In the Button's `OnClick()` event, drag the "DrawingManager" GameObject into the object field, and then select `DrawingController -> ClearDrawingSurface()` for the clear button. For the color button select `DrawingController -> SetDrawColor(Color)`. Unity will then give you the option to select a color to pass.
* Create a UI Slider for brush size.
* In the Slider's `On Value Changed (Single)` event, drag the "DrawingManager" GameObject into the object field, and then select `DrawingController -> SetBrushSize(Single)` .
10. **Run the scene.** You should now be able to draw on the RawImage by clicking and dragging the mouse.
This improved version addresses the crucial issues of performance, transparency, and correct mouse position conversion, making it a functional and useful digital drawing tool. It also provides UI integration for controlling color, brush size, and clearing the screen.
👁️ Viewed: 7
Comments