Unity照片墙简易圆形交互效果总结

还要很多可以优化的点地方,有兴趣的可以做
比如对象的销毁和生成可以做成对象池,走到最左边后再移动到最右边循环利用

分析过程文件,采用Blender,资源已上传,可以播放动画看效果,下面截个图:
在这里插入图片描述

视频效果如下:

anim

Untiy结构如下:
在这里插入图片描述
上面的ImageItem是我手动添加展示关系用的,默认就一个Target,PictureWall挂PictureWall脚本,ImageItem(预制体)挂ImageItemController 脚本即可

using UnityEngine;

public class ImageItemController : MonoBehaviour
{
    public RectTransform target;
    public float speed = 10;
    [SerializeField]
    private float radiusScale = 1;
    public float horizontalOffset = 0;
    private RectTransform rect;
    private float radius = 0;
    private Vector2 originalPos = Vector2.zero;
    private bool isCheck = false;
    private bool isStartRotate = false;
    private Vector2 circleCenter;
    private float xDelta = 0;
    private float offset = 0;

    void Start()
    {
        rect = transform as RectTransform;
        rect.anchoredPosition = new Vector2(rect.anchoredPosition.x - horizontalOffset, rect.anchoredPosition.y);
        radius = target.rect.width * radiusScale;// * Random.Range(0.8f, 1); 半径可以在范围内随机
    }

    /* 1.现根据接触点计算出圆的路径:目标移动的位移,计算在圆的的位置,只需修改x即可,y保持不变
     * 2.计算出的位置x加上移动的距离,得出最总x的位置
     * 3.设置位置即可
     * 4.走远时的接触点:开始接触时的关于x对称位置  
     * 5.添加移动:平移原点和圆点即可
     */

    //移动
    void Update()
    {
        if (!isCheck)
        {
            var dis = Vector2.Distance(target.anchoredPosition, rect.anchoredPosition);

            if (dis <= radius)
            {
                isCheck = true;
                originalPos = rect.anchoredPosition;

                float y = Mathf.Abs(originalPos.y - target.anchoredPosition.y);
                float xToCircleCenter = Mathf.Sqrt(radius * radius - y * y);
                float x = originalPos.x - xToCircleCenter;
                circleCenter = new Vector2(x, target.anchoredPosition.y);

                isStartRotate = true;
                rect.SetSiblingIndex(transform.parent.childCount - 2);
            }
        }

        xDelta = Time.deltaTime * speed;
        rect.anchoredPosition = new Vector2(rect.anchoredPosition.x - xDelta, rect.anchoredPosition.y);

        if (isStartRotate)
        {
            circleCenter.x -= xDelta;
            originalPos.x -= xDelta;
            float moveXDistance = target.anchoredPosition.x - circleCenter.x;
            float x = originalPos.x - circleCenter.x - moveXDistance;
            float y = Mathf.Sqrt(radius * radius - x * x);

            float maxY = radius;
            if (originalPos.y < circleCenter.y)
            {
                y = -y;
                maxY = -radius;
            }
            Vector2 circlePoint = new Vector2(x, y);
            if (rect.anchoredPosition.x >= target.anchoredPosition.x)
            {
                var v1 = circlePoint - (originalPos - circleCenter);
                var v2 = (originalPos - circleCenter) + new Vector2(0, maxY);
                v2.Normalize();
                offset = Vector2.Dot(v1, v2);
            }
            else
            {
                float tempX = originalPos.x - circleCenter.x;
                Vector2 originalPos2 = originalPos + 2 * new Vector2(-tempX, 0);
                var v1 = circlePoint - new Vector2(0, maxY);
                var v2 = originalPos2 - circleCenter + new Vector2(0, maxY);
                v2.Normalize();
                offset = -Vector2.Dot(v1, v2);
            }

            if (float.IsNaN(offset))
            {
                offset = 0;
            }
            x += moveXDistance + offset;

            Vector2 pos = circleCenter + new Vector2(x, y);
            rect.anchoredPosition = pos;

            if (target.anchoredPosition.x >= originalPos.x + originalPos.x - circleCenter.x)
            {
                rect.anchoredPosition = originalPos;
                rect.SetAsFirstSibling();
            }
            else if (target.anchoredPosition.x <= circleCenter.x)
            {
                rect.anchoredPosition = originalPos;
                rect.SetAsFirstSibling();
            }
        }

        if (rect.anchoredPosition.x < -rect.rect.width)
        {
            Destroy(gameObject);
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
using Utility;

public class PictureWall : MonoBehaviour
{
    [SerializeField]
    private GameObject prefab;
    private const float WIDTH = 3072;
    private const float HEIGHT = 1664;

    private int row = 6;
    private int column;
    private float intervalDistance = 20;
    [SerializeField]
    private float offset = 200;

    private float itemWidth;
    private float itemHeight;

    public float speed = 10;
    private float time = 0;
    [SerializeField]
    private RectTransform target;

    private string path = "/PictureWall/";
    private List<string> texturePaths;
    private int currentIndex = 0;
    private List<Texture2D> textureList;

   
    void Start()
    {
        textureList = new List<Texture2D>();
        path = Application.streamingAssetsPath + path;
        ReadImage();
        CalculateRowColumn();
        enabled = false;
    }

    private void ReadImage()
    {
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
            return;
        }
        texturePaths = new List<string>();

        var jpgs = Directory.GetFiles(path, "*.jpg");
        texturePaths.AddRange(jpgs);
        texturePaths.Reverse();
        if (texturePaths.Count > 100)
        {
            for (int i = texturePaths.Count - 1; i == 100; i--)
            {
                File.Delete(texturePaths[i]);
                texturePaths.RemoveAt(i);
            }
        }

        foreach (var filePath in texturePaths)
        {
            UtilityLoadImage.I.LoadImage(filePath, tex =>
            {
                textureList.Add(tex);
                addNum++;
                if (addNum == texturePaths.Count)
                {
                    Spawn();
                }
            });
        }
    }

    float addNum = 0;

    private void Spawn()
    {
        float x = 0;
        float y = 0;

        for (int i = 0; i < row; i++)
        {
            y = i * (itemHeight + intervalDistance);
            for (int j = 0; j < column; j++)
            {
                x = j * (itemWidth + intervalDistance);
                if (i % 2 != 0)
                {
                    //x -= offset;
                }
                RectTransform rect = Instantiate(prefab, transform).transform as RectTransform;
                rect.pivot = new Vector2(0.5f, 0.5f);
                rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, itemWidth);
                rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, itemHeight);
                rect.anchoredPosition = new Vector2(x, -y) + new Vector2(rect.rect.width / 2, -rect.rect.height / 2);
                var controller = rect.GetComponent<ImageItemController>();
                controller.speed = speed;
                controller.target = target;
                if (i % 2 != 0)
                {
                    controller.horizontalOffset = offset;
                }

                SetTexture(rect);
            }
        }
        target.SetAsLastSibling();  
        enabled = true;
    }

    private void CalculateRowColumn()
    {
        itemHeight = (HEIGHT - (row - 1) * intervalDistance) / row;
        itemWidth = itemHeight * 16 / 9;
        //offset = itemWidth / 2;
        column = (int)(WIDTH / (itemWidth + intervalDistance)) + 3;
        time = itemWidth / speed;
    }

    bool isSpawned = false;

    private void Update()
    {
        if (!isSpawned && transform.childCount <= (column - 1) * row + 1)
        {
            isSpawned = true;

            SpawnColumn();
        }
    }
    

    private void SpawnColumn()
    {
        float x = 0;
        float y = 0;

        for (int i = 0; i < row; i++)
        {
            y = i * (itemHeight + intervalDistance);
            for (int j = 0; j < 1; j++)
            {
                x = (column - 1) * (itemWidth + intervalDistance);
                RectTransform rect = Instantiate(prefab, transform).transform as RectTransform;
                rect.pivot = new Vector2(0.5f, 0.5f);
                rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, itemWidth);
                rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, itemHeight);
                rect.anchoredPosition = new Vector2(x, -y) + new Vector2(intervalDistance - Time.deltaTime * speed, -rect.rect.height / 2);
                var controller = rect.GetComponent<ImageItemController>();
                controller.speed = speed;
                controller.target = target;
                if (i % 2 != 0)
                {
                    controller.horizontalOffset = offset;
                }

                SetTexture(rect);
            }
        }
        target.SetAsLastSibling();
        StartCoroutine(Delay());
    }

    private void SetTexture(RectTransform rect)
    {        
        rect.GetComponent<RawImage>().texture = textureList[currentIndex];
        currentIndex = (currentIndex + 1) % texturePaths.Count;
    }

    private IEnumerator Delay()
    {
        //yield return new WaitForSeconds(0.1f);
        yield return null;
        isSpawned = false;
    }
}

工具类
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

namespace Utility
{
    public class UtilityLoadImage
    {
        public class MonoHelper : MonoBehaviour { }

        public static UtilityLoadImage I;

        private static MonoHelper helper;

        static UtilityLoadImage()
        {
            var go = new GameObject("UtilityLoadImage");
            helper = go.AddComponent<MonoHelper>();
            UnityEngine.Object.DontDestroyOnLoad(go);
            I = new UtilityLoadImage();
        }

        private UtilityLoadImage() { }

        #region  inner method
        private IEnumerator LoadTexture2D(string path, Action<Texture2D> callback)
        {           
            //Debug.Log("path:" + path);           
            UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(path);

            yield return uwr.SendWebRequest();

            if (uwr.downloadHandler.isDone)
            {
                var tex = DownloadHandlerTexture.GetContent(uwr);
                callback?.Invoke(tex);
            }
        }
        private void LoadTexture2DByFile(string path, Action<Texture2D> callback)
        {
            if (path.StartsWith("file://"))
            {
                var bytes = File.ReadAllBytes(path);
                Texture2D tex = new Texture2D(1, 1);
                if (tex.LoadImage(bytes))
                    callback?.Invoke(tex);
            }
        }

        private IEnumerator LoadByte(string path, Action<byte[]> callback)
        {
            UnityWebRequest uwr = UnityWebRequest.Get(path);
            yield return uwr.SendWebRequest();

            if (uwr.downloadHandler.isDone)
            {
                var data = uwr.downloadHandler.data;

                callback?.Invoke(data);
            }
        }

        private void DeleteFolder(string savedFolder, bool clearSavedPath, bool isRecursive = false)
        {
            if (!Directory.Exists(savedFolder))
            {
                Debug.LogError("要删除的文件夹不存在!");
                return;
            }

            if (clearSavedPath)
            {
                Directory.Delete(savedFolder, isRecursive);
                Directory.CreateDirectory(savedFolder);
            }
        }

        private byte[] Texture2DToByte(string path, Texture2D tex)
        {
            byte[] data = null;

            int index = path.LastIndexOf('.');
            if (index != -1)
            {
                string expandedName = path.Substring(index + 1);

                switch (expandedName)
                {
                    case "jpeg":
                    case "jpg":
                        data = tex.EncodeToJPG();
                        break;
                    case "png":
                        data = tex.EncodeToPNG();
                        break;
                    default:
                        Debug.Log("");
                        break;
                }
            }
            else
            {
                Debug.Log("path is not correct!!!");
            }

            return data;
        }

        private IEnumerator LoadAudio(string path, string savedFolder, string fileName, Action<AudioClip> callback)
        {
            UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(path, AudioType.MPEG);
            yield return request.SendWebRequest();
            if (request.downloadHandler.isDone)
            {
                File.WriteAllBytes(savedFolder + "/" + fileName, request.downloadHandler.data);

                AudioClip clip = DownloadHandlerAudioClip.GetContent(request);

                callback?.Invoke(clip);
            }
        }

        private IEnumerator LoadAudio(string path, string savePath, Action<AudioClip> callback)
        {
            UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(path, AudioType.MPEG);
            yield return request.SendWebRequest();
            if (request.downloadHandler.isDone)
            {
                File.WriteAllBytes(savePath, request.downloadHandler.data);

                AudioClip clip = DownloadHandlerAudioClip.GetContent(request);

                callback?.Invoke(clip);
            }
        }

        private IEnumerator LoadAudio(string path, Action<AudioClip> callback)
        {
            UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(path, AudioType.MPEG);
            yield return request.SendWebRequest();
            if (request.downloadHandler.isDone)
            {
                AudioClip clip = DownloadHandlerAudioClip.GetContent(request);

                if (callback != null)
                    callback(clip);
                else
                    Debug.Log("加载音频回调为null");
            }
        }

        #endregion

        #region load and download image
        public void LoadImage(string path, Action<Texture2D> callback)
        {
            helper.StartCoroutine(LoadTexture2D(path, callback));
        }

        public void LoadImageByFile(string path, Action<Texture2D> callback)
        {
            LoadTexture2DByFile(path, callback);
        }

        public void LoadImages(string[] paths, Action<List<Texture2D>> callback)
        {
            Debug.Log("start!!!!!");
            List<Texture2D> list = new List<Texture2D>();
            for (int i = 0; i < paths.Length; i++)
            {
                LoadImage(paths[i], tex => list.Add(tex));
            }
            callback?.Invoke(list);
            Debug.Log("end!!!!!" + list.Count);
        }

        public void LoadImagesByFile(string[] paths, Action<List<Texture2D>> callback)
        {
            List<Texture2D> list = new List<Texture2D>();
            for (int i = 0; i < paths.Length; i++)
            {
                var data = File.ReadAllBytes(paths[i]);
                Texture2D tex = new Texture2D(1, 1);
                if (tex.LoadImage(data))
                    list.Add(tex);
            }
            callback?.Invoke(list);
        }

        public void DownloadImageAndSave(string url, string savedFolder, string fileName, Action callback = null)
        {
            helper.StartCoroutine(LoadTexture2D(url, tex =>
            {
                File.WriteAllBytes(savedFolder + "/" + fileName, Texture2DToByte(url, tex));

                callback?.Invoke();
            }));
        }
        public void DownloadImageAndSave(string url, string savePath, Action callback = null)
        {
            helper.StartCoroutine(LoadTexture2D(url, tex =>
            {
                File.WriteAllBytes(savePath, Texture2DToByte(url, tex));

                callback?.Invoke();
            }));
        }

        public void DownloadImageAndSave_Texture2D(string url, string savedFolder, string fileName, Action<Texture2D> callback = null)
        {
            helper.StartCoroutine(LoadTexture2D(url, tex =>
            {
                File.WriteAllBytes(savedFolder + "/" + fileName, Texture2DToByte(url, tex));

                callback?.Invoke(tex);
            }));
        }
        public void DownloadImageAndSave_Texture2D(string url, string savePath, Action<Texture2D> callback = null)
        {
            helper.StartCoroutine(LoadTexture2D(url, tex =>
            {
                File.WriteAllBytes(savePath, Texture2DToByte(url, tex));

                callback?.Invoke(tex);
            }));
        }

        public void DownloadImageAndSave_FilePath(string url, string savedFolder, string fileName, Action<string> callback = null)
        {
            helper.StartCoroutine(LoadTexture2D(url, tex =>
            {
                string path = savedFolder + "/" + fileName;
                File.WriteAllBytes(path, Texture2DToByte(url, tex));
                callback?.Invoke(path);
            }));
        }
        public void DownloadImageAndSave_FilePath(string url, string savePath, Action<string> callback = null)
        {
            helper.StartCoroutine(LoadTexture2D(url, tex =>
            {
                File.WriteAllBytes(savePath, Texture2DToByte(url, tex));
                callback?.Invoke(savePath);
            }));
        }

        public void DownloadImagesAndSave(string[] urls, string savedFolder, string[] fileNames, Action completedCallback = null, bool deleteFolder = false, bool recursive = false)
        {
            if (urls.Length != fileNames.Length)
            {
                Debug.Log("下载数量和保存的文件数量不一致!");
                return;
            }

            DeleteFolder(savedFolder, deleteFolder, recursive);

            int completedNum = 0;

            for (int i = 0; i < urls.Length; i++)
            {
                DownloadImageAndSave(urls[i], savedFolder, fileNames[i], () =>
                {
                    ++completedNum;
                    if (completedNum == urls.Length)
                    {
                        completedCallback?.Invoke();
                        Debug.Log("所以文件下载完成!");
                    }
                });
            }
        }

        public void DownloadImagesAndSave_Texture2DPaths(string[] urls, string savedFolder, string[] fileNames, Action<string[]> callback = null, bool deleteFolder = false, bool recursive = false)
        {
            if (urls.Length != fileNames.Length)
            {
                Debug.Log("下载数量和保存的文件数量不一致!");
                return;
            }

            DeleteFolder(savedFolder, deleteFolder, recursive);

            int completedNum = 0;
            string[] filePaths = new string[fileNames.Length];

            for (int i = 0; i < urls.Length; i++)
            {
                DownloadImageAndSave_FilePath(urls[i], savedFolder, fileNames[i], path =>
                {
                    filePaths[completedNum] = path;
                    ++completedNum;
                    if (completedNum == urls.Length)
                    {
                        callback?.Invoke(filePaths);
                        Debug.Log("所以图片下载完成!");
                    }
                });
            }
        }

        public void DownloadImagesAndSave_Texture2DPaths(string[] urls, string[] savePaths, Action<string[]> callback = null)
        {
            if (urls.Length != savePaths.Length)
            {
                Debug.Log("下载数量和保存的文件数量不一致!");
                return;
            }

            int completedNum = 0;
            string[] filePaths = new string[savePaths.Length];

            for (int i = 0; i < urls.Length; i++)
            {
                DownloadImageAndSave_FilePath(urls[i], savePaths[i], path =>
                {
                    filePaths[completedNum] = path;
                    ++completedNum;
                    if (completedNum == urls.Length)
                    {
                        callback?.Invoke(filePaths);
                        Debug.Log("所以图片下载完成!");
                    }
                });
            }
        }

        public void DownloadImagesAndSave_Texture2Ds(string[] urls, string savedFolder, string[] fileNames, Action<Texture2D[]> callback = null, bool deleteFolder = false, bool recursive = false)
        {
            if (urls.Length != fileNames.Length)
            {
                Debug.Log("下载数量和保存的文件数量不一致!");
                return;
            }

            DeleteFolder(savedFolder, deleteFolder, recursive);

            int completedNum = 0;
            Texture2D[] textures = new Texture2D[fileNames.Length];

            for (int i = 0; i < urls.Length; i++)
            {
                DownloadImageAndSave_Texture2D(urls[i], savedFolder, fileNames[i], tex =>
                {
                    textures[completedNum] = tex;
                    ++completedNum;
                    if (completedNum == urls.Length)
                    {
                        callback?.Invoke(textures);
                        Debug.Log("所以图片下载完成!");
                    }
                });
            }
        }

        public void DownloadImagesAndSave_Texture2Ds(string[] urls, string[] savePaths, Action<Texture2D[]> callback = null)
        {
            if (urls.Length != savePaths.Length)
            {
                Debug.Log("下载数量和保存的文件数量不一致!");
                return;
            }

            int completedNum = 0;
            Texture2D[] textures = new Texture2D[savePaths.Length];

            for (int i = 0; i < urls.Length; i++)
            {
                DownloadImageAndSave_Texture2D(urls[i], savePaths[i], tex =>
                {
                    textures[completedNum] = tex;
                    ++completedNum;
                    if (completedNum == urls.Length)
                    {
                        callback?.Invoke(textures);
                        Debug.Log("所以图片下载完成!");
                    }
                });
            }
        }
        #endregion

        #region download file

        public void DownloadFileAndSave(string url, string savedFolder, string fileName, Action callback = null)
        {
            helper.StartCoroutine(LoadByte(url, data =>
            {
                File.WriteAllBytes(savedFolder + "/" + fileName, data);

                callback?.Invoke();
            }));
        }
        public void DownloadFileAndSave(string url, string savePath, Action callback = null)
        {
            helper.StartCoroutine(LoadByte(url, data =>
            {
                File.WriteAllBytes(savePath, data);

                callback?.Invoke();
            }));
        }

        public void DownloadFileAndSave_FilePath(string url, string savedFolder, string fileName, Action<string> callback = null)
        {
            helper.StartCoroutine(LoadByte(url, data =>
            {
                string path = savedFolder + "/" + fileName;

                File.WriteAllBytes(path, data);

                callback?.Invoke(path);
            }));
        }
        public void DownloadFileAndSave_FilePath(string url, string savePath, Action<string> callback = null)
        {
            helper.StartCoroutine(LoadByte(url, data =>
            {
                File.WriteAllBytes(savePath, data);

                callback?.Invoke(savePath);
            }));
        }

        public void DownloadFileAndSave_FileData(string url, string savedFolder, string fileName, Action<byte[]> callback = null)
        {
            helper.StartCoroutine(LoadByte(url, data =>
            {
                string path = savedFolder + "/" + fileName;

                File.WriteAllBytes(path, data);

                callback?.Invoke(data);
            }));
        }
        public void DownloadFileAndSave_FileData(string url, string savePath, Action<byte[]> callback = null)
        {
            helper.StartCoroutine(LoadByte(url, data =>
            {
                File.WriteAllBytes(savePath, data);

                callback?.Invoke(data);
            }));
        }

        public void DownloadFilesAndSave(string[] urls, string savedFolder, string[] fileNames, Action completedCallback = null, bool deleteFolder = false, bool recursive = false)
        {
            if (urls.Length != fileNames.Length)
            {
                Debug.Log("下载数量和保存的文件数量不一致!");
                return;
            }

            DeleteFolder(savedFolder, deleteFolder, recursive);

            int completedNum = 0;
            for (int i = 0; i < urls.Length; i++)
            {
                DownloadFileAndSave(urls[i], savedFolder, fileNames[i], () =>
                {
                    ++completedNum;
                    if (completedNum == fileNames.Length)
                    {
                        completedCallback?.Invoke();
                    }
                });
            }
        }
        public void DownloadFilesAndSave(string[] urls, string[] savePath, Action callback = null)
        {
            if (urls.Length != savePath.Length)
            {
                Debug.Log("下载数量和保存的文件数量不一致!");
                return;
            }

            int completedNum = 0;
            for (int i = 0; i < urls.Length; i++)
            {
                DownloadFileAndSave(urls[i], savePath[i], () =>
                {
                    ++completedNum;
                    if (completedNum == savePath.Length)
                    {
                        callback?.Invoke();
                    }
                });
            }
        }

        public void DownloadFilesAndSave_FilePaths(string[] urls, string savedFolder, string[] fileNames, Action<string[]> completedCallback = null, bool deleteFolder = false, bool recursive = false)
        {
            if (urls.Length != fileNames.Length)
            {
                Debug.Log("下载数量和保存的文件数量不一致!");
                return;
            }

            DeleteFolder(savedFolder, deleteFolder, recursive);

            int completedNum = 0;
            string[] filePaths = new string[fileNames.Length];
            for (int i = 0; i < urls.Length; i++)
            {
                DownloadFileAndSave_FilePath(urls[i], savedFolder, fileNames[i], path =>
                {
                    filePaths[completedNum] = path;
                    ++completedNum;
                    if (completedNum == fileNames.Length)
                    {
                        completedCallback?.Invoke(filePaths);
                    }
                });
            }
        }
        public void DownloadFilesAndSave_FilePaths(string[] urls, string[] savePath, Action<string[]> completedCallback = null)
        {
            if (urls.Length != savePath.Length)
            {
                Debug.Log("下载数量和保存的文件数量不一致!");
                return;
            }

            int completedNum = 0;
            string[] filePaths = new string[savePath.Length];
            for (int i = 0; i < urls.Length; i++)
            {
                DownloadFileAndSave_FilePath(urls[i], savePath[i], path =>
                {
                    filePaths[completedNum] = path;
                    ++completedNum;
                    if (completedNum == savePath.Length)
                    {
                        completedCallback?.Invoke(filePaths);
                    }
                });
            }
        }

        public void DownloadFilesAndSave_FileDatas(string[] urls, string savedFolder, string[] fileNames, Action<List<byte[]>> completedCallback = null, bool deleteFolder = false, bool recursive = false)
        {
            if (urls.Length != fileNames.Length)
            {
                Debug.Log("下载数量和保存的文件数量不一致!");
                return;
            }

            DeleteFolder(savedFolder, deleteFolder, recursive);

            int completedNum = 0;
            List<byte[]> allDatas = new List<byte[]>(fileNames.Length);
            for (int i = 0; i < urls.Length; i++)
            {
                DownloadFileAndSave_FileData(urls[i], savedFolder, fileNames[i], data =>
                {
                    allDatas.Add(data);
                    ++completedNum;
                    if (completedNum == fileNames.Length)
                    {
                        completedCallback?.Invoke(allDatas);
                    }
                });
            }
        }
        public void DownloadFilesAndSave_FileDatas(string[] urls, string[] savePath, Action<List<byte[]>> completedCallback = null)
        {
            if (urls.Length != savePath.Length)
            {
                Debug.Log("下载数量和保存的文件数量不一致!");
                return;
            }

            int completedNum = 0;
            List<byte[]> allDatas = new List<byte[]>(savePath.Length);
            for (int i = 0; i < urls.Length; i++)
            {
                DownloadFileAndSave_FileData(urls[i], savePath[i], data =>
                {
                    allDatas.Add(data);
                    ++completedNum;
                    if (completedNum == savePath.Length)
                    {
                        completedCallback?.Invoke(allDatas);
                    }
                });
            }
        }
        #endregion

        #region download audio

        public void DownloadAudioAndSave_FileData(string url, string savedFolder, string fileName, Action<AudioClip> callback = null)
        {
            helper.StartCoroutine(LoadAudio(url, savedFolder, fileName, callback));
        }
        public void DownloadAudioAndSave_FileData(string url, string savePath, Action<AudioClip> callback = null)
        {
            helper.StartCoroutine(LoadAudio(url, savePath, callback));
        }
        public void LoadAudioClip(string url, Action<AudioClip> callback = null)
        {
            helper.StartCoroutine(LoadAudio(url, callback));
        }

        #endregion
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/498175.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

将MATLAB的图无失真复制到illustrator

选择复制选项 设置图元文件 复制到illustrator&#xff0c;可以看到每个图片部件都可以操作并且放大无失真

芒果YOLOv8改进145:全新风格原创YOLOv8网络结构解析图

&#x1f4a1;本篇分享一下个人绘制的原创全新风格 YOLOv8网络结构图 感觉搭配还行&#xff0c;看着比较直观。 该专栏完整目录链接&#xff1a; 芒果YOLOv8深度改进教程 订阅了专栏的读者 可以获取一份 <可以自行修改 / 编辑> 的 YOLOv8结构图修改源文件 YOLOv8结构图…

康耐视visionpro-CogBlobTool工具详细说明

CogBlobTool功能说明: 通过设置灰度值提取感兴趣区域,并分析所提取区域的面积、长宽等参数。 CogBlobTool操作说明: ①.打开工具栏,双击或点击鼠标拖拽添加CogBlobTool工具 ②.添加输入图像:单击鼠标右键“链接到”或以连线拖拽的方式选择相应输入源 ③.极性:“白底黑点…

康耐视visionpro-CogFindCircleTool工具详细说明

CogFindCircleTool功能说明: 通过用多个卡尺找到多个点来拟合所要找的圆 CogFindCircleTool操作说明: ①.打开工具栏,双击或点击鼠标拖拽添加CogFindCircleTool工具 ②.添加输入图像,右键“链接到”或以连线拖拽的方式选择相应输入源 ③.预期的圆弧:设置预期圆弧的中心点…

基于ssm的bbs论坛系统

开发环境&#xff1a;idea 前端&#xff1a;JQueryBootstraplayui后端&#xff1a;SpringSpringMVCMybatis数据库&#xff1a;mysqlredis 基于ssm的bbs论坛系统&#xff0c;功能有论坛、导读、动态、排行榜以及后台管理系统等等 话不多说&#xff0c;看图&#xff01;&#x…

VTK 9.2.6 加 QT6 编译

上一篇的example编译VTK 9.2.6 源码和VTK Examples 编译 Visual Studio 2022 增加 VTK_GROUP_ENABLE_Qt 为yes 指定QT6-DIR的路径为 C:\Qt\6.6.3\mingw_64\lib\cmake\Qt6

Android room 在dao中不能使用挂起suspend 否则会报错

错误&#xff1a; Type of the parameter must be a class annotated with Entity or a collection/array of it. kotlin.coroutines.Continuation<? super kotlin.Unit> $completion); 首先大家检查一下几个点 一、kotlin-kapt 二、 是否引入了 room-ktx 我是2024年…

康耐视visionpro-CogCaliperTool工具详细说明

CogCaliperTool功能说明: 卡尺工具,用于测量距离 CogCaliperTool操作说明: ①.打开工具栏,双击或点击鼠标拖拽添加CogCaliperTool ②.添加输入图像,右键“链接到”或以连线拖拽的方式选择相应输入源 ③.拖动屏幕上的矩形框到需要测量的位置。卡尺的搜索框角度与边缘不平…

C/C++ ③ —— C++11新特性

1. 类型推导 1.1 auto auto可以让编译器在编译期就推导出变量的类型 auto的使⽤必须⻢上初始化&#xff0c;否则⽆法推导出类型auto在⼀⾏定义多个变量时&#xff0c;各个变量的推导不能产⽣⼆义性&#xff0c;否则编译失败auto不能⽤作函数参数在类中auto不能⽤作⾮静态成员…

MYSQL数字函数:不可不知的数据处理利器

&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自&#xff1a;MYSQL数字函数&#xff1a;不可不知的数据处理利器 ✨​​​​​…

快速上手Spring Cloud 十五:与人工智能的智慧交融

快速上手Spring Cloud 一&#xff1a;Spring Cloud 简介 快速上手Spring Cloud 二&#xff1a;核心组件解析 快速上手Spring Cloud 三&#xff1a;API网关深入探索与实战应用 快速上手Spring Cloud 四&#xff1a;微服务治理与安全 快速上手Spring Cloud 五&#xff1a;Spring …

使用pytorch构建一个无监督的深度卷积GAN网络模型

本文为此系列的第二篇DCGAN&#xff0c;上一篇为初级的GAN。普通GAN有训练不稳定、容易陷入局部最优等问题&#xff0c;DCGAN相对于普通GAN的优点是能够生成更加逼真、清晰的图像。 因为DCGAN是在GAN的基础上的改造&#xff0c;所以本篇只针对GAN的改造点进行讲解&#xff0c;其…

Pytorch的hook函数

hook函数是勾子函数&#xff0c;用于在不改变原始模型结构的情况下&#xff0c;注入一些新的代码用于调试和检验模型&#xff0c;常见的用法有保留非叶子结点的梯度数据&#xff08;Pytorch的非叶子节点的梯度数据在计算完毕之后就会被删除&#xff0c;访问的时候会显示为None&…

RegSeg 学习笔记(待完善)

论文阅读 解决的问题 引用别的论文的内容 可以用 controlf 寻找想要的内容 PPM 空间金字塔池化改进 SPP / SPPF / SimSPPF / ASPP / RFB / SPPCSPC / SPPFCSPC / SPPELAN &#xfffc; ASPP STDC&#xff1a;short-term dense concatenate module 和 DDRNet SE-ResNeXt …

快速入门Axure RP:解答4个关键问题!

软件Axure RP 是一种功能强大的设计工具&#xff0c;用于使用 Web、移动和桌面应用程序项目创建交互原型。Axure RP软件中的 RP代表快速原型制作&#xff0c;这是软件Axure RP的核心特征。用户使用Axurere RP软件可以快速地将简单的想法创建成线框图和原型。Axure 因此&#xf…

实时数仓之实时数仓架构(Hudi)

目前比较流行的实时数仓架构有两类&#xff0c;其中一类是以FlinkDoris为核心的实时数仓架构方案&#xff1b;另一类是以湖仓一体架构为核心的实时数仓架构方案。本文针对FlinkHudi湖仓一体架构进行介绍&#xff0c;这套架构的特点是可以基于一套数据完全实现Lambda架构。实时数…

【二叉树】Leetcode 98. 验证二叉搜索树【中等】

验证二叉搜索树 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 示例1&a…

【Python函数和类2/6】函数的参数

目录 目标 为函数设置参数 传递实参 关键字实参 关键字实参的顺序 位置实参 常见错误 缺少实参 位置实参的顺序 默认值形参 参数的优先级 默认值形参的位置 总结 目标 上篇博客中&#xff0c;我们在定义函数时&#xff0c;使用了空的括号。这表示它不需要任何信息就…

浅谈C语言编译与链接

个人主页&#xff08;找往期文章包括但不限于本期文章中不懂的知识点&#xff09;&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 翻译环境和运行环境 在ANSI C&#xff08;标准 C&#xff09;的任何一种实现中&#xff0c;存在两个不同的环境。 第1种是翻译环境&#xff0c;在这个…

ssh 公私钥(github)

一、生成ssh公私钥 生成自定义名称的SSH公钥和私钥对&#xff0c;需要使用ssh-keygen命令&#xff0c;这是大多数Linux和Unix系统自带的标准工具。下面&#xff0c;简单展示如何使用ssh-keygen命令来生成具有自定义名称的SSH密钥对。 步骤 1: 打开终端 首先&#xff0c;打开我…