Unity?制作一個(gè)分?jǐn)?shù)統(tǒng)計(jì)系統(tǒng)
項(xiàng)目中經(jīng)常遇到分?jǐn)?shù)統(tǒng)計(jì)的需求,例如我們執(zhí)行了某項(xiàng)操作或做了某個(gè)題目,操作正確則計(jì)分,相反則不計(jì)分失去該項(xiàng)分?jǐn)?shù),為了應(yīng)對(duì)需求需要一個(gè)分?jǐn)?shù)統(tǒng)計(jì)系統(tǒng)。
首先定義一個(gè)分?jǐn)?shù)信息的數(shù)據(jù)結(jié)構(gòu),使用Serializable特性使其可序列化:
using System; using UnityEngine; namespace SK.Framework { /// <summary> /// 分?jǐn)?shù)信息 /// </summary> [Serializable] public class ScoreInfo { /// <summary> /// ID /// </summary> public int id; /// <summary> /// 描述 /// </summary> [TextArea] public string description; /// <summary> /// 分值 /// </summary> public float value; } }
ScoreInfo類可序列化后,創(chuàng)建ScoreProfile類繼承ScriptableObject使其作為可通過(guò)菜單創(chuàng)建的Asset資產(chǎn):
using UnityEngine; namespace SK.Framework { /// <summary> /// 分?jǐn)?shù)配置文件 /// </summary> [CreateAssetMenu] public class ScoreProfile : ScriptableObject { public ScoreInfo[] scores = new ScoreInfo[0]; } }
使用ScoreIDConstant類編寫(xiě)所有分?jǐn)?shù)項(xiàng)ID常量,創(chuàng)建ScoreID特性并使用PropertyDrawer使其可在面板選擇:
namespace SK.Framework { public sealed class ScoreIDConstant { public const int INVALID = -1; } }
using UnityEngine; #if UNITY_EDITOR using UnityEditor; using System; using System.Reflection; using System.Collections; #endif namespace SK.Framework { public class ScoreIDAttribute : PropertyAttribute { } #if UNITY_EDITOR [CustomPropertyDrawer(typeof(ScoreIDAttribute))] public class ScoreIDPropertyAttributeDrawer : PropertyDrawer { private int[] scoreIDArray; private GUIContent[] scoreIDConstArray; public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { return base.GetPropertyHeight(property, label); } public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { if (scoreIDConstArray == null) { ArrayList constants = new ArrayList(); FieldInfo[] fieldInfos = typeof(ScoreIDConstant).GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); for (int i = 0; i < fieldInfos.Length; i++) { var fi = fieldInfos[i]; if (fi.IsLiteral && !fi.IsInitOnly) constants.Add(fi); } FieldInfo[] fieldInfoArray = (FieldInfo[])constants.ToArray(typeof(FieldInfo)); scoreIDArray = new int[fieldInfoArray.Length]; scoreIDConstArray = new GUIContent[fieldInfoArray.Length]; for (int i = 0; i < fieldInfoArray.Length; i++) { scoreIDConstArray[i] = new GUIContent(fieldInfoArray[i].Name); scoreIDArray[i] = (int)fieldInfoArray[i].GetValue(null); } } var index = Array.IndexOf(scoreIDArray, property.intValue); index = Mathf.Clamp(index, 0, scoreIDArray.Length); index = EditorGUI.Popup(position, label, index, scoreIDConstArray); property.intValue = scoreIDArray[index]; } } #endif }
有了ScoreID特性后,用于ScoreInfo中的id字段:
using System; using UnityEngine; namespace SK.Framework { /// <summary> /// 分?jǐn)?shù)信息 /// </summary> [Serializable] public class ScoreInfo { /// <summary> /// ID /// </summary> [ScoreID] public int id; /// <summary> /// 描述 /// </summary> [TextArea] public string description; /// <summary> /// 分值 /// </summary> public float value; } }
數(shù)據(jù)可配置后,創(chuàng)建分?jǐn)?shù)項(xiàng)Score類,聲明以下字段:Flag表示該分?jǐn)?shù)項(xiàng)的標(biāo)識(shí),注冊(cè)分?jǐn)?shù)項(xiàng)時(shí)返回該標(biāo)識(shí),用于后續(xù)獲取或取消該分?jǐn)?shù)項(xiàng)分值;Description即分?jǐn)?shù)項(xiàng)的描述;Value表示該分?jǐn)?shù)項(xiàng)的分值;IsObtained用于標(biāo)記該分?jǐn)?shù)項(xiàng)的分值是否已經(jīng)獲得。
namespace SK.Framework { /// <summary> /// 分?jǐn)?shù)項(xiàng) /// </summary> public class Score { /// <summary> /// 標(biāo)識(shí) /// </summary> public string Flag { get; private set; } /// <summary> /// 描述 /// </summary> public string Description { get; private set; } /// <summary> /// 分值 /// </summary> public float Value { get; private set; } /// <summary> /// 是否已經(jīng)獲得分值 /// </summary> public bool IsObtained { get; set; } public Score(string flag, string description, float value) { Flag = flag; Description = description; Value = value; } } }
為了實(shí)現(xiàn)一個(gè)分?jǐn)?shù)組合,例如某項(xiàng)操作,通過(guò)A操作方式可獲得5分,通過(guò)B操作方式可獲得3分,它們之間是互斥的,即獲得了前者的5分,就不會(huì)獲得后者的3分,創(chuàng)建ScoreGroup類:
using System.Collections.Generic; namespace SK.Framework { /// <summary> /// 分?jǐn)?shù)組合 /// </summary> public class ScoreGroup { /// <summary> /// 組合描述 /// </summary> public string Description { get; private set; } /// <summary> /// 計(jì)分模式 /// Additive表示組合內(nèi)分值進(jìn)行累加 /// MutuallyExclusive表示組內(nèi)各分?jǐn)?shù)項(xiàng)互斥 獲得其中一項(xiàng)分值 則取消其它項(xiàng)分值 /// </summary> public ValueMode ValueMode { get; private set; } public List<Score> Scores { get; private set; } public ScoreGroup(string description, ValueMode valueMode, params Score[] scores) { Description = description; ValueMode = valueMode; Scores = new List<Score>(scores); } public bool Obtain(string flag) { var target = Scores.Find(m => m.Flag == flag); if (target != null) { switch (ValueMode) { case ValueMode.Additive: target.IsObtained = true; break; case ValueMode.MutuallyExclusive: for (int i = 0; i < Scores.Count; i++) { Scores[i].IsObtained = Scores[i] == target; } break; default: break; } if (ScoreMaster.DebugMode) { ScoreMaster.LogInfo($"獲取分?jǐn)?shù)組合 [{Description}] 中標(biāo)識(shí)為 [{flag}] 的分值 [{target.Description}]"); } return true; } if (ScoreMaster.DebugMode) { ScoreMaster.LogError($"分?jǐn)?shù)組合 [{Description}] 中不存在標(biāo)識(shí)為 [{flag}] 的分?jǐn)?shù)項(xiàng)."); } return false; } public bool Cancle(string flag) { var target = Scores.Find(m => m.Flag == flag); if (target != null) { if (ScoreMaster.DebugMode) { ScoreMaster.LogInfo($"取消分?jǐn)?shù)組合 [{Description}] 中標(biāo)識(shí)為 [{flag}] 的分?jǐn)?shù)項(xiàng)分值 [{target.Description}]"); } target.IsObtained = false; return true; } if (ScoreMaster.DebugMode) { ScoreMaster.LogError($"分?jǐn)?shù)組合 [{Description}] 中不存在標(biāo)識(shí)為 [{flag}] 的分?jǐn)?shù)項(xiàng)."); } return false; } } }
namespace SK.Framework { /// <summary> /// 計(jì)分方式 /// </summary> public enum ValueMode { /// <summary> /// 累加的 /// </summary> Additive, /// <summary> /// 互斥的 /// </summary> MutuallyExclusive, } }
最終編寫(xiě)分?jǐn)?shù)管理類,封裝Create、Obtain、Cancle、GetSum函數(shù),分別用于創(chuàng)建分?jǐn)?shù)組合、獲取分?jǐn)?shù)、取消分?jǐn)?shù)、獲取總分,實(shí)現(xiàn)Editor類使分?jǐn)?shù)信息在Inspector面板可視化:
using System; using UnityEngine; using System.Collections.Generic; #if UNITY_EDITOR using UnityEditor; using System.Reflection; #endif namespace SK.Framework { public class ScoreMaster : MonoBehaviour { #region NonPublic Variables private static ScoreMaster instance; [SerializeField] private ScoreProfile profile; private readonly Dictionary<string, ScoreGroup> groups = new Dictionary<string, ScoreGroup>(); #endregion #region Public Properties public static ScoreMaster Instance { get { if (instance == null) { instance = FindObjectOfType<ScoreMaster>(); } if (instance == null) { instance = new GameObject("[SKFramework.Score]").AddComponent<ScoreMaster>(); instance.profile = Resources.Load<ScoreProfile>("Score Profile"); if (instance.profile == null && DebugMode) { LogError("加載分?jǐn)?shù)信息配置表失敗."); } } return instance; } } #endregion #region NonPublic Methods private string[] CreateScore(string description, ValueMode valueMode, params int[] idArray) { Score[] scores = new Score[idArray.Length]; string[] flags = new string[idArray.Length]; for (int i = 0; i < idArray.Length; i++) { var info = Array.Find(profile.scores, m => m.id == idArray[i]); if (info != null) { var flag = Guid.NewGuid().ToString(); flags[i] = flag; scores[i] = new Score(flag, info.description, info.value); if (DebugMode) LogInfo($"創(chuàng)建分?jǐn)?shù)ID為 [{idArray[i]}] 的分?jǐn)?shù)項(xiàng) [{info.description}] flag: {flag}"); } else if (DebugMode) { LogError($"配置中不存在ID為 [{idArray[i]}] 的分?jǐn)?shù)信息."); } } ScoreGroup group = new ScoreGroup(description, valueMode, scores); groups.Add(description, group); if (DebugMode) { LogInfo($"創(chuàng)建分?jǐn)?shù)組合 [{description}] 計(jì)分模式[{valueMode}]"); } return flags; } private bool ObtainValue(string groupDescription, string flag) { if (groups.TryGetValue(groupDescription, out ScoreGroup target)) { return target.Obtain(flag); } if (DebugMode) { LogError($"不存在分?jǐn)?shù)組合 [{groupDescription}]."); } return false; } private bool CancleValue(string groupDescription, string flag) { if (groups.TryGetValue(groupDescription, out ScoreGroup target)) { return target.Cancle(flag); } if (DebugMode) { LogError($"不存在分?jǐn)?shù)組合 [{groupDescription}]."); } return false; } private float GetSumValue() { float retV = 0f; foreach (var kv in groups) { var scores = kv.Value.Scores; for (int i = 0; i < scores.Count; i++) { var score = scores[i]; if (score.IsObtained) { retV += score.Value; } } } return retV; } #endregion #region Public Methods /// <summary> /// 創(chuàng)建分?jǐn)?shù)組合 /// </summary> /// <param name="description">分?jǐn)?shù)組合描述</param> /// <param name="valueMode">分?jǐn)?shù)組計(jì)分方式</param> /// <param name="idArray">分?jǐn)?shù)信息ID組合</param> /// <returns>返回分?jǐn)?shù)項(xiàng)標(biāo)識(shí)符組合</returns> public static string[] Create(string description, ValueMode valueMode, params int[] idArray) { return Instance.CreateScore(description, valueMode, idArray); } /// <summary> /// 獲取分?jǐn)?shù)組合中指定標(biāo)識(shí)分?jǐn)?shù)項(xiàng)的分值 /// </summary> /// <param name="groupDescription">分?jǐn)?shù)組合</param> /// <param name="flag">分?jǐn)?shù)項(xiàng)標(biāo)識(shí)</param> /// <returns>獲取成功返回true 否則返回false</returns> public static bool Obtain(string groupDescription, string flag) { return Instance.ObtainValue(groupDescription, flag); } /// <summary> /// 取消分?jǐn)?shù)組合中指定標(biāo)識(shí)分?jǐn)?shù)項(xiàng)的分值 /// </summary> /// <param name="groupDescription">分?jǐn)?shù)組合</param> /// <param name="flag">分?jǐn)?shù)項(xiàng)標(biāo)識(shí)</param> /// <returns></returns> public static bool Cancle(string groupDescription, string flag) { return Instance.CancleValue(groupDescription, flag); } /// <summary> /// 獲取總分值 /// </summary> /// <returns>總分值</returns> public static float GetSum() { return Instance.GetSumValue(); } #endregion #region Debugger public static bool DebugMode = true; public static void LogInfo(string info) { Debug.Log($"<color=cyan><b>[SKFramework.Score.Info]</b></color> --> {info}"); } public static void LogWarn(string warn) { Debug.Log($"<color=yellow><b>[SKFramework.Score.Warn]</b></color> --> {warn}"); } public static void LogError(string error) { Debug.Log($"<color=red><b>[SKFramework.Score.Error]</b></color> --> {error}"); } #endregion } #if UNITY_EDITOR [CustomEditor(typeof(ScoreMaster))] public class ScoreMasterInspector : Editor { private SerializedProperty profile; private Dictionary<string, ScoreGroup> groups; private Dictionary<ScoreGroup, bool> groupFoldout; private void OnEnable() { profile = serializedObject.FindProperty("profile"); } public override void OnInspectorGUI() { EditorGUILayout.PropertyField(profile); if (GUI.changed) { serializedObject.ApplyModifiedProperties(); EditorUtility.SetDirty(target); } if (!Application.isPlaying) return; Color color = GUI.color; GUI.color = Color.cyan; OnRuntimeGUI(); GUI.color = color; } private void OnRuntimeGUI() { if (groupFoldout == null) { groups = typeof(ScoreMaster).GetField("groups", BindingFlags.Instance | BindingFlags.NonPublic) .GetValue(ScoreMaster.Instance) as Dictionary<string, ScoreGroup>; groupFoldout = new Dictionary<ScoreGroup, bool>(); } foreach (var kv in groups) { if (!groupFoldout.ContainsKey(kv.Value)) { groupFoldout.Add(kv.Value, false); } ScoreGroup group = kv.Value; groupFoldout[group] = EditorGUILayout.Foldout(groupFoldout[group], group.Description); if (groupFoldout[group]) { GUILayout.Label($"計(jì)分模式: {(group.ValueMode == ValueMode.Additive ? "累加" : "互斥")}"); for (int i = 0; i < group.Scores.Count; i++) { Score score = group.Scores[i]; GUILayout.BeginVertical("Box"); GUI.color = score.IsObtained ? Color.green : Color.cyan; GUILayout.Label($"描述: {score.Description}"); GUILayout.Label($"標(biāo)識(shí): {score.Flag}"); GUILayout.BeginHorizontal(); GUILayout.Label($"分值: {score.Value} {(score.IsObtained ? "√" : "")}"); GUI.color = Color.cyan; GUILayout.FlexibleSpace(); GUI.color = Color.yellow; if (GUILayout.Button("Obtain", "ButtonLeft", GUILayout.Width(50f))) { ScoreMaster.Obtain(group.Description, score.Flag); } if (GUILayout.Button("Cancle", "ButtonRight", GUILayout.Width(50f))) { ScoreMaster.Cancle(group.Description, score.Flag); } GUI.color = Color.cyan; GUILayout.EndHorizontal(); GUILayout.EndVertical(); } } } GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUILayout.Label($"總分: {ScoreMaster.GetSum()}", "LargeLabel"); GUILayout.Space(50f); GUILayout.EndHorizontal(); } } #endif }
測(cè)試:
namespace SK.Framework { public sealed class ScoreIDConstant { public const int INVALID = -1; public const int TEST_A = 0; public const int TEST_B = 1; public const int TEST_C = 2; public const int TEST_D = 3; } }
using UnityEngine; using SK.Framework; public class Foo : MonoBehaviour { private string[] flags; private void Start() { flags = ScoreMaster.Create("測(cè)試", ValueMode.MutuallyExclusive, ScoreIDConstant.TEST_A, ScoreIDConstant.TEST_B, ScoreIDConstant.TEST_C, ScoreIDConstant.TEST_D); } private void OnGUI() { if (GUILayout.Button("A", GUILayout.Width(200f), GUILayout.Height(50f))) { ScoreMaster.Obtain("測(cè)試", flags[0]); } if (GUILayout.Button("B", GUILayout.Width(200f), GUILayout.Height(50f))) { ScoreMaster.Obtain("測(cè)試", flags[1]); } if (GUILayout.Button("C", GUILayout.Width(200f), GUILayout.Height(50f))) { ScoreMaster.Obtain("測(cè)試", flags[2]); } if (GUILayout.Button("D", GUILayout.Width(200f), GUILayout.Height(50f))) { ScoreMaster.Obtain("測(cè)試", flags[3]); } GUILayout.Label($"總分: {ScoreMaster.GetSum()}"); } }
?
以上就是Unity 制作一個(gè)分?jǐn)?shù)統(tǒng)計(jì)系統(tǒng)的詳細(xì)內(nèi)容,更多關(guān)于Unity的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#表達(dá)式樹(shù)Expression基礎(chǔ)講解
這篇文章介紹了C#表達(dá)式樹(shù)Expression和基本用法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-12-12基于C#實(shí)現(xiàn)的多生產(chǎn)者多消費(fèi)者同步問(wèn)題實(shí)例
這篇文章主要介紹了基于C#實(shí)現(xiàn)的多生產(chǎn)者多消費(fèi)者同步問(wèn)題,包括了加鎖與釋放鎖,以及對(duì)應(yīng)臨界資源的訪問(wèn)。是比較實(shí)用的技巧,需要的朋友可以參考下2014-09-09C#實(shí)現(xiàn)計(jì)算器精簡(jiǎn)版
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)計(jì)算器精簡(jiǎn)版,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01c#實(shí)現(xiàn)16進(jìn)制和字符串之間轉(zhuǎn)換的代碼
#中十六進(jìn)制字符串的轉(zhuǎn)換函數(shù)2007-05-05