赞
踩
比如在英雄联盟中的选择英雄界面,有很多的图标供我们选择,而我们进入游戏之后只需要选择的那两三个图标而已,这是如果我们将所有图标都打成图集,就造成内存浪费,因为我们只需要两三个而已,那么我们有什么办法让我们只将要用到的图标进行打图集,其他的不打进图集吗?有的,那就是动态打图集。
效果如下
点击下面三个框中的一个,再点击上面十个图标中的一个,就完成了选择图标,一个个选。
打图集肯定要有打图集的算法,这里提供了打图集的算法供我们使用,去下载就好了。
图集分块算法地址: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~10表示的是上面那10个小图标,打图集的话,肯定要知道图片路径的,所以我定义 了一个图片路径的类。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RuntimeAltasItem : MonoBehaviour
{
public string Path;
}
挂载在这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; } } } }
我们选中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; } }
新建一个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, }
新建一个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; } }
新建一个名为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 }
新建一个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); } }
新建一个名为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); } } }
上面的代码中的文本动画用到了Dotween插件,可自行去下载,或者可以自己手写一个简单的过渡动画也可以。
以上就是动态打图集并演示的过程。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。