Unity 制作一个分数统计系统

作者:CoderZ1010 时间:2021-11-30 03:01:52 

项目中经常遇到分数统计的需求,例如我们执行了某项操作或做了某个题目,操作正确则计分,相反则不计分失去该项分数,为了应对需求需要一个分数统计系统。

首先定义一个分数信息的数据结构,使用Serializable特性使其可序列化:


using System;
using UnityEngine;

namespace SK.Framework
{
   /// <summary>
   /// 分数信息
   /// </summary>
   [Serializable]
   public class ScoreInfo
   {
       /// <summary>
       /// ID
       /// </summary>
       public int id;
       /// <summary>
       /// 描述
       /// </summary>
       [TextArea]
       public string description;
       /// <summary>
       /// 分值
       /// </summary>
       public float value;
   }
}

ScoreInfo类可序列化后,创建ScoreProfile类继承ScriptableObject使其作为可通过菜单创建的Asset资产:


using UnityEngine;

namespace SK.Framework
{
   /// <summary>
   /// 分数配置文件
   /// </summary>
   [CreateAssetMenu]
   public class ScoreProfile : ScriptableObject
   {
       public ScoreInfo[] scores = new ScoreInfo[0];
   }
}

Unity 制作一个分数统计系统

使用ScoreIDConstant类编写所有分数项ID常量,创建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>
   /// 分数信息
   /// </summary>
   [Serializable]
   public class ScoreInfo
   {
       /// <summary>
       /// ID
       /// </summary>
       [ScoreID]
       public int id;
       /// <summary>
       /// 描述
       /// </summary>
       [TextArea]
       public string description;
       /// <summary>
       /// 分值
       /// </summary>
       public float value;
   }
}

Unity 制作一个分数统计系统

数据可配置后,创建分数项Score类,声明以下字段:Flag表示该分数项的标识,注册分数项时返回该标识,用于后续获取或取消该分数项分值;Description即分数项的描述;Value表示该分数项的分值;IsObtained用于标记该分数项的分值是否已经获得。


namespace SK.Framework
{
   /// <summary>
   /// 分数项
   /// </summary>
   public class Score
   {
       /// <summary>
       /// 标识
       /// </summary>
       public string Flag { get; private set; }
       /// <summary>
       /// 描述
       /// </summary>
       public string Description { get; private set; }
       /// <summary>
       /// 分值
       /// </summary>
       public float Value { get; private set; }
       /// <summary>
       /// 是否已经获得分值
       /// </summary>
       public bool IsObtained { get; set; }

public Score(string flag, string description, float value)
       {
           Flag = flag;
           Description = description;
           Value = value;
       }
   }
}

为了实现一个分数组合,例如某项操作,通过A操作方式可获得5分,通过B操作方式可获得3分,它们之间是互斥的,即获得了前者的5分,就不会获得后者的3分,创建ScoreGroup类:


using System.Collections.Generic;

namespace SK.Framework
{
   /// <summary>
   /// 分数组合
   /// </summary>
   public class ScoreGroup
   {
       /// <summary>
       /// 组合描述
       /// </summary>
       public string Description { get; private set; }
       /// <summary>
       /// 计分模式
       /// Additive表示组合内分值进行累加
       /// MutuallyExclusive表示组内各分数项互斥 获得其中一项分值 则取消其它项分值
       /// </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($"获取分数组合 [{Description}] 中标识为 [{flag}] 的分值 [{target.Description}]");
               }
               return true;
           }
           if (ScoreMaster.DebugMode)
           {
               ScoreMaster.LogError($"分数组合 [{Description}] 中不存在标识为 [{flag}] 的分数项.");
           }
           return false;
       }
       public bool Cancle(string flag)
       {
           var target = Scores.Find(m => m.Flag == flag);
           if (target != null)
           {
               if (ScoreMaster.DebugMode)
               {
                   ScoreMaster.LogInfo($"取消分数组合 [{Description}] 中标识为 [{flag}] 的分数项分值 [{target.Description}]");
               }
               target.IsObtained = false;
               return true;
           }
           if (ScoreMaster.DebugMode)
           {
               ScoreMaster.LogError($"分数组合 [{Description}] 中不存在标识为 [{flag}] 的分数项.");
           }
           return false;
       }
   }
}

namespace SK.Framework
{
   /// <summary>
   /// 计分方式
   /// </summary>
   public enum ValueMode
   {
       /// <summary>
       /// 累加的
       /// </summary>
       Additive,
       /// <summary>
       /// 互斥的
       /// </summary>
       MutuallyExclusive,
   }
}

最终编写分数管理类,封装Create、Obtain、Cancle、GetSum函数,分别用于创建分数组合、获取分数、取消分数、获取总分,实现Editor类使分数信息在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("加载分数信息配置表失败.");
                   }
               }
               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($"创建分数ID为 [{idArray[i]}] 的分数项 [{info.description}] flag: {flag}");
               }
               else if (DebugMode)
               {
                   LogError($"配置中不存在ID为 [{idArray[i]}] 的分数信息.");
               }
           }
           ScoreGroup group = new ScoreGroup(description, valueMode, scores);
           groups.Add(description, group);
           if (DebugMode)
           {
               LogInfo($"创建分数组合 [{description}] 计分模式[{valueMode}]");
           }
           return flags;
       }
       private bool ObtainValue(string groupDescription, string flag)
       {
           if (groups.TryGetValue(groupDescription, out ScoreGroup target))
           {
               return target.Obtain(flag);
           }
           if (DebugMode)
           {
               LogError($"不存在分数组合 [{groupDescription}].");
           }
           return false;
       }
       private bool CancleValue(string groupDescription, string flag)
       {
           if (groups.TryGetValue(groupDescription, out ScoreGroup target))
           {
               return target.Cancle(flag);
           }
           if (DebugMode)
           {
               LogError($"不存在分数组合 [{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>
       /// 创建分数组合
       /// </summary>
       /// <param name="description">分数组合描述</param>
       /// <param name="valueMode">分数组计分方式</param>
       /// <param name="idArray">分数信息ID组合</param>
       /// <returns>返回分数项标识符组合</returns>
       public static string[] Create(string description, ValueMode valueMode, params int[] idArray)
       {
           return Instance.CreateScore(description, valueMode, idArray);
       }
       /// <summary>
       /// 获取分数组合中指定标识分数项的分值
       /// </summary>
       /// <param name="groupDescription">分数组合</param>
       /// <param name="flag">分数项标识</param>
       /// <returns>获取成功返回true 否则返回false</returns>
       public static bool Obtain(string groupDescription, string flag)
       {
           return Instance.ObtainValue(groupDescription, flag);
       }
       /// <summary>
       /// 取消分数组合中指定标识分数项的分值
       /// </summary>
       /// <param name="groupDescription">分数组合</param>
       /// <param name="flag">分数项标识</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($"计分模式: {(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($"标识: {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
}

测试:


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;
   }
}

Unity 制作一个分数统计系统


using UnityEngine;
using SK.Framework;

public class Foo : MonoBehaviour
{
   private string[] flags;

private void Start()
   {
       flags = ScoreMaster.Create("测试", 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("测试", flags[0]);
       }
       if (GUILayout.Button("B", GUILayout.Width(200f), GUILayout.Height(50f)))
       {
           ScoreMaster.Obtain("测试", flags[1]);
       }
       if (GUILayout.Button("C", GUILayout.Width(200f), GUILayout.Height(50f)))
       {
           ScoreMaster.Obtain("测试", flags[2]);
       }
       if (GUILayout.Button("D", GUILayout.Width(200f), GUILayout.Height(50f)))
       {
           ScoreMaster.Obtain("测试", flags[3]);
       }
       GUILayout.Label($"总分: {ScoreMaster.GetSum()}");
   }
}

Unity 制作一个分数统计系统

Unity 制作一个分数统计系统 

来源:https://blog.csdn.net/qq_42139931/article/details/121654343

标签:Unity,分数,统计系统
0
投稿

猜你喜欢

  • C#命令模式用法实例

    2021-10-21 12:46:02
  • java开源调度如何给xxljob加k8s执行器

    2021-09-17 16:41:50
  • java实现socket从服务器连续获取消息的示例

    2021-10-22 02:50:55
  • SpringBoot框架集成ElasticSearch实现过程示例详解

    2023-02-02 08:44:05
  • Android切面编程知识点详解

    2023-05-05 04:18:04
  • Java中JavaBean对象和Map的互相转换方法实例

    2021-07-12 04:46:05
  • Mybatis 入参类型方式全面详解

    2023-10-16 20:03:40
  • 详解Spring Boot 事务的使用

    2022-08-09 01:59:07
  • android实现raw文件夹导入数据库代码

    2023-07-02 04:26:28
  • Java设计模式之状态模式State Pattern详解

    2023-11-07 07:55:01
  • Android基准配置文件Baseline Profile方案提升启动速度

    2022-08-12 18:54:42
  • Java经典面试题最全汇总208道(二)

    2023-11-09 08:13:39
  • C#添加、读取Word脚注尾注的方法

    2022-12-24 02:12:22
  • Java利用Easyexcel导出excel表格的示例代码

    2023-05-30 23:36:04
  • 关于mybatis3中@SelectProvider的使用问题

    2021-11-20 17:03:19
  • java线程池ThreadPoolExecutor的八种拒绝策略示例详解

    2021-06-24 11:31:10
  • Android横竖屏幕切换生命周期详解

    2023-11-02 03:29:45
  • JAVA多线程并发下的单例模式应用

    2022-09-15 01:27:31
  • 详解Spring Boot 项目部署到heroku爬坑

    2021-05-28 06:21:07
  • ReentrantLock从源码解析Java多线程同步学习

    2023-10-13 02:32:55
  • asp之家 软件编程 m.aspxhome.com