iis服务器助手广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >Unity 制作一个分数统计系统
  • 650
分享到

Unity 制作一个分数统计系统

2024-04-02 19:04:59 650人浏览 八月长安
摘要

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

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

首先定义一个分数信息的数据结构,使用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];
    }
}

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

数据可配置后,创建分数项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;
    }
}


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的资料请关注编程网其它相关文章!

--结束END--

本文标题: Unity 制作一个分数统计系统

本文链接: https://www.lsjlt.com/news/159116.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Unity 制作一个分数统计系统
    项目中经常遇到分数统计的需求,例如我们执行了某项操作或做了某个题目,操作正确则计分,相反则不计分失去该项分数,为了应对需求需要一个分数统计系统。 首先定义一个分数信息的数据结构,使用...
    99+
    2024-04-02
  • Unity如何制作一个分数统计系统
    这篇文章将为大家详细讲解有关Unity如何制作一个分数统计系统,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。首先定义一个分数信息的数据结构,使用Serializable特性使其可序列化:using&nbs...
    99+
    2023-06-21
  • Unity利用XML制作一个简易的登录系统
    通过XML文件保存账号密码,存储到本地,不连接数据库的简易登录系统。 1.创建一个XML文件,设置一个初始的账号密码。 public void Creat() { ...
    99+
    2024-04-02
  • 基于Unity制作一个简易的计算器
    目录一、前言二、效果图及源工程三、实现1.界面搭建2.代码实现四、后记一、前言 Hello,又见面了,今天分享如何使用Unity制作计算器,难度中等,可以用来学习,或者当成其他项目的...
    99+
    2024-04-02
  • 如何使用Unity制作一个简易的计算器
    这篇文章给大家分享的是有关如何使用Unity制作一个简易的计算器的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、前言Hello,又见面了,今天分享如何使用Unity制作计算器,难度中等,可以用来学习,或者当成其...
    99+
    2023-06-29
  • 基于PyQT5制作一个课堂点名系统
    刷抖音的时候发现一个老师在用的课堂点名系统。用PyQt5实现了一下同款,导入学生姓名,测试了一下完美运行。 操作效果展示: 完整源代码块还是放在了文章的最后面 使用的时候准备好学生...
    99+
    2024-04-02
  • 如何设计一个RPC系统?
    RPC是一种方便的网络通信编程模型,由于和编程语言的高度结合,大大减少了处理网络数据的复杂度,让代码可读性也有可观的提高。但是RPC本身的构成却比较复杂,由于受到编程语言、网络模型、使用习惯的约束,有大量的妥协和取舍之处。本文就是通过分析几...
    99+
    2023-06-05
  • 怎么设计一个RPC系统
    这篇文章主要讲解了“怎么设计一个RPC系统”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么设计一个RPC系统”吧!由于RPC底层的网络开发一般和具体使用环境有关,而编程实现手段也非常多样化...
    99+
    2023-06-02
  • 跟大神一起15分钟制作一个属于自己的Linux操作系统!
    计算机已成为现代人日常工作、学习和生活中必不可少的工具。操作系统是计算机之魂,作为用户使用计算机的接口,它负责调度执行各个用户程序,使计算机完成特定的任务;作为计算机硬件资源的管理者,它负责协调计算机中各类设备高效地工作。操作系统的重要性不...
    99+
    2023-06-05
  • 一条sql实现统计总数、分组分别统计总数
    wshanshi:个人使用记录… 一、 方法一 SELECT COALESCE( sex, '总数' ),COUNT( id ) '人数'FROMtestGROUP BYsex WIT...
    99+
    2023-09-02
    sql 数据库 mysql
  • 使用python怎么制作一个云打卡系统
    这期内容当中小编将会给大家带来有关使用python怎么制作一个云打卡系统,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。python的数据类型有哪些python的数据类型:1. 数字类型,包括int(整型)...
    99+
    2023-06-14
  • 为什么Unix系统是分布式计算的首选操作系统之一?
    Unix系统是分布式计算的首选操作系统之一,这并不是一句空话。在本文中,我们将深入探讨Unix系统作为分布式计算平台的优势,并分析为什么Unix系统能够成为分布式计算领域的佼佼者。 Unix系统的可扩展性 Unix系统是一个高度可扩展的...
    99+
    2023-09-10
    unix 分布式 javascript
  • 如何制作一套ERP/CRM系统
    这篇文章将为大家详细讲解有关如何制作一套ERP/CRM系统,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。根据传统开发的速度,开发系统一般情况下,就算是中小型企业的管理系统都可能要3~6个月的...
    99+
    2023-06-03
  • 基于Matlab制作一个不良图片检测系统
    目录不良图片检测部分part.0 图片导入part.1 检查是否为肤色part.2 皮肤区域标记part.3 通过皮肤区域特点判定是否为不良图片完整代码批量处理部分不良图片检测部分 ...
    99+
    2024-04-02
  • c#语言使用Unity粒子系统制作手雷爆炸
    目录一、创建地形二、应用资源包三、制作手雷一、创建地形 1、GameObject ->3D Object-> Terrain,创建带有地形属性的平面 2、Terrain-...
    99+
    2024-04-02
  • c#怎么使用Unity粒子系统制作手雷爆炸
    这篇文章主要介绍“c#怎么使用Unity粒子系统制作手雷爆炸”,在日常操作中,相信很多人在c#怎么使用Unity粒子系统制作手雷爆炸问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”c#怎么使用Unity粒子系统...
    99+
    2023-06-30
  • 如何设计一个JavaScript插件系统
    这篇文章给大家介绍如何设计一个JavaScript插件系统,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。WordPress有插件、 jQuery有插件、Gatsby、Eleventy和...
    99+
    2024-04-02
  • 个人网站制作系统怎么做
    个人网站制作系统可以分为以下几个步骤:1.确定需求:首先确定个人网站的需求,包括网站的主题、功能、页面结构等。2.选择技术:根据需求...
    99+
    2023-06-05
    网站制作系统
  • 利用Python制作一个简单的天气播报系统
    目录前言工具天气数据来源代码实现总结前言 大家好,我是辣条 相信大家都能感觉到最近天气的多变,好几次出门半路天气转变。辣条也深受其扰,直接给我整感冒,就差被隔离起来了,既然天气我没法...
    99+
    2024-04-02
  • unity怎么统计环境重置次数
    在Unity中统计环境重置次数可以通过以下步骤实现:1. 创建一个整型变量用于存储环境重置次数,例如`resetCount`。2. ...
    99+
    2023-08-20
    unity
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作