You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

清洁游戏开发:鼠标快速拖动绘制直线断线及SetPixel优化求助

清洁类游戏快速拖动鼠标绘制线条断裂问题解决

我正在开发一款清洁类游戏,遇到这样的问题:快速拖动鼠标绘制直线(清洁区域)时线条会出现断裂,慢速拖动时绘制的直线显示正常。

快速拖动线条断裂效果

问题根源

你的代码里有个关键逻辑:计算当前鼠标位置与上一次绘制位置的曼哈顿距离,若小于7则直接跳过绘制。快速拖动时,鼠标每帧移动的跨度可能超过7,但中间那些距离不够的帧会被跳过;就算触发绘制,也只在当前点刷一次刷子,两点之间的空白区域完全没填充,最终导致线条断裂。

解决方案

核心思路是填充鼠标移动路径上的所有像素点,而不是只处理离散的帧位置。具体修改如下:

  1. 移除原代码中距离判断的跳过逻辑
  2. 使用直线算法(比如Bresenham)计算两次鼠标位置之间的所有像素点,逐个用刷子覆盖
  3. 封装绘制逻辑,避免代码冗余

修改后的完整代码

private void Update()
{
    if (Input.GetMouseButton(0))
    {
        if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out RaycastHit hit))
        {
            Vector2 textureCoord = hit.textureCoord;
            int pixelX = (int)(textureCoord.x * _templateDirtMask.width);
            int pixelY = (int)(textureCoord.y * _templateDirtMask.height);
            Vector2Int currentPaintPos = new Vector2Int(pixelX, pixelY);

            // 第一次绘制初始化上一次位置
            if (lastPaintPixelPosition == Vector2Int.zero)
            {
                lastPaintPixelPosition = currentPaintPos;
                PaintAtPosition(currentPaintPos);
                return;
            }

            // 绘制上一次位置到当前位置的连续线条
            DrawLineBetween(lastPaintPixelPosition, currentPaintPos);
            lastPaintPixelPosition = currentPaintPos;
        }
    }
}

// 在指定像素位置绘制刷子
private void PaintAtPosition(Vector2Int position)
{
    int pixelXOffset = position.x - (_brush.width / 2);
    int pixelYOffset = position.y - (_brush.height / 2);

    for (int x = 0; x < _brush.width; x++)
    {
        for (int y = 0; y < _brush.height; y++)
        {
            int targetX = pixelXOffset + x;
            int targetY = pixelYOffset + y;
            // 边界检查,防止访问纹理外的像素
            if (targetX < 0 || targetX >= _templateDirtMask.width || targetY < 0 || targetY >= _templateDirtMask.height)
                continue;

            Color pixelDirt = _brush.GetPixel(x, y);
            Color pixelDirtMask = _templateDirtMask.GetPixel(targetX, targetY);

            float removedAmount = pixelDirtMask.g - (pixelDirtMask.g * pixelDirt.g);
            dirtAmount -= removedAmount;

            _templateDirtMask.SetPixel(targetX, targetY, new Color(0, pixelDirtMask.g * pixelDirt.g, 0));
        }
    }
    _templateDirtMask.Apply();
}

// 使用Bresenham算法绘制两点间的连续线条
private void DrawLineBetween(Vector2Int start, Vector2Int end)
{
    int x0 = start.x;
    int y0 = start.y;
    int x1 = end.x;
    int y1 = 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)
    {
        PaintAtPosition(new Vector2Int(x0, y0));

        if (x0 == x1 && y0 == y1)
            break;

        int e2 = 2 * err;
        if (e2 > -dy)
        {
            err -= dy;
            x0 += sx;
        }
        if (e2 < dx)
        {
            err += dx;
            y0 += sy;
        }
    }
}

关键修改说明

  • 删掉了原代码中paintPixelDistance < maxPaintDistance的跳过逻辑,不会再漏掉任何需要绘制的帧
  • 新增PaintAtPosition方法封装刷子绘制逻辑,代码更简洁易维护
  • 用Bresenham直线算法遍历鼠标移动路径上的所有像素点,确保快速拖动时线条完全连续
  • 添加纹理边界检查,避免因坐标越界导致的报错

内容的提问来源于stack exchange,提问作者Newboy11

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 05:55:18