VS任务列表
在C#代码中输入类似的注释
然后打开Visual Studio的“任务列表”,快捷键“CTRL + ,T”,或者“视图——任务列表”,就能够显示所有在代码中标记的需要做的任务。
选择英雄部分
我们希望选择一个英雄时,它的头像变亮,其他的头像变暗。
英雄头像选择
每一个英雄头像都是一个Button,它们都动态挂载HeroItem
脚本。这个脚本负责头像明暗的逻辑,并且声明一个回调,这个回调调用父级的方法,从而让每个英雄头像正确地变亮变暗。
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
| using UnityEngine; using UnityEngine.UI; using DG.Tweening; using System;
public class HeroItem : MonoBehaviour { private Color defaultColor = Color.gray; private Color selectedColor = Color.white; private float tweenTime = 0.3f; private Image image; private Action<int> callBack; void Start() { image = GetComponent<Image>(); GetComponent<Button>().onClick.AddListener(Selected); Unselected(); } void Selected() { image.DOKill(); image.DOColor(selectedColor, tweenTime); callBack?.Invoke(transform.GetSiblingIndex()); } public void Unselected() { image.DOKill(); image.DOColor(defaultColor, tweenTime); } public void AddResetListener(Action<int> callback) { this.callBack = callback; } }
|
SelectHero
SelectHero
是每一个HeroItem
的父对象,负责给每一个Item动态挂载HeroItem
脚本,以及监听每个Item的点击回调
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
| using System.Collections.Generic; using UnityEngine;
public class SelectHero : MonoBehaviour { private List<HeroItem> heroItems; void Start() { heroItems = new List<HeroItem>(transform.childCount); HeroItem heroItem = null; foreach (Transform trans in transform) { heroItem = trans.gameObject.AddComponent<HeroItem>(); heroItem.AddResetListener(ResetState); heroItems.Add(heroItem); } } private void ResetState(int index) { for (int i = 0; i < heroItems.Count; i++) { if (i == index) continue; heroItems[i].Unselected(); } } }
|
数据持久化
IDataMemory
Scripts——Module——DataMemory文件夹内,新建IDataMemory
文件
1 2 3 4 5 6 7
| public interface IDataMemory { T Get<T>(string key); void Set<T>(string key, T value); void Clear(string key); void ClearAll(); }
|
PlayerPrefsMemory及优化
在DataMemory文件夹内新建PlayerPrefsMemory
,做一个PlayerPrefs的实现,
优化前的版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class PlayerPrefsMemory : IDataMemory { public T Get<T>(string key) { Type type = typeof(T); TypeConverter converter = TypeDescriptor.GetConverter(type); if (typeof(T) == typeof(int)) { int value = PlayerPrefs.GetInt(key, 0); return (T)converter.ConvertTo(value, type); } if (typeof(T) == typeof(string)) { string value = PlayerPrefs.GetString(key, ""); return (T)converter.ConvertTo(value, type); } } }
|
发现在Get方法中有重复的逻辑,产生重复的核心是PlayerPrefs.Get
有三个重载
注意这里的TypeDescriptor.GetConverter
API。Get
方法返回的是泛型T,而PlayerPrefs
只能返回string、float、int三种类型,我们只能通过converter来转换成符合的泛型T来返回。
优化思路:声明一个字典,提前把每一个Type对应的PlayerPrefs.Get
方法对应好,在使用时直接将Type作为key从字典获取对应的方法即可。这个字典相当于一个配置表,这也是通过配置数据来减少逻辑修改的思想,配置数据是可以及时修改的,而核心的方法逻辑不能总是修改。
优化后的版本:
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
| using System.Collections.Generic; using UnityEngine; using System; using System.ComponentModel;
public class PlayerPrefsMemory : IDataMemory { private Dictionary<Type, Func<string, object>> dataGetter = new Dictionary<Type, Func<string, object>>() { {typeof(int), key => PlayerPrefs.GetInt(key,0) }, {typeof(string), key => PlayerPrefs.GetString(key,"") }, {typeof(float), key => PlayerPrefs.GetFloat(key,0f) }, }; private Dictionary<Type, Action<string, object>> dataSetter = new Dictionary<Type, Action<string, object>>() { {typeof(int), (key,value) => PlayerPrefs.SetInt(key,(int)value) }, {typeof(string), (key,value) => PlayerPrefs.SetString(key,(string)value) }, {typeof(float), (key,value) => PlayerPrefs.SetFloat(key,(float)value) }, }; public T Get<T>(string key) { Type type = typeof(T); TypeConverter converter = TypeDescriptor.GetConverter(type); if(dataGetter.ContainsKey(type)) { return (T)converter.ConvertTo(dataGetter[type].Invoke(key), type); } else { Debug.LogError("当前数据存储中无此类型数据,类型名:" + type.Name); return default; } }
public void Set<T>(string key, T value) { Type type = typeof(T); if (dataSetter.ContainsKey(type)) { dataSetter[type].Invoke(key, value); } else { Debug.LogErrorFormat("当前数据存储中无此类型数据,数据为key:{0} value:{1}", key, value); } } public void Clear(string key) { PlayerPrefs.DeleteKey(key); }
public void ClearAll() { PlayerPrefs.DeleteAll(); } }
|
DataMgr
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
| public class DataMgr : NormalSingleton<DataMgr>, IDataMemory { private IDataMemory m_data; public DataMgr() { m_data = new PlayerPrefsMemory(); } public void Clear(string key) { m_data.Clear(key); }
public void ClearAll() { m_data.ClearAll(); }
public T Get<T>(string key) { return m_data.Get<T>(key); }
public void Set<T>(string key, T value) { m_data.Set<T>(key, value); } }
|
针对接口编程,我们可以随时切换实现,比如直接实现一个JsonMemory
,只需要实现相同的IDataMemory
接口就好了
1 2 3 4 5 6 7 8 9
| public class DataMgr : NormalSingleton<DataMgr>, IDataMemory { private IDataMemory m_data; public DataMgr() { m_data = new JsonMemory(); } }
|