Unity开发绘画板——03.简单的实现绘制功能
从本篇文章开始,将带着大家一起写代码,我不会直接贴出成品代码,而是会把写代码的历程以及遇到的问题、如何解决这些问题都记录在文章里面,当然,同一个问题的解决方案可能会有很多,甚至有更好更高效的方式是我所没有想到的,大家也可以在评论区一起讨论一下。
1.监听鼠标输入
要实现绘画的功能,监听鼠标的输入是首先要解决的问题,我们希望按下鼠标左键之后开始在屏幕中绘制,要实现这个也比较简单。
我们先在Scripts文件夹下创建一个新脚本,命名为Painter.cs, 并将其挂载到场景中的Painter节点上,我们将在Painter.cs的Update中实现该逻辑。
public class Painter : MonoBehaviour
{void Update(){if (Input.GetMouseButtonDown(0)){//开始绘制var mousePosition = Input.mousePosition; //获取当前鼠标位置}if (Input.GetMouseButton(0)){//绘制}if (Input.GetMouseUp(0)){//停止绘制}}
}
2.绘制图案
通过Graphics.DrawTexture接口向RenderTexture中绘制图案,这里的图案其实就是我们的笔刷样式了。
例如我们现在有一张圆形的图案:

想要用这个作为笔刷在一张空的RenderTexture上去作画,实现的代码也很简单,在Painter.cs中实现如下方法:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Painter : MonoBehaviour
{public RenderTexture renderTexture;public Texture2D brushTexture; //笔刷纹理public float brushSize = 5f;void Update(){if (Input.GetMouseButtonDown(0)){}if (Input.GetMouseButton(0)){var mousePosition = Input.mousePosition;DrawBrush((int)mousePosition.x, (int)mousePosition.y);}}private void DrawBrush(int x, int y){//纹理绘制的位置和大小Rect brushRect = new Rect(x, y, brushSize, brushSize);//指定渲染目标为renderTextureGraphics.SetRenderTarget(renderTexture);//保存当前矩阵状态GL.PushMatrix();//设置像素矩阵GL.LoadPixelMatrix(0, renderTexture.width, 0, renderTexture.height);//绘制纹理Graphics.DrawTexture(brushRect, brushTexture);//恢复之前的矩阵状态GL.PopMatrix();//重置渲染目标Graphics.SetRenderTarget(null);}}
由于笔刷颜色是白色的,画板背景也是白色,为了让绘制结果能被看到,我们先把背景颜色调成黑色,改变MainCamera的Background即可(改变线条颜色我们之后会讲到),执行效果如下图所示:

可见已经有点感觉了,但是表现是很多间断的点,而不是连续的线,因为我们的绘制是由Update驱动的,所以绘制速度也受Update帧率影响,当鼠标速度快的时候尤其明显。
为了解决这个问题,我们可以考虑在点与点之间进行线性插值,绘制更多的点,这样就会显得连续了。
原理如下图:

从A点到B点之间线性插入若干点。
为了达到此目的,我们需要从一下几个点入手:
- 我们期望这些插入点的密度是可以通过参数调节的
- 我们在绘制当前的位置时需要知道上一次绘制的位置
简单改一下代码:
新增一个previousMousePos字段,用于记录上一次笔刷位置
新增函数 private void DrawLine(Vector2 start, Vector2 end) 用于绘制两点之间的连线
优化后的代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Painter : MonoBehaviour
{public RenderTexture renderTexture;public Texture2D brushTexture; //笔刷纹理public float brushSize = 5f; //笔刷大小public float resolutionMultiplier = 5; //线性插值密度调节private Vector2 previousMousePos; //记录上一帧鼠标的位置 void Update(){if (Input.GetMouseButtonDown(0)){previousMousePos = Input.mousePosition;}if (Input.GetMouseButton(0)){var mousePosition = Input.mousePosition;DrawLine(previousMousePos, mousePosition);previousMousePos = mousePosition;}}private void DrawLine(Vector2 start, Vector2 end){float distance = Vector2.Distance(start, end);int steps = Mathf.CeilToInt(distance * resolutionMultiplier);for (int i = 0; i <= steps; i++){float t = i / (float)steps;int x = Mathf.FloorToInt(Mathf.Lerp(start.x, end.x, t));int y = Mathf.FloorToInt(Mathf.Lerp(start.y, end.y, t));DrawBrush(x, y);}}private void DrawBrush(int x, int y){Rect brushRect = new Rect(x, y, brushSize, brushSize);Graphics.SetRenderTarget(renderTexture);GL.PushMatrix();GL.LoadPixelMatrix(0, renderTexture.width, 0, renderTexture.height);Graphics.DrawTexture(brushRect, brushTexture);GL.PopMatrix();Graphics.SetRenderTarget(null);}}
效果如下,感觉还是不错的~

这样我们就简单实现了绘制的功能,下一章我们来实现滑动条调节画笔的大小~
相关文章:
Unity开发绘画板——03.简单的实现绘制功能
从本篇文章开始,将带着大家一起写代码,我不会直接贴出成品代码,而是会把写代码的历程以及遇到的问题、如何解决这些问题都记录在文章里面,当然,同一个问题的解决方案可能会有很多,甚至有更好更高效的方式是…...
R语言的基础知识R语言函数总结
R语言与数据挖掘:公式;数据;方法 R语言特征 对大小写敏感通常,数字,字母,. 和 _都是允许的(在一些国家还包括重音字母)。不过,一个命名必须以 . 或者字母开头,并且如果以 . 开头&…...
龙年国庆专属姓氏头像
关注▲洋洋科创星球▲一起成长! 2024年,我们迎来了龙年,龙年国庆姓氏头像! 慢慢找! 你的和你朋友的都有。 01赵 02 钱 03 孙 04 李 05 周 06 吴 07 郑 08 王 09 冯 010 陈 011 褚 012 卫 013 蒋 014 沈 015 韩 姓氏…...
基于Es和智普AI实现的语义检索
1、什么是语义检索 语义检索是一种利用自然语言处理(NLP)和人工智能(AI)技术来理解搜索查询的语义,以提供更准确和相关搜索结果的搜索技术,语义检索是一项突破性的技术,旨在通过深入理解单词和…...
URI和URL的区别
1: 将 URI 转换为 URL import java.net.URI; import java.net.URL;public class UriToUrlExample {public static void main(String[] args) {// 创建一个 URI 对象URI uri = new URI("http://example.com/path/to/resource");// 将 URI 转换为 URLtry {URL url = u…...
Java 入门指南:获取对象的内存地址
文章目录 hashCode()应用重写 hashCode() 方法示例 Symstem . indentityHashCode()应用 注意事项 在 Java 开发中,了解对象的内存管理是十分重要的,尽管 Java 的设计初衷是让开发者更专注于业务逻辑而非底层资源管理。但在某些情况下,了解对象…...
【Linux】项目自动化构建工具-make/Makefile 详解
🔥 个人主页:大耳朵土土垚 🔥 所属专栏:Linux系统编程 这里将会不定期更新有关Linux的内容,欢迎大家点赞,收藏,评论🥳🥳🎉🎉🎉 文章目…...
嵌入式开发中学习C++的用处?
这个问题一直有同学在问,其实从我的角度是一定是需要学的,最直接的就是你面试大厂的嵌入式岗位或者相关岗位,最后一定会问c,而很多人是不会的,这就是最大的用处,至于从技术角度考量倒是其次,因为…...
基于SAM大模型的遥感影像分割工具,用于创建交互式标注、识别地物的能力,可利用Flask进行封装作为Web后台服务
如有帮助,支持一下(GitHub - Lvbta/ImageSegmentationTool-SAM: An interactive annotation case developed based on SAM for remote sensing image annotation, which can generate corresponding segmentation results based on point, multi-point, …...
Selenium入门
Selenium 是一个用于自动化 web 应用程序测试的工具,它支持多种浏览器和编程语言。 下载驱动程序:根据你的浏览器类型和版本,下载相应的 WebDriver。例如,Chrome 浏览器需要 ChromeDriver。 安装 Selenium 库 pip install sele…...
USB 3.1 Micro-A 与 Micro-B 插头,Micro-AB 与 Micro-B 插座,及其引脚定义
连接器配对 下表列出 USB 插座可接受的插头: USB 3.1 Micro-B 连接器 USB 3.1 Micro-B 插头和 USB 3.1 Micro-B 插座连接器是为小型手持设备和其他可能使用小尺寸连接器的应用而定义的。其定义使得 USB 3.1 Micro-B 插座既可以接受 USB 3.1 Micro-B 插头ÿ…...
MySQL多版本并发控制MVCC实现原理
MVCC MVCC 是多版本并发控制方法,用来解决读和写之间的冲突,比如脏读、不可重复读问题,MVCC主要针对读操作做限制,保证每次读取到的数据都是本次读取之前的已经提交事务所修改的。 概述 当一个事务要对数据库中的数据进行selec…...
【并查集】[ABC372E] K-th Largest Connected Components 题解
题意 前置阅读:并查集算法介绍 洛谷链接 Atcoder 链接 给定 n ( 1 ≤ n ≤ 2 1 0 5 ) n(1 \leq n \leq 2\times 10^5) n(1≤n≤2105) 个点,初始没有边,您要进行以下操作: 1 a b,表示连接一条 ( a , b ) (a,b) …...
HarmonyOS面试题(持续更新中)
1、用过线程通信吗,线程是怎么进行通信的? emitter 和 eventHub 相同: 都是基于事件总线的 区别是: ① eventHub当前线程内通信 ② emitter是同一进程不同线程或者同一进程和同一线程也可以通信 2、页面和组件的生命周期 …...
QT中QWidget和QObject的区别与联系是什么
在Qt框架中,QWidget和QObject是两个核心类,它们各自扮演着不同的角色,但又紧密相连。以下是关于它们区别与联系的详细解释: 区别 基类和功能定位: QObject是Qt中所有类的基类,包括几乎所有的Qt对象。它提供…...
解决macOS安装redis以后不支持远程链接的问题
参考文档:https://blog.csdn.net/qq_37703224/article/details/142542179?spm1001.2014.3001.5501 安装的时候有个提示, 使用指定配置启动: /opt/homebrew/opt/redis/bin/redis-server /opt/homebrew/etc/redis.conf那么我们可以尝试修改这个配置文件: code /opt/homebrew/…...
2024年研究生数学建模“华为杯”E题——肘部法则、k-means聚类、目标检测(python)、ARIMA、逻辑回归、混淆矩阵(附:目标检测代码)
文章目录 一、情况介绍二、思路情况二、代码展示三、感受 一、情况介绍 前几天也是参加了研究生数学建模竞赛(也就是华为杯),也是和本校的两个数学学院的朋友在网上组的队伍。昨天(9.25)通宵干完论文(一条…...
绝了,自从用了它,我每天能多摸鱼2小时!
大家好,我是可乐。 俗话说的好:“摸鱼一时爽,一直摸鱼一直爽”。 作为一个程序员,是否有过调试代码熬到深夜?是否有过找不到解决方案而挠秃头顶? 但现在你即将要解放了,用了这款工具——秘塔…...
C语言指针系列1——初识指针
祛魅:其实指针这块儿并不难,有人说难只是因为基础到进阶没有处理好,大家要好好跟着一步一步学习,今天我们先来认识一下指针 指针定义:指针就是内存地址,指针变量是用来存放内存地址的变量,在同一…...
传神论文中心|第26期人工智能领域论文推荐
在人工智能领域的快速发展中,我们不断看到令人振奋的技术进步和创新。近期,开放传神(OpenCSG)传神社区发现了一些值得关注的成就。传神社区本周也为对AI和大模型感兴趣的读者们提供了一些值得一读的研究工作的简要概述以及它们各自…...
网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
