文章目录

  • 12.1 皮皮的梦想背包
  • 12.2 准备道具图片
  • 12.3 UGUI打图集
    • 12.3.1 设置图集模式:Always Enabled(Legacy Sprite Packer)
    • 12.3.2 设置图片为Sprite(2D and UI)类型
    • 12.3.3 设置Packing Tag
    • 12.3.4 使用Sprite Packer打图集
  • 12.4 制作精灵预设,通过预设加载精灵
  • 12.5 json配置表
  • 12.6 配置表读取
  • 12.7 制作界面预设
  • 12.8 资源管理器
  • 12.9 界面管理器
  • 12.10 背包数据读写
  • 12.11 背包管理器
  • 12.12 事件管理器
  • 12.13 界面代码
    • 12.13.1 大厅界面
    • 12.13.2 背包界面
    • 12.13.3 提示语UI
  • 12.14 程序入口脚本

简介:我是一名Unity游戏开发工程师,皮皮是我养的猫,会讲人话,它接到了喵星的特殊任务:学习编程,学习Unity游戏开发。
于是,发生了一系列有趣的故事。

12.1 皮皮的梦想背包

皮皮:“太阳天空照,花儿对我笑,小鸟说早早早,你为什么背着小书包?”
我:“我去上ang班,天天不迟到,爱学习爱工作,要不哪里有钱买猫粮。”
皮皮:“包里都装什么东西呀?有吃的吗?”
我打开我的双肩包:“没有吃的,有雨伞、水杯、工牌、纸巾、移动电源、充电器、笔、本子、各种卡,还有水电煤的单子。”
皮皮:“我也想要一个背包,不过我想要里面装满了我的玩具和爱吃的食物。”
我:“现在就给你做一个吧。”

模块设计如下

本工程使用的Unity版本为2020.1.14f1c1 (64-bit),工程已上传到GitHub,感兴趣的同学可以下载下来学习。
GitHub地址:https://github.com/linxinfa/Unity-BackpackDemo

12.2 准备道具图片


皮皮:“你这道具图片也太没诚意了。”
我:“不要在意这些细节。”
我们需要把图片放到Unity工程中,如下,放在Assets/Atlas/Bag文件夹中。

皮皮:“为什么不放在Resources文件夹中呢,这样可以直接通过Resources.Load加载图片。”
我:“因为后面我要将这些图片打成一个图集,放在Resources中的图片是不会被打进图集中的。”

12.3 UGUI打图集

UGUI中,我们可以使用ImageRawImage组件来显示图片。
皮皮:“RawImageImage有什么区别呢?”
RawImageImage都继承MaskableGraphic,但ImageRawImage的封装重一些。
RawImage只为我们提供了修改UV的方法,主要用来显示未加工的图片,比如背景图;而Image提供了四种ImageTypeSimple(普通)、Sliced(切割)、Tiled(平铺)、Filled(填充),显示图片的方式丰富;另外,Image支持精灵图的图集合批,可以减少DrawCall
道具种类繁多,道具图片可以打在一个图集中,这样显示多个道具图片可以合成一个批次显示,提升性能。
皮皮:“问题来了,怎么打图集呢?”

12.3.1 设置图集模式:Always Enabled(Legacy Sprite Packer)

点击菜单Edit - Project Settings...,打开Project Settings窗口。

点击Editor标签页,将Sprite PackerMode选为Always Enabled(Legacy Sprite Packer)

12.3.2 设置图片为Sprite(2D and UI)类型

将图片的Texture Type设置为为Sprite(2D and UI)

设置成功后,会有个小箭头,点开可以看到对应的Sprite

12.3.3 设置Packing Tag

想要把多张图片打在一个图集中,需要将这些图片的Packing Tag设置为相同的值。
比如都设置成Bag

不过这样每次都手动设置有点麻烦,写个Editor工具监听图片导入,自动修改图片格式,并设置Packing Tag为所在文件夹名字。
代码如下,将其保存为PostTexture.cs放在Editor文件夹中。

using UnityEngine;
using UnityEditor;
using System.IO;public class PostTexture : AssetPostprocessor
{/// <summary>/// 监听图片导入,自动修改图片格式,并设置Packing Tag为所在文件夹名字/// </summary>/// <param name="texture"></param>void OnPostprocessTexture(Texture2D texture){var dirName = Path.GetDirectoryName(assetPath);// 文件夹名字作为图集名字string atlasName = new DirectoryInfo(dirName).Name;TextureImporter textureImporter = assetImporter as TextureImporter;// 设置为Sprite(2D and UI)textureImporter.textureType = TextureImporterType.Sprite;// 设置设置Packing Tag为图集名字textureImporter.spritePackingTag = atlasName;// 不使用mipmap,否则图片可能会变模糊textureImporter.mipmapEnabled = false;}
}
12.3.4 使用Sprite Packer打图集

点击菜单Window - 2D - Sprite Packer

点击Pack按钮,即可将多张图片打成一个图集了。

打图集成,即可看到对应的图集了。

如果有多张图集,可以在View Atlas下拉菜单中切换显示。

12.4 制作精灵预设,通过预设加载精灵

使用过NGUI的同学应该知道,在NGUI中图集会有一个对应的图集预设,要加载某个精灵图,需要先加载对应的图集。UGUI则不同,直接加载对应的精灵即可。但是上面我们的精灵并不是放在Resources文件夹中,是不会被打进包体中的。如果将精灵图拷贝到Resources文件夹中,则会产生双份资源,造成资源冗余。
解决办法就是将精灵图包装成预设,把预设放在Resources文件夹中,这样就可以通过Resources.Load接口加载精灵图了。
预设中使用Sprite Renderer组件,设置它的Sprite属性为具体的精灵图。

这样挨个制作精灵预设很麻烦,所以写个Editor工具批量执行。
将下面的代码保存为MakeSpritePrefabs.cs放在Editor文件夹中。

using UnityEngine;
using UnityEditor;
using System.IO;public class MakeSpritePrefabs
{[MenuItem("Tools/MakeSpritePrefabs")]private static void Make(){// 创建 Assets/Resources/Sprite 文件夹,用来存放生成的精灵预设var prefabRootDir = Path.Combine(Application.dataPath, "Resources/Sprite");if (!Directory.Exists(prefabRootDir)){Directory.CreateDirectory(prefabRootDir);}// 遍历 Assets/Atlas 文件夹中的 png 图片var pngRootDir = Path.Combine(Application.dataPath, "Atlas");var pngFils = Directory.GetFiles(pngRootDir, "*.png", SearchOption.AllDirectories);foreach (string filePath in pngFils){string assetPath = filePath.Substring(filePath.IndexOf("Assets"));Sprite sprite = AssetDatabase.LoadAssetAtPath<Sprite>(assetPath);GameObject go = new GameObject(sprite.name);go.AddComponent<SpriteRenderer>().sprite = sprite;var prefabPath = Path.Combine(prefabRootDir, sprite.name + ".prefab");prefabPath = prefabPath.Substring(prefabPath.IndexOf("Assets"));// 将 gameObject 保存为预设PrefabUtility.SaveAsPrefabAsset(go, prefabPath);GameObject.DestroyImmediate(go);}// 刷新目录AssetDatabase.Refresh();}
}

点击菜单Tools - MakeSpritePrefabs即可自动将精灵图制作成预设了。
接着,我们就可以通过Resources.Load接口加载精灵图了。

var obj = Resources.Load<GameObject>("Sprite/bj");
var sprite = .GetComponent<SpriteRenderer>().sprite;

12.5 json配置表

我们将道具以json格式弄成配置表,如下

[{"id":1,"name":"逗猫棒","sprite":"dmb","desc":"逗猫棒是一种深受猫咪喜爱的玩具"},{"id":2,"name":"猫布丁","sprite":"mbd","desc":"猫布丁是以香滑布丁粉为主要材料制作而成的一道甜品"},{"id":3,"name":"猫薄荷","sprite":"mbh","desc":"猫薄荷是指能让家猫等猫科动物产生幻觉的一类多年生草本植物"},{"id":4,"name":"鸡胸肉","sprite":"jxr","desc":"鸡胸肉,是鸡身上最大的两块肉,富含丰富的蛋白质"},{"id":5,"name":"猫麦草","sprite":"mmc","desc":"猫麦草可以刺激猫的肠胃蠕动,帮助猫吐出在胃中结成团的毛球"},{"id":6,"name":"猫罐头","sprite":"mgt","desc":"猫罐头是根据猫咪的特殊身体因素研制而成的食物,营养丰富"},{"id":7,"name":"猫条","sprite":"mt","desc":"猫条是一种猫的零食,口味多种多样"}
]

12.6 配置表读取

将上面的配置表保存为PropCfg.json放在Resources目录中。

接着我们就可以通过Resources.load加载配置文件,再通过LitJson解析。

关于LitJson,第十一章已讲过,可以查阅第十一章:《学Unity的猫》——第十一章:Unity猫咪救济管理系统,山岗的星光

using System.Collections.Generic;
using UnityEngine;
using LitJson;public class PropCfg
{/// <summary>/// 读取配置表/// </summary>public void LoadCfg(){var cfgJson = Resources.Load<TextAsset>("PropCfg");var cfg = JsonMapper.ToObject<List<PropCfgItem>>(cfgJson.text);foreach(var cfgItem in cfg){m_cfgDic[cfgItem.id] = cfgItem;}}/// <summary>/// 通过道具id获取道具配置/// </summary>/// <param name="id">道具id</param>/// <returns></returns>public PropCfgItem GetCfg(int id){if (m_cfgDic.ContainsKey(id))return m_cfgDic[id];return null;}/// <summary>/// 配置表/// </summary>private Dictionary<int, PropCfgItem> m_cfgDic = new Dictionary<int, PropCfgItem>();/// <summary>/// 单例模式/// </summary>private static PropCfg s_instance;public static PropCfg Instance{get{if (null == s_instance)s_instance = new PropCfg();return s_instance;}}
}/// <summary>
/// 道具配置数据结构
/// </summary>
public class PropCfgItem
{public int id;public string name;public string sprite;public string desc;
}

12.7 制作界面预设

大厅界面:PlazaPanel.prefab

背包界面:BackpackPanel.prefab

提示语预设:TipsUi.prefab

12.8 资源管理器

编写一个资源管理器ResourceMgr,用于加载资源,缓存资源。

using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 资源管理器
/// </summary>
public class ResourceMgr
{/// <summary>/// 加载精灵图/// </summary>/// <param name="name">精灵名</param>/// <returns></returns>public Sprite LoadSprite(string name){if (m_spritesDic.ContainsKey(name))return m_spritesDic[name];var obj = Resources.Load<GameObject>("Sprite/" + name);var sprite = obj.GetComponent<SpriteRenderer>().sprite;m_spritesDic[name] = sprite;return sprite;}/// <summary>/// 加载界面预设/// </summary>/// <param name="name">界面名</param>/// <returns></returns>public GameObject LoadPanel(string name){GameObject prefab = null;if (m_panelsDic.ContainsKey(name))prefab = m_panelsDic[name];else{prefab = Resources.Load<GameObject>("Panel/" + name);m_panelsDic[name] = prefab;}return prefab;}/// <summary>/// 精灵资源/// </summary>private Dictionary<string, Sprite> m_spritesDic = new Dictionary<string, Sprite>();/// <summary>/// 界面资源/// </summary>private Dictionary<string, GameObject> m_panelsDic = new Dictionary<string, GameObject>();/// <summary>/// 单例模式/// </summary>private static ResourceMgr s_instance;public static ResourceMgr Instance{get{if (null == s_instance)s_instance = new ResourceMgr();return s_instance;}}
}

例:加载精灵图

// 加载猫麦草精灵图
var sprite = ResourceMgr.Instance.LoadSprite("mmc");

12.9 界面管理器

编写一个界面管理器PanelMgr,用于显示界面。

using UnityEngine;/// <summary>
/// 界面管理器
/// </summary>
public class PanelMgr
{/// <summary>/// 初始化/// </summary>/// <param name="canvas"></param>public void Init(Canvas canvas){m_canvasTrans = canvas.transform;}/// <summary>/// 显示界面/// </summary>/// <param name="panelName"></param>/// <returns></returns>public GameObject ShowPanel(string panelName){var prefab = ResourceMgr.Instance.LoadPanel(panelName);var obj = Object.Instantiate(prefab);obj.transform.SetParent(m_canvasTrans, false);return obj;}/// <summary>/// 缓存Canvas/// </summary>private Transform m_canvasTrans;private static PanelMgr s_instance;public static PanelMgr Instance{get{if (null == s_instance)s_instance = new PanelMgr();return s_instance;}}
}/// <summary>
/// 界面名称
/// </summary>
public class PanelName
{public const string PlazaPanel = "PlazaPanel";public const string BackpackPanel = "BackpackPanel";public const string TipsUi = "TipsUi";
}

例:显示背包界面

// 显示背包界面
var obj = PanelMgr.Instance.ShowPanel(PanelName.BackpackPanel);

12.10 背包数据读写

封装一个本地数据读写的类,模拟数据库,用到了PlayerPrefsLitJson这两个类。
通过PlayerPrefs可以方便地进行数据读写,通过LitJson可以方便地进行json数据格式转换。

using System.Collections.Generic;
using UnityEngine;
using LitJson;/// <summary>
/// 道具数据库
/// </summary>
public class PropsDatabase
{/// <summary>/// 从本地读取数据/// </summary>/// <returns></returns>public static Dictionary<int, int> LoadData(){PlayerPrefs.SetString("props", "{}");var jsonStr = PlayerPrefs.GetString("props", "{}");Debug.Log("LoadData: \n" + jsonStr);var result = JsonMapper.ToObject<Dictionary<string, int>>(jsonStr);Dictionary<int, int> data = new Dictionary<int, int>();foreach (var item in result){data[int.Parse(item.Key)] = item.Value;}return data;}/// <summary>/// 写入数据到本地/// </summary>/// <param name="data"></param>public static void SaveData(Dictionary<int, int> data){var jsonStr = JsonMapper.ToJson(data);PlayerPrefs.SetString("props", jsonStr);}
}

12.11 背包管理器

再封装一个背包管理器BackPackMgr,提供读取道具数据和增减道具数量的方法。

using System.Collections.Generic;/// <summary>
/// 背包管理器
/// </summary>
public class BackPackMgr
{/// <summary>/// 初始化/// </summary>public void Init(){// 从本地读取背包数据m_propsDic = PropsDatabase.LoadData();}/// <summary>/// 修改道具数量/// </summary>/// <param name="id">道具id</param>/// <param name="deltaCnt">数量变化值</param>public void ChangePropCnt(int id, int deltaCnt){if (m_propsDic.ContainsKey(id)){m_propsDic[id] += deltaCnt;}else{m_propsDic[id] = deltaCnt;}if (m_propsDic[id] < 0){m_propsDic[id] = 0;}// 写入数据到本地PropsDatabase.SaveData(m_propsDic);// 抛出事件EventDispatcher.instance.DispatchEvent(EventNameDef.EVENT_UPDATE_PROP_CNT, id, m_propsDic[id]);}/// <summary>/// 背包道具数据缓存,key: id, value: cnt/// </summary>private Dictionary<int, int> m_propsDic = new Dictionary<int, int>();public Dictionary<int, int> propsDic{get { return m_propsDic; }}/// <summary>/// 单例模式/// </summary>private static BackPackMgr s_instance;public static BackPackMgr Instance{get{if (null == s_instance)s_instance = new BackPackMgr();return s_instance;}}
}

例:增加一个id1的道具(逗猫棒)

// 增加一个逗猫棒道具
BackPackMgr.Instance.ChangePropCnt(1, 1);

12.12 事件管理器

上面背包管理器中用到了事件管理器,当道具数量发生改变的时候,要通知界面ui更新显示。使用观察者模式,通过事件订阅的方式实现道具数量更新时同步更新ui的显示。
事件管理器如下

using UnityEngine;
using System.Collections.Generic;/// <summary>
/// 事件委托
/// </summary>
public delegate void MyEventHandler(params object[] objs);/// <summary>
/// 事件管理器
/// </summary>
public class EventDispatcher
{/// <summary>/// 注册事件的响应函数/// </summary>/// <param name="type"></param>/// <param name="handler"></param>public void Regist(string type, MyEventHandler handler){if (handler == null)return;if (!listeners.ContainsKey(type)){listeners.Add(type, new Dictionary<int, MyEventHandler>());}var handlerDic = listeners[type];var handlerHash = handler.GetHashCode();if (handlerDic.ContainsKey(handlerHash)){handlerDic.Remove(handlerHash);}listeners[type].Add(handler.GetHashCode(), handler);}/// <summary>/// 注销事件的响应函数/// </summary>/// <param name="type"></param>/// <param name="handler"></param>public void UnRegist(string type, MyEventHandler handler){if (handler == null)return;if (listeners.ContainsKey(type)){listeners[type].Remove(handler.GetHashCode());if (null == listeners[type] || 0 == listeners[type].Count){listeners.Remove(type);}}}/// <summary>/// 抛出事件,触发之前注册过的响应函数/// </summary>/// <param name="evt"></param>/// <param name="objs"></param>public void DispatchEvent(string evt, params object[] objs){if (listeners.ContainsKey(evt)){var handlerDic = listeners[evt];if (handlerDic != null && 0 < handlerDic.Count){var dic = new Dictionary<int, MyEventHandler>(handlerDic);foreach (var f in dic.Values){try{f(objs);}catch (System.Exception ex){Debug.LogErrorFormat(szErrorMessage, evt, ex.Message, ex.StackTrace);}}}}}/// <summary>/// 清理事件/// </summary>/// <param name="key"></param>public void ClearEvents(string key){if (listeners.ContainsKey(key)){listeners.Remove(key);}}/// <summary>/// 事件监听缓存/// </summary>private Dictionary<string, Dictionary<int, MyEventHandler>> listeners = new Dictionary<string, Dictionary<int, MyEventHandler>>();private readonly string szErrorMessage = "DispatchEvent Error, Event:{0}, Error:{1}, {2}";/// <summary>/// 单例模式/// </summary>private static EventDispatcher s_instance;public static EventDispatcher Instance{get{if (null == s_instance)s_instance = new EventDispatcher();return s_instance;}}
}

例:事件订阅

// 事件订阅
EventDispatcher.Instance.Regist("EVENT_UPDATE_PROP_CNT", OnEventUpdatePropCnt);
// 注销事件订阅
EventDispatcher.Instance.UnRegist("EVENT_UPDATE_PROP_CNT", OnEventUpdatePropCnt);

抛出事件

EventDispatcher.Instance.DispatchEvent("EVENT_UPDATE_PROP_CNT", id, cnt);

响应函数

private void OnEventUpdatePropCnt(params object[] args)
{int id = (int)args[0];int cnt = (int)args[1];
}

12.13 界面代码

12.13.1 大厅界面

大厅界面主要是两个按钮,一个按钮点击了随机增加一个道具,另一个按钮点击了打开背包界面。

using UnityEngine;
using UnityEngine.UI;/// <summary>
/// 大厅界面
/// </summary>
public class PlazaPanel : MonoBehaviour
{public Button packpackBtn;public Button addPropBtn;void Start(){// 背包按钮packpackBtn.onClick.AddListener(() => {PanelMgr.Instance.ShowPanel(PanelName.BackpackPanel);});// 加道具按钮addPropBtn.onClick.AddListener(() => {// 随机加一个道具var propId = Random.Range(1, 8);BackPackMgr.Instance.ChangePropCnt(propId, 1);var cfg = PropCfg.Instance.GetCfg(propId);TipsUi.Show("恭喜获得" + cfg.name + "x1");});}
}
12.13.2 背包界面

背包界面稍复杂一点点,界面左边用一个Scroll View用来显示道具列表,选中某个道具,界面右边显示道具信息,点击使用按钮,扣除一个道具。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class BackpackPanel : MonoBehaviour
{private void Awake(){// 订阅事件EventDispatcher.Instance.Regist(EventNameDef.EVENT_UPDATE_PROP_CNT, OnEventUpdatePropCnt);}private void OnDestroy(){// 注销事件EventDispatcher.Instance.UnRegist(EventNameDef.EVENT_UPDATE_PROP_CNT, OnEventUpdatePropCnt);}private void Start(){propItemUi.SetActive(false);// 关闭按钮closeBtn.onClick.AddListener(() =>{Destroy(gameObject);});// 使用道具按钮usePropBtn.onClick.AddListener(() =>{if (-1 == m_selectPropId) return;var cfg = PropCfg.Instance.GetCfg(m_selectPropId);TipsUi.Show("使用了" + cfg.name);BackPackMgr.Instance.ChangePropCnt(m_selectPropId, -1);});// 创建道具列表CreatePropList();}/// <summary>/// 道具数量发生变化/// </summary>/// <param name="args"></param>private void OnEventUpdatePropCnt(params object[] args){int id = (int)args[0];int cnt = (int)args[1];if (cnt <= 0){if (m_propUiDic.ContainsKey(id)){Destroy(m_propUiDic[id].obj);m_propUiDic.Remove(id);if (id == m_selectPropId)AutoSelectOneProp();}}else{if (m_propUiDic.ContainsKey(id)){m_propUiDic[id].cnt.text = "x" + cnt;}else{CreatePropItem(id, cnt);}}}/// <summary>/// 创建道具列表/// </summary>private void CreatePropList(){foreach (var item in BackPackMgr.Instance.propsDic){CreatePropItem(item.Key, item.Value);}// 自动选择一个道具AutoSelectOneProp();}/// <summary>/// 自动选择一个道具/// </summary>private void AutoSelectOneProp(){var itor = m_propUiDic.GetEnumerator();if (itor.MoveNext()){var item = itor.Current.Value;item.tgl.isOn = true;itor.Dispose();OnPropItemSelected(item.propId);}else{OnPropItemSelected(-1);}}/// <summary>/// 创建一个道具item/// </summary>/// <param name="propId">道具id</param>/// <param name="cnt">道具数量</param>private void CreatePropItem(int propId, int cnt){if (m_propUiDic.ContainsKey(propId)) return;if (cnt <= 0) return;PropItemUI ui = new PropItemUI();var obj = Instantiate(propItemUi);obj.SetActive(true);obj.transform.SetParent(propItemUi.transform.parent, false);ui.obj = obj;ui.propId = propId;ui.icon = obj.transform.Find("Button/Icon").GetComponent<Image>();ui.cnt = obj.transform.Find("Button/Cnt").GetComponent<Text>();ui.tgl = obj.transform.Find("Button").GetComponent<Toggle>();ui.tgl.onValueChanged.AddListener((v) =>{if (ui.tgl.isOn) OnPropItemSelected(propId);});var cfg = PropCfg.Instance.GetCfg(propId);if (null != cfg){var sprite = ResourceMgr.Instance.LoadSprite(cfg.sprite);ui.icon.sprite = sprite;//ui.icon.SetNativeSize();}ui.cnt.text = "x" + cnt;m_propUiDic[propId] = ui;}/// <summary>/// 道具被选中/// </summary>/// <param name="propId"></param>private void OnPropItemSelected(int propId){m_selectPropId = propId;if (-1 == m_selectPropId){// 没有道具被选中rightInfoRoot.SetActive(false);}else{rightInfoRoot.SetActive(true);var cfg = PropCfg.Instance.GetCfg(propId);nameText.text = cfg.name;descText.text = cfg.desc;icon.sprite = ResourceMgr.Instance.LoadSprite(cfg.sprite);}}public Button closeBtn;public GameObject propItemUi;public GameObject rightInfoRoot;public Text nameText;public Text descText;public Image icon;public Button usePropBtn;private int m_selectPropId;/// <summary>/// 道具列表ui缓存/// </summary>private Dictionary<int, PropItemUI> m_propUiDic = new Dictionary<int, PropItemUI>();private class PropItemUI{public int propId;public GameObject obj;public Image icon;public Toggle tgl;public Text cnt;}
}
12.13.3 提示语UI

制作一个提示语UI的动画,动画播放结束后通过帧事件调用OnAnimationEnd方法销毁自己。


TipsUi组件挂在TipsUi.prefab上。

using UnityEngine;
using UnityEngine.UI;public class TipsUi : MonoBehaviour
{public Text tipsText;/// <summary>/// 显示提示语/// </summary>/// <param name="text"></param>public static void Show(string text){var obj = PanelMgr.Instance.ShowPanel(PanelName.TipsUi);var bhv = obj.GetComponent<TipsUi>();bhv.tipsText.text = text;}/// <summary>/// 动画结束,销毁自己/// </summary>public void OnAnimationEnd(){Destroy(gameObject);}
}

12.14 程序入口脚本

写一个程序入口脚本,挂到Canvas上,并赋值Canvas对象。

using UnityEngine;public class UIMain : MonoBehaviour
{public Canvas canvas;// 执行初始化private void Awake(){PropCfg.Instance.LoadCfg();BackPackMgr.Instance.Init();PanelMgr.Instance.Init(canvas);}void Start(){// 显示大厅界面PanelMgr.Instance.ShowPanel(PanelName.PlazaPanel);}
}

完成。
如果有什么疑问,欢迎留言或私信。


《学Unity的猫》——第十三章:Unity使用Animator控制动画播放,皮皮猫打字机游戏

《学Unity的猫》——第十二章:使用Unity制作背包,皮皮的梦想背包相关推荐

  1. 《学Unity的猫》——第十一章:Unity猫咪救济管理系统,山岗的星光

    文章目录 11.1 山岗的星光 11.2 Unity猫咪救济管理系统 11.3 设置UI摄像机 11.4 设置Canvas 11.5 制作登录界面预设 11.6 制作大厅界面预设 11.7 制作信息界 ...

  2. 《学Unity的猫》——第五章:规范Unity的工程目录结构

    文章目录 5.1 不要把玩具乱丢 5.2 Unity工程目录结构 5.3 Resources.Load实战测试 5.3.1 资源下载与导入 5.3.2 目录整理 5.3.3 代码动态加载资源 5.3. ...

  3. 从头开始学Tableau-第十二章(实战3 地图实践)

    从头开始学Tableau-第十二章(实战3 地图实践) 第一章 第二章 第三章 第四章 第五章 第六章 第七章 第八章 第九章 第十章 第十章 数据源 这个实践主要是用地图来呈现数据,我们所用的是保险 ...

  4. 第十二章_网络搭建及训练

    文章目录 第十二章 网络搭建及训练 CNN训练注意事项 第十二章 TensorFlow.pytorch和caffe介绍 12.1 TensorFlow 12.1.1 TensorFlow是什么? 12 ...

  5. MLAPP————第十二章 隐线性模型

    第十二章 隐线性模型 12.1 要素分析(factor analysis) 在我们之前提到的混合模型中,数据的生成都是由某个隐状态控制的,然后是那个隐状态控制的分布生成的,但是这样的模型在表示上还是具 ...

  6. Shiro第十二章-与Spring集成、配置文件初解

    简介 Shiro的组件都是Javabean/pojo式的组件,所以非常容易使用Spring进行组件管理,可以非常方便得从ini配置转为Spring配置(如xml配置文件). JavaSE 依赖 < ...

  7. 在Jetson Nano上学习ROS的记录(版本Ubuntu18.04,课程来源赵虚左老师的《ROS理论与实践》)第十二章 机器人导航(仿真)

    系列文章目录 第一章 ROS空间创建.helloworld的实现.开启多个节点 第二章 话题通信 第三章 服务通信 第四章 参数服务器 第五章 常用指令 第六章 通信机制实操 第七章 ROS通信机制进 ...

  8. Tensorflow 2.x(keras)源码详解之第十二章:keras中的损失函数之BinaryCrossentropy详解

      大家好,我是爱编程的喵喵.双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中.从事机器学习以及相关的前后端开发工作.曾在阿里云.科大讯飞.CCF等比赛获得多次Top名次.现 ...

  9. 人工智能:第十二章 智能控制

    第十二章智能控制 教学内容:能控制是一类无需人的干预就能够自主地驱动智能机器实现其目标的过程,也是用机器模拟人类智能的又一重要领域.本章介绍智能控制的基本概念.基本理论.基本方法及应用. 教学重点:智 ...

最新文章

  1. bzoj4589: Hard Nim
  2. 一些在线的WebEdit编辑器
  3. opengl加载显示3D模型ms3d类型文件
  4. 区分Activity的四种加载模式(转)
  5. 推荐一款移动端的web UI控件 -- mobiscroll
  6. Linux内核系统架构介绍
  7. 小学生们在B站讲算法,这么内卷么?
  8. PostgreSQL的日志文件和数据加载
  9. Android Studio 安装遇到问题及解决方法
  10. img加载在IE11,chrome,FF下的不同
  11. Python答题并统计的小程序
  12. c语言头文件的使用和写法,C语言头文件的使用与写法
  13. vb.net写的一个简单计算器(未完善)
  14. 扫雷小游戏制作全过程
  15. 用生成对抗网络给雪人上色,探索人工智能时代的美学
  16. js 判断企业微信打开
  17. dss数字签名技术java_DSS数字签名标准
  18. JQuery解决跳转无效的问题(.location.href)
  19. Java刷算法:收藏大法
  20. 深聊全链路压测之:第二十三讲 | 如何改造性能监控。

热门文章

  1. 武汉有哪些好的IT公司?
  2. 多媒体会议系统,会议室解决方案
  3. 5位数的数字黑洞是多少_目前对于6174数字黑洞现象是否有合理的解释或证明?...
  4. cocos2d-x横版格斗游戏教程4
  5. Cerebro:一个好用的单细胞数据展示shiny工具
  6. Bitly缩短网址服务 - Blog透视镜
  7. session购物车操作
  8. 基于JS的设计模式01(kerwin老师笔记)
  9. 省时、省心、省钱的网管软件
  10. 零基础学C/C++151——检索大写字母