Cadre simple de développement de problèmes Unity

Dans le processus de développement normal, vous rencontrez généralement beaucoup de développement de questions, les plus courantes incluent des questions à choix multiples, des questions à remplir, des questions et réponses, etc. Bien sûr, le développement de ces fonctions est très simple, mais s'il n'existe pas de cadre universel de gestion, mais qu'un ensemble de codes est écrit pour chaque ou chaque type de question, alors les itérations et la maintenance ultérieures en seront grandement affectées. pour résoudre ce problème, pendant mon temps libre, j'ai créé un cadre de questions simple qui peut prendre en charge et étendre tous les types de questions mentionnés ci-dessus.

Tout d'abord, j'ai défini une interface IQuestion. Toutes les questions ultérieures seront héritées de cette interface. Le contenu de l'interface est le suivant

public interface IQuestion
{
    /// <summary>
    /// quesItem列表
    /// </summary>
    List<IQuesItem> questionItemList { get; set; }
    /// <summary>
    /// quesItem的数量
    /// </summary>
    int quesItemCount { get; }
    /// <summary>
    /// 增加一个QuesItem
    /// </summary>
    /// <param name="item"></param>
    void AddQuesItem(IQuesItem item);
    /// <summary>
    /// 移除一个QuesItem
    /// </summary>
    /// <param name="item"></param>
    void RemoveQuesItem(IQuesItem item);
    /// <summary>
    /// 清空所有QuesItem
    /// </summary>
    /// <param name="item"></param>
    void ClearQuesItem();
    /// <summary>
    /// 提交
    /// </summary>
    /// <returns></returns>
    bool Submit();
    /// <summary>
    /// 是否回答正确
    /// </summary>
    /// <returns></returns>
    bool IsRight();
    /// <summary>
    /// 根据索引获取QuesItem
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    IQuesItem GetQuesItem(int id);
}

Ensuite, une nouvelle interface IQuesItem est introduite dans l'interface IQuestion ci-dessus.
Qu’est-ce que IQuesItem ?
En fait, en bref, prenons comme exemple une question à choix multiples. Chaque option A/B/C/D est un QuesItem. S'il s'agit d'une question à remplir, alors chaque blanc qui doit être saisi est un QuesItem.
Pourquoi introduire l'interface IQuesItem ?
Parce que étant donné que l'affichage, l'effet et la méthode de fonctionnement de chaque unité opérationnelle peuvent être différents à un stade ultérieur, alors à ce stade, toutes ces situations possibles sont très appropriées pour le traitement polymorphe, et chaque unité opérationnelle à un stade ultérieur sera une personne indépendante. , améliorer la flexibilité de l'unité opérationnelle

/// <summary>
/// 操作单元
/// </summary>
public interface IQuesItem
{
    /// <summary>
    /// 目标结果
    /// </summary>
    object targetAnswer { get;}
    /// <summary>
    /// 实际当前结果
    /// </summary>
    object curAnswer { get; }
    /// <summary>
    /// 实际当前结果改变
    /// </summary>
    /// <param name="value">改变之后</param>
    void OnChanged(object value);
    /// <summary>
    /// 是否回答正确
    /// </summary>
    /// <returns></returns>
    bool IsRight();
    /// <summary>
    /// 回答正确之后的反应
    /// </summary>
    void OnRight();
    /// <summary>
    /// 回答错误之后的反应
    /// </summary>
    void OnError();
}

En fait, la structure de base a été déterminée au moment d'écrire ces lignes. Plus tard, l'objet question hérite de IQuestion, et toutes les unités opérationnelles héritent de IQuesItem, puis le polymorphisme est effectué dans les objets spécifiques.
Mais à ce moment-là, nous avons constaté que, comme l'interface ne pouvait pas implémenter de méthodes spécifiques, afin d'améliorer la vitesse de développement, j'ai réalisé une autre encapsulation entre l'objet réel et l'interface du sujet.

public abstract class Question : MonoBehaviour,IQuestion
{
    private IQuestion iquestion=>this;
    List<IQuesItem> IQuestion.questionItemList { get; set; }
    public int quesItemCount => iquestion?.questionItemList == null ? 0 : iquestion.questionItemList.Count;

    protected virtual void Awake()
    {
        IQuesItem[] items = transform.GetComponentsInChildren<IQuesItem>();
        if (items!=null&&items.Length>0)
            for (int i = 0; i < items.Length; i++)AddQuesItem(items[i]);
    }

    public IQuesItem GetQuesItem(int id)
    {
        if (id>=quesItemCount) return null;
        return iquestion.questionItemList[id];
    }

    /// <summary>
    /// 提交答案
    /// </summary>
    /// <returns>返回正误</returns>
    public virtual void OnSubmit()
    {
        bool isRight = iquestion.Submit();
        SubmitResult(isRight);
    }

    protected abstract void SubmitResult(bool isRight);

    /// <summary>
    /// 添加一个QuesItem
    /// </summary>
    /// <param name="item"></param>
    public void AddQuesItem(IQuesItem item)
    {
        if (iquestion.questionItemList==null)iquestion.questionItemList=new List<IQuesItem>();
        iquestion.questionItemList.Add(item);
    }

    /// <summary>
    /// 移除一个QuesItem
    /// </summary>
    /// <param name="item"></param>
    public void RemoveQuesItem(IQuesItem item)
    {
        if (quesItemCount==0) return;
        iquestion.questionItemList.Remove(item);
    }

    /// <summary>
    /// 清空所有QuesItem
    /// </summary>
    public void ClearQuesItem()
    {
        if (iquestion.questionItemList!=null)iquestion.questionItemList.Clear();
    }

    /// <summary>
    /// 当前回答是否正确
    /// </summary>
    /// <returns>返回正误</returns>
    public bool IsRight()
    {
        if (quesItemCount==0) return false;
        bool isRight = true;
        for (int i = 0; i < quesItemCount; i++)
        {
            if (!GetQuesItem(i).IsRight())
            {
                isRight = false;
                break;
            }
        }
        return isRight;
    }

    #region 私有
    bool IQuestion.Submit()
    {
        if (quesItemCount == 0) return false;
        IQuesItem curItem = null;
        bool isRight = true;
        for (int i = 0; i < quesItemCount; i++)
        {
            curItem = GetQuesItem(i);
            if (curItem.IsRight())curItem.OnRight();
            else
            {
                isRight = false;
                curItem.OnError();
            }
        }
        return isRight;
    }

    #endregion
    
}

Ensuite, vous pouvez voir que l'objet Question hérite de MonoBehaviour et de l'interface IQuestion. Premièrement, il hérite de MonoBehaviour pour simplifier la configuration des données initiales. Il hérite de l'interface IQuestion pour réaliser les fonctions de base de la question. De plus, Question est un objet abstrait dans le but d'empêcher l'utilisation directe de la question et de modifier les données internes et la logique pendant le développement du programme pour garantir l'unité de cet objet.
Par conséquent, nous pouvons hériter de tous les objets question de Question au lieu de l’interface IQuestion.

En fait, c'est terminé quand je l'écris, mais si je ne vous laisse pas voir l'effet, j'ai toujours l'impression qu'il manque quelque chose.
Mo Fang ! Passons un test simple avec des questions à choix multiples.
Ensuite, j'ai nommé l'objet question à choix multiple ChooseQues, et bien sûr je dois hériter de Question.

/// <summary>
/// 选择题
/// </summary>
public class ChooseQues : Question
{
    [SerializeField]
    private Text tipText;
    [SerializeField]
    private Button submitBtn;

    private void Start()
    {
        submitBtn.onClick.AddListener(OnSubmit);
    }

    protected override void SubmitResult(bool isRight)
    {
        tipText.text = isRight ? "回答正确" : "回答错误";
    }

}

Et ça ? L'objet question à choix multiple n'a besoin que d'écrire ce code pour réaliser les fonctions de soumission et d'invite. . . . .
Il manque quelque chose ?
C'est vrai, chaque objet d'unité d'opération (option) est manquant, j'appelle donc chaque unité d'opération ToggleItem, car j'utilise Toggle pour créer chaque option, bien sûr, vous pouvez également utiliser d'autres méthodes.

public class ToggleItem : MonoBehaviour,IQuesItem
{
    [SerializeField]
    private bool answer;
    public object targetAnswer => answer;
    public object curAnswer => toggle.isOn;
    private Toggle toggle;
    private Text effectText;

    private void Start()
    {
        toggle = GetComponent<Toggle>();
        toggle.onValueChanged.AddListener(isSelect=>OnChanged(isSelect));
        effectText = transform.Find("EffectText")?.GetComponent<Text>();
    }

    public void OnChanged(object value)
    {
        Debug.Log(transform.name+": 变为:"+(bool)value);
    }

    public bool IsRight()
    {
        return (bool) curAnswer == (bool) targetAnswer;
    }

    public void OnRight()
    {
        if (effectText)effectText.text = "选择正确";
    }

    public void OnError()
    {
        if (effectText)effectText.text = "选择错误";
    }
}

J'ai vraiment fini d'écrire cette fois. .
Vous pouvez voir que cet objet a un champ targetAnswer, qui est la réponse cible (peut être configuré dans le panneau). L'interface est de type objet, car son type de données ne peut pas être corrigé ultérieurement. Par exemple, la valeur booléenne peut être utilisée comme réponse cible ici. .
Il existe également un champ appelé curAnswer, qui est la réponse actuellement saisie, il nous suffit donc évidemment de juger si les résultats des deux sont synergiques dans IsRight().
effectText crée un effet simple (invite de texte, ce bloc d'opération est-il correct ?). Bien sûr, vous pouvez également créer des effets très sympas. Écrivez-les directement dans OnRight() [l'effet après avoir répondu correctement] et OnError() [L'effet effet après avoir mal répondu] ira bien.
Les rendus sont les suivants :

Insérer la description de l'image ici
Insérer la description de l'image ici
Insérer la description de l'image ici
Insérer la description de l'image ici
Enfin, le lien du code source est joint. Le code source implémente également les types de questions à remplir et le puzzle à grille de neuf carrés.
Code source

Je suppose que tu aimes

Origine blog.csdn.net/weixin_42498461/article/details/123550925
conseillé
Classement