Unity二维码生成全攻略:免费本地化实现与性能优化实践 1. 项目概述为什么Unity开发者需要一个二维码工具在Unity项目开发中无论是制作一个需要分享下载链接的移动端游戏还是一个需要引导用户访问特定网页的AR应用二维码都是一个极其便捷的交互入口。然而很多开发者尤其是刚入门的同学在面对“生成二维码”这个需求时往往会陷入一个误区要么去网上找一些付费的插件要么尝试调用外部API不仅增加了项目成本和复杂度还可能引入网络依赖和隐私风险。我自己在开发一个线下活动签到应用时就遇到过这个问题。当时需要为每个参会者动态生成包含个人信息的签到二维码如果使用在线API不仅速度慢、有并发限制在无网络环境下更是直接“罢工”。于是我决定自己动手封装一个完全免费、纯本地运行、且功能足够强大的Unity二维码生成工具。这个工具的核心目标就三个免费、易用、功能全。它不仅能将文本URL、字符串转换成二维码还能将图片、甚至视频的访问路径或缩略信息编码进去满足绝大多数Unity项目的日常需求。今天我就把这个工具的完整实现思路、核心代码以及我踩过的坑毫无保留地分享出来。无论你是想为你的游戏添加一个分享功能还是为你的企业应用制作资产标签这篇内容都能让你在10分钟内拥有一个属于自己的、高性能的二维码生成模块。2. 核心需求解析与方案选型在动手编码之前我们必须先明确工具要解决的具体问题并选择最合适的技术路径。盲目开始只会导致代码重构甚至推倒重来。2.1 功能需求拆解基于“文本、图片、视频二维码”这个标题我们可以将需求分解为三个层次基础文本编码这是核心功能。能够将任意字符串如https://your-game.comPLAYER_ID:1001编码成标准的QR Code二维码图像。这是所有功能的基础。图片信息嵌入这里的“图片二维码”通常不是指将整张图片的像素数据编码进二维码那样会超出二维码的容量限制导致无法识别而是指两种实用场景场景一生成一个二维码扫描后直接显示一张网络图片。这本质上是将图片的URL地址作为文本进行编码。场景二生成一个二维码扫描后跳转到一个展示该图片的网页或触发应用内显示该图片的逻辑。这需要编码一个带有特定参数的URL或自定义协议字符串。视频信息嵌入与图片类似直接编码视频文件数据不现实。更实用的场景是编码视频流地址如rtmp://live.example.com/stream或视频文件URL。编码一个跳转到视频播放页面的链接。在离线场景下编码一个视频的标识ID由本地应用解析后从本地存储加载播放。所以我们的工具本质上是一个灵活的字符串编码器只是输入的字符串来源和格式需要针对“图片”和“视频”场景进行友好封装。2.2 技术方案对比与选型在Unity中生成二维码主流方案有以下几种方案原理优点缺点适用场景外部插件 (如 ZXing.Net)引入成熟的第三方 .NET 二维码库。功能强大、稳定、支持多种编码格式QR Code, Data Matrix等。需要导入DLL或源码可能增加包体大小部分高级插件收费。追求稳定、功能全面且不介意引入外部依赖的项目。纯C#实现 (如 QRCoder)使用纯C#编写的开源二维码生成库。完全免费、开源、可定制性强无原生依赖。性能可能略低于高度优化的C库需要自行处理与Unity纹理的转换。希望完全掌控代码、避免任何版权风险的项目。系统API调用 (移动平台)调用Android的ZXing库或iOS的CoreImage。直接使用系统能力可能更省电。平台相关代码不统一需要写平台桥接功能受系统限制。对包体大小极度敏感且功能需求简单的平台特定应用。在线API生成将数据发送到云端服务器返回二维码图片。无需本地计算资源实现简单。严重依赖网络有延迟、并发限制和隐私泄露风险离线不可用。基本不推荐用于生产环境仅用于原型验证。我的选择与理由我选择了纯C#实现的 QRCoder 库。原因如下完全免费与开源这符合我们“亲测免费”的标题核心没有任何法律或财务风险。源码在GitHub上可以随意查看、修改和分发。零依赖与高可控性它不依赖任何Unity未包含的本地库跨平台PC、移动端、WebGL兼容性极好。所有代码都在掌控之中调试和优化方便。功能足够强大QRCoder支持QR Code的所有标准版本和纠错等级可以生成位图数据这正好满足我们将数据转换为UnityTexture2D的需求。性能可接受对于生成频率不高如每秒不超过10次的场景其纯C#的性能完全足够。如果遇到性能瓶颈我们可以对其进行针对性优化如对象池、异步生成。因此本工具将以QRCoder为核心引擎围绕它构建一个易于Unity开发者使用的工具类。3. 工具核心实现与代码详解接下来我们进入实战环节。我会分步骤讲解如何将QRCoder集成到Unity中并封装成方便调用的工具。3.1 环境准备与QRCoder导入首先你需要获取QRCoder。最推荐的方式是通过Unity的Package Manager使用Git URL安装这样可以方便地更新。打开你的Unity项目。打开Window - Package Manager。点击左上角的号选择Add package from git URL...。输入QRCoder的Git仓库地址https://github.com/codebude/QRCoder.git点击Add。Unity会自动下载并导入这个库。如果Git方式失败你也可以直接从GitHub Releases页面下载QRCoder.dll文件将其放入你项目的Assets/Plugins文件夹下。但Package Manager方式是首选便于管理。3.2 核心工具类封装我们创建一个名为QRCodeTool.cs的脚本。这个类将提供静态方法方便在任何地方调用。using UnityEngine; using QRCoder; using System.Drawing; // 注意QRCoder使用System.Drawing但Unity中需要转换 using System.IO; using System; public static class QRCodeTool { /// summary /// 将文本生成二维码纹理 /// /summary /// param nameplainText要编码的文本/param /// param namepixelsPerModule每个模块的像素大小影响清晰度和纹理尺寸/param /// param nameeccLevel纠错等级 (L, M, Q, H)/param /// returns生成的Texture2D/returns public static Texture2D GenerateQRCodeTexture(string plainText, int pixelsPerModule 10, QRCodeGenerator.ECCLevel eccLevel QRCodeGenerator.ECCLevel.M) { if (string.IsNullOrEmpty(plainText)) { Debug.LogError(生成二维码的文本内容不能为空); return CreateBlankTexture(); } try { // 1. 使用QRCoder生成二维码数据 using (QRCodeGenerator qrGenerator new QRCodeGenerator()) using (QRCodeData qrCodeData qrGenerator.CreateQrCode(plainText, eccLevel)) using (PngByteQRCode qrCode new PngByteQRCode(qrCodeData)) { // 2. 获取PNG格式的字节流 byte[] pngBytes qrCode.GetGraphic(pixelsPerModule); // 3. 将字节流加载到Unity的Texture2D中 Texture2D tex new Texture2D(2, 2); // 临时尺寸LoadImage会覆盖 if (tex.LoadImage(pngBytes)) { tex.filterMode FilterMode.Point; // 使用点过滤保持像素清晰 tex.Apply(); return tex; } else { Debug.LogError(从字节流加载纹理失败。); return CreateBlankTexture(); } } } catch (Exception e) { Debug.LogError($生成二维码时发生异常: {e.Message}); return CreateBlankTexture(); } } /// summary /// 为图片URL生成二维码纹理 /// /summary /// param nameimageUrl网络图片的完整URL/param /// param namepixelsPerModule每个模块的像素大小/param /// returns生成的Texture2D/returns public static Texture2D GenerateQRCodeForImageUrl(string imageUrl, int pixelsPerModule 10) { // 直接将URL作为文本编码 return GenerateQRCodeTexture(imageUrl, pixelsPerModule); } /// summary /// 为视频URL生成二维码纹理编码视频链接 /// /summary /// param namevideoUrl网络视频的URL或视频页面链接/param /// param namepixelsPerModule每个模块的像素大小/param /// returns生成的Texture2D/returns public static Texture2D GenerateQRCodeForVideoUrl(string videoUrl, int pixelsPerModule 10) { // 直接将视频URL作为文本编码 return GenerateQRCodeTexture(videoUrl, pixelsPerModule); } /// summary /// 生成一个自定义协议二维码用于触发应用内特定行为如打开本地视频 /// /summary /// param namescheme协议头如 myapp:///param /// param nameaction行为如 openVideo/param /// param nameparameters参数字典/param /// param namepixelsPerModule每个模块的像素大小/param /// returns生成的Texture2D/returns public static Texture2D GenerateQRCodeForCustomScheme(string scheme, string action, System.Collections.Generic.Dictionarystring, string parameters, int pixelsPerModule 10) { // 构建自定义URL字符串例如myapp://openVideo?id123pathlocal/video.mp4 string url ${scheme}{action}; if (parameters ! null parameters.Count 0) { url ?; bool isFirst true; foreach (var kvp in parameters) { if (!isFirst) url ; url ${Uri.EscapeDataString(kvp.Key)}{Uri.EscapeDataString(kvp.Value)}; isFirst false; } } return GenerateQRCodeTexture(url, pixelsPerModule); } /// summary /// 将二维码纹理保存为PNG文件用于编辑器扩展或运行时保存 /// /summary /// param nametexture二维码纹理/param /// param namefilePath完整文件路径/param public static void SaveTextureToFile(Texture2D texture, string filePath) { if (texture null) { Debug.LogError(纹理为空无法保存。); return; } byte[] bytes texture.EncodeToPNG(); System.IO.File.WriteAllBytes(filePath, bytes); Debug.Log($二维码已保存至: {filePath}); } // 创建一个空纹理用于错误处理 private static Texture2D CreateBlankTexture(int size 256) { Texture2D tex new Texture2D(size, size); Color[] colors new Color[size * size]; for (int i 0; i colors.Length; i) { colors[i] Color.white; } tex.SetPixels(colors); tex.Apply(); return tex; } }代码关键点解析using System.DrawingQRCoder内部使用了System.Drawing的Bitmap等类来构建图像。但在Unity环境下我们最终需要的是Texture2D。所以我们的流程是QRCoder生成PNG字节流 - Unity用Texture2D.LoadImage加载。这避免了直接操作Bitmap带来的兼容性问题。pixelsPerModule这是控制二维码“密度”的关键参数。一个“模块”对应二维码中的一个黑白小方块。这个值越大生成的图片分辨率越高越清晰但纹理尺寸也越大。通常设置为5-20之间在移动设备屏幕上10是一个兼顾清晰度和性能的常用值。ECCLevel(纠错等级)这是二维码的容错能力。分为L(约7%)、M(约15%)、Q(约25%)、H(约30%)。等级越高二维码能被遮挡或污损的面积越大但编码相同数据所需的模块数也越多二维码会更复杂。对于普通文本使用 M (Medium) 级别是一个很好的平衡。FilterMode.Point这是非常重要的一步二维码是像素艺术必须使用FilterMode.Point点过滤来确保渲染时像素边缘锐利不使用任何插值模糊。否则二维码会变得难以识别。自定义协议GenerateQRCodeForCustomScheme方法展示了如何生成一个用于触发应用内行为的二维码。例如扫描后可以打开应用内的某个界面、播放指定的本地视频文件等。这需要你的应用注册相应的URL Scheme移动端或自定义协议桌面端。3.3 在Unity中快速使用与测试创建一个测试脚本QRCodeTester.cs挂载到任意GameObject上如Main Camera。using UnityEngine; using UnityEngine.UI; // 如果需要显示在UI上 public class QRCodeTester : MonoBehaviour { public RawImage qrCodeDisplay; // 在UI上显示 public Renderer targetRenderer; // 在3D物体上显示可选 [Header(测试内容)] public string testText Hello, Unity QR Code!; public string testImageUrl https://example.com/my-image.jpg; public string testVideoUrl https://example.com/my-video.mp4; [Header(生成设置)] public int pixelsPerModule 10; void Start() { // 示例1生成文本二维码并显示在UI上 if (qrCodeDisplay ! null) { Texture2D tex QRCodeTool.GenerateQRCodeTexture(testText, pixelsPerModule); qrCodeDisplay.texture tex; } // 示例2生成图片URL二维码并应用到3D物体材质上 if (targetRenderer ! null) { Texture2D tex QRCodeTool.GenerateQRCodeForImageUrl(testImageUrl, pixelsPerModule); targetRenderer.material.mainTexture tex; } // 示例3保存视频URL二维码到本地在编辑器下运行 #if UNITY_EDITOR Texture2D videoQRCode QRCodeTool.GenerateQRCodeForVideoUrl(testVideoUrl, pixelsPerModule); string savePath Application.dataPath /../SavedQRCode.png; // 保存到项目根目录的同级 QRCodeTool.SaveTextureToFile(videoQRCode, savePath); #endif // 示例4生成自定义协议二维码 var parameters new System.Collections.Generic.Dictionarystring, string { {id, 1001}, {type, video}, {path, StreamingAssets/intro.mp4} }; Texture2D customQR QRCodeTool.GenerateQRCodeForCustomScheme(myunityapp://, openAsset, parameters, pixelsPerModule); // 可以将customQR赋值给另一个RawImage或Renderer进行查看 } }将QRCodeTester脚本挂载到场景中并将一个带有RawImage组件的UI对象拖拽到qrCodeDisplay字段。运行游戏你就能立刻在UI上看到生成的“Hello, Unity QR Code!”二维码了。用手机扫码软件如微信、支付宝扫一下应该能正确识别出这段文本。4. 高级功能与性能优化实战基础功能跑通后我们需要考虑更复杂的生产环境需求比如生成大量二维码、动态更新内容、以及优化性能。4.1 动态更新与异步生成在VR展厅应用中每个展品都需要一个实时更新的二维码内容可能是当前展品的详细介绍页URL。我们不可能每帧都生成需要动态更新且避免卡顿。using System.Collections; using UnityEngine; public class DynamicQRCodeDisplay : MonoBehaviour { public RawImage displayImage; public string baseUrl https://my-exhibition.com/item?id; public int currentItemId 1; public float updateInterval 5.0f; // 每5秒更新一次 private Texture2D _currentQRTexture; private Coroutine _updateCoroutine; void Start() { StartUpdatingQRCode(); } void StartUpdatingQRCode() { if (_updateCoroutine ! null) StopCoroutine(_updateCoroutine); _updateCoroutine StartCoroutine(UpdateQRCodeRoutine()); } IEnumerator UpdateQRCodeRoutine() { while (true) { string fullUrl baseUrl currentItemId; // 使用协程进行异步生成避免在主线程进行大量计算导致卡顿 yield return StartCoroutine(GenerateQRCodeAsync(fullUrl)); yield return new WaitForSeconds(updateInterval); currentItemId; // 模拟切换到下一个展品 } } IEnumerator GenerateQRCodeAsync(string text) { // 在实际项目中对于非常复杂的二维码或低端设备可以将生成逻辑放到另一个线程。 // 这里使用协程模拟一个“耗时”操作并在一帧后应用结果。 Texture2D newTex null; bool isDone false; // 在实际实现中这里可以调用一个在后台线程运行的方法 // 为了示例我们直接在主线程生成但用yield return null来分割帧 System.Threading.Tasks.Task.Run(() { newTex QRCodeTool.GenerateQRCodeTexture(text); isDone true; }); while (!isDone) { yield return null; // 等待后台任务完成 } // 销毁旧的纹理避免内存泄漏 if (_currentQRTexture ! null) Destroy(_currentQRTexture); _currentQRTexture newTex; displayImage.texture _currentQRTexture; Debug.Log($二维码已更新为: {text}); } void OnDestroy() { if (_updateCoroutine ! null) StopCoroutine(_updateCoroutine); if (_currentQRTexture ! null) Destroy(_currentQRTexture); } }注意Unity的Texture2D不是线程安全的。上述示例中我们在另一个线程Task.Run中调用QRCodeTool.GenerateQRCodeTexture是因为该工具内部最终在GetGraphic和LoadImage阶段会涉及大量计算和内存分配这些操作在非主线程执行可以避免帧率下降。但是对Texture2D的赋值displayImage.texture ...必须在主线程进行。这是一种简化的异步模式对于更复杂的场景可能需要使用UnityWebRequest或Job System进行更精细的线程控制。4.2 性能优化关键对象池与纹理复用如果你需要在同一帧或短时间内生成大量二维码例如为游戏中的100个道具批量生成唯一二维码频繁创建和销毁Texture2D会导致GC垃圾回收频繁触发引起卡顿。这时对象池是必备的优化手段。using System.Collections.Generic; using UnityEngine; public class QRCodeTexturePool { private QueueTexture2D _texturePool; private int _textureSize; private FilterMode _filterMode; public QRCodeTexturePool(int defaultSize 256, int preWarmCount 5, FilterMode filterMode FilterMode.Point) { _textureSize defaultSize; _filterMode filterMode; _texturePool new QueueTexture2D(); // 预热预先创建一些纹理对象 for (int i 0; i preWarmCount; i) { _texturePool.Enqueue(CreateNewTexture()); } } public Texture2D GetTexture() { if (_texturePool.Count 0) { return _texturePool.Dequeue(); } // 池中无可用纹理创建新的 Debug.LogWarning(QR纹理池为空创建新纹理。考虑增加预热数量。); return CreateNewTexture(); } public void ReturnTexture(Texture2D texture) { if (texture null) return; // 可选将纹理重置为白色或其他默认状态 Color[] resetColors new Color[texture.width * texture.height]; for (int i 0; i resetColors.Length; i) resetColors[i] Color.white; texture.SetPixels(resetColors); // texture.Apply(); // 如果立即需要空白状态可以Apply _texturePool.Enqueue(texture); } private Texture2D CreateNewTexture() { Texture2D tex new Texture2D(_textureSize, _textureSize, TextureFormat.RGBA32, false); tex.filterMode _filterMode; tex.wrapMode TextureWrapMode.Clamp; return tex; } public void ClearPool() { while (_texturePool.Count 0) { Texture2D tex _texturePool.Dequeue(); if (tex ! null) Object.Destroy(tex); } } } // 使用对象池的工具类方法 public static Texture2D GenerateQRCodeTextureWithPool(string plainText, QRCodeTexturePool pool, int pixelsPerModule 10, QRCodeGenerator.ECCLevel eccLevel QRCodeGenerator.ECCLevel.M) { Texture2D targetTex pool.GetTexture(); // ... 使用QRCoder生成pngBytes ... byte[] pngBytes GenerateQRCodeBytes(plainText, pixelsPerModule, eccLevel); // 假设这个方法返回字节 // 重用纹理而不是创建新的 if (targetTex.LoadImage(pngBytes)) { targetTex.Apply(); return targetTex; } // 如果失败归还纹理并返回null或创建新的 pool.ReturnTexture(targetTex); return null; }通过对象池我们显著减少了动态内存分配和GC压力。在需要高频生成二维码的场合如列表界面性能提升会非常明显。4.3 生成“图片/视频”二维码的进阶思路之前提到直接编码文件数据不现实。但我们可以通过编码“索引”来实现更丰富的功能。例如在一个博物馆导览App中离线内容包将所有图片、视频、音频资源打包并建立一个索引文件如JSON。生成二维码二维码内容不是文件本身而是一个唯一ID如asset://painting/蒙娜丽莎。应用解析App扫描二维码后解析出协议asset://和路径painting/蒙娜丽莎然后根据这个路径从本地索引中查找并加载对应的图片资源进行展示。// 生成指向本地资源的二维码 string localAssetCode myapp://openLocalAsset?typeimageid2024_expo_001; Texture2D localQR QRCodeTool.GenerateQRCodeTexture(localAssetCode); // 在App的二维码解析模块中 void OnQRCodeScanned(string result) { if (result.StartsWith(myapp://)) { // 解析URL参数 Uri uri new Uri(result); string action uri.Host; // openLocalAsset var queryParams ParseQueryString(uri.Query); // 自定义解析函数 string assetType queryParams[type]; string assetId queryParams[id]; // 根据assetType和assetId从本地数据库或Resources/StreamingAssets加载资源 LoadLocalAsset(assetType, assetId); } else { // 处理普通网页URL等 Application.OpenURL(result); } }这种方式将二维码变成了一个轻量的“触发器”真正的内容存储在本地完美解决了离线、大容量媒体内容的关联问题。5. 常见问题、排查技巧与避坑指南在实际使用中你肯定会遇到各种各样的问题。下面是我在多个项目中总结出来的“血泪经验”。5.1 二维码生成失败或内容错误问题现象可能原因解决方案生成的纹理全白或全黑。1. 输入的文本为空或为null。2.Texture2D.LoadImage失败。3. QRCoder生成字节流时出错。1. 在生成前检查字符串是否有效。2. 检查pngBytes数组是否为空。尝试将字节流保存到文件看是否是有效的PNG。3. 用try-catch包裹生成代码查看具体异常信息。二维码可以生成但扫码软件无法识别。1.纹理过滤模式不是FilterMode.Point导致像素模糊。2. 二维码尺寸太小模块数低于识别下限。3. 纠错等级太低有轻微污损。4. 渲染到屏幕时被拉伸或压缩变形。1.务必设置texture.filterMode FilterMode.Point。2. 增大pixelsPerModule或确保最终显示尺寸足够大物理尺寸大于2cm x 2cm。3. 提高ECCLevel到Q或H。4. 检查UI Image或3D材质的缩放比例确保是1:1。扫描出的内容与预期不符多了乱码或少了字符。字符串编码问题。QRCoder默认使用UTF-8。如果包含特殊字符如Emoji、中文需要确保编码一致。在生成前对字符串进行URL编码或确保其是纯ASCII字符。对于复杂文本可以先用System.Text.Encoding.UTF8.GetBytes检查。在WebGL平台上无法生成。System.Drawing命名空间在WebGL中不可用。但我们通过PNG字节流绕开了它。如果还有问题可能是由于QRCoder内部使用了WebGL不支持的API。确保使用的是纯C#版本的QRCoder并且我们只使用了PngByteQRCode这个类。如果仍失败考虑寻找一个完全兼容WebGL的纯C#二维码库分支。5.2 性能与内存问题问题在同一帧生成几十个二维码游戏明显卡顿。排查使用Unity Profiler查看CPU开销和GC Alloc。你会发现大量的时间花在GetGraphic和LoadImage上并且每帧都有可观的GC内存分配。解决对象池如上文所述这是解决GC问题的首要方案。分帧生成不要在同一帧完成所有生成。使用协程每帧生成1-2个。降低质量在可以接受的范围内减小pixelsPerModule。从20降到10纹理像素数变为1/4生成和加载速度会快很多。预生成对于静态内容如固定的URL在加载场景时或资源打包阶段就生成好二维码纹理运行时直接使用。5.3 平台特定问题iOS/Android确保Texture2D的格式和读写权限正确。如果需要在运行时保存到相册需要申请相应的存储权限。IL2CPP Stripping如果发布时遇到因为代码裁剪导致QRCoder功能丢失需要在Project Settings - Player - Other Settings - Managed Stripping Level中尝试降低级别如从High降到 Medium或者为QRCoder相关代码添加[Preserve]属性。中文内容包含中文的文本生成二维码在部分扫码器上可能出现乱码。这是因为不同扫码器对文本编码的解读不同。一个稳妥的做法是在生成前将字符串进行URL编码string encodedText Uri.EscapeDataString(yourChineseText);。这样二维码里存储的就是百分号编码绝大多数扫码器都能正确还原。5.4 一个关于“清晰度”的终极技巧即使设置了FilterMode.Point有时在低分辨率屏幕上或UI缩放后二维码边缘依然会模糊。这是因为纹理像素与屏幕像素没有完美对齐。解决方案确保你的二维码显示在Canvas Scaler设置为Scale With Screen Size或Constant Pixel Size的UI下并且RawImage的RectTransform尺寸是pixelsPerModule * 模块数量的整数倍。例如你的二维码是25x25个模块pixelsPerModule10那么纹理尺寸是250x250。你最好将RawImage的宽高也设置为250。这样可以最大程度避免缩放带来的模糊。6. 工具扩展与编辑器集成为了让这个工具在开发过程中更加顺手我们可以为其创建简单的编辑器扩展。6.1 自定义Inspector工具创建一个编辑器脚本QRCodeToolEditor.cs放在Assets/Editor/文件夹下。using UnityEditor; using UnityEngine; [CustomEditor(typeof(QRCodeTester))] // 为我们之前的测试脚本定制Inspector public class QRCodeToolEditor : Editor { public override void OnInspectorGUI() { DrawDefaultInspector(); // 绘制默认字段 QRCodeTester tester (QRCodeTester)target; GUILayout.Space(10); if (GUILayout.Button(立即生成文本二维码)) { if (tester.qrCodeDisplay ! null) { Texture2D tex QRCodeTool.GenerateQRCodeTexture(tester.testText, tester.pixelsPerModule); tester.qrCodeDisplay.texture tex; EditorUtility.SetDirty(tester.qrCodeDisplay); // 标记为已修改 } } if (GUILayout.Button(保存当前文本二维码到项目根目录)) { Texture2D tex QRCodeTool.GenerateQRCodeTexture(tester.testText, tester.pixelsPerModule); string path EditorUtility.SaveFilePanel(保存二维码, , MyQRCode, png); if (!string.IsNullOrEmpty(path)) { QRCodeTool.SaveTextureToFile(tex, path); AssetDatabase.Refresh(); // 刷新Unity项目窗口 } // 注意这里生成的tex需要销毁避免内存泄漏简单示例中省略 } } }这样在Inspector面板上就会多出两个按钮方便你在编辑模式下快速测试和保存二维码无需运行游戏。6.2 创建编辑器窗口批量生成对于需要批量生成大量二维码如为游戏内所有物品生成ID二维码的需求一个独立的编辑器窗口非常有用。using UnityEditor; using UnityEngine; using System.Collections.Generic; public class QRCodeBatchGeneratorWindow : EditorWindow { [MenuItem(Tools/二维码批量生成器)] public static void ShowWindow() { GetWindowQRCodeBatchGeneratorWindow(批量生成二维码); } private Liststring sourceTexts new Liststring() { Item_001, Item_002, Item_003 }; private string exportFolder Assets/GeneratedQRCode/; private int pixelsPerModule 10; private Vector2 scrollPos; void OnGUI() { GUILayout.Label(批量生成设置, EditorStyles.boldLabel); pixelsPerModule EditorGUILayout.IntField(模块像素大小, pixelsPerModule); exportFolder EditorGUILayout.TextField(导出文件夹, exportFolder); GUILayout.Space(10); GUILayout.Label(二维码内容列表 (每行一个), EditorStyles.boldLabel); EditorGUILayout.BeginVertical(GUI.skin.box); scrollPos EditorGUILayout.BeginScrollView(scrollPos, GUILayout.Height(150)); string combinedText EditorGUILayout.TextArea(string.Join(\n, sourceTexts)); sourceTexts new Liststring(combinedText.Split(new[] { \n, \r }, System.StringSplitOptions.RemoveEmptyEntries)); EditorGUILayout.EndScrollView(); EditorGUILayout.EndVertical(); GUILayout.Space(20); if (GUILayout.Button(生成并导出所有二维码, GUILayout.Height(30))) { if (!System.IO.Directory.Exists(exportFolder)) { System.IO.Directory.CreateDirectory(exportFolder); } for (int i 0; i sourceTexts.Count; i) { string text sourceTexts[i]; if (string.IsNullOrWhiteSpace(text)) continue; Texture2D tex QRCodeTool.GenerateQRCodeTexture(text, pixelsPerModule); string fileName $QR_{i:000}_{text.Replace(/, _).Replace(:, _)}.png; string fullPath System.IO.Path.Combine(exportFolder, fileName); QRCodeTool.SaveTextureToFile(tex, fullPath); EditorUtility.DisplayProgressBar(批量生成中, $正在生成 {fileName}, (float)i / sourceTexts.Count); DestroyImmediate(tex); // 及时销毁临时纹理 } EditorUtility.ClearProgressBar(); AssetDatabase.Refresh(); EditorUtility.DisplayDialog(完成, $已生成 {sourceTexts.Count} 个二维码到 {exportFolder}, 确定); } } }这个窗口工具允许你粘贴一列文本比如从Excel复制的物品ID然后一键生成所有对应的二维码图片并保存到指定文件夹极大提升了生产效率。走到这里你已经拥有了一个从核心生成、到性能优化、再到编辑器集成的完整Unity二维码解决方案。它免费、强大、且完全受你控制。你可以根据自己项目的具体需求对这个工具进行任意裁剪和扩展比如增加Logo中心图、颜色定制等功能。希望这个详尽的实现过程能帮助你在未来的项目中轻松扫除二维码相关的所有障碍。