涂涂乐绘图板算是我独立完成的第二个项目,从无到有算起来大概花了一个多月的时间。其中碰到了许许多多的坑,同时也补充了各种各样的知识,总之收获挺大。接下来大概分成五个部分详细介绍各部分功能的实现方法。

(一)鼠标拖动绘图的实现

</pre><span style="font-size:24px;"></span><pre name="code" class="html" style="font-size:24px;">function OnGUI () {GUI.skin = gskin;var ulx =ul.transform.position.x;var uly = Screen.height -(ul.transform.position.y);GUI.DrawTexture (Rect (ulx,uly,baseTexMain[0].width,baseTexMain[0].height),baseTexMain[4]);
}

GUI.DrawTexture的功能在于能够在屏幕上绘制texture的纹理,即baseTexMain[4]。这里有一个包含五张透明Texture2d的数组,这么做是为了实现之后的撤销功能。

创建一个空物体并拖动到动物背景图的左上角,以此作为透明图的左上角,我们是在这张透明图上作画。这里要说的是,由于不同设备的分辨率适配问题,我选取的透明图长宽是大于绘图区域的。因此我们在确定了左上角的同时仍然需要确定绘图区域的右下角并限制绘图范围。

<span style="white-space:pre">  var mouse : Vector2 = Input.mousePosition; </span>
</pre><pre name="code" class="csharp"><span style="white-space:pre">  var imgRectReal : Rect = Rect (ulx,uly,rux-ulx,ruy-uly); </span>//UI上的绘图区域
<span style="white-space:pre">  </span>if(mouse.x>ru.transform.position.x)mouse.x=ru.transform.position.x;if(mouse.y>(Screen.height-ru.transform.position.y))mouse.y=Screen.height-ru.transform.position.y;//当鼠标位置超出绘图区域时强行限定

防止绘图误操作到其它区域的按钮:

 if (Input.GetKeyDown ("mouse 0")) //鼠标按下时
<pre name="code" class="csharp" style="color: rgb(90, 90, 90); line-height: 29.7px;"> if (Input.GetKey ("mouse 0")) //鼠标拖动时
 if (Input.GetKeyUp ("mouse 0")) //鼠标弹起时
<span style="white-space:pre">  </span>{if(imgRectReal.Contains(mouse))}//如果在绘图区域则执行绘图操作
</pre><pre name="code" class="csharp">function Brush (p1 : Vector2,p2 : Vector2)
{
    brush.width = ButtonControl.brushwidth;var col=ButtonControl.brushcolor;Drawing.PaintLine (p1,p2,brush.width,col,brush.hardness,baseTexMain[4]);baseTexMain[4].Apply ();
}

调用Drawing脚本里的PaintLine方法:(其中,p1和p2分别是鼠标按下和弹起时的坐标位置,brush.width是画笔宽度,col是画笔颜色,hardness是锯齿效果,值越高锯齿效果越明显,以及透明图baseTexMain[4])

static function PaintLine (from : Vector2,to : Vector2,rad : float,col : Color,hardness : float,tex : Texture2D) {width = rad*2;if(from==to){return;//to.x=to.x+0.01;}//Debug.Log("Mouse Drag!!!");//Debug.Log("from"+from);//Debug.Log("to"+to);//Debug.Log("rad"+rad);extent = rad;stY = Mathf.Clamp (Mathf.Min (from.y,to.y)-extent,0,tex.height);stX =  Mathf.Clamp (Mathf.Min (from.x,to.x)-extent,0,tex.width);endY = Mathf.Clamp (Mathf.Max (from.y,to.y)+extent,0,tex.height);endX = Mathf.Clamp (Mathf.Max (from.x,to.x)+extent,0,tex.width);lengthX = endX-stX;lengthY = endY-stY;sqrRad = rad*rad;sqrRad2 = (rad+1)*(rad+1);var pixels : Color[] = tex.GetPixels (stX,stY,lengthX,lengthY,0);start = Vector2 (stX,stY);//Debug.Log (widthX + "   "+ widthY + "   "+ widthX*widthY);for (y=0;y<lengthY;y++) {for (x=0;x<lengthX;x++) {p = Vector2 (x,y) + start;center = p + Vector2(0.5,0.5);var dist : float = (center-NearestPointStrict(from,to,center)).sqrMagnitude;if (dist>sqrRad2) {continue;}dist = GaussFalloff (Mathf.Sqrt(dist),rad) * 500;//Debug.Log("hardness:"+hardness);//dist = (samples[i]-pos).sqrMagnitude;if (dist>0) {c =Color.Lerp (pixels[y*lengthX+x],col,dist);} else {c =pixels[y*lengthX+x];}pixels[y*lengthX+x]=c;}}tex.SetPixels (start.x,start.y,lengthX,lengthY,pixels,0);return tex;}

以上是绘图的核心代码,主要运用两个函数Texture2D.GetPixels和Texture2D.SetPixels。这里根据画笔颜色和宽度对每帧调用时画笔的起始位置间画直线(所以当我们拖动鼠标快速画圆形时可以清楚地看到是由一段段线段组成的,不过对于实际功能没有影响)。

(二)画笔撤销功能的实现和优化

撤销功能的基本思路是:创建一个Texture2D数组,包含五张透明图,每次鼠标在绘图区域抬起时即触发数组内的压入操作。

<span style="white-space:pre">  </span>for(var i:int=3;i>0;i--){var pixelss:Color[]= baseTexMain[i-1].GetPixels(0,0,baseTexMain[0].width,baseTexMain[0].height,0);baseTexMain[i].SetPixels(0, 0, baseTexMain[0].width, baseTexMain[0].height, pixelss, 0);baseTexMain[i].Apply();}pixelss= baseTexMain[3].GetPixels(0,0,baseTexMain[0].width,baseTexMain[0].height,0);baseTexMain[4].SetPixels(0, 0, baseTexMain[0].width, baseTexMain[0].height, pixelss, 0);baseTexMain[4].Apply();

以上是画笔的撤销功能,这里的功能是共可撤销三次,在每次完整的循环里从baseTexMian[3]开始,baseTexMain[3]使用baseTexMain[2]覆盖,以此类推3变成了2,2变成了1,1变成了0。同理我们在绘图的时候每次都执行撤销的逆操作。

绘图功能的的优化:以上我们实现绘图和撤销是完整的Get和Set上下两张透明图的像素,在实际完成后,发现性能很差,每次画笔抬起都会有明显的卡顿,优化方案如下。

<span style="white-space:pre">  </span>var minx=mouse.x-ulx;var miny=ruy-mouse.y;var maxx=mouse.x-ulx;var maxy=ruy-mouse.y;if(onlyonce == 1)
<span style="white-space:pre"> </span>{GetRect[4,0]=minx;GetRect[4,1]=miny;GetRect[4,2]=maxx;GetRect[4,3]=maxy;onlyonce--;
<span style="white-space:pre"> </span>}//将鼠标笔画的最左最右最上最下的四个点坐标写入二维数组GetRect;if(minx<=GetRect[4,0])GetRect[4,0]=minx;if(miny<=GetRect[4,1])GetRect[4,1]=miny;if(maxx>=GetRect[4,2])GetRect[4,2]=maxx;if(maxy>=GetRect[4,3])GetRect[4,3]=maxy;

首先,上面那段代码是鼠标拖动时进行的操作,即,在每一次的鼠标拖动过程中,我们都将记录鼠标拖动到的最左最右的X坐标值和最上最下的Y坐标值,并且在鼠标抬起的时候操作它们。

    //当鼠标在绘图区域内弹起的时候,数组内的texture往下压入一层;if (Input.GetKeyUp ("mouse 0") )
<span style="white-space:pre">  </span> {if (imgRectReal.Contains (mouse))
<span style="white-space:pre"> </span>{var y:int =0;var x:int =0;for(var iiround:int=0;iiround<4;iiround++)
<span style="white-space:pre">     </span>{for(var loopfour:int=0;loopfour<4;loopfour++)
<span style="white-space:pre">     </span>{GetRect[iiround,loopfour]=GetRect[iiround+1,loopfour];
<span style="white-space:pre">     </span>}
<span style="white-space:pre">     </span>}for(var iii:int =0;iii<4;iii++)
<span style="white-space:pre">     </span>{if(iii==3)
<span style="white-space:pre">         </span>{var xx:int = parseInt(GetRect[iii,0])-20;var yy:int = parseInt(GetRect[iii,1])+baseTexMain[0].height-ruy+uly-20;var xxwidth = parseInt(GetRect[iii,2])-parseInt(GetRect[iii,0])+40;var yyheight = parseInt(GetRect[iii,3])-parseInt(GetRect[iii,1])+40;if(xx<0) xx=0;if(yy<0) yy=0;                  var pixelss3:Color[]= baseTexMain[iii+1].GetPixels(xx,yy,xxwidth,yyheight,0);baseTexMain[iii].SetPixels(xx,yy,xxwidth,yyheight,pixelss3, 0);baseTexMain[iii].Apply();
<span style="white-space:pre">         </span>}if(iii<3)
<span style="white-space:pre">         </span>{var xxx:int = parseInt(GetRect[iii,0])-20;var yyy:int = parseInt(GetRect[iii,1])+baseTexMain[0].height-ruy+uly-20;var xxxwidth = parseInt(GetRect[iii,2])-parseInt(GetRect[iii,0])+40;var yyyheight = parseInt(GetRect[iii,3])-parseInt(GetRect[iii ,1])+40;if(xxx<0) xxx=0;if(yyy<0) yyy=0;var pixelss:Color[]= baseTexMain[iii+1].GetPixels(xxx,yyy,xxxwidth,yyyheight,0);baseTexMain[iii].SetPixels(xxx,yyy,xxxwidth,yyyheight,pixelss, 0);baseTexMain[iii].Apply();
<span style="white-space:pre">         </span>}
<span style="white-space:pre">     </span>}
<span style="white-space:pre"> </span>}<span style="white-space:pre">       </span>Debug.Log("GetKeyUp");dragStart=Vector2.zero;dragEnd=Vector2.zero;
<span style="white-space:pre"> </span>}preDrag = dragEnd;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}

首先在鼠标抬起时,我们都将在拖动过程中得到的四个值压入数组的下一层,我们用xx和yy代表我们要取的矩形的左上顶点,xxwidth和yyheight代表矩形宽高,我们不需要对整张透明图Get和Set像素,我们只需对该次鼠标拖动的矩形区域Get和Set像素就能够替换掉其上或其下的透明图。用这种方法在操作像素的面积上比之前者大大缩小了。
(三)橡皮擦和删除功能

橡皮擦功能与画笔相同,只是将画笔颜色置为透明。

而删除操作,仍然用Get和Set像素的操作,用完全透明图重置数组里的所有透明图。

涂涂乐的详细实现之一--画笔核心功能相关推荐

  1. IDA的详细使用指南以及核心功能讲解

    IDA的详细使用指南 1.下载安装 首先打开网页搜索吾爱破解,进入吾爱破解官网,找到逆向资源区栏目 在该栏目中找到IDA软件资源 点击进入,获取网盘地址以及提取码 进入网盘进行下载 下载好之后打开压缩 ...

  2. 一文详细讲解API网关核心功能和API管理扩展

    本文将详细讲解API网关的基础概念,使用场景和核心功能,以及基于API网关核心引擎做的API全生命周期管理功能扩展等,最后介绍当前主流的开源API网关引擎. API网关概述 在微服务架构体系里面,我们 ...

  3. 涂涂乐的详细实现之三--文件IO操作

    这篇内容旨在详细介绍我在完成涂涂乐这个项目中用到的关于文件存储命名等的思路和操作. (一)截图操作 截图的基本思路:在绘图区域确定一个矩形,创建一个Texture2D获取这部分像素并生成图片存储在文件 ...

  4. 涂涂乐的详细实现之四--unity3d调用EmguCV实现图片识别

    涂涂乐严格来说有两个版本,一种是前面详细介绍过的,鼠标控制画图截图发送给服务端实现模型上色:另一种是通过实物图彩笔绘图之后通过扫描仪生成图片发送给服务端来实现模型上色. 动物模型有多种,贴图有多种,在 ...

  5. 手把手教你做个AR涂涂乐

    前段时间公司有一个AR涂涂乐的项目,虽然之前接触过AR也写过小Demo,但是没有完整开发过AR项目.不过经过1个多星期的学习,现在已经把项目相关的技术都学会了,在此向互联网上那些乐于分享的程序员前辈们 ...

  6. ​手把手教你做个AR涂涂乐 ​

    前段时间公司有一个AR涂涂乐的项目,虽然之前接触过AR也写过小Demo,但是没有完整开发过AR项目.不过经过1个多星期的学习,现在已经把项目相关的技术都学会了,在此向互联网上那些乐于分享的程序员前辈们 ...

  7. Unity_6 涂涂乐案例

    前言 最近学习的东西都是一些零零散散的,看了一下笔记,了解了一个地图插件Minimap之类的:还有一些C#语法:委托.Lambda表达式.另外还有有关vuforia,EasyAR Sense,AVPr ...

  8. EasyAR涂涂乐代码分析

    来说一下对EasyAR sdk中自带的unity Samples中的Coloring3D这个项目的理解(例子程序可以去官网下载 最后会列出所有用到网站的网址). 这个项目的效果就是我们常见的" ...

  9. (示例3)涂涂乐开发教程

    涂涂乐开发教程 本文将向您介绍如何使用 HiAR SDK 创建一个简单的涂涂乐应用. Step 1.基础开发及设置 请先按照"创建 Hello World"中的 Step1-Ste ...

最新文章

  1. 1-jQuery - AJAX load() 方法【基础篇】
  2. 24 年前的 IE 仍能在 Win10中运行,这无敌兼容性与你的代码比比?
  3. mysql 存过 if_mysql中 储存过程 if exists 该如何写呀
  4. 微软sccm服务器,System Center 2012R2系列之SCCM部署
  5. Cachefiled
  6. 基于ABP落地领域驱动设计-02.聚合和聚合根的最佳实践和原则
  7. [Node.js] 模块化 -- 中间件和跨域
  8. Netapp日志出现auth.dc.trace.DCConnection.errorMsg:error报错
  9. 仿58网,赶集网,百姓网swfupload图片上传效果(asp.net 2.0)
  10. xml字符串转xml对象,xml对象转json对象
  11. [置顶]常用存储过程集锦
  12. java.lang.ClassNotFoundException: org.springframework.web.util.WebAppRootListener
  13. 由乱序播放说开了去-数组的打乱算法Fisher–Yates Shuffle
  14. JM8.6的解码端去方块滤波代码详述
  15. QUT期末考试《电子商务概论》思维导图
  16. 网易云服务器怎么上传文件,怎么把本地歌曲上传到网易云上
  17. FFmpeg 加水印 加马赛克
  18. 欢迎加入可一科技,见证区块链技术的力量
  19. html编辑个人信息页面,编辑个人信息.html
  20. 四年,工作、辞职、学习

热门文章

  1. 存储系统性能 - 带宽计算
  2. hxxp://www.hao923.com.cn/劫持浏览器
  3. isis宣告网络_ISIS是一个分级的链接状态路由协议
  4. 非线性可视化(3)混沌系统
  5. 百度搜索结果页面的参数 提示词输入搜索框方式(rsv_sug5)
  6. ModBus RTU-上位机与PLC通信
  7. C#上位机 西门子PLC通信 S7NET协议
  8. html5 自动矢量化,ArcScan自动矢量化
  9. Mathmatica多项式带余除法代码
  10. mysql insert返回值_各种SQL Insert 返回值