本文基于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();
}
}
}