当前位置:   article > 正文

Unity 动态打图集并完成小Demo的实现

动态打图集

为什么要动态打图集

比如在英雄联盟中的选择英雄界面,有很多的图标供我们选择,而我们进入游戏之后只需要选择的那两三个图标而已,这是如果我们将所有图标都打成图集,就造成内存浪费,因为我们只需要两三个而已,那么我们有什么办法让我们只将要用到的图标进行打图集,其他的不打进图集吗?有的,那就是动态打图集。

效果如下
点击下面三个框中的一个,再点击上面十个图标中的一个,就完成了选择图标,一个个选。
在这里插入图片描述

如何进行动态打图集

打图集肯定要有打图集的算法,这里提供了打图集的算法供我们使用,去下载就好了。

图集分块算法地址:https://github.com/DaVikingCode/UnityRuntimeSpriteSheetsGenerator/tree/master/Assets/RuntimeSpriteSheetsGenerator/Scripts

下载AssetPacker文件和RectanglePacking文件就可以了,总共有6个C#脚本,将它拖入到Unity中。

有了上面的打图集算法,我们写个生成图集的类。新建一个名为AssetPackerMgr的空物体,将AssetPackerMgr .cs挂载上去。

using System;
using System.Collections;
using System.Collections.Generic;
using DaVikingCode.AssetPacker;
using UnityEngine;

public class AssetPackerMgr : MonoBehaviour
{
    public static AssetPackerMgr Instance { get; private set; }
    private Dictionary<string,AssetPacker> _packers = new Dictionary<string, AssetPacker>();

    private void Awake()
    {
        //这样写单例是因为避免存在两个AssetPackerMgr,比如跳到下个场景再跳回来的时候。
        if (Instance == null)
        {
            Instance = this;
            GameObject.DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }

    /// <summary>
    /// 生成新图集的方法
    /// </summary>
    /// <param name="altasName">图集名</param>
    /// <param name="paths">路径</param>
    /// <param name="complete">打完图集后的回调</param>
    public void GentatorNewAltas(string altasName,Dictionary<string,string> paths,Action complete = null)
    {
        if(paths == null)
            return;
        
        AssetPacker packer = new GameObject(altasName).AddComponent<AssetPacker>();
        packer.transform.SetParent(transform);
        packer.cacheName = altasName;
        foreach (KeyValuePair<string,string> path in paths)
        {
            packer.AddTextureToPack(path.Value,path.Key);
        }
        packer.Process();
        //打完图集后的回调方法,即为异步加载场景
        packer.OnProcessCompleted.AddListener(() =>
        {
            if (complete != null)
                complete();
        });
        
        _packers.Add(altasName,packer);
    }

    /// <summary>
    /// 获取AssetPacker
    /// </summary>
    /// <param name="altasName"></param>
    /// <returns></returns>
    public AssetPacker GetAltas(string altasName)
    {
        if (_packers.ContainsKey(altasName))
        {
            return _packers[altasName];
        }
        else
        {
            Debug.LogError("can not find altas name is "+altasName);
            return null;
        }
    }

    /// <summary>
    /// 清除altasName所对应的AssetPacker
    /// </summary>
    /// <param name="altasName"></param>
    public void ClearAltas(string altasName)
    {
        if (_packers.ContainsKey(altasName))
        {
            AssetPacker packer = _packers[altasName];
            _packers.Remove(altasName);
            Destroy(packer.gameObject);
        }
        else
        {
            Debug.LogError("can not remove altas,because it can not find,name is"+altasName);
            return;
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92

在这里插入图片描述
图片中的1~10表示的是上面那10个小图标,打图集的话,肯定要知道图片路径的,所以我定义 了一个图片路径的类。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RuntimeAltasItem : MonoBehaviour
{
    public string Path;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

挂载在这10个图标上。

我这里写个编辑器的扩展方法,来获取这十个图标的路径,因为编辑器下有获取路径的API。

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

public class SpritePathHelper : MonoBehaviour
{
   
   [MenuItem("GameObject/SpritePathHelper",false,0)]
   private static void SetPath()
   {
      foreach (GameObject go in Selection.gameObjects)
      {
         foreach (Transform trans in go.transform)
         {
            if(trans.GetComponent<Image>() == null)
               continue;

            Sprite sprite = trans.GetComponent<Image>().sprite;
            string path = AssetDatabase.GetAssetPath(sprite);
            //Application.dataPath后面包含Assets,path前面也包含Assets,组合后重复了,所以用Substring(6)从Assets之后取值
            path = Application.dataPath + path.Substring(6);
            RuntimeAltasItem item = trans.GetComponent<RuntimeAltasItem>();
            if (item == null)
               item = trans.gameObject.AddComponent<RuntimeAltasItem>();

            item.Path = path;
         }
      }
   }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

我们选中SelectView,然后点击GameObject/SpritePathHelper就完成了获取图片路径并赋值了。

SelectView上挂载一个同名脚本,用来给每个图标添加监听事件,点击后就执行委托事件。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class SelectView : MonoBehaviour
{
    private Action<Sprite, string> _onSelected;
    
    // Start is called before the first frame update
    void Start()
    {
        foreach (var child in GetComponentsInChildren<Button>())
        {
            child.onClick.AddListener(() =>
            {
                Sprite sprite = child.GetComponent<Image>().sprite;
                RuntimeAltasItem item = child.GetComponent<RuntimeAltasItem>();

                if (_onSelected != null)
                    _onSelected(sprite, item.Path);
            });
        }
    }

    /// <summary>
    /// 提供一个添加委托的方法
    /// </summary>
    /// <param name="selected"></param>
    public void AddSelectedListener(Action<Sprite, string> selected)
    {
        _onSelected = selected;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

新建一个ShowItem.cs,后面会通过代码添加该脚本到Icon1,Icon2,Icon3的。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ShowItem : MonoBehaviour
{
    public ShowName ID { get; private set; }
    private Image _image;
    private RuntimeAltasItem _altasItem;

    /// <summary>
    /// 初始化数据
    /// </summary>
    /// <param name="id"></param>
    public void Init(ShowName id)
    {
        ID = id;
        _image = GetComponent<Image>();
        _altasItem = GetComponent<RuntimeAltasItem>();
    }

    /// <summary>
    /// 设置图片
    /// </summary>
    /// <param name="sprite"></param>
    public void SetSprite(Sprite sprite)
    {
        _image.sprite = sprite;
    }

    /// <summary>
    /// 给Button添加点击事件
    /// </summary>
    /// <param name="selected"></param>
    public void AddListener(Action selected)
    {
        gameObject.GetComponent<Button>().onClick.AddListener(()=>selected());
    }

    /// <summary>
    /// 返回ID和图片路径
    /// </summary>
    /// <returns></returns>
    public KeyValuePair<string, string> GetData()
    {
        return new KeyValuePair<string, string>(ID.ToString(),_altasItem.Path);
    }
}

public enum ShowName
{
    ICON_1,
    ICON_2,
    ICON_3,
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

新建一个SelectShowView 挂载到Show上。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SelectShowView : MonoBehaviour
{
    private ShowItem _selectedItem;
    private RuntimeAltasItem _altasItem;
    
    // Start is called before the first frame update
    void Start()
    {
        ShowName id = ShowName.ICON_1;
        foreach (Transform trans in transform)
        {
            RuntimeAltasItem altasItem = trans.gameObject.AddComponent<RuntimeAltasItem>();
            ShowItem item =trans.gameObject.AddComponent<ShowItem>();
            
            item.Init(id);
            id++;
            
            item.AddListener(() =>
            {
                _selectedItem = item;
                _altasItem = altasItem;
            });
        }
    }

    public void SetShowItem(Sprite sprite,string path)
    {
        _selectedItem.SetSprite(sprite);
        _altasItem.Path = path;
    }

    /// <summary>
    /// 返回存要显示的图片的ID和图片路径的字典
    /// </summary>
    /// <returns></returns>
    public Dictionary<string,string> GetPaths()
    {
        //存要显示的图片的ID和图片路径的字典
        Dictionary<string,string> temp = new Dictionary<string, string>();
        KeyValuePair<string, string> tempPair;
        foreach (ShowItem item in GetComponentsInChildren<ShowItem>())
        {
            tempPair = item.GetData();
            temp.Add(tempPair.Key,tempPair.Value);
        }
        return temp;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

新建一个名为Complete的Button,添加SelectedComplete .cs脚本,当选择完图标的时候,可以执行异步加载场景。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class SelectedComplete : MonoBehaviour
{
    public void Init(Func<Dictionary<string,string>> getPaths,LoadingView loadingView)
    {
        GetComponent<Button>().onClick.AddListener(() =>
        {
            loadingView.SetActiveState(true);
            AssetPackerMgr.Instance.GentatorNewAltas("Test",getPaths(),()=>loadingView.SwitchScene(SceneName.Game.ToString()));
        });
    }
}

public enum SceneName
{
    SelectView,
    Game
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

新建一个ShowView.cs,挂载在Canvas上

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ShowView : MonoBehaviour
{
    private SelectShowView _selectShowView;
    
    void Start()
    {
        GetComponentInChildren<SelectView>().AddSelectedListener(SetShowItem);
        _selectShowView = GetComponentInChildren<SelectShowView>();
        GetComponentInChildren<SelectedComplete>().Init(()=>
            _selectShowView.GetPaths(),
            GetComponentInChildren<LoadingView>(true));
    }

    /// <summary>
    /// 设置将上面选择的图标显示在下面的Image上
    /// </summary>
    /// <param name="sprite"></param>
    /// <param name="path"></param>
    private void SetShowItem(Sprite sprite,string path)
    {
        if(_selectShowView == null)
            return;
        
        _selectShowView.SetShowItem(sprite,path);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

新建一个名为Loading的Image,设置成纯色的全屏背景充当异步加载的过度背景,上面添加LoadingView .cs,在Loading下面新建一个Text子物体,用于制作异步加载的过渡动画。Loading先设置隐藏。

using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class LoadingView : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        GetComponentInChildren<Text>().DOText("Loading...", 3).SetLoops(-1, LoopType.Restart);
    }

    /// <summary>
    /// 设置加载界面的隐藏显示
    /// </summary>
    /// <param name="isShow"></param>
    public void SetActiveState(bool isShow)
    {
        gameObject.SetActive(isShow);
    }

    /// <summary>
    /// 异步加载场景
    /// </summary>
    /// <param name="sceneName">需要加载的场景</param>
    public void SwitchScene(string sceneName)
    {
        StartCoroutine(LoadScene(sceneName));
    }

    private IEnumerator LoadScene(string scenceName)
    {
        AsyncOperation async = SceneManager.LoadSceneAsync(scenceName);
        async.allowSceneActivation = false;

        while (!async.isDone)
        {
            if (async.progress >= 0.9f)
            {
                yield return new WaitForSeconds(2);
                async.allowSceneActivation = true;//为true的时候才会跳转场景
            }
            
            yield return new WaitForSeconds(0.5f);
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

上面的代码中的文本动画用到了Dotween插件,可自行去下载,或者可以自己手写一个简单的过渡动画也可以。

在这里插入图片描述
以上就是动态打图集并演示的过程。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/636947
推荐阅读
相关标签
  

闽ICP备14008679号