本文基于Unity3D 免费插件 FreeDraw 增改

1.主要是两个list ,一个记录已经画下的,一个记录要回退的 

直接复制 ,按鼠标键,A D 键使用。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;

namespace FreeDraw
{
    [RequireComponent(typeof(SpriteRenderer))]
    [RequireComponent(typeof(Collider2D))]  // REQUIRES A COLLIDER2D to function
    // 1. Attach this to a read/write enabled sprite image
    // 2. Set the drawing_layers  to use in the raycast
    // 3. Attach a 2D collider (like a Box Collider 2D) to this sprite
    // 4. Hold down left mouse to draw on this texture!
    /* 1。将此附加到启用读/写的sprite图像
    /  2。设置要在光线投射中使用的绘图层
    / / 3。将一个二维对撞机(就像一个盒子对撞机2d)附加到这个精灵上
    / / 4。按住鼠标左键绘制此纹理!
    */
    public class Drawable : MonoBehaviour
    {
        // 画笔颜色
        public static Color Pen_Colour = Color.red;     // 更改这些设置以更改默认图形设置
        // 画笔宽度(实际上是半径,以像素为单位)
        public static int Pen_Width = 3;


        public delegate void Brush_Function(Vector2 world_position);
        //这是当左键单击发生时调用的函数。
        //传入您自己的自定义画笔以更改画笔类型
        //在唤醒方法中设置默认函数
        public Brush_Function current_brush;

        public LayerMask Drawing_Layers;

        // 画布每次重置为 0000的颜色
        public Color Reset_Colour = new Color(0, 0, 0, 0);  // 默认情况下,将画布重置为透明

        // 用于引用此特定文件,而不将所有方法设置为静态
        public static Drawable drawable;
        // 必须在Unity的文件编辑器中设置了读/写功能
        Sprite drawable_sprite;
        Texture2D drawable_texture;

        Vector2 previous_drag_position;
        Color[] clean_colours_array;    //全部情况 
        Color transparent;
        Color32[] cur_colors;           //当前需要改变颜色的位置
        Color32[] start_colors;         //初始空白画面,用作清空处理使用
        bool mouse_was_previously_held_down = false;
        bool no_drawing_on_current_drag = false;


        void Awake()
        {
            drawable = this;
            // 此处设置默认画笔
            current_brush = PenBrush;

            drawable_sprite = this.GetComponent<SpriteRenderer>().sprite;
            drawable_texture = drawable_sprite.texture;

            // 初始化要使用的干净像素
            clean_colours_array = new Color[(int)drawable_sprite.rect.width * (int)drawable_sprite.rect.height];
            for (int x = 0; x < clean_colours_array.Length; x++)
                clean_colours_array[x] = Reset_Colour;

            //  每次开启都重置画布图像
                ResetCanvas();

            start_colors = drawable_texture.GetPixels32();
        }


        // 默认画笔类型。有宽度和颜色。
        //以世界坐标传递一个点
        //将世界周围的像素点更改为静态钢笔颜色
        public void PenBrush(Vector2 world_point)
        {
            //获得世界坐标对应的像素
            Vector2 pixel_pos = WorldToPixelCoordinates(world_point);

            cur_colors = drawable_texture.GetPixels32(); //获得 所有值

            if (previous_drag_position == Vector2.zero)
            {
                // 第一次点击画线,起始点在鼠标
                MarkPixelsToColour(pixel_pos, Pen_Width, Pen_Colour);
            }
            else
            {
                // 第二帧开始,从起始点到鼠标移动的位置
                ColourBetween(previous_drag_position, pixel_pos, Pen_Width, Pen_Colour);
            }
            ApplyMarkedPixelChanges();

            previous_drag_position = pixel_pos;
        }


        // UI使用的辅助方法来设置用户想要的画笔
        //为实现的任何新画笔创建一个新的画笔
        public void SetPenBrush()
        {
            // PenBrush是要设置为当前画笔的方法的名称
            current_brush = PenBrush;
        }

        private bool isDraw = false;


        //update

       
        //检测用户单击的时间,然后调用相应的函数
        void Update()
        {
            // 用户是否按住鼠标左键?
            bool mouse_held_down = Input.GetMouseButton(0);
            if (mouse_held_down && !no_drawing_on_current_drag)
            {
                // 将鼠标坐标转换为世界坐标
                Vector2 mouse_world_position = Camera.main.ScreenToWorldPoint(Input.mousePosition);

                // 检查当前鼠标位置是否与我们的图像重叠
                Collider2D hit = Physics2D.OverlapPoint(mouse_world_position, Drawing_Layers.value);


                if (hit != null && hit.transform != null)
                {
                    //我们正在使用我们正在绘制的纹理! 
                    //使用当前画笔的任何功能
                    
                    isDraw = true;
                    current_brush(mouse_world_position);
                   
                }

                else
                {
                    // 没有超过边框 纹理
                    previous_drag_position = Vector2.zero;
                    if (!mouse_was_previously_held_down)
                    {
                        // 这是一个新的拖动,用户左键单击画布
                        //确保在开始新拖动之前不进行绘制
                        no_drawing_on_current_drag = true;
                    }
                }
            }
            // 如果鼠标已释放
            else if (!mouse_held_down)
            {
                previous_drag_position = Vector2.zero;
                no_drawing_on_current_drag = false;
            }
            mouse_was_previously_held_down = mouse_held_down;


            // 按鼠标左键 画线并记录 
            if (Input.GetMouseButtonUp(0))
            {
                if (isDraw)
                {
                    //鼠标释放的时候记录数据
                    Record();
                    isDraw = false;
                }
                
            }

            //后腿
            if (Input.GetKeyDown(KeyCode.A))
            {
                RemoveLastColor();
            }

            //前进
            if (Input.GetKeyDown(KeyCode.D))
            {
                RestoreBackColor();
            }
        }

        public void Record()
        {
            last = drawable_texture.GetPixels32();    //记录当前画面线在哪
            recordColors.Add(last);
            RestoreColors.Clear();
        }
        private Color32[] last = new Color32[1];   //最后一个  用作撤销使用
        List<Color32[]> recordColors = new List<Color32[]>(1);
       // Color32[] backColors = new Color32[1] ;        //用于撤销之前再还原

        private List<Color32[]> RestoreColors = new List<Color32[]>(1); //用于撤销之前再还原

        /// <summary>
        /// 撤销最后一个
        /// </summary>
        public void RemoveLastColor()
        {
            if (recordColors.Count == 0) return;
            Debug.Log("撤销的数量" + recordColors.Count);
            if (isOnClane == false)
                RestoreColors.Add(drawable_texture.GetPixels32()); // 再删除之前添加到还原记录 0 -1 -2
            else
                isOnClane = false;

            if (recordColors.Count > 1)      // 默认里面有一个空白的(start 执行添加) 从第二个开始才是记录第一笔
            {
                int index = recordColors.Count - 2;

                Color32[] color = recordColors[index];
                drawable_texture.SetPixels32(color);
                drawable_texture.Apply();
                //删除最后一个画线
                // last = recordColors[recordColors.Count - 1];    
                last = drawable_texture.GetPixels32();
            }
            else if (recordColors.Count == 1)
            {
                drawable_texture.SetPixels32(start_colors);
                drawable_texture.Apply();
            }

            recordColors.RemoveAt(recordColors.Count - 1);
        }

        /// <summary>
        /// 还原 
        /// </summary>
        public void RestoreBackColor()
        {

            if (RestoreColors.Count <= 0) return;
            Debug.Log("还原的:" + RestoreColors.Count);
            //得到当前画面的画线
            int index = RestoreColors.Count - 1;
            Color32[] color = RestoreColors[index];  //应用复原 0 - 1 -2
            drawable_texture.SetPixels32(color);
            drawable_texture.Apply();


            last = drawable_texture.GetPixels32();   //在还原之前,得到记录的值
            //复原后,需要把记录的数据添加一次 ,添加到复原的画面
            recordColors.Add(last);
            RestoreColors.RemoveAt(RestoreColors.Count - 1);

            Debug.Log("还原后再添加到撤销:" + recordColors.Count);
            //last = RestoreColors[recordColors.Count - 1];
        }


        private bool isOnClane = false;  //是否按下了

        /// <summary>
        /// 清空当面面板的记录数据
        /// </summary>
        public void Clane_LastColor()
        {
            drawable_texture.SetPixels32(start_colors);
            drawable_texture.Apply();
            if (!recordColors.Contains(start_colors))
            {
                recordColors.Add(start_colors);
            }
            Debug.Log("清除之后" + recordColors.Count);
            isOnClane = true;
            RestoreColors.Clear();
        }



        // 将像素的颜色设置为从起点到终点的直线,以确保中间的所有内容都是彩色的。
        public void ColourBetween(Vector2 start_point, Vector2 end_point, int width, Color color)
        {
            // 从开始到结束的距离
            float distance = Vector2.Distance(start_point, end_point);
            Vector2 direction = (start_point - end_point).normalized;

            Vector2 cur_position = start_point;

            // 根据自上次更新以来经过的时间量,计算应该在起始点和结束点之间插入多少次
            float lerp_steps = 1 / distance;

            for (float lerp = 0; lerp <= 1; lerp += lerp_steps)
            {
                cur_position = Vector2.Lerp(start_point, end_point, lerp);
                MarkPixelsToColour(cur_position, width, color);
            }
        }


        /// <summary>
        /// 将像素标记为彩色
        /// </summary>
        /// <param name="center_pixel">开始点到结束点</param>
        /// <param name="pen_thickness">画笔的宽度</param>
        /// <param name="color_of_pen">画笔的颜色</param>
        public void MarkPixelsToColour(Vector2 center_pixel, int pen_thickness, Color color_of_pen)
        {
            // 计算出我们需要在每个方向(x和y)上色多少像素。
            int center_x = (int)center_pixel.x;
            int center_y = (int)center_pixel.y;
            //int extra_radius = Mathf.Min(0, pen_thickness - 2);

            for (int x = center_x - pen_thickness; x <= center_x + pen_thickness; x++)
            {
                // 检查X是否环绕图像,这样我们就不会在图像的另一侧绘制像素。
                if (x >= (int)drawable_sprite.rect.width || x < 0)
                    continue;

                for (int y = center_y - pen_thickness; y <= center_y + pen_thickness; y++)
                {
                    if(y!=1800) //如果超过了1080 就不用修改了
                        MarkPixelToChange(x, y, color_of_pen);
                }
            }
        }



        /// <summary>
        /// 获得标记要更改的像素
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="color"></param>
        public void MarkPixelToChange(int x, int y, Color color)
        {
            // 需要将x和y坐标转换为数组的平面坐标
            int array_pos = y * (int)drawable_sprite.rect.width + x;

            // 检查这个位置是否有效
            if (array_pos > cur_colors.Length || array_pos < 0)
                return;

            cur_colors[array_pos] = color;
        }

         

        /// <summary>
        /// 应用标记的像素更改
        /// </summary>
        public void ApplyMarkedPixelChanges()
        {
            drawable_texture.SetPixels32(cur_colors);
            drawable_texture.Apply();
        }

        /// <summary>
        /// 世界到像素坐标
        /// </summary>
        /// <param name="world_position"></param>
        /// <returns></returns>
        public Vector2 WorldToPixelCoordinates(Vector2 world_position)
        {
            // 将坐标更改为此图像的本地坐标
            Vector3 local_pos = transform.InverseTransformPoint(world_position);

            // 将这些更改为像素坐标
            float pixelWidth = drawable_sprite.rect.width;
            float pixelHeight = drawable_sprite.rect.height;
            float unitsToPixels = pixelWidth / drawable_sprite.bounds.size.x * transform.localScale.x;

            //需要把我们的坐标居中
            float centered_x = local_pos.x * unitsToPixels + pixelWidth / 2;
            float centered_y = local_pos.y * unitsToPixels + pixelHeight / 2;

            // 将当前鼠标位置舍入到最近的像素
            Vector2 pixel_pos = new Vector2(Mathf.RoundToInt(centered_x), Mathf.RoundToInt(centered_y));

            return pixel_pos;
        }


        /// <summary>
        /// 重置功能  将每个像素更改为重置颜色
        /// </summary>
        public void ResetCanvas()
        {
            drawable_texture.SetPixels(clean_colours_array);
            drawable_texture.Apply();
            RestoreColors.Clear();
            recordColors.Clear();
        }

        // 直接为像素上色。此方法比使用markpixelstocolour慢,然后使用applymarkedpixelchanges
        // setpixels32比setpixel快得多
        // 基于中心厚度(笔半径),中心像素和中心像素周围的像素数目
        public void ColourPixels(Vector2 center_pixel, int pen_thickness, Color color_of_pen)
        {
            // 计算出我们需要在每个方向(x和y)上色多少像素。
            int center_x = (int)center_pixel.x;
            int center_y = (int)center_pixel.y;
            //int extra_radius = Mathf.Min(0, pen_thickness - 2);

            for (int x = center_x - pen_thickness; x <= center_x + pen_thickness; x++)
            {
                for (int y = center_y - pen_thickness; y <= center_y + pen_thickness; y++)
                {
                    drawable_texture.SetPixel(x, y, color_of_pen);
                }
            }

            drawable_texture.Apply();
        }

        private void OnDestroy()
        {
            ResetCanvas();
        }


    }
}