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

11.1 山岗的星光
稀疏的星光,清辉洒地,流浪猫小花凝望着窗外,它没有想到一个月前它还是一只无家可归的流浪小猫,此时已经有了一个温暖的家。
事情要回到一个月前的那个夜晚,秋风瑟瑟,小花在路边蜷缩着身子,有个人类经过它的身旁,那个人站住了,嘴里发出蹩脚的喵喵声,慢慢想要靠近,小花见状立刻躲进了斜坡中的草丛里,那人停留片刻后便离开了。
两天后,小花又看到了那个人,而且还带着一只猫一台电脑,那只猫朝着小花挥手:“喂,过来这边,不要怕,我们不是坏人。”
小花不敢靠前,那只猫又喊道:“我叫皮皮,这个是我的铲屎官,这里有吃的,快过来。”
后来,小花不仅饱饱的吃了一餐,还得知了他们正在做一个猫咪救济管理系统,皮皮是项目的策划猫,它希望可以为那些流浪猫提供基础的保障,实名登记流浪猫,统一管理,调度资源,在设定的救济点投放食物和过冬物资,并与人类铲屎官合作,希望可以给流浪猫寻找合适的家。
每一只来到地球的喵星人都有一个唯一的编号,只要登记了这个编号,就可以随时随地通过猫电波进行通信,小花成为了救济系统的第一只猫。
很快便有人类来领养了小花,以前,小花总是坐在小斜坡上望着星星,它不知道自己从哪里来的,那里还有很多和它一样的猫,有的从来没见过,有的离开了又回来了,有的离开了就再也没有回来,只有星星一直陪着它。现在它有了家,它也想和皮皮它们一起,帮助流离失所的猫寻找温暖的家。
山岗的星光,愿你不再流浪,被世界温柔以待。
11.2 Unity猫咪救济管理系统
猫咪救济管理系统,模块设计如下:

运行效果如下:

本工程使用的Unity版本为2020.1.14f1c1 (64-bit),工程已上传到GitHub,感兴趣的同学可以下载下来学习。
GitHub地址:https://github.com/linxinfa/Unity-CatInfoSystem
11.3 设置UI摄像机
创建一个摄像机,重命名为UICamera。

设置Culling Mask只渲染UI层。

设置视口Projection为正交视口Orthographic。

设置Main Camera不渲染UI层。

11.4 设置Canvas
创建Canvas,并设置Render Mode为Screen Space -Camera,设置Render Camera为UICamera,再把Canvas Scaler组件的UI Scale Mode设置为Scale With Screen Size。

11.5 制作登录界面预设
LoginPanel.prefab


两个Input Field组件,用来输入账号和密码(account和password),一个Button组件作为登录按钮(LoginButton)。
11.6 制作大厅界面预设
PlazaPanel.prefab


一个Button组件作为登记按钮(AddButton),一个Scroll View组件作为拖拉列表,一个Item作为列表的项,一个Button组件作为退出登录按钮(LogoutButton)。其中,Item中使用多个Text组件作为信息显示。
11.7 制作信息界面预设
InfoPnel.prefab


使用三个Button组件作为确定、删除、关闭按钮,使用多个Input Field组件和一个Drop Down组件作为信息输入。
11.8 UI界面管理器:UIManager
UI预设资源放在Resources目录中。

封装一个UIManager类,用来加载界面预设资源和显示界面。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIManager
{
public void Init(Canvas canvas)
{
m_canvasTrans = canvas.transform;
}
/// <summary>
/// 显示界面
/// </summary>
/// <param name="panelName">界面名称</param>
/// <returns></returns>
public GameObject ShowPanel(string panelName)
{
GameObject prefabObj = null;
if (m_panelRes.ContainsKey(panelName))
{
prefabObj = m_panelRes[panelName];
}
else
{
prefabObj = Resources.Load<GameObject>(panelName);
m_panelRes[panelName] = prefabObj;
}
GameObject panelObj = Object.Instantiate(prefabObj);
panelObj.transform.SetParent(m_canvasTrans, false);
return panelObj;
}
private Transform m_canvasTrans;
/// <summary>
/// 界面资源缓存
/// </summary>
private Dictionary<string, GameObject> m_panelRes = new Dictionary<string, GameObject>();
/// <summary>
/// 单例模式
/// </summary>
private static UIManager s_instance;
public static UIManager Instance
{
get
{
if (null == s_instance)
s_instance = new UIManager();
return s_instance;
}
}
}
/// <summary>
/// 界面名称,对应预设文件名称
/// </summary>
public class PanelName
{
public const string LOGIN_PANEL = "LoginPanel";
public const string PLAZA_PANEL = "PlazaPanel";
public const string INFO_PANEL = "InfoPanel";
}
11.9 封装猫信息的类:CatInfo
猫的信息中有个uuid是系统生成的,id是猫星人的编号,由喵星人手动输入。
/// <summary>
/// 猫咪信息
/// </summary>
public class CatInfo
{
public CatInfo()
{
uuid = System.Guid.NewGuid().ToString("N");
}
/// <summary>
/// 系统唯一标识符
/// </summary>
public string uuid;
/// <summary>
/// 喵星身份编号
/// </summary>
public string id;
/// <summary>
/// 昵称
/// </summary>
public string nickname;
/// <summary>
/// 品种
/// </summary>
public string kind;
/// <summary>
/// 毛色
/// </summary>
public string color;
/// <summary>
/// 性别,0:母,1:公
/// </summary>
public int gender;
/// <summary>
/// 出生
/// </summary>
public string birth;
/// <summary>
/// 流浪原因
/// </summary>
public string reason;
}
11.10 猫信息管理器:CatManager
封装一个猫信息的管理类,CatManager,负责数据加载和增删改查。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CatManager
{
public void Init()
{
// 从数据库加载数据
m_datas = CatDataBase.QueryDatas();
}
/// <summary>
/// 新增或修改猫信息
/// </summary>
/// <param name="info"></param>
public void AddOrModify(CatInfo info)
{
CatDataBase.AddOrModify(info);
// 抛出事件,更新界面
EventDispatcher.instance.DispatchEvent(EventNameDef.EVENT_ADD_OR_MODIFY_CAT, info);
}
/// <summary>
/// 删除猫信息
/// </summary>
/// <param name="uuid"></param>
public void Del(string uuid)
{
CatDataBase.Del(uuid);
// 抛出事件,更新界面
EventDispatcher.instance.DispatchEvent(EventNameDef.EVENT_DEL_CAT, uuid);
}
public Dictionary<string, CatInfo> data
{
get {
return m_datas; }
}
/// <summary>
/// 猫信息数据缓存
/// </summary>
private Dictionary<string, CatInfo> m_datas = null;
/// <summary>
/// 单例模式
/// </summary>
private static CatManager s_instance;
public static CatManager Instance
{
get
{
if (null == s_instance)
s_instance = new CatManager();
return s_instance;
}
}
}
11.11 数据存储:CatDataBase
封装一个CatDataBase类,负责数据的本地写入和读取。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using LitJson;
using System.IO;
/// <summary>
/// 猫数据库
/// </summary>
public class CatDataBase
{
/// <summary>
/// 新增或修改数据
/// </summary>
/// <param name="info"></param>
public static void AddOrModify(CatInfo info)
{
if (s_datas.ContainsKey(info.uuid))
{
s_datas[info.uuid] = info;
}
else
{
s_datas.Add(info.uuid, info);
}
SaveData();
}
/// <summary>
/// 删数据
/// </summary>
/// <param name="uuid"></param>
public static void Del(string uuid)
{
if (!s_datas.ContainsKey(uuid)) return;
s_datas.Remove(uuid);
SaveData();
}
/// <summary>
/// 查询所有猫信息
/// </summary>
/// <returns></returns>
public static Dictionary<string, CatInfo> QueryDatas()
{
string jsonStr = PlayerPrefs.GetString("cats", null);
if (string.IsNullOrEmpty(jsonStr))
{
s_datas = new Dictionary<string, CatInfo>();
}
else
{
s_datas = JsonMapper.ToObject<Dictionary<string, CatInfo>>(jsonStr);
}
return s_datas;
}
/// <summary>
/// 使用PlayerPrefs将数据写入本地
/// </summary>
public static void SaveData()
{
string jsonStr = JsonMapper.ToJson(s_datas);
PlayerPrefs.SetString("cats", jsonStr);
}
private static Dictionary<string, CatInfo> s_datas = new Dictionary<string, CatInfo>();
}
其中写入本地数据用到了Unity提供的PlayerPrefs类。
读取数据接口
public static string GetString(string key, string defaultValue);
写入数据接口
public static void SetString(string key, string value);
11.12 Json库:LitJson
需要将数据序列号到本地文件中,采用Json的格式,所以需要导入一个Json库,从GitHub下载LitJson源码:
https://github.com/LitJSON/litjson/releases/tag/0.15.0

下载后将源码导入到工程中。

使用时引入命名空间。
using LitJson;
主要用到JsonMapper的两个接口。
object转json字符串接口
public static string ToJson (object obj)
json字符串转object接口
public static T ToObject<T> (string json)
11.13 观察者模式
数据更新的时候要更新ui的显示,这里采用观察者模式,ui界面的代码监听事件,数据变化时抛出事件,从而实现ui的显示刷新。
封装一个事件订阅和发送的类:EventDispatcher。
using UnityEngine;
using System.Collections.Generic;
public delegate void MyEventHandler(params object[] objs);
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;
}
}
}
定义事件
public class EventNameDef
{
/// <summary>
/// 新增或修改一只猫的信息
/// </summary>
public const string EVENT_ADD_OR_MODIFY_CAT = "EVENT_ADD_OR_MODIFY_CAT";
/// <summary>
/// 删除一只猫的信息
/// </summary>
public const string EVENT_DEL_CAT = "EVENT_DEL_CAT";
}
11.14 登录界面代码:LoginPanel
登录界面代码LoginPanel,挂在LoginPanel.prefab上。
简单判断下账号和密码,如果账号为admin且密码为123456则进入大厅界面。
(实际开发需要有账号注册,与服务器通信才能完成登录,这里只是演示效果)。
using UnityEngine;
using UnityEngine.UI;
public class LoginPanel : MonoBehaviour
{
public InputField nameInput;
public InputField pwdInput;
public Button loginBtn;
void Start()
{
loginBtn.onClick.AddListener(() =>
{
if ("admin" == nameInput.text && "123456" == pwdInput.text)
{
Destroy(gameObject);
UIManager.Instance.ShowPanel(PanelName.PLAZA_PANEL);
}
});
}
}
绑定ui对象

11.15 大厅界面代码:PlazaPanel
大厅界面代码PlazaPanel,挂在PlazaPanel.prefab上。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlazaPanel : MonoBehaviour
{
public Button logoutBtn;
public Button signBtn;
public GameObject catItemObj;
private Transform listRoot;
void Awake()
{
// 注册事件
EventDispatcher.instance.Regist(EventNameDef.EVENT_ADD_OR_MODIFY_CAT, OnEventAddOrModifyCat);
EventDispatcher.instance.Regist(EventNameDef.EVENT_DEL_CAT, OnEventDelCat);
CatManager.Instance.Init();
catItemObj.SetActive(false);
listRoot = catItemObj.transform.parent;
}
void Start()
{
// 退出登录
logoutBtn.onClick.AddListener(() =>
{
Destroy(gameObject);
UIManager.Instance.ShowPanel(PanelName.LOGIN_PANEL);
});
// 登记新信息
signBtn.onClick.AddListener(() =>
{
UIManager.Instance.ShowPanel(PanelName.INFO_PANEL);
});
// 遍历数据,创建列表
foreach (CatInfo catInfo in CatManager.Instance.data.Values)
{
CreateItem(catInfo);
}
}
/// <summary>
/// 创建信息列表的一行ui
/// </summary>
/// <param name="info"></param>
void CreateItem(CatInfo info)
{
var obj = Instantiate(catItemObj);
obj.SetActive(true);
obj.transform.SetParent(listRoot, false);
var itemUi = obj.GetComponent<CatListItem>();
// 使用数据更新ui
itemUi.UpdateUi(info);
// 缓存,方便后面更新ui
m_catUiList[info.uuid] = itemUi;
}
/// <summary>
/// 数据发生变化,更新ui
/// </summary>
/// <param name="args"></param>
void OnEventAddOrModifyCat(params object[] args)
{
var info = args[0] as CatInfo;
if (m_catUiList.ContainsKey(info.uuid))
{
m_catUiList[info.uuid].UpdateUi(info);
}
else
{
// 创建多一行
CreateItem(info);
}
}
/// <summary>
/// 信息被删除,删除对应的ui
/// </summary>
/// <param name="args"></param>
void OnEventDelCat(params object[] args)
{
var uuid = (string)args[0];
if (m_catUiList.ContainsKey(uuid))
{
var ui = m_catUiList[uuid];
if(null != ui)
{
Destroy(ui.gameObject);
}
m_catUiList.Remove(uuid);
}
}
/// <summary>
/// 界面被销毁
/// </summary>
private void OnDestroy()
{
// 注销事件
EventDispatcher.instance.UnRegist(EventNameDef.EVENT_ADD_OR_MODIFY_CAT, OnEventAddOrModifyCat);
EventDispatcher.instance.UnRegist(EventNameDef.EVENT_DEL_CAT, OnEventDelCat);
}
private Dictionary<string, CatListItem> m_catUiList = new Dictionary<string, CatListItem>();
}
绑定PlazaPanel组件的ui对象。

其中,列表的行ui单独封装成一个组件CatListItem,挂在Item节点上。
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 列表的一行ui
/// </summary>
public class CatListItem : MonoBehaviour
{
public Text idText;
public Text nameText;
public Text kindText;
public Text colorText;
public Text genderText;
public Text birthText;
public Text resonText;
public Button itemBtn;
public void Start()
{
// 列表的行被点击,打开信息界面
itemBtn.onClick.AddListener(() =>
{
var panelObj = UIManager.Instance.ShowPanel(PanelName.INFO_PANEL);
var infoPanel = panelObj.GetComponent<InfoPanel>();
infoPanel.Init(m_info);
});
}
/// <summary>
/// 更新ui
/// </summary>
/// <param name="info"></param>
public void UpdateUi(CatInfo info)
{
m_info = info;
idText.text = info.id;
nameText.text = info.nickname;
kindText.text = info.kind;
colorText.text = info.color;
genderText.text = 0 == info.gender ? "母" : "公";
birthText.text = info.birth;
resonText.text = info.reason;
}
private CatInfo m_info;
}
绑定CatListItem组件的ui对象。

11.16 信息界面代码:InfoPanel
信息界面代码InfoPanel,挂在InfoPanel.prefab上。
using UnityEngine;
using UnityEngine.UI;
public class InfoPanel : MonoBehaviour
{
public InputField idInput;
public InputField kindInput;
public InputField nameInput;
public InputField colorInput;
public Dropdown genderDropdown;
public InputField birthInput;
public InputField reasonInput;
public Button okBtn;
public Button delBtn;
public Button closeBtn;
public CatInfo m_catInfo;
public void Init(CatInfo info)
{
m_catInfo = info;
idInput.text = info.id;
kindInput.text = info.kind;
nameInput.text = info.nickname;
colorInput.text = info.color;
genderDropdown.value = info.gender;
birthInput.text = info.birth;
reasonInput.text = info.reason;
}
private CatInfo MakeCatInfo()
{
if (null == m_catInfo)
m_catInfo = new CatInfo();
m_catInfo.id = idInput.text;
m_catInfo.kind = kindInput.text;
m_catInfo.nickname = nameInput.text;
m_catInfo.color = colorInput.text;
m_catInfo.gender = genderDropdown.value;
m_catInfo.birth = birthInput.text;
m_catInfo.reason = reasonInput.text;
return m_catInfo;
}
void Start()
{
okBtn.onClick.AddListener(() =>
{
// 新增或修改
CatManager.Instance.AddOrModify(MakeCatInfo());
Destroy(gameObject);
});
delBtn.onClick.AddListener(() =>
{
// 删除
if(null != m_catInfo)
{
CatManager.Instance.Del(m_catInfo.uuid);
}
Destroy(gameObject);
});
closeBtn.onClick.AddListener(() =>
{
Destroy(gameObject);
});
}
}
绑定ui对象。

完成。
如果有任何疑问欢迎留言或私信。
《学Unity的猫》——第十二章:使用Unity制作背包,皮皮的梦想背包
转载:https://blog.csdn.net/linxinfa/article/details/110467442