复盘一下我用过的设计模式

建造者模式

保卫萝卜中使用了建造者模式。UML图如下:

接口:

public interface IBuilder<T>
{
    //获取到游戏物体身上的脚本对象,从而去赋值
    T GetProductorClass(GameObject gameObject);

    //使用工厂去获取具体的游戏对象
    GameObject GetProduct();

    //获取数据信息
    void GetData(T productClassGo);

    //获取特有资源与信息
    void GetOtherResource(T productClassGo);
}

 建造怪兽类

using UnityEngine;

public class MonsterBuilder : IBuilder<Monster>
{
    public int m_monsterID;
    private GameObject monsterGo;

    public void GetData(Monster productClassGo)
    {
        productClassGo.monsterID = m_monsterID;
        productClassGo.HP = m_monsterID * 100;
        productClassGo.currentHP = productClassGo.HP;
        productClassGo.moveSpeed = m_monsterID > 7 ? 7:m_monsterID;  //速度太快了。子弹根本追不上
        productClassGo.initMoveSpeed = m_monsterID > 7 ? 7 : m_monsterID;
        productClassGo.prize = m_monsterID * 50;
    }

    public void GetOtherResource(Monster productClassGo)
    {
        productClassGo.GetMonsterProperty();
    }

    public GameObject GetProduct()
    {
        GameObject itemGo = GameController.Instance.GetGameObjectResource("MonsterPrefab");
        Monster monster = GetProductorClass(itemGo);
        GetData(monster);
        GetOtherResource(monster);
        return itemGo;
    }

    public Monster GetProductorClass(GameObject gameObject)
    {
        return gameObject.GetComponent<Monster>();
    }
}

塔的建造者

using UnityEngine;

/// <summary>
/// 塔的建造者
/// </summary>
public class TowerBuilder : IBuilder<Tower>
{
    public int m_towerID;
    private GameObject towerGO;
    public int m_towerLevel;  //塔的等级

    public void GetData(Tower productClassGo)
    {
        productClassGo.towerID = m_towerID;
    }

    public void GetOtherResource(Tower productClassGo)
    {
        productClassGo.GetTowerProperty();
    }

    public GameObject GetProduct()
    {
        GameObject gameObject = GameController.Instance.GetGameObjectResource
            ("Tower/ID" + m_towerID.ToString() + "/TowerSet/" + m_towerLevel.ToString());
        Tower tower = GetProductorClass(gameObject);
        GetData(tower);
        GetOtherResource(tower);
        return gameObject;
    }

    public Tower GetProductorClass(GameObject gameObject)
    {
        return gameObject.GetComponent<Tower>();
    }
}

建造者模式与工厂模式的区别:

建造者设计模式(Builder Design Pattern)和工厂设计模式(Factory Design Pattern)都是面向对象设计中的创建型模式,但它们解决的问题和应用场景有所不同。

  • 在工厂方法中主要看中产品的整体创建,一般不考虑创建的各个部分细节;
  • 建造者模式一般用于对复杂产品的创建,可以进行分步骤详细创建;

建造者模式:

主要组件:

  •     Director(指挥者):负责使用构造器接口来构建一个复杂对象。
  •     Builder(构造器):定义对象的构建过程,包括设置属性、添加部件等方法。
  •     ConcreteBuilder(具体构造器):实现构造器接口,实现具体的构建方法。
  •     Product(产品):最终构建出的复杂对象。

建造者模式的优点是将对象的构建过程封装,使得代码更加清晰,同时能够灵活地构建不同的对象。

工厂模式:

工厂设计模式旨在通过一个工厂来创建对象,将对象的创建过程封装起来,客户端代码无需直接调用构造函数。它分为简单工厂、工厂方法和抽象工厂等形式。

  • 主要组件:

    • Factory(工厂):负责创建对象的接口或类。
    • ConcreteFactory(具体工厂):实现工厂接口,实际创建对象的地方。
    • Product(产品):工厂创建的对象。

工厂模式的主要优点是将对象的创建和客户端解耦,客户端只需通过工厂来获取对象,不需要关心对象的具体创建过程。

目的不同:

  •     建造者模式关注于创建复杂对象的构建过程,将构建过程和表示分离。
  •     工厂模式关注于对象的创建,将对象的创建过程封装在工厂中,以便在客户端中使用。

复杂性:

  •     建造者模式通常用于创建复杂对象,因为对象的构建过程可能涉及多个步骤和配置选项。
  •     工厂模式可以用于创建不同类型的对象,包括简单对象和复杂对象。

关注点:

  •     建造者模式更关注于对象的构建过程,尤其适合需要按照一定步骤构建对象的情况。
  •     工厂模式更关注于对象的创建,强调封装创建过程,便于对象创建的管理。

综上所述,建造者模式适用于构建复杂对象,而工厂模式适用于创建对象的封装和管理。选择适当的模式取决于你的设计需求和对象创建的复杂性。

建造者模式的优缺点

【设计模式】建造者模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )-CSDN博客

建造者模式优点 :

  • 封装性好 : 创建使用 分离 ;
  • 扩展性好 : 建造类之间 相互独立 , 在 一定程度上解耦 ;

建造者模式缺点 :

  • 增加类数量 : 产生多余的 Builder 对象 ;
  • 内部修改困难 : 如果 产品内部发生变化 , 建造者也要相应修改 ;

资源工厂模式:

资源工厂接口

/// <summary>
/// 其他种类资源工厂的接口,每种工程获取的资源都不同
/// </summary>

public interface IBaseResourceFactory<T>
{
    T GetSingleResources(string resourcePath);

}

音频资源工厂

using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 音频资源工厂
/// </summary>

public class AudioClipFactory : IBaseResourceFactory<AudioClip>
{
    //资源字典
    protected Dictionary<string, AudioClip> factoryDict = new Dictionary<string, AudioClip>();
    protected string loadPath;

    public AudioClipFactory()
    {
        loadPath = "AudioClips/";
    }

    public AudioClip GetSingleResources(string resourcePath)
    {
        AudioClip itemGo = null;
        string itemLoadPath = loadPath + resourcePath;

        if (factoryDict.ContainsKey(resourcePath))
        {
            itemGo = factoryDict[resourcePath];
        }
        else
        {
            itemGo = Resources.Load<AudioClip>(itemLoadPath);
            factoryDict.Add(resourcePath, itemGo);
        }

        if (itemGo == null)
        {
            Debug.Log(resourcePath + "的资源获取失败,失败路径为:" + itemLoadPath);
        }

        return itemGo;
    }


}

protected Dictionary<string, RuntimeAnimatorController> factoryDict = new Dictionary<string, RuntimeAnimatorController>();

不同地资源有一个不同的路径前缀,同一种资源的路径前缀都一样,所以比较方便写成这样的

获取的资源可以用一个字典存起来方便之后继续使用

责任链模式

参考:实战:设计模式之责任链设计模式深度解析 - 知乎

例子:关卡游戏中,只有当你通过第一关才能进入第二关,通过第二关才能进入第三关。以此类推。

关卡与关卡之间形成一条链,每个关卡知道自己的下一个关卡是什么,知道自己通关的条件是什么,在合适的时机将责任传递给下一个关卡,也可以在自己这一环节结束任务

客户端负责组装责任链,但是并不关心最终谁处理了任务

什么是责任链设计模式

  • 客户端发出一个请求,链上的对象都有机会来处理这一请求,而客户端不需要知道谁是具体的处理对象。
  • 多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。
  • 将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。其过程实际上是一个递归调用。

责任链客户端

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Level
{
    public int totalRound;  //一共有几波怪
    public Round[] roundList;
    public int currentRound;

    public Level(int roundNum, List<Round.RoundInfo> roundInfoList)
    {
        totalRound = roundNum;
        roundList = new Round[totalRound];
        //对round数组的赋值
        for (int i = 0; i < totalRound; i++)
        {
            roundList[i] = new Round(roundInfoList[i].mMonsterIDList, i, this);
        }
        //设置任务链
        for (int i = 0; i < totalRound; i++)
        {
            if (i == totalRound - 1)
            {
                break;
            }
            roundList[i].setNextRound(roundList[i + 1]);
        }
    }

    public void HandleRound()
    {
        if (currentRound >= totalRound)
        {
            //游戏胜利的方法
            currentRound--;
            GameController.Instance.normalModelPanel.ShowGameWinPage();
        }
        else if (currentRound == totalRound - 1)
        {
            //最后一波怪的UI显示,音乐播放
            GameController.Instance.normalModelPanel.ShowFinalWaveUi();
        }
        else
        {
            roundList[currentRound].Handle(currentRound);
        }
    }

    //调用最后一回合的Handle方法
    public void HandleLastRound()
    {
        roundList[currentRound].Handle(currentRound);
    }

    public void AddRoundNum()
    {
        currentRound++;
    }
}

每一个处理责任的实体

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Round
{
    [System.Serializable]  //序列化,显示在inspector面板中
    public struct RoundInfo
    {
        public int[] mMonsterIDList;
    }

    public RoundInfo roundInfo;
    protected Round mNextRound;
    protected int mRoundID;
    protected Level mLevel;

    public Round(int[] monsterIDList, int roundID, Level level)
    {
        mLevel = level;
        roundInfo.mMonsterIDList = monsterIDList;
        mRoundID = roundID;
    }

    //设置任务链的下一个处理对象
    public void setNextRound(Round nextRound)
    {
        mNextRound = nextRound;
    }

    public void Handle(int roundID)
    {
        if (mRoundID < roundID)
        {
            mNextRound.Handle(roundID);
        }
        else
        {
            //产生怪物
            GameController.Instance.mMonsterIDList = roundInfo.mMonsterIDList;
            GameController.Instance.CreateMonster();
            GameController.Instance.creatingMonster = true;
        }
    }
}

责任链模式的优缺点

优点

  • 动态组合,使请求者和接受者解耦。
  • 请求者和接受者松散耦合:请求者不需要知道接受者,也不需要知道如何处理。每个职责对象只负责自己的职责范围,其他的交给后继者。各个组件间完全解耦。
  • 动态组合职责:职责链模式会把功能分散到单独的职责对象中,然后在使用时动态的组合形成链,从而可以灵活的分配职责对象,也可以灵活的添加改变对象职责。

缺点

  • 产生很多细粒度的对象:因为功能处理都分散到了单独的职责对象中,每个对象功能单一,要把整个流程处理完,需要很多的职责对象,会产生大量的细粒度职责对象。
  • 不一定能处理:每个职责对象都只负责自己的部分,这样就可以出现某个请求,即使把整个链走完,都没有职责对象处理它。这就需要提供默认处理,并且注意构造链的有效性。

备忘录模式

参考:https://www.cnblogs.com/jing99/p/12617294.html

备忘录(Memento)模式的定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。该模式又叫快照模式。

  备忘录模式能记录一个对象的内部状态,当用户后悔时能撤销当前操作,使数据恢复到它原先的状态。

备忘录模式的优缺点

备忘录模式是一种对象行为型模式,其主要优点如下:

  • 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
  • 实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
  • 简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。

其主要缺点是:

  • 资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。

Memento

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using LitJson;
using System.IO;

public class Memento : MonoBehaviour
{
    //写入文件
    public void SaveByJson()
    {
        PlayerManager playerManager = GameManager.Instance.playerManager;
        string filePath = Application.streamingAssetsPath + "/Json" + "/playerManagerFX.json";
        string saveJsonStr = JsonMapper.ToJson(playerManager);
        StreamWriter sw = new StreamWriter(filePath);
        sw.WriteLine(saveJsonStr);
        sw.Close();
    }

    //读取文件
    public PlayerManager LoadByJson()
    {
        PlayerManager playerManager = new PlayerManager();
        string filePath = "";
        if (GameManager.Instance.initPlayerManager)
        {
            
            //filePath = Application.streamingAssetsPath + "/Json" + "/playerManagerInitData.json";
            filePath = Application.streamingAssetsPath + "/Json" + "/playerManager.json";
        }
        else
        {
            filePath = Application.streamingAssetsPath + "/Json" + "/playerManager.json"; 
        }

        if (File.Exists(filePath))
        {
            StreamReader sr = new StreamReader(filePath);
            string jsonStr = sr.ReadToEnd();
            sr.Close();
            playerManager = JsonMapper.ToObject<PlayerManager>(jsonStr);
            return playerManager;
        }
        else
        {
            Debug.Log("PlayerManager 读取失败");
        }
        return null;
    }
}

被存储的类 PlayerManager

using System.Collections.Generic;
using System.Collections;

/// <summary>
/// 玩家的管理,负责保存以及加载各种玩家以及游戏的信息
/// </summary>

public class PlayerManager
{
    public int adventureModelNum;  //冒险模式解锁的地图个数
    public int burriedLevelNum;  //隐藏关卡解锁的地图个数
    public int bossModelNum;  //boss模式ko的Boss
    public int coin; //获得金币的总数
    public int killMonsterNum;  //杀怪总数
    public int killBossNum;  //杀掉BOSS的总数
    public int clearItemNum;  //清理道具的总数
    public List<bool> unLockedNormalModelBigLevelList;  //大关卡
    public List<Stage> unLockedNormalModelLevelList;  //所有的小关卡
    public List<int> unLockedNormalModelLevelNum;  //解锁小关卡的数量

    //怪物窝数据
    public int cookies;
    public int milk;
    public int nest;
    public int diamonds;
    public List<MonsterPetData> monsterPetDataList;  //宠物喂养信息

    用于测试
    //public PlayerManager()
    //{
    //    adventureModelNum = 100;
    //    burriedLevelNum = 100;
    //    bossModelNum = 100;
    //    coin = 100;
    //    killBossNum = 100;
    //    killMonsterNum = 100;
    //    clearItemNum = 100;
    //    unLockedNormalModelLevelNum = new List<int>()
    //    {
    //        2,2,2
    //    };
    //    unLockedNormalModelBigLevelList = new List<bool>()
    //    {
    //        true,true,true
    //    };
    //    unLockedNormalModelLevelList = new List<Stage>()
    //    {
    //        new Stage(10,2,new int[]{1,2 },false,0,1,1,true,false),
    //        new Stage(10,2,new int[]{2,2 },false,0,2,1,true,false),
    //        new Stage(10,2,new int[]{3,2 },false,0,3,1,true,false),
    //        new Stage(10,2,new int[]{4,2 },false,0,1,1,true,false),
    //        new Stage(10,2,new int[]{5,2 },false,0,1,1,true,false),
    //        new Stage(10,3,new int[]{7,2,4 },false,0,5,1,true,true),
    //        new Stage(10,2,new int[]{7,2 },false,0,1,2,true,false),
    //        new Stage(10,2,new int[]{8,2 },false,0,2,2,true,false),
    //    };
    //}

    //用于玩家初始Json文件的制作
    //public PlayerManager()
    //{
    //    adventureModelNum = 0;
    //    burriedLevelNum = 0;
    //    bossModelNum = 0;
    //    coin = 0;
    //    killMonsterNum = 0;
    //    killBossNum = 0;
    //    clearItemNum = 0;
    //    cookies = 100;
    //    milk = 100;
    //    nest = 1;
    //    diamonds = 10;
    //    unLockedNormalModelLevelNum = new List<int>()
    //    {
    //        1,0,0
    //    };
    //    unLockedNormalModelBigLevelList = new List<bool>()
    //    {
    //        true,false,false
    //    };
    //    unLockedNormalModelLevelList = new List<Stage>()
    //    {
    //           new Stage(10,1,new int[]{ 1},false,0,1,1,true,false),
    //           new Stage(9,1,new int[]{ 2},false,0,2,1,false,false),
    //           new Stage(8,2,new int[]{ 1,2},false,0,3,1,false,false),
    //           new Stage(10,1,new int[]{ 3},false,0,4,1,false,false),
    //           new Stage(9,3,new int[]{ 1,2,3},false,0,5,1,false,true),
    //           new Stage(8,2,new int[]{ 2,3},false,0,1,2,false,false),
    //           new Stage(10,2,new int[]{ 1,3},false,0,2,2,false,false),
    //           new Stage(9,1,new int[]{ 4},false,0,3,2,false,false),
    //           new Stage(8,2,new int[]{ 1,4},false,0,4,2,false,false),
    //           new Stage(10,2,new int[]{ 2,4},false,0,5,2,false,true),
    //           new Stage(9,2,new int[]{ 3,4},false,0,1,3,false,false),
    //           new Stage(8,1,new int[]{ 5},false,0,2,3,false,false),
    //           new Stage(7,2,new int[]{ 4,5},false,0,3,3,false,false),
    //           new Stage(10,3,new int[]{ 1,3,5},false,0,4,3,false,false),
    //           new Stage(10,3,new int[]{ 1,4,5},false,0,5,3,false,true)
    //    };
    //    monsterPetDataList = new List<MonsterPetData>()
    //    {
    //        new MonsterPetData()
    //        {
    //            monsterID=1,
    //            monsterLevel=1,
    //            remainCookies=0,
    //            remainMilk=0
    //        },

    //    };
    //}

    //用于玩家所有关卡都解锁的Json文件的制作
    //public PlayerManager()
    //{
    //    adventureModelNum = 12;
    //    burriedLevelNum = 3;
    //    bossModelNum = 0;
    //    coin = 999;
    //    killMonsterNum = 999;
    //    killBossNum = 0;
    //    clearItemNum = 999;
    //    cookies = 1000;
    //    milk = 1000;
    //    nest = 10;
    //    diamonds = 1000;
    //    unLockedNormalModelLevelNum = new List<int>()
    //    {
    //        5,5,5
    //    };
    //    unLockedNormalModelBigLevelList = new List<bool>()
    //    {
    //        true,true,true
    //    };
    //    unLockedNormalModelLevelList = new List<Stage>()
    //    {
    //           new Stage(10,1,new int[]{ 1},false,0,1,1,true,false),
    //           new Stage(9,1,new int[]{ 2},false,0,2,1,true,false),
    //           new Stage(8,2,new int[]{ 1,2},false,0,3,1,true,false),
    //           new Stage(10,1,new int[]{ 3},false,0,4,1,true,false),
    //           new Stage(9,3,new int[]{ 1,2,3},false,0,5,1,false,true),
    //           new Stage(8,2,new int[]{ 2,3},false,0,1,2,true,false),
    //           new Stage(10,2,new int[]{ 1,3},false,0,2,2,true,false),
    //           new Stage(9,1,new int[]{ 4},false,0,3,2,true,false),
    //           new Stage(8,2,new int[]{ 1,4},false,0,4,2,true,false),
    //           new Stage(10,2,new int[]{ 2,4},false,0,5,2,false,true),
    //           new Stage(9,2,new int[]{ 3,4},false,0,1,3,true,false),
    //           new Stage(8,1,new int[]{ 5},false,0,2,3,true,false),
    //           new Stage(7,2,new int[]{ 4,5},false,0,3,3,true,false),
    //           new Stage(10,3,new int[]{ 1,3,5},false,0,4,3,true,false),
    //           new Stage(10,3,new int[]{ 1,4,5},false,0,5,3,false,true)
    //    };
    //    monsterPetDataList = new List<MonsterPetData>()
    //    {
    //        new MonsterPetData()
    //        {
    //            monsterID=1,
    //            monsterLevel=1,
    //            remainCookies=0,
    //            remainMilk=0
    //        },
    //        new MonsterPetData()
    //        {
    //            monsterID=2,
    //            monsterLevel=1,
    //            remainCookies=0,
    //            remainMilk=0
    //        },
    //        new MonsterPetData()
    //        {
    //            monsterID=3,
    //            monsterLevel=1,
    //            remainCookies=0,
    //            remainMilk=0
    //        }
    //    };
    //}

    public void SaveData()
    {
        Memento memento = new Memento();
        memento.SaveByJson();
    }

    public void ReadData()
    {
        Memento memento = new Memento();
        PlayerManager playerManager = memento.LoadByJson();

        adventureModelNum = playerManager.adventureModelNum;
        burriedLevelNum = playerManager.burriedLevelNum;
        bossModelNum = playerManager.bossModelNum;
        coin = playerManager.coin;
        killBossNum = playerManager.killBossNum;
        killMonsterNum = playerManager.killMonsterNum;
        clearItemNum = playerManager.clearItemNum;
        cookies = playerManager.cookies;
        milk = playerManager.milk;
        nest = playerManager.nest;
        diamonds = playerManager.diamonds;
        //列表
        unLockedNormalModelBigLevelList = playerManager.unLockedNormalModelBigLevelList;
        unLockedNormalModelLevelList = playerManager.unLockedNormalModelLevelList;
        unLockedNormalModelLevelNum = playerManager.unLockedNormalModelLevelNum;
        monsterPetDataList = playerManager.monsterPetDataList;
    }
}

备忘录模式的应用场景

  • 需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能。
  • 需要提供一个可回滚操作的场景,如 Word、记事本、Photoshop,Eclipse 等软件在编辑时按 Ctrl+Z 组合键,还有数据库中事务操作。

为了节省内存,可以和原型模式配合使用

可以通过实现 memento 接口自己储存自己

中介者模式

负责各个面板之间成员变量的交互以及与管理类之间的交互。

using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// UI中介,上层与管理者做交互,下层与UI面板做交互
/// </summary>

public class UIFacade
{
    //管理者
    public UIManager mUIManager;
    private GameManager mGameManager;
    private AudioSourceManager mAudioSourceManager;
    public PlayerManager mPlayerManager;

    //UI面板
    public Dictionary<string, IBasePanel> currentScenePanelDict =
        new Dictionary<string, IBasePanel>();

    //其他成员变量
    private GameObject mask;  //转换场景时的遮罩
    private Image maskImage;

    public Transform canvasTransform;

    //场景状态
    public IbaseSceneState currentSceneState;  //当前状态
    public IbaseSceneState lastSceneState;  //上一个状态

    public UIFacade(UIManager uiManager)
    {
        mGameManager = GameManager.Instance;
        mPlayerManager = mGameManager.playerManager;
        mUIManager = uiManager;
        mAudioSourceManager = mGameManager.audioSourceManager;
        InitMask();
    }

    //初始化遮罩
    public void InitMask()
    {
        canvasTransform = GameObject.Find("Canvas").transform;
        //这样写太复杂了,所以在 GameManager中封装了一系列获取资源的方法
        //mask = mGameManager.factoryManager.factoryDict[FactoryType.UIFactory].GetIteM("Img_Mask");
        //利用封装的方法来获取资源
        //mask = mGameManager.GetGameObjectResource(FactoryType.UIFactory, "Img_Mask");
        //再次封装实现外观模式
        //mask = GetGameObjectResource(FactoryType.UIFactory, "Img_Mask");

        mask = CreatUIAndSetUIPosition("Img_Mask");
        maskImage = mask.GetComponent<Image>();
    }

    //改变当前场景的状态
    public void ChangeSceneState(IbaseSceneState baseSceneState)
    {
        lastSceneState = currentSceneState;
        ShowMask();
        currentSceneState = baseSceneState;
    }

    //显示遮罩的方法
    public void ShowMask()
    {
        //数字越大越后渲染
        mask.transform.SetSiblingIndex(10);
        Tween t = DOTween.To(() => maskImage.color,
            tolColor => maskImage.color = tolColor, new Color(0, 0, 0, 1), 2f);
        //回调函数,当动画播放结束时调用注册的函数
        t.OnComplete(ExitSceneComplete);  //小写的那个是没用的
    }

    //离开当前场景的方法
    private void ExitSceneComplete()
    {
        lastSceneState.ExitScene();
        currentSceneState.EnterScene();
        HideMask();
    }

    //隐藏遮罩
    public void HideMask()
    {
        mask.transform.SetSiblingIndex(10);
        DOTween.To(() => maskImage.color,
            tolColor => maskImage.color = tolColor, new Color(0, 0, 0, 0), 2f);
    }

    //实例化当前场景所有面板,并存入字典
    public void InitDict()
    {
        //foreach (var item in mUIManager.currentScenePanelDict)
        //{
        //    Debug.Log(item.Value);
        //}
        foreach (var item in mUIManager.currentScenePanelDict)
        {
            item.Value.transform.SetParent(canvasTransform);
            item.Value.transform.localPosition = Vector3.zero;
            item.Value.transform.localScale = Vector3.one;
            //所有的item都继承自IBasePanel,所以可以用IBasePanel来接收
            IBasePanel basePanel = item.Value.GetComponent<IBasePanel>();
            if (basePanel == null)
            {
                Debug.Log("获取面板上IBasePanel脚本失败");
            }
            //初始化Panel状态
            basePanel.InitPanel();
            currentScenePanelDict.Add(item.Key, basePanel);
        }
    }

    //清空Panel字典
    public void ClearDict()
    {
        currentScenePanelDict.Clear();
        mUIManager.ClearDict();
    }

    //添加UIPanel到UIManager字典
    public void AddPanelToDict(string uiPanelName)
    {
        mUIManager.currentScenePanelDict.Add
            (uiPanelName, GetGameObjectResource(FactoryType.UIPanelFactory, uiPanelName));
    }

    //实例化UI
    public GameObject CreatUIAndSetUIPosition(string uiName)
    {
        GameObject itemGo = GetGameObjectResource(FactoryType.UIFactory, uiName);
        itemGo.transform.SetParent(canvasTransform);
        itemGo.transform.localPosition = Vector3.zero;
        itemGo.transform.localScale = Vector3.one;
        return itemGo;
    }

    //在 UIFacade 中将方法再次封装,实现外观模式,实现解耦

    //获取资源的方法
    public Sprite GetSprite(string resourcePath)
    {
        return mGameManager.GetSprite(resourcePath);
    }

    //获取AudioClip
    public AudioClip GetAudioClip(string resourcePath)
    {
        return mGameManager.GetAudioClip(resourcePath);
    }

    //获得 Animator Controller
    public RuntimeAnimatorController GetRuntimeAnimatorController(string resourcePath)
    {
        return mGameManager.GetRunTimeAnimatorController(resourcePath);
    }

    //获取游戏物体的方法
    public GameObject GetGameObjectResource(FactoryType factoryType, string resourcePath)
    {
        return mGameManager.GetGameObjectResource(factoryType, resourcePath);
    }

    //将游戏物体放回对象池
    public void PushGameObjectToFactory(FactoryType factoryType, string resourcePath, GameObject itemGo)
    {
        mGameManager.PushGameObjectToFactory(factoryType, resourcePath, itemGo);
    }

    /// <summary>
    /// 音乐播放的有关方法
    /// </summary>
    //开关音乐
    public void CloseOrOpenBGMusic()
    {
        mAudioSourceManager.CloseOrOpenBGMusic();
    }

    public void CloseOrOpenEffectMusic()
    {
        mAudioSourceManager.CloseOrOpenEffectMusic();
    }

    //播放按钮音效
    public void PlayButtonAudioClip()
    {
        mAudioSourceManager.PlayButtonAudioClip();
    }

    //播放翻书音效
    public void PlayPagingAudioClip()
    {
        mAudioSourceManager.PlayPagingAudioClip();
    }
}
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 负责管理UI的管理者
/// </summary>

public class UIManager
{
    public UIFacade mUIFacade;
    public Dictionary<string, GameObject> currentScenePanelDict;
    private GameManager mGameManager;

    public UIManager()
    {
        mGameManager = GameManager.Instance;
        currentScenePanelDict = new Dictionary<string, GameObject>();
        mUIFacade = new UIFacade(this);
        mUIFacade.currentSceneState = new StartLoadSceneState(mUIFacade);

    }

    //将UIPanel放回工厂
    private void PushUIPanel(string uiPanelName, GameObject uiPanelGo)
    {
        mGameManager.PushGameObjectToFactory(FactoryType.UIPanelFactory, uiPanelName, uiPanelGo);
    }

    //清空字典
    public void ClearDict()
    {
        foreach (var item in currentScenePanelDict)
        {

            PushUIPanel(item.Value.name.Substring(0, item.Value.name.Length - 7), item.Value);
        }
        currentScenePanelDict.Clear();
    }


}

中介者(Mediator)模式的定义:定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则(最少知道原则)的典型应用。

中介者模式是一种对象行为型模式,其主要优点如下。

  1. 类之间各司其职,符合迪米特法则。
  2. 降低了对象之间的耦合性,使得对象易于独立地被复用。
  3. 将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。

其主要缺点是:中介者模式将原本多个对象直接的相互依赖变成了中介者和多个同事类的依赖关系。当同事类越多时,中介者就会越臃肿,变得复杂且难以维护。

应用场景

  • 当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时。
  • 当想创建一个运行于多个类之间的对象,又不想生成新的子类时。

2.在游戏开发中的实现方式

Unity游戏开发——中介者模式 - 知乎

在游戏开发中我们是这样实现中介者模式的

首先游戏会有两大系统类:“游戏系统类”、“界面类”

我们定义好这两种抽象类,并定义一些生命周期有关方法,然后在构造方法中传入中介者的实例。当每创建一个新的系统类时,都要继承其中一个抽象类。这样每个系统都会持有一个的中介者。我们在做子系统功能的时候,就不需要关心这个功能会跟哪个系统产生关联,只需要通过中介者的实例,通知中介者当前子系统想完成什么功能就可以了,剩下的交给中介者去处理子系统之间的引用逻辑。当然,所有子系统的构造都在中介者中完成,中介者拥有所有子系统的访问权限。

外观模式

外观(Facade)模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。

//这样写太复杂了,所以在 GameManager中封装了一系列获取资源的方法
mask = mGameManager.factoryManager.factoryDict[FactoryType.UIFactory].
GetIteM("Img_Mask");
        //利用封装的方法来获取资源
mask = mGameManager.GetGameObjectResource(FactoryType.UIFactory, "Img_Mask");
        //再次封装实现外观模式
mask = GetGameObjectResource(FactoryType.UIFactory, "Img_Mask");
mask = CreatUIAndSetUIPosition("Img_Mask");

//实例化UI
    public GameObject CreatUIAndSetUIPosition(string uiName)
    {
        GameObject itemGo = GetGameObjectResource(FactoryType.UIFactory, uiName);
        itemGo.transform.SetParent(canvasTransform);
        itemGo.transform.localPosition = Vector3.zero;
        itemGo.transform.localScale = Vector3.one;
        return itemGo;
    }

//获取游戏物体的方法
    public GameObject GetGameObjectResource(FactoryType factoryType, string resourcePath)
    {
        return mGameManager.GetGameObjectResource(factoryType, resourcePath);
    }

//获取游戏物体
    public GameObject GetGameObjectResource(FactoryType factoryType, string resourcePath)
    {
        return factoryManager.factoryDict[factoryType].GetIteM(resourcePath);
    }

一层层封装,减少了调用的复杂度

外观(Facade)模式是“迪米特法则”的典型应用,它有以下主要优点。

  1. 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
  2. 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
  3. 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。

外观(Facade)模式的主要缺点如下。

  1. 不能很好地限制客户使用子系统类,很容易带来未知风险。
  2. 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。

外观模式的结构与实现

外观(Facade)模式的结构比较简单,主要是定义了一个高层接口。它包含了对各个子系统的引用,客户端可以通过它访问各个子系统的功能。现在来分析其基本结构和实现方法。

对象池

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;

public class PoolManager : MonoBehaviour
{
    public List<GameObject> poolPrefabs;

    private List<ObjectPool<GameObject>> poolEffectList = new List<ObjectPool<GameObject>>();

    private Queue<GameObject> soundQueue = new Queue<GameObject>();

    private void Start()
    {
        CreatePool();
    }

    private void OnEnable()
    {
        EventHandler.ParticleEffectEvent += OnParticleEffectEvent;
        EventHandler.InitSoundEffect += InitSoundEffect;
    }

    private void OnDisable()
    {
        EventHandler.ParticleEffectEvent -= OnParticleEffectEvent;
        EventHandler.InitSoundEffect -= InitSoundEffect;
    }

    /// <summary>
    /// 生成对象池
    /// </summary>
    private void CreatePool()
    {
        foreach (GameObject item in poolPrefabs)
        {
            Transform parent = new GameObject(item.name).transform;
            parent.SetParent(transform);

            var newPool = new ObjectPool<GameObject>(
                () => Instantiate(item, parent),
                e => { e.SetActive(true); },
                e => { e.SetActive(false); },
                e => { Destroy(e); }
            );

            poolEffectList.Add(newPool);
        }
    }

    private void OnParticleEffectEvent(ParticleEffectType effectType, Vector3 pos)
    {
        //Debug.Log(effectType);
        //根据特效补全
        ObjectPool<GameObject> objPool = effectType switch
        {
            ParticleEffectType.LeavesFalling01 => poolEffectList[0],
            ParticleEffectType.LeavesFalling02 => poolEffectList[1],
            ParticleEffectType.Rock => poolEffectList[2],
            ParticleEffectType.ReapableScenery => poolEffectList[3],
            _ => null
        };

        GameObject obj = objPool.Get();
        obj.transform.position = pos;
        StartCoroutine(ReleaseRoutine(objPool, obj));
    }

    private IEnumerator ReleaseRoutine(ObjectPool<GameObject> pool,GameObject obj)
    {
        yield return new WaitForSeconds(1.5f);
        pool.Release(obj);
    }

    //private void InitSoundEffect(SoundDetails soundDetails)
    //{
    //    ObjectPool<GameObject> pool = poolEffectList[4];
    //    var obj = pool.Get();

    //    obj.GetComponent<Sound>().SetSound(soundDetails);
    //    StartCoroutine(DisableSound(pool, obj, soundDetails));
    //}

    //private IEnumerator DisableSound(ObjectPool<GameObject> pool ,GameObject obj,SoundDetails soundDetails)
    //{
    //    yield return new WaitForSeconds(soundDetails.soundClip.length);
    //    pool.Release(obj);
    //}

    private void CreateSoundPool()
    {
        var parent = new GameObject(poolPrefabs[4].name).transform;
        parent.SetParent(transform);

        for (int i = 0; i < 20; i++)
        {
            GameObject newObj = Instantiate(poolPrefabs[4], parent);
            newObj.SetActive(false);
            soundQueue.Enqueue(newObj);
        }
    }

    private GameObject GetPoolObject()
    {
        if (soundQueue.Count < 2)
            CreateSoundPool();
        return soundQueue.Dequeue();
    }

    private void InitSoundEffect(SoundDetails soundDetails)
    {
        var obj = GetPoolObject();
        obj.GetComponent<Sound>().SetSound(soundDetails);
        obj.SetActive(true);

        StartCoroutine(DisableSound(obj, soundDetails.soundClip.length));
    }

    private IEnumerator DisableSound(GameObject obj,float duration)
    {
        yield return new WaitForSeconds(duration);
        obj.SetActive(false);
        soundQueue.Enqueue(obj);
    }
}

对象池模式主要适用于以下应用场景。

  • 1.需要频繁创建和销毁对象
  • 2.对象大小相仿
  • 3.在堆上进行对象内存分配十分缓慢或者会导致内存碎片
  • 4.每个对象都封装了像数据库或者网络连接这样昂贵又可以重用的资源。

 池中对象都被使用,无法在创建的解决办法

  • 1.增加池子大小。根据每个游戏场景需求和游戏内存来定。
  • 2.不创建新的对象。对于不重要的对象,或者不明显的对象可以考虑不再创建。
  • 3.强制干掉一个已有的对象。

重用对象不会自动清除数据,保证初始化代码将完全的初始化所有的数据。在池中对象不再使用后,需要清除它对其他对象的所有引用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值