以下大部分内容由AI生成,本页仅作为记录

生成二维码

首先导入ZXing.net包,也可以使用NuGetForUnity包快速导入。使用下面代码创建。

NuGetForUnityd导入的包不是unity适配化的,直接去github ZXing.net Release界面下载压缩包,里面有一个专门的unity使用的

https://github.com/micjahn/ZXing.Net/releases/
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上传,直接上链接

https://bestdocshub.pages.dev/HTTP/getting-started/uploads/#upload-an-multipartform-data-form

下面的示例代码仅做记录

            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);
        }
    }
}