以下大部分内容由AI生成,本页仅作为记录
生成二维码
首先导入ZXing.net包,也可以使用NuGetForUnity包快速导入。使用下面代码创建。
NuGetForUnityd导入的包不是unity适配化的,直接去github ZXing.net Release界面下载压缩包,里面有一个专门的unity使用的

using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using ZXing;
using ZXing.Common;
using ZXing.QrCode;
namespace Script
{
public static class QRGenerator
{
/// <summary>
/// 生成基础二维码
/// </summary>
/// <param name="content">二维码内容</param>
/// <param name="size">二维码尺寸(正方形)</param>
/// <returns>生成的二维码纹理</returns>
public static Texture2D GenerateQRCode(string content, int size = 256)
{
return GenerateQRCode(content, size, size, Color.black, Color.white);
}
/// <summary>
/// 生成自定义颜色的二维码
/// </summary>
/// <param name="content">二维码内容</param>
/// <param name="width">宽度</param>
/// <param name="height">高度</param>
/// <param name="codeColor">二维码颜色</param>
/// <param name="bgColor">背景颜色</param>
/// <returns>生成的二维码纹理</returns>
public static Texture2D GenerateQRCode(string content, int width, int height, Color codeColor, Color bgColor)
{
if (string.IsNullOrEmpty(content))
{
Debug.LogError("二维码内容不能为空");
return null;
}
var writer = new BarcodeWriter
{
Format = BarcodeFormat.QR_CODE,
Options = new QrCodeEncodingOptions
{
Width = width,
Height = height,
Margin = 1, // 设置边距
CharacterSet = "UTF-8" // 编码格式
}
};
Color32[] pixels = writer.Write(content);
Texture2D texture = new Texture2D(width, height);
texture.SetPixels32(pixels);
texture.Apply();
return texture;
}
/// <summary>
/// 生成带Logo的二维码
/// </summary>
/// <param name="content">二维码内容</param>
/// <param name="size">二维码尺寸(正方形)</param>
/// <param name="logo">Logo纹理</param>
/// <param name="logoSize">Logo尺寸</param>
/// <returns>生成的二维码纹理</returns>
public static Texture2D GenerateQRCodeWithLogo(string content, int size, Texture2D logo, int logoSize = 0)
{
return GenerateQRCodeWithLogo(content, size, size, Color.black, Color.white, logo, logoSize);
}
/// <summary>
/// 生成带Logo的自定义颜色二维码
/// </summary>
/// <param name="content">二维码内容</param>
/// <param name="width">宽度</param>
/// <param name="height">高度</param>
/// <param name="codeColor">二维码颜色</param>
/// <param name="bgColor">背景颜色</param>
/// <param name="logo">Logo纹理</param>
/// <param name="logoSize">Logo尺寸(0表示自动计算)</param>
/// <returns>生成的二维码纹理</returns>
public static Texture2D GenerateQRCodeWithLogo(
string content,
int width,
int height,
Color codeColor,
Color bgColor,
Texture2D logo,
int logoSize = 0)
{
Texture2D qrTexture = GenerateQRCode(content, width, height, codeColor, bgColor);
if (logo == null)
return qrTexture;
// 自动计算Logo大小(不超过二维码1/5)
int finalLogoSize = logoSize > 0
? Mathf.Min(logoSize, Mathf.Min(width, height)) / 3 : Mathf.Min(width, height) / 5;
// 调整Logo尺寸
Texture2D resizedLogo = ResizeTexture(logo, finalLogoSize, finalLogoSize);
// 将Logo绘制到二维码中心
int startX = (width - finalLogoSize) / 2;
int startY = (height - finalLogoSize) / 2;
Color[] logoPixels = resizedLogo.GetPixels();
qrTexture.SetPixels(startX, startY, finalLogoSize, finalLogoSize, logoPixels);
qrTexture.Apply();
// 清理临时纹理
if (logo != resizedLogo)
UnityEngine.Object.Destroy(resizedLogo);
return qrTexture;
}
/// <summary>
/// 保存二维码为PNG文件
/// </summary>
/// <param name="qrTexture">二维码纹理</param>
/// <param name="fileName">文件名(不带扩展名)</param>
/// <param name="savePath">保存路径(为空时使用Application.persistentDataPath)</param>
/// <returns>完整文件路径</returns>
public static string SaveQRCode(Texture2D qrTexture, string fileName = "QRCode", string savePath = null)
{
if (qrTexture == null)
{
Debug.LogError("二维码纹理为空");
return null;
}
if (string.IsNullOrEmpty(savePath))
savePath = Application.persistentDataPath;
string fullPath = Path.Combine(savePath, fileName + ".png");
try
{
byte[] bytes = qrTexture.EncodeToPNG();
File.WriteAllBytes(fullPath, bytes);
Debug.Log("二维码已保存至: " + fullPath);
return fullPath;
}
catch (Exception e)
{
Debug.LogError("保存二维码失败: " + e.Message);
return null;
}
}
#region 辅助方法
private static Color32 ToColor32(Color color)
{
return new Color32(
(byte)(color.r * 255),
(byte)(color.g * 255),
(byte)(color.b * 255),
(byte)(color.a * 255)
);
}
private static Texture2D ResizeTexture(Texture2D source, int newWidth, int newHeight)
{
source.filterMode = FilterMode.Bilinear;
RenderTexture rt = RenderTexture.GetTemporary(newWidth, newHeight);
rt.filterMode = FilterMode.Bilinear;
RenderTexture.active = rt;
Graphics.Blit(source, rt);
Texture2D result = new Texture2D(newWidth, newHeight);
result.ReadPixels(new Rect(0, 0, newWidth, newHeight), 0, 0);
result.Apply();
RenderTexture.ReleaseTemporary(rt);
return result;
}
#endregion
}
}Unity主线程调度器
using System;
using System.Collections.Concurrent;
using UnityEngine;
/// <summary>
/// 安全地将操作调度到Unity主线程执行
/// </summary>
public class UnityMainThreadDispatcher : MonoBehaviour
{
private static UnityMainThreadDispatcher _instance;
private static readonly ConcurrentQueue<Action> _executionQueue = new ConcurrentQueue<Action>();
private static volatile bool _isApplicationQuitting;
/// <summary>
/// 获取调度器实例(自动创建如果不存在)
/// </summary>
public static UnityMainThreadDispatcher Instance
{
get
{
if (_isApplicationQuitting)
{
Debug.LogWarning("[MainThreadDispatcher] 应用正在退出,不再创建新实例");
return null;
}
if (_instance == null)
{
var instances = FindObjectsOfType<UnityMainThreadDispatcher>();
if (instances.Length > 1)
{
Debug.LogError("[MainThreadDispatcher] 存在多个实例,这是不允许的");
return instances[0];
}
else if (instances.Length == 1)
{
_instance = instances[0];
}
else
{
GameObject obj = new GameObject("MainThreadDispatcher");
_instance = obj.AddComponent<UnityMainThreadDispatcher>();
DontDestroyOnLoad(obj);
Debug.Log("[MainThreadDispatcher] 自动创建实例");
}
}
return _instance;
}
}
/// <summary>
/// 将操作加入主线程执行队列
/// </summary>
/// <param name="action">要执行的操作</param>
public static void Enqueue(Action action)
{
if (action == null)
{
Debug.LogWarning("[MainThreadDispatcher] 尝试加入空操作");
return;
}
if (Instance != null)
{
_executionQueue.Enqueue(action);
}
else
{
Debug.LogError("[MainThreadDispatcher] 调度器不可用,操作被丢弃");
}
}
/// <summary>
/// 延迟初始化(可选)
/// </summary>
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void AutoInitialize()
{
// 提前初始化实例
var _ = Instance;
}
private void Awake()
{
if (_instance == null)
{
_instance = this;
}
else if (_instance != this)
{
Debug.LogError("[MainThreadDispatcher] 检测到重复实例,即将销毁");
Destroy(gameObject);
return;
}
}
private void Update()
{
// 执行所有排队操作
while (_executionQueue.TryDequeue(out var action))
{
try
{
action?.Invoke();
}
catch (Exception ex)
{
Debug.LogError($"[MainThreadDispatcher] 执行操作时出错: {ex}");
}
}
}
private void OnDestroy()
{
if (_instance == this)
{
_isApplicationQuitting = true;
}
}
}调用方法
// 在任何线程中调用(包括异步回调、HTTP回调等)
UnityMainThreadDispatcher.Enqueue(() => {
// 这里的代码会在主线程执行
gameObject.SetActive(true);
Debug.Log("这是在主线程执行的代码");
});时间戳
using System;
using System.Diagnostics;
using UnityEngine;
/// <summary>
/// Unity时间戳工具类
/// 提供多种时间戳获取方式
/// </summary>
public static class TimestampUtility
{
private static Stopwatch _stopwatch;
[RuntimeInitializeOnLoadMethod]
private static void Initialize()
{
_stopwatch = Stopwatch.StartNew();
}
#region 系统时间相关
/// <summary>
/// 获取当前Unix时间戳(秒)
/// </summary>
public static long UnixTimestampSeconds => DateTimeOffset.UtcNow.ToUnixTimeSeconds();
/// <summary>
/// 获取当前Unix时间戳(毫秒)
/// </summary>
public static long UnixTimestampMilliseconds => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
/// <summary>
/// 获取系统计时周期数(100纳秒间隔)
/// </summary>
public static long SystemTicks => DateTime.Now.Ticks;
/// <summary>
/// 获取格式化的本地时间字符串
/// </summary>
/// <param name="format">格式字符串(默认:yyyy-MM-dd HH:mm:ss.fff)</param>
public static string FormattedLocalTime(string format = "yyyy-MM-dd HH:mm:ss.fff")
{
return DateTime.Now.ToString(format);
}
#endregion
#region Unity游戏时间相关
/// <summary>
/// 获取游戏运行时间(秒,受TimeScale影响)
/// </summary>
public static float GameTime => Time.time;
/// <summary>
/// 获取游戏运行时间(秒,不受TimeScale影响)
/// </summary>
public static float GameTimeUnscaled => Time.unscaledTime;
/// <summary>
/// 获取从游戏启动开始的帧数
/// </summary>
public static int FrameCount => Time.frameCount;
#endregion
#region 高精度计时
/// <summary>
/// 获取高精度计时器周期数
/// </summary>
public static long HighPrecisionTicks => _stopwatch.ElapsedTicks;
/// <summary>
/// 获取高精度计时毫秒数
/// </summary>
public static long HighPrecisionMilliseconds => _stopwatch.ElapsedMilliseconds;
/// <summary>
/// 将计时周期数转换为秒
/// </summary>
public static double TicksToSeconds(long ticks)
{
return ticks / (double)Stopwatch.Frequency;
}
#endregion
#region 实用方法
/// <summary>
/// 生成基于时间戳的唯一ID
/// </summary>
public static string GenerateTimeBasedID()
{
return $"{DateTime.Now:yyyyMMddHHmmssfff}_{Guid.NewGuid().ToString("N").Substring(0, 8)}";
}
/// <summary>
/// 将Unix时间戳转换为DateTime
/// </summary>
public static DateTime UnixToDateTime(long unixTimestamp, bool isMilliseconds = false)
{
return isMilliseconds
? DateTimeOffset.FromUnixTimeMilliseconds(unixTimestamp).DateTime
: DateTimeOffset.FromUnixTimeSeconds(unixTimestamp).DateTime;
}
#endregion
}使用
using UnityEngine;
public class TimestampExample : MonoBehaviour
{
void Start()
{
// 基本时间戳
Debug.Log($"Unix秒级时间戳: {TimestampUtility.UnixTimestampSeconds}");
Debug.Log($"Unix毫秒级时间戳: {TimestampUtility.UnixTimestampMilliseconds}");
// 格式化时间
Debug.Log($"本地格式化时间: {TimestampUtility.FormattedLocalTime()}");
Debug.Log($"自定义格式时间: {TimestampUtility.FormattedLocalTime("yyyyMMdd_HHmmss")}");
// 游戏时间
Debug.Log($"游戏运行时间: {TimestampUtility.GameTime}");
Debug.Log($"真实游戏时间: {TimestampUtility.GameTimeUnscaled}");
// 高精度计时
Debug.Log($"高精度周期数: {TimestampUtility.HighPrecisionTicks}");
Debug.Log($"高精度毫秒数: {TimestampUtility.HighPrecisionMilliseconds}");
// 实用方法
Debug.Log($"生成唯一ID: {TimestampUtility.GenerateTimeBasedID()}");
}
void Update()
{
// 测量代码执行时间
long start = TimestampUtility.HighPrecisionTicks;
// 执行一些操作...
for (int i = 0; i < 1000; i++) { }
long end = TimestampUtility.HighPrecisionTicks;
double elapsedSeconds = TimestampUtility.TicksToSeconds(end - start);
Debug.Log($"操作耗时: {elapsedSeconds}秒");
}
}运行模式下Texture转换成Texture2D
/// <summary>
/// 将任意 Texture 转换为 Texture2D(兼容 RenderTexture/Texture2D/WebCamTexture 等)
/// </summary>
private Texture2D TextureToTexture2D(Texture texture)
{
if (texture == null)
{
Debug.LogError("Texture is null!");
return null;
}
// 如果已经是 Texture2D,直接返回副本
if (texture is Texture2D tex2D)
{
Texture2D copy = new Texture2D(tex2D.width, tex2D.height, tex2D.format, tex2D.mipmapCount > 1);
Graphics.CopyTexture(tex2D, copy);
return copy;
}
// 创建目标 Texture2D(自动匹配格式)
TextureFormat format = texture is WebCamTexture ? TextureFormat.RGBA32 : TextureFormat.ARGB32;
Texture2D result = new Texture2D(texture.width, texture.height, format, false);
// 使用 RenderTexture 转换
RenderTexture rt = RenderTexture.GetTemporary(
texture.width,
texture.height,
0, // 深度缓冲区
RenderTextureFormat.ARGB32 // 确保支持颜色数据
);
// 保存当前 RenderTexture 状态
RenderTexture previousRT = RenderTexture.active;
RenderTexture.active = rt;
// 复制纹理数据
Graphics.Blit(texture, rt);
result.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
result.Apply();
// 恢复状态并释放临时 RT
RenderTexture.active = previousRT;
RenderTexture.ReleaseTemporary(rt);
return result;
}使用Unity函数
使用BestHttp上传,直接上链接
下面的示例代码仅做记录
var request = new HTTPRequest(new Uri(url), HTTPMethods.Post, OnRequestFinished);
//添加token
request.AddHeader("Authorization", $"Bearer {Boot.Instance.appConfig.ServerAPI.Token}");
//添加multipart/form-data
var formStream = new MultipartFormDataStream()
.AddField("storage_id", "2")
.AddField("album_id", "1");
formStream.AddStreamField(
"file", new MemoryStream(pngData),
$"{System.DateTime.Now:yyyyMMdd_HHmmss}_{Random.Range(0, 1000)}.png",
"image/png");
request.UploadSettings.UploadStream = formStream;
request.Send();Transfrom的一些常用API
Camera.main.WordToScreenPoint()//三维空间坐标转屏幕坐标
rectTransformUtility.ScreenPointToLocalPointInRectangel
//屏幕点是否在矩形内,参数:在哪个矩形内,点,摄像机,返回的v2位置,屏幕点就会转为canvas上的点
rectTransformUtility.rectanglecontainsscreenpoint
//判断第二个参数代表的位置是否在第一个参数rect的范围内,
Transform.TransformPoint(Vector3 position)
//将一个坐标点从局部坐标系转换到全局坐标系。
Transform.InverseTransformPoint(Vector3 position)
//将坐标点从全局坐标系转换到局部坐标系。
Transform.TransformDirection(Vector3 direction)
//将一个方向从局部坐标系转换到全局坐标系。
Transform.InverseTransformDirection(Vector3 direction):
//将一个方向从全局坐标系转换到局部坐标系。
Transform.TransformVector(Vector3 vector):
//将一个向量从局部坐标系转换到全局坐标系。
Transform.InverseTransformVector(Vector3 vector)
//将一个向量从全局坐标系转换到局部坐标系。Kinect常用代码
kinectmanager.instance.isinitialized//是否初始化完成
kinectmanager.instance.getuserLbtex()//获取深度数据流
kinectManager.instance.instance.isuserdetected()//是否检测到用户
kinectManager.instance.instance.getprimaryuserid()//获取第一个或者最近的用户
kinectManager.instance.isjointracked(用户ID,关节枚举)
kinectInterop.instance.getjointkinectposition(用户ID,关节枚举)//获取关节位置,返货vector3
kinectInterop.instance.getRightHandState()//获取右手状态使用R3响应式切换分辨率
//切换分辨率
var Switchfullscreen = Observable.EveryUpdate().Where(_ => Input.GetKeyDown(KeyCode.Tab));
var obserSwitchfullscreen = Switchfullscreen.Chunk(Switchfullscreen.Debounce(TimeSpan.FromMilliseconds(250))); //不多说,原理我没研究,但效果跟我上面的实现一样
obserSwitchfullscreen.Where(xs => xs.Length >= 3)
.Subscribe((units =>
{
Resolution[] resolutions = Screen.resolutions;//获取设置当前屏幕分辩率
if (Screen.fullScreen)
{
Screen.SetResolution(resolutions[0].width, resolutions[0].height, false);//设置当前分辨率
Screen.fullScreen = false;
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
}
else
{
Screen.SetResolution(resolutions[resolutions.Length - 1].width, resolutions[resolutions.Length - 1].height, true);//设置当前分辨率
Screen.fullScreen = true; //设置成全屏
Cursor.visible = false;
Cursor.lockState = CursorLockMode.None;
}
}));
//全屏
Resolution[] resolutions = Screen.resolutions;//获取设置当前屏幕分辩率
Screen.SetResolution(resolutions[resolutions.Length - 1].width, resolutions[resolutions.Length - 1].height, true);//设置当前分辨率
Screen.fullScreen = true; //设置成全屏
Cursor.visible = false;
Cursor.lockState = CursorLockMode.None;图片自适应
public float SetSize(Texture2D photo, RectTransform container, bool maintainAspectRatio = true)
{
// 参数检查
if (photo == null)
{
Debug.LogError("Texture2D photo is null!");
return 0f;
}
if (container == null)
{
Debug.LogError("RectTransform container is null!");
return 0f;
}
// 获取容器尺寸
float containerHeight = container.rect.height;
float containerWidth = container.rect.width;
// 获取图片原始尺寸
float imageHeight = photo.height;
float imageWidth = photo.width;
// 计算宽高比
float aspectRatio = imageWidth / imageHeight;
float newWidth, newHeight;
if (maintainAspectRatio)
{
// 保持原始宽高比
newHeight = containerHeight;
newWidth = newHeight * aspectRatio;
// 检查是否超出容器宽度
if (newWidth > containerWidth)
{
// 如果超出,则根据宽度重新计算
newWidth = containerWidth;
newHeight = newWidth / aspectRatio;
}
}
else
{
// 不保持宽高比,直接填充高度
newHeight = containerHeight;
newWidth = containerWidth;
}
// 应用计算出的尺寸
container.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, newWidth);
container.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, newHeight);
return newWidth;
}public float SetSize(Texture2D photo, RectTransform container, bool maintainAspectRatio = true)
{
// 参数检查
if (photo == null)
{
Debug.LogError("Texture2D photo is null!");
return 0f;
}
if (container == null)
{
Debug.LogError("RectTransform container is null!");
return 0f;
}
// 获取容器尺寸
float containerHeight = container.rect.height;
float containerWidth = container.rect.width;
// 获取图片原始尺寸
float imageHeight = photo.height;
float imageWidth = photo.width;
// 计算宽高比
float aspectRatio = imageWidth / imageHeight;
float newWidth, newHeight;
if (maintainAspectRatio)
{
// 保持原始宽高比
newHeight = containerHeight;
newWidth = newHeight * aspectRatio;
// 检查是否超出容器宽度
if (newWidth > containerWidth)
{
// 如果超出,则根据宽度重新计算
newWidth = containerWidth;
newHeight = newWidth / aspectRatio;
}
}
else
{
// 不保持宽高比,直接填充高度
newHeight = containerHeight;
newWidth = containerWidth;
}
// 应用计算出的尺寸
container.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, newWidth);
container.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, newHeight);
return newWidth;
}洗牌
/// <summary>
/// 使用更安全的随机数生成器打乱列表
/// </summary>
/// <typeparam name="T">列表元素类型</typeparam>
/// <param name="list">要打乱的列表</param>
private void ShuffleList<T>(List<T> list)
{
using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
{
int n = list.Count;
while (n > 1)
{
byte[] box = new byte[1];
do rng.GetBytes(box);
while (!(box[0] < n * (byte.MaxValue / n)));
int k = (box[0] % n);
n--;
(list[k], list[n]) = (list[n], list[k]);
}
}
}硬旋转
using UnityEngine;
using System.Collections;
public class RotateScript : MonoBehaviour
{
// 旋转角度(度)
public float rotationAngle = 30f;
// 旋转间隔时间(秒)
public float rotationInterval = 0.5f;
// 旋转轴
public Vector3 rotationAxis = Vector3.up;
// 是否使用协程方式
public bool useCoroutine = true;
// 累积时间
private float timeCounter = 0f;
void Start()
{
if (useCoroutine)
{
// 使用协程方式
StartCoroutine(RotateCoroutine());
}
}
void Update()
{
if (!useCoroutine)
{
// 使用时间累积方式
timeCounter += Time.deltaTime;
if (timeCounter >= rotationInterval)
{
// 旋转物体
transform.Rotate(rotationAxis, rotationAngle);
// 重置计时器
timeCounter = 0f;
}
}
}
IEnumerator RotateCoroutine()
{
while (true)
{
// 等待指定时间
yield return new WaitForSeconds(rotationInterval);
// 旋转物体
transform.Rotate(rotationAxis, rotationAngle);
}
}
}
评论