用QFramework重构飞机大战(Siki Andy的)(上)(00-05 到游戏界面之前的所有面板)

GitHub

//

官网的
全民飞机大战(第一季)-----框架设计篇(Unity 2017.3)
全民飞机大战(第二季)-----游戏逻辑篇(Unity 2017.3)
全民飞机大战(第三季)-----完善功能篇(Unity 2017.3)
全民飞机大战(第四季)-----新手引导篇

//

B站各放几集
全民飞机大战(第一季)-----框架设计篇(Unity 2017.3)
全民飞机大战(第二季)-----游戏逻辑篇(Unity 2017.3)
全民飞机大战(第三季)-----完善功能篇(Unity 2017.3)
全民飞机大战(第四季)-----新手引导篇

watch 案例的脚本数量

534/2(因为.cs.meta)=267个脚本

----------------------------------------------------------------

untiy 2017

bug C#版本untiy2017

项目的原始unity版本是2017,对应的的C#版本是4.6,不支持ref,out,这对QF影响大,所以不用unity2017.
2023-06-19 Untiy进阶 C#知识补充2------C#版本与Unity的关系

-------------------------------------------------------

bug 导入2020.3.23f1c1(入坑时的最新LTS版本)--------------------

bug 不存在NUnit

直接删除using,那里没用掉

bug Dotween报错VectorOptions

看到0引用,先注释掉算了# if false

bug 引用缺失using System;using System.Reflection;

补上去

bug dll重复,删除其中一个是显示 hook引用,删不了

删除另外一个

bug 命名冲突导致报错

modify 作用一样的保留自己的(因为自己的才能导包带走复用),删掉原来的

比如拓展方法

transform.Rect()

transform.AddComponent();

transform.Find(path).gameObject.AddComponent();

比如相同的类

Tags

modify 加上命名空间的限制

bug加载不到LoadingView

这块报空了

----------------------------------------------

00 总的流程,大的模块

modify (重点)脚本太多,用一个脚本来汇总,方便查找

csharp 复制代码
/****************************************************
    文件:Framework_AirCombat.cs
	作者:lenovo
    邮箱: 
    日期:2023/7/31 20:29:31
	功能: 飞机大战的框架整理汇总
*****************************************************/

using LitJson;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor.VersionControl;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR;
using static BindUtil;
using Random = UnityEngine.Random;


public class Framework_AirCombat : MonoBehaviour
{
    #region 生命
    /// <summary>首次载入且Go激活</summary>
    void Start()
    {

        //UI
        {
            View();
            LifeTime();
            Controller();
            LifeTime_Controller();
            SpecificUI_MainUI();
            SpecificUI_Effect();
            SpecificUI_GameUI();

        }
        Manager();
        Reader();
        Attribute();
        Config();
        Util();
        Loader();
        Data();
        TaskQueue();
        Message();
        Action();
        Pool();
        Coroutine();
        Model();
        Singleton();
        Test();
        //游戏中的
        { 
            Path();
            Input();    
            Behaviour(); 
            Ani();
            Bullet();
            BulletMgr();
            Component();
            Effect();
            EffectView();
            EnemyCreater();
            GameProcess();
            Factory();
            Map();
            InitGame();
            PlayerController();
            Trajectory();
            SpecialState();
            View_Logic();
            GameEvent gameEvent;
            Guide();
        }
    }
    #endregion

    #region 辅助
    private void SpecialState()
    {
        LevelUpState levelUpState;
        //
        IBuff iBuff;
        IBuffCarrier iBuffCarrier;
        IDebuff iDeff;
        IDebufferCarrier iDebufferCarrier;
        ISpecialState iSpecialState;
        SpecialStateMgrBase specialStateMgrBase;
        //
        PlayerBuffMgr playerBuffMgr;

    }

    private void Trajectory()
    {
        ITrajectory iTrajectory;
        EllipseTrajectory ellipseTrajectory;
        RotateTrajectory rotateTrajectory;
        StraightTrajectory straightTrajectory;
        VTrajectory vTrajectory;

    }

    private void PlayerController()
    {
        PlayerController playerController;

    }

    private void InitGame()
    {
        GameLayerMgr gameLayerMgr;
        GameRoot gameRoot;
    }

    private void Map()
    {
        MapCloud mapCloud;
        MapItem mapItem;
        MapMgr mapMgr;
    }

    private void Factory()
    {
        ItemFactory itemFactory;
        PathFactory pathFactory;
        SpecialStateFactory specialStateFactory;

    }

    private void EnemyCreater()
    {
        IEnemyCreater iEnemyCreater;
        MissileEnemyCreater missileEnemyCreater;
        PlaneEnemyCreater planeEnemyCreater;
        //
        ISubEnemyCreaterMgr iSubEnemyCreaterMgr;
        BossCreaterMgr bossCreaterMgr;
        ElitesCreaterMgr elitesCreaterMgr;
        EnemyCreaterMgr enemyCreaterMgr;
        MissileCreaterMgr missileCreaterMgr;
        NormalCreaterMgr normalCreaterMgr;
        //
        LoadCreaterData loadCreaterData;
        GameProcess();
    }

    private void GameProcess()
    {
        GameProcessMgr gameProcessMgr;
        GameProcessNormalEvent gameProcessNormalEvent;
        GameProcessTriggerEvent gameProcessTriggerEvent;
        IGameProcessNormalEvent  iGameProcessNormalEvent;
        IGameProcessTriggerEvent iGameProcessTriggerEvent;

    }
    private void Component()
    {
        AutoDespawnComponent autoDespawnComponent;
        DestroyDelay autoDestroyComponent;
        //
        BossBulletEventComponent bossBulletEventComponent;
        BulletCollideMsgComponent bulletCollideMsgComponent;
        CameraMove cameraMove;
        ColliderComponent colliderComponent;
        EnemyTypeComponent enemyTypeComponent;
        IColliderMsg colliderMsg;
        IComponent iComponent;
        ItemCollideMsgComponent itemCollideMsgComponent;
        LifeComponent lifeComponent;
        MoveComponent moveComponent;
        PlaneCollideMsgComponent planeCollideMsgComponent;
        RenderComponent renderComponent;

    }

    private void BulletMgr()
    {
        BulletEffectMgr bulletEffectMgr;
        EmitBulletMgr emitBulletMgr;
        EnemyBulletMgr enemyBulletMgr;
        SpawnBulletPointMgr spawnBulletPointMgr;
    }

    private void Bullet()
    {
        IBullet iBullet;
        Bullet bullet;     
        BulletMgr();
    }

    private void Behaviour()
    {
        IBehaviour iBehaviour;
        PlayerBehaviour playerBehaviour;
        EnemyBehaviour enemyBehaviour;
        BulletBehaviour bulletBehaviour;

    }

    private void Path()
    {
        IPath iPath;
        PathBase pathBase;
        EllipsePath ellipsePath;
        EnterPath enterPath;
        EnterPath.MoveDirection moveDirection;
        StayOnTopPath stayOnTopPath;
        StraightPath straightPath;
        WPath wPath;
        PathMgr pathMgr;
        //
        IEnterPath iEnetrPath;
        RightToLeft rightToLeft;
        LeftToRight leftToRight;
        UpToDown upToDown;


    }

    private void Test()
    {
        BulletConfigTest bulletConfigTest;
        ITest iTest;
        MsgEventTest msgEventTest;
        ShowFPS showFPS;
    }

    private void Manager()
    {
        AudioMgr audioMgr;
        ConfigMgr configMgr;
        CoroutineMgr coroutineMgr;
        DataMgr dataMgr;
        DelayDetalCoroutineMgr delayDetalCoroutineMgr;
        InputMgr inputMgr;                
        LifeCycleMgr lifeCycleMgr;
        LoadMgr loadMgr;
        MessageMgr messageMgr;
        PoolMgr poolMgr;
        ReaderMgr readerMgr;
        SceneMgr sceneMgr;
        SubMsgMgr subMsgMgr;
        TaskQueueMgr taskQueueMgr;
        TestMgr testMgr;
        UILayerMgr uiLayerMgr;
        UIManager uiManager;
        //
        PathMgr pathMgr;
        BulletMgr();
        //
        GameDataMgr gameDataMgr;
        //

        GuideMgr guideMgr;

    }

    private void SpecificUI_GameUI()
    {
        GameResultController gameResultController;
        GameUIController gameUIController;
        LifeController lifeController;
        LifeItemController lifeItemController;
        PowerController powerController;
        SettingController settingController;
        ShieldController shieldController;
        //
        GameResultView gameResultView;
        GameUIView gameUIView;
        Life life;
        LifeItem lifeItem;
        Power power;
        SettingView settingView;
        Shield shield;
        Warning warning;


    }
    private void Ani()
    {
        AniMgr aniMgr;
        FrameAni frameAni;
        PlayerEnterAni playerEnterAni;
    }

    private void SpecificUI_Effect()
    {
        ItemCDEffect itemCDEffect;
        ItemEffect itemEffect;
    }


    /// <summary>具体的面板</summary>
    private void SpecificUI_MainUI()
    {
        HeroItemController heroItemController;
        LevelItemController levelItemController;
        LevelRootController levelRootController;
        LevelsController levelsController;
        LoadingController loadingController;
        PlanePropertyController planePropertyController;
        PropertyItemController propertyItemController;
        SelectedHeroController selectedHeroController;
        SelectHeroController selectHeroController;
        StartController startController;
        StrengthenController strengthenController;
        SwitchPlayerController switchPlayerController;
        //
        DialogView dialogView;
        HeroItem heroItem;
        LevelItem levelItem;
        LevelRoot levelRoot;
        LevelsView levelsView;
        LoadingView loadingView;
        Money money;
        PlaneProperty planeProperty;
        PropertyItem propertyItem;
        SelectedHeroView selectedHeroView;
        SelectHero selectHero;
        StrengthenView strengthenView;
        SwitchPlayer switchPlayer;
    }

    private void Singleton()
    {
        MonoSingleton<AMono> monoSingleton;
        NormalSingleton<A> normalSingleton;

    }

    private void LifeTime_Controller()
    {
        IControllerInit iControllerInit;
        IControllerShow iControllerShow;
        IControllerHide iControllerHide;
        IControllerUpdate iControllerUpdate;
    }

    private void Controller()
    {
        IController iController;
        LifeTime_Controller();
        ControllerBase controllerBase;
    }

    private void Model()
    {
        GameModel gameModel;
        GameStateModel gameStateModel;
        PlaneSpritesModel planeSpritesModel;
        //
        IBullet iBullet;
        IBulletModel iBulletModel;
        IEnemyBulletModel iEnemyBulletModel;
        IEnemyBossBulletModel iEnemyBossBulletModel;
        EnemyBossBulluetModelBase enemyBossBulluetModelBase;
        EnemyBoss0BulluetModel enemyBoss0BulluetModel;
        EnemyBoss1BulluetModel enemyBoss1BulluetModel;

        EnemyBulluetModel enemyBulluetModel;
        PlayerBulletModel playerBulletModel;
        PowerBulletModel powerBulletModel;


    }

    private void Coroutine()
    {
        CoroutineItem.CoroutineState coroutineState;
        CoroutineItem coroutineItem;
        DelayDetalCoroutineItem delayDetalCoroutineItem;
        CoroutineController coroutineController;
    }

    private void Pool()
    {

        GameObjectPool gameObjectPool;
        ObjectPool<A> objectPool;
    }

    private void Action()
    {
        ActionMgr actionMgr;
    }

    private void Message()
    {
        IMessageSystem iMessageSystem;
        MessageSystem messageSystem;
    }

    private void Input()
    {
        IInputModule iInputModule;
        InputModule inputModule;
        InputState inputState;
        InputMgr inputMgr;

    }

    private void View()
    {
        LifeTime();
        IView iView;
        ViewBase viewBase;
        //
        LevelRoot levelRoot;
        View_Logic();
    }

    void View_Logic()
    { 
        // Enemy
        EnemyLifeItem enemyLifeItem;
        EnemyLifeView enemyLifeView;
        MissileView missileView;
        MissileWarning missileWarning;
        PlaneEnemyView planeEnemyView;
        // ItemView
        AddBulletView addBulletView;
        AddExpView addExpView;
        ItemViewBase itemViewBase;
        PowerItemView powerItemView;
        ShieldItemView shieldItemView;
        StarView starView;
        //
        BulletDestroyAniView bulletDestroyAniView;
        IGameView iGameView;
        PlaneDestroyAniView planeDestroyAniView;
        PlayerView playerView;
        PowerView powerView;
        ShieldView shieldView;    
    
    }

    private void TaskQueue()
    {
        TaskQueue taskQueue;
        TaskQueueMgr taskQueueMgr;
        LevelRoot levelRoot;
    }

    private void Data()
    {
        IDataMemory iDataMemory;
        PlayerPrefsMemory playerPrefsMemory;
        JsonMemory jsonMemory;
        DataMgr dataMgr;
        //
        //Logic/Data
        { 
            AllEnemyData allEnemyData;
            EnemyData enemyData;
            //
            AllBulletData allBulletData;
            BulletData bulletData;
            NormalBulletData normalBulletData;
            BossBulletData bossBulletData;
            //
            EnemyCreaterConfigData enemyCreaterConfigData;
            LevelData levelData;
            ICreaterData iCreaterData;
            PlaneCreaterData planeCreaterData;
            MissileCreaterData missileCreaterData;
            //
            GameDataMgr gameDataMgr;
            //
            EnemyTrajectoryDataMgr enemyTrajectoryDataMgr;
            ITrajectoryData iTrajectoryData;
            StraightTrajectoryData straightTrajectoryData;
            VTrajectoryData vTrajectoryData;
            EllipseData ellipseData;
            RotateData rotateData;        
        }

        //




    }

    private void EffectView()
    {

        EffectViewBase effectViewBase;
        AddBulletEffectView addBulletEffectView;
        AddExpEffectView addExpEffectView;
        MissileWarningEffectView missileWarningEffectView;
        StarEffectView starEffectView;

    }

    private void Effect()
    {
        IEffect iEffect;
        IEffectView iEffectView;
        EffectViewBase effectViewBase;

        FlyIntoUI flyIntoUI;
        RotateEffect rotateEffect;
        ScaleEffect scaleEffect;
        ShowAndHideEffect showAndHideEffect;
        SlowSpeedEffect slowSpeedEffect;
        //
    }

    private void Loader()
    {
        ILoader loader;
        ABLoader abLoader;
        ResourceLoader resourceLoader;
    }

    private void Util()
    {
        BindUtil bindUtil;
        BulletUtil bulletUtil;
        DataMgr.Single.SetJsonData("key", new JsonData());//DataUtil
        Empty4Raycast empty4Raycast;
        transform.ButtonAction("xxx", () => { });
        GameUtil gameUtil;
        JsonUtil jsonUtil;
        KeysUtil keysUtil;
        UiUtil uiUtil;
        //
        BulletTrajectoryDataUtil bulletTrajectoryDataUtil;
        //
        Guide_Util();

    }
    void Guide_Util()
    { 
          GuideGenerateIDTool guideGenerateIDTool;
    }

    void Guide()
    {
        Guide_Util();
        //
        Guide_Frame();
        Guide_Business();
        GuideMgr guideMgr;
    }

    void Guide_Frame()
    { 
        IGuide iGuide;
        IGuideRoot iGuideRoot;
        GuideBase guideBase;
        //
        IGuideBehaviour  iGuideBehaviour;
        GuideBehaviourBase guideBehaviourBase;
        //
        IGuideGroup iGuideGroup;
        GuideGroupBase<IGuideGroup> guideGroupBase;
        //
        GuideMgr guideMgr;    
    }
    void Guide_Business()
    {
        //Module
        ExplainDialogModule explainDialogModule;
        HandModule handModule;
        HighLightModule highLightModule;
        //Guide
        SelectedHeroViewGuide selectedHeroViewGuide;
        StartViewGuide startViewGuide;
        //Group
        ShowHerosGroup showHerosGroup;
        StartViewGroup startViewGroup;
        //Demo
        TestDemo.StartViewGuide testDemo_StartViewGuide;
        TestDemo.DemoGuideGroupA testDemo_DemoGuideGroupA;
        TestDemo.Demo1GuideGroup testDemo_Demo1GuideGroup;
        TestDemo.Demo2GuideGroup testDemo_Demo2GuideGroup;
        TestDemo.ClickButtonA testDemo_ClickButtonA;
        //
        TestDemo.GuideMgr testDemo_MuideMgr;
        //Behaviour
        ShowHerosBehaviour showHerosBehaviour;
        StartGameBehaviour startGameBehaviour;
        WelcomeGuideBehaviour welcomeGuideBehaviour;




    }

    private void Config()
    {
        BulletEffectPoolConfig bulletEffectPoolConfig;
        LifeCycleAddConfig lifeCycleAddConfig;
        LifeCycleConfig lifeCycleConfig;
        PoolConfig poolConfig;
        ReaderConfig readerConfig;
        SceneConfig sceneConfig;
        UILayerConfig uiLayerConfig;
    }
    void Reader()
    {
        IReader reader;
        JsonReader jsonReader;
        ReaderMgr readerMgr;
        ReaderConfig readerConfig;
        // 数据单位
        IKey iKey;
        Key key;
        KeyQueue keyQueue;
    }


    void Attribute()
    {
        InitCustomAttributes initCustomAttributes;
        BindPrefabAttribute bindPrefabAttribute;
        BulletAttribute bulletAttribute;

        //
        BindUtil bindUtil;
        BindPriorityComparer bindPriorityComparer;


    }
    void LifeTime()
    {
        IInit init;
        IInitParent iInitParent;
        IShow show;
        IHide hide;
        IUpdate update;
        IDestroy destroy;
    }

#endregion



#region 内部类(举例,没有实际用处)
class A
{ }
    class AMono:MonoBehaviour
    { }
    #endregion
}

watch 以下,按初学者朴素的理解来划分

玩家

敌人AI轨迹

csharp 复制代码
    /// <summary>轨迹</summary>
    private void Trajectory()
    {
        ITrajectory iTrajectory;
        EllipseTrajectory ellipseTrajectory;
        RotateTrajectory rotateTrajectory;
        StraightTrajectory straightTrajectory;
        VTrajectory vTrajectory;
        TrajectoryData();
        Path();
    }
    void TrajectoryData()
    { 
        EnemyTrajectoryDataMgr enemyTrajectoryDataMgr;
        ITrajectoryData iTrajectoryData;
        StraightTrajectoryData straightTrajectoryData;
        VTrajectoryData vTrajectoryData;
        EllipseData ellipseData;
        RotateData rotateData;     
    }
        private void Path()
    {
        IPath iPath;
        PathBase pathBase;
        EllipsePath ellipsePath;
        StayOnTopPath stayOnTopPath;
        StraightPath straightPath;
        WPath wPath;
        PathMgr pathMgr;
        //
        IEnterPath iEnetrPath;
        EnterPath enterPath;
        EnterPath.MoveDirection moveDirection;
        RightToLeft rightToLeft;
        LeftToRight leftToRight;
        UpToDown upToDown;
    }

总的关系 各种轨迹:PathBase:IPath

方向 IPath.GetDir()

PathMgr中用了PathFactory的GetDir(),就是将变化最频繁的部分摘了出来

csharp 复制代码
public class PathFactory 
{
    public static IPath GetPath(TrajectoryType trajectoryType)
    {
        switch (trajectoryType)
        {
            case TrajectoryType.STRAIGHT:
                return new StraightPath();
            case TrajectoryType.W:
                return new WPath();
            case TrajectoryType.STAY_ON_TOP:
                return new StayOnTopPath();
            case TrajectoryType.ELLIPSE:
                return new EllipsePath();
            case TrajectoryType.ROTATE:
                Debug.LogError("PathFactory未初始该轨道:" + trajectoryType);
                return null;
            default:
                Debug.LogError("PathFactory的当前轨迹未添加,名称:" + trajectoryType);
                return null;
        }
    }
}

初始位置 IPath.GetInitPos()

升级

输赢 ,扣分

声音

动画

读存档、数据

01 流程

可以看到LifeCycleMgr 初始化时就会读取json

优先用PlayerPrefers拿不到就去取json的

csharp 复制代码
    private void GetDataByPlayerPrefersOrJson()
    {
        LifeCycleMgr lifeCycleMgr;
        LifeCycleAddConfig lifeCycleAddConfig;
        ConfigMgr configMgr;
        DataMgr dataMgr;
        DataMgr.Single.SetObject("key",new object()); //PlayerPrefers
        DataMgr.Single.SetJsonData("key", new JsonData());//DataUtil
    }

02 json数据

这段主要在强化面板时用到

它会这么存

key,value

0level,0

0attackTime,1

0atttack,后面那串json

0attackname,攻击

0attackvalue,5

...

以此类推

csharp 复制代码
{
  "planes": [
    {
      "planeId": 0,
      "level": 0,
      "attackTime":1,
      "attack": {"name":"攻击","value":5,"cost":180,"costUnit":"star","grouth":10,"maxVaue": 500},
      "fireRate": {"name":"攻速","value":80,"cost":200,"costUnit":"star","grouth":1,"maxVaue": 100},
      "life": {"name":"生命","value":100,"cost":200,"costUnit":"star","grouth":50,"maxVaue": 1000},
      "upgrades": {"name":"升级","coefficient": 2,"max":4,"0": 100,"1": 200,"2": 300,"3": 400,"costUnit":"diamond"}
    },

watch DataMgr

modify 拆分一下枚举,全部大写加_

bug 报错

按图二的位置去改成相应的 大写+_(因为前面改了枚举)

但是枚举关联着资源,也就是资源名称,没必要让资源名称也变成枚举的格式,这些就不先不改,主要是AudioVolume

地图

两个脚本,两张图轮换

csharp 复制代码
        private void Map()
        {
            MapMgr mapMgr;
            MapItem mapItem;
        }

特效

csharp 复制代码
        private void Effect()
        {
            BulletDestroyAniView bulletDestroyAniView;
            PlaneDestroyAniView planeDestroyAniView;
            FrameAni frameAni;
        }

敌人

-------------------------------------------------------

QF预设置

生成脚本的命名空间

-------------------------------------------------------

00 Scene Test

modify UI两者之间的位置关系

改写其中的逻辑


csharp 复制代码
   /// <summary>基于参照物baseRect(和放置物不同面板placeParent)和间隔offset,上下左右放置物体placeRect</summary>
    public static Component BaseBy(this Component t
        , RectTransform placeParent
        , RectTransform baseRect
        , Dir dir
        , float offset) 
    {
        RectTransform placeRect = t.GetComponent<RectTransform>();
        LayoutElement placeLayout = placeRect.GetComponent<LayoutElement>();//placeRect要有组件。
        //
        float horizontal_LocalX = 0f;
        float horizontal_LocalY=0f;
        float vertical_LocalY=0f;
        float vertical_LocalX = 0f;
        if (dir == Dir.LEFT || dir == Dir.RIGHT)
        { 
            float base_HalfW = Mathf.Abs(baseRect.rect.xMin);
            float panel_HalfW = placeParent.rect.width / 2;
            float text_AfterW = panel_HalfW - (base_HalfW + offset);
            placeLayout.preferredWidth = (text_AfterW < placeParent.rect.width) ? text_AfterW : -1;
            //
             horizontal_LocalX = (panel_HalfW - text_AfterW / 2);
             horizontal_LocalY = placeRect.transform.World2Local(baseRect).y;        
        }
        if (dir == Dir.UP || dir == Dir.DOWN)
        {
            float base_HalfH = Mathf.Abs(baseRect.rect.yMin);
            float panel_HalfH = placeParent.rect.height / 2;
            float text_AfterH = panel_HalfH - (base_HalfH + offset);
            placeLayout.preferredWidth = (text_AfterH < placeParent.rect.width) ? text_AfterH : -1;
            //
             vertical_LocalY = (panel_HalfH - text_AfterH / 2);
             vertical_LocalX = placeRect.transform.World2Local(baseRect).x;
        }


        switch (dir)
        {

            case Dir.LEFT:
                {
                    placeRect.transform.localPosition = new Vector3(-horizontal_LocalX, horizontal_LocalY);
                }
                break;
            case Dir.RIGHT:
                {
                    placeRect.transform.localPosition = new Vector3(horizontal_LocalX, horizontal_LocalY);
                }
                break;
            case Dir.UP:
                {
                    placeRect.transform.localPosition = new Vector3(vertical_LocalX, vertical_LocalY);
                }
                break;
            case Dir.DOWN:
                {
                    placeRect.transform.localPosition = new Vector3(vertical_LocalX, -vertical_LocalY);
                }
                break;
            default: break;
        }
        return placeRect;
    }

---------------------------------------------------------

01 Scene Start面板、Main场景

StartGamePanel

csharp 复制代码
using UnityEngine;
using UnityEngine.UI;
using QFramework;

namespace QFramework.Example
{
	public class StartGamePanelData : UIPanelData
	{
	}
	public partial class StartGamePanel : UIPanel
	{
		protected override void OnInit(IUIData uiData = null)
		{
			mData = uiData as StartGamePanelData ?? new StartGamePanelData();
			// please add init code here
			ResKit.Init();
            if (true)
            {
				StartGameBtn.onClick.AddListener(() => { 
					UIKit.OpenPanel<SelectHeroPanel>();
					CloseSelf();
				});
            }
            else //原来的写法。重名了报错
            {
               //UIManager.Single.Show(Paths.PREFAB_SELECTED_HERO_VIEW);
            }


            AudioMgr.Single.PlayBGM();
        }
		
		protected override void OnOpen(IUIData uiData = null)
		{
		}
		
		protected override void OnShow()
		{
		}
		
		protected override void OnHide()
		{
		}
		
		protected override void OnClose()
		{
		}
	}
}

---------------------------------------------------------

02 Scene StartGame

bug 加了DontDestroyOnLoad。设置父节点无效

图一是无效的

图二不加就可以设置

watch 从AudioMgr中看资源加载模块

AudioMgr

LoadMgr

ResourceLoade

ILoader

01 开始StartController的 AudioMgr.Single.PlayOnce(UIAduio.UI_StartGame.ToString());

02 在一个字典 _clips中查找

03 字典的初始化

csharp 复制代码
    private void InitClips()
    {
        var clips = LoadMgr.Single.LoadAll<AudioClip>(Paths.AUDIO_FOLDER);

        foreach (var clip in clips)
        {
            _clips.Add(clip.name, clip);
        }
    }

04 初始化用到了LoadMgr中的 ResourceLoader:ILoader

csharp 复制代码
public interface ILoader
{
    GameObject LoadPrefab(string path);
    GameObject LoadPrefabAndInstantiate(string path, Transform parent = null);
    void LoadConfig(string path, Action<object> complete);
    T Load<T>(string path) where T : Object;
    T[] LoadAll<T>(string path) where T : Object;
}

05 ResourceLoader中就是熟悉的unity资源加载方法

watch 从 ResourceLoader 中看 CoroutineMgr

ResourceLoader

CoroutineMgr

CoroutineController

CoroutineItem

CoroutineState

watch COnsole中的打印

相应脚本

Assets/Scripts/Manager/TestMgr.cs

Assets/Scripts/Test

bug Unity C# interfaces causing "script class cannot be found"

01 类名与脚本名不一致

02 节点上加的脚本与已有的冲突(两个脚本都:Graphic)

bug 写完类似 : Graphic的脚本后无效,或者先有后运行后无效

对相应的脚本进行Reimport

watch UGUI的父节点

目前遇到的两个拼UI

01 Andy使用的父节点是有一定的宽和高的(改动过)

02 忘记了是哪个视频(好像是siki学院,Plane的暗黑破坏神教程),他采用的是LeftTopPin(命名方式)的父节点,父节点的高宽都是默认的(100高,100宽)。就是你在某一个分辨率(指Game窗口下的那个分辨率)随便拼UI,最后用空节点(默认)锚定(左上,右上等),将随便拼的UI(在左上,右下等),拖到那个空节点

watch 面板 DialogView

watch

发现QF有两种UI思路。

一种是IController+Command,Event

一种是能自动生成Desigher的那种,就没有继承IController

-------------------------------

03 Scene 英雄选择(第二个转QF的界面)

选择效果

效果

设定选择颜色和时间

主要是这3个属性。整个框架也是复制QF的PointGame的

csharp 复制代码
        /// <summary>实际使用的是在父节点中的索引</summary>
        BindableProperty<int> SelectHeroID { get; }
        /// <summary>选中后变化的颜色</summary>
        BindableProperty<Color> SelectHeroColor { get; }
        /// <summary>选中后变化颜色需要的时间</summary>
        BindableProperty<float> SelectHeroColorTime { get; }
csharp 复制代码
    #region Model

    internal class PlaneModel
    {
    }

    internal interface IPlaneModel
    {
    }

    // 1. 定义一个 Model 对象
    public interface IAirCombatAppModel : IModel
    {
        BindableProperty<int> Star { get; }
        BindableProperty<int> Diamond { get; }
        BindableProperty<int> SelectPlaneID { get; }
        /// <summary>实际使用的是在父节点中的索引</summary>
        BindableProperty<int> SelectHeroID { get; }
        BindableProperty<Color> SelectHeroColor { get; }
        BindableProperty<float> SelectHeroColorTime { get; }
    }
    public class AirCombatAppModel : AbstractModel, IAirCombatAppModel
    {
        public BindableProperty<int> Star { get; } = new BindableProperty<int>();
        public BindableProperty<int> Diamond { get; } = new BindableProperty<int>();
        public BindableProperty<int> SelectPlaneID { get; } = new BindableProperty<int>();
        public BindableProperty<int> SelectHeroID { get; } = new BindableProperty<int>();
        public BindableProperty<Color> SelectHeroColor { get; } = new BindableProperty<Color>();
        public BindableProperty<float> SelectHeroColorTime { get; } = new BindableProperty<float>();


        protected override void OnInit()
        {
            IStorage storage = this.GetUtility<IStorage>();

            // 设置初始值(不触发事件)
            Star.SetValueWithoutEvent(storage.LoadInt(nameof(Star)));
            Diamond.SetValueWithoutEvent(storage.LoadInt(nameof(Diamond)));
            SelectHeroID.SetValueWithoutEvent(storage.LoadInt(nameof(SelectHeroID)));
            SelectPlaneID.SetValueWithoutEvent(storage.LoadInt(nameof(SelectPlaneID)));
            SelectHeroColor.SetValueWithoutEvent(storage.LoadString(nameof(SelectHeroColor)).ToColor());
            SelectHeroColorTime.SetValueWithoutEvent(storage.LoadFloat(nameof(SelectHeroColorTime)));
           
            // 当数据变更时 存储数据
            Star.Register(newCount =>
            {
                storage.SaveInt(nameof(Star), newCount);
            });
            Diamond.Register(newCount =>
            {
                storage.SaveInt(nameof(Diamond), newCount);
            });
            SelectHeroID.Register(newCount =>
            {
                storage.SaveInt(nameof(SelectHeroID), newCount);
            });
            SelectPlaneID.Register(newCount =>
            {
                storage.SaveInt(nameof(SelectPlaneID), newCount);
            });
            SelectHeroColor.Register(newCount =>
            {
                storage.SaveString(nameof(SelectHeroColor), newCount.ToString());
            });
            SelectHeroColorTime.Register(newCount =>
            {
                storage.SaveFloat(nameof(SelectHeroColorTime), newCount);
            });
        }
    }

    #endregion

面板代码

主要是

01 Color.gray,0.5f,这个两个参数应该是写死的,但为了方便改动,协程可以设置的

02 HeroSelectable,控制选择效果和其它

csharp 复制代码
			this.SendCommand(new SetSelectHeroParaCommand(Color.gray,0.5f));
			foreach (RectTransform rect in HeroTrans)
			{
				_heroRectLst.Add(rect);
			}

			foreach (var rect in _heroRectLst)
			{
				rect.AddComponentIfNull<HeroSelectable>();
			}
csharp 复制代码
using UnityEngine;
using UnityEngine.UI;
using QFramework;
using System.Collections.Generic;

namespace QFramework.AirCombat
{

	public class SelectHeroPanelData : UIPanelData
	{
	}
	public partial class SelectHeroPanel : UIPanel	,IController
	{

		List<RectTransform> _heroRectLst = new List<RectTransform>();

		protected override void OnInit(IUIData uiData = null)
		{
			mData = uiData as SelectHeroPanelData ?? new SelectHeroPanelData();
			// please add init code here

			#region 原文	


			/**
			foreach (Transform trans in HeroTrans)
			{ 
			   trans.AddComponent<HeroItem>();
			   trans.AddComponent<HeroItemController>();
			}
            HeroItem heroItem;
            HeroItemController heroItemController;
            SelectHero selectHero;
            SelectHeroController selectHeroController;
            SelectedHeroView selectedHeroView;
            SelectedHeroController selectedHeroController;
			**/
			#endregion


			#region 初始化


			this.SendCommand(new SetSelectHeroParaCommand(Color.gray,0.5f));
			foreach (RectTransform rect in HeroTrans)
			{
				_heroRectLst.Add(rect);
			}

			foreach (var rect in _heroRectLst)
			{
				rect.AddComponentIfNull<HeroSelectable>();
			}
			#endregion


			#region 按钮


			StartBtn.onClick.AddListener(() => { 
				//UIManager.Single.Show(Paths.PREFAB_LEVELS_VIEW);
				"进入游戏".LogInfo();
			});

			StrengthenBtn.onClick.AddListener(() => {
				this.SendCommand < OpenStrengthenPanelCommand>();   
				CloseSelf();
            });

			ExitBtn.onClick.AddListener(() => {
				this.SendCommand<ExitGameCommand>();
            });
			#endregion
        }


		#region 辅助
		protected override void OnOpen(IUIData uiData = null)
		{
		}
		
		protected override void OnShow()
		{
		}
		
		protected override void OnHide()
		{
		}
		
		protected override void OnClose()
		{
		}

        public IArchitecture GetArchitecture()
        {
			return AirCombatApp.Interface;
        }
		#endregion

    }
}

面板子项和对Event的响应

响应就是 this.RegisterEvent

csharp 复制代码
/****************************************************
    文件:HeroSelectable.cs
	作者:lenovo
    邮箱: 
    日期:2023/8/12 19:4:2
	功能:
*****************************************************/

using QFramework.AirCombat;
using QFramework;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
using UnityEngine.UI;
using DG.Tweening;

namespace QFramework.AirCombat
{
    public class HeroSelectable : MonoBehaviour ,IController
    {
        #region 属性

        #endregion

        #region 生命


        /// <summary>首次载入且Go激活</summary>
        void Start()
        {
            this.RegisterEvent<SelectHeroEvent>( _ => 
            { 
                int idx = transform.GetSiblingIndex();
                IAirCombatAppModel model = this.GetModel<IAirCombatAppModel>();
                Image image = GetComponent<Image>();

                bool selected = idx == model.SelectHeroID;
                if (selected)
                {
                    image.DOKill();
                    image.DOColor(model.SelectHeroColor, model.SelectHeroColorTime);
                }
                else
                {
                    image.DOKill();
                    image.DOColor(Color.white, model.SelectHeroColorTime);
                }
            });

            GetComponent<Button>().onClick.AddListener(() =>
            { 
                IAirCombatAppModel model = this.GetModel<IAirCombatAppModel>();
                Debug.Log("HeroSelectable");
                //
                this.SendCommand(new SelectHeroCommand(transform.GetSiblingIndex()));
            
            });
        }
        #endregion


        #region 实现
        public IArchitecture GetArchitecture()
        {
            return AirCombatApp.Interface;
        }
        #endregion
    }
}

选择命令

csharp 复制代码
    public class SelectHeroCommand : AbstractCommand
    {
        int _heroIdx = 0;

        public SelectHeroCommand(int heroIdx)
        {
            
            _heroIdx = heroIdx;
        }

        protected override void OnExecute()
        {
            this.GetModel<IAirCombatAppModel>().SelectHeroID.Value = _heroIdx;
            this.SendEvent<SelectHeroEvent>();

        }
    }

选中时的音效

停止旧的

  AudioMgr.Single.Stop(cur.Index2String<Hero>());
csharp 复制代码
    public class SelectHeroCommand : AbstractCommand
    {
        int _heroIdx = 0;

        public SelectHeroCommand(int heroIdx)
        {
            
            _heroIdx = heroIdx;
        }

        protected override void OnExecute()
        {
            int cur = this.GetModel<IAirCombatAppModel>().SelectHeroID.Value;
            this.GetModel<IAirCombatAppModel>().SelectHeroID.Value = _heroIdx;
            AudioMgr.Single.Stop(cur.Index2String<Hero>());
            
            this.SendEvent<SelectHeroEvent>();

        }
    }

播放新的

AudioMgr.Single.Play(idx.Index2String<Hero>());
csharp 复制代码
/****************************************************
    文件:HeroSelectable.cs
	作者:lenovo
    邮箱: 
    日期:2023/8/12 19:4:2
	功能:
*****************************************************/

using QFramework.AirCombat;
using QFramework;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
using UnityEngine.UI;
using DG.Tweening;
using System.Runtime.ConstrainedExecution;

namespace QFramework.AirCombat
{
    public class HeroSelectable : MonoBehaviour ,IController
    {
        #region 属性

        #endregion

        #region 生命



        /// <summary>首次载入且Go激活</summary>
        void Start()
        {
            this.RegisterEvent<SelectHeroEvent>( _ => 
            { 
                int idx = transform.GetSiblingIndex();
                IAirCombatAppModel model = this.GetModel<IAirCombatAppModel>();
                Image image = GetComponent<Image>();

                bool selected = idx == model.SelectHeroID;
                if (selected)
                {
                    AudioMgr.Single.Play(idx.Index2String<Hero>());
                    image.DOKill();
                    image.DOColor(model.SelectHeroColor, model.SelectHeroColorTime);
                }
                else
                {
                    image.DOKill();
                    image.DOColor(Color.white, model.SelectHeroColorTime);
                }
            });
            ......

stars ExtendEnum转来转去

这块是需要 根据英雄的兄弟索引 去转对应值的 Hero枚举 ,枚举对应的字符串

csharp 复制代码
/****************************************************
    文件:ExtendEnum.cs
	作者:lenovo
    邮箱: 
    日期:2023/7/24 18:0:35
	功能:
*****************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
 

public static partial class ExtendEnum  //转来转去
{


    public enum ExtendEnum_XXX
    {
        A=0,
        B=1,
        C=2,

    }
  
    public static void Example()
    {
        ExtendEnum_XXX para = ExtendEnum_XXX.A;
        string str = para.ToString();
        int idx = (int)para;
        Debug.LogFormat("String2Enum:({0}){1}" 
            , str.String2Enum<ExtendEnum_XXX>().GetType()
            , str.String2Enum<ExtendEnum_XXX>()  );
        Debug.LogFormat("Index2Enum:({0}){1}"
            , idx.Index2Enum<ExtendEnum_XXX>().GetType()
            , idx.Index2Enum<ExtendEnum_XXX>()  );
        //      
        Debug.LogFormat("String2Index:({0}){1}"
            , str.String2Index<ExtendEnum_XXX>().GetType()
            , str.String2Index<ExtendEnum_XXX>()  );
        Debug.LogFormat("Enum2Index:({0}){1}" 
            , para.Enum2Index().GetType()
            , para.Enum2Index()  );
        //      
        Debug.LogFormat("Index2String:({0}){1}" 
            , idx.Index2String<ExtendEnum_XXX>().GetType()
            , idx.Index2String<ExtendEnum_XXX>()  );
        Debug.LogFormat("Enum2String:({0}){1}" 
            , para.Enum2String().GetType()
            , para.Enum2String()  );
    }


    /// <summary>字符串转枚举</summary>
    public static T String2Enum<T>(this string str) where T:Enum
    {
        return (T)Enum.Parse(typeof(T), str);
    }
    /// <summary.值转枚举</summary>
    public static T Index2Enum<T>(this int idx) where T : Enum
    {
       string str= Enum.GetName(typeof(T), idx);
        return str.String2Enum <T>();
    }


    public static int String2Index<T>(this string str) where T : Enum
    {
        T t= (T)Enum.Parse(typeof(T), str);
        return (int)Enum.Parse(typeof(T), str);
    }

    public static int Enum2Index<T>(this T t) where T : Enum
    {
        string str= t.ToString();
        return (int)Enum.Parse(typeof(T), str);
    }

    public static string Index2String<T>(this int idx) where T : Enum
    {
        return Enum.GetName(typeof(T), idx);
    }

    public static string Enum2String<T>(this T t) where T : Enum
    {
        return t.ToString();
    }
}

stars btn.onClick.AddListenerAfterRemoveAll();

csharp 复制代码
    public static ButtonClickedEvent AddListenerAfterRemoveAll(this ButtonClickedEvent onClick 
        , UnityAction action)
    {
        onClick.RemoveAllListeners();
        onClick.AddListener(action);
        return onClick;
    }

-------------------------------------------------------

04 Scene 升级界面(是从这里开始试着转QF)

bug 升级界面的数据没拿到

初始化改成设置父节点时,把数据初始化的方法去掉了

bug untiy卡死

在VS修改脚本后,回到Unity,,此时Unity处于打开带有改脚本的一个预制体中,然后卡死

stars DoIfNotNull

csharp 复制代码
    /// <summary>不为空就执行</summary>
    public static Action DoIfNotNull(this Action cb)
    {
        if (cb != null)
        {
            cb();
        }
        return cb;
    }

效果

csharp 复制代码
    public void UpdateFunc()
    {
        Image.fillAmount -= Time.deltaTime / Const.CD_EFFECT_TIME;
        if (Image.fillAmount <= 0)
        {
            LifeCycleMgr.Single.Remove(LifeName.UPDATE, this);
            _cb.DoIfNotNull();
        }
    }

stars DoIfNotNull(T)(尖括号显示不了)

ActionMgr.Execute(T t)可以改

csharp 复制代码
    public static void Example()
    {
        Action<int> intAction = (para) => { Debug.Log(para.ToString()); };
        intAction.DoIfNotNull<int>(99);
    }
   
    public static Action<T> DoIfNotNull<T>(this Action<T> cb,T t) 
    {
        if (cb != null)
        {
            cb(t);
        }
        return cb;
    }

modify ButtonAction

把主体的代码拆出去以后复用,所以区别就是项目多加了声音事件

csharp 复制代码
using System;
using UnityEngine;
using UnityEngine.UI;

public static class ExtendUtil
{

    /// <summary>
    /// 类似于  transform.ButtonAction("Add", AddAction);
    /// <para /> 类似于transform.ButtonAction("OK/Start", () => { UIManager.Single.Show(Paths.PREFAB_LEVELS_VIEW); });
    /// </summary>
    public static void ButtonAction(this Transform trans
        , string path
        , Action action
        , bool useDefaultAudio = true)
    {
       
        if (useDefaultAudio)
        {
            action += AddButtonAudio;
        }
        trans.ButtonAction_Common(path,action);

    }

    private static void AddButtonAudio()
    {
        AudioMgr.Single.PlayOnce(UIAduio.UI_ClickButton.ToString());
    }


}

star 拆出来的复用部分

csharp 复制代码
    /// <summary>
    /// 类似于  transform.ButtonAction("Add", AddAction);
    /// <para /> 类似于transform.ButtonAction("OK/Start", () => { UIManager.Single.Show(Paths.PREFAB_LEVELS_VIEW); });
    /// </summary>
    public static void ButtonAction_Common(this Transform trans
        , string path
        , Action action)
    {
        var tar = trans.Find(path);
        if (tar == null)
        {
            Debug.LogError("ButtonAction查找物体为空,路径为:" + path);
        }
        else
        {
            var button = tar.GetComponent<Button>();
            if (button == null)
            {
                Debug.LogError("ButtonAction物体上没有Button组件,物体名称:" + tar.name);
            }
            else
            {
                button.onClick.AddListener(() => action());
            }
        }
    }

modify 金币钻石的数据

效果

初始数据

csharp 复制代码
        DataMgr.Single.ClearAll();
        { //弄点数据
            DataMgr.Single.Set(DataKeys.STAR,666);                
            DataMgr.Single.Set(DataKeys.DIAMOND,100);      
             //按他的设置来设置的话
            GameStateModel.Single.SetMoney(DataKeys.STAR,666);
            GameStateModel.Single.SetMoney(DataKeys.DIAMOND,100);  
        }   

获得数据和显示

csharp 复制代码
            if (false)
            {
               // Util.Get("Star/BG/Text").SetText(DataMgr.Single.Get<int>(DataKeys.STAR));
                //Util.Get("Diamond/BG/Text").SetText(DataMgr.Single.Get<int>(DataKeys.DIAMOND));
            }
            else
            {
                StarText.SetText(DataMgr.Single.Get<int>(DataKeys.STAR));
                DiamondText.SetText(DataMgr.Single.Get<int>(DataKeys.DIAMOND));
                //DataMgr.Single.Get<int>(DataKeys.STAR).LogInfo();
                //DataMgr.Single.Get<int>(DataKeys.STAR).LogInfo();
            }

bug Slider在PlayerPrefs的数据拿不到

DataMgr在PlayerPrefs的数据拿不到就去那Json拿。

方法是ConfigMgr在Init()时,

用到了DataUtil.SetObjectData(string oldkey, JsonData data)。

。。。

ConfigMgr由LifeCycleMgr调用LifeCycleAddConfig时用到

。。

结果是数据初始比较慢,导致打开强化面板时数据还没初始好

csharp 复制代码
    private void GetDataByPlayerPrefersOrJson()
    {
        LifeCycleMgr lifeCycleMgr;
        LifeCycleAddConfig lifeCycleAddConfig;
        ConfigMgr configMgr;
        DataMgr dataMgr;
        DataMgr.Single.SetJsonData("key", new JsonData());//DataUtil

    }

watch 改写的UI

watch 原本的UI

stars SetText

csharp 复制代码
    public static Transform SetText(this Transform trans, object obj)
    {
        Text text = trans.GetComponent<Text>();
        if (text == null)
        {
            throw new System.Exception("SetText异常");
        }
        if (text != null)
        {
            text.text = obj.ToString();
        }

        //
        return trans;
    }

    public static GameObject SetText(this GameObject go, object obj)
    {
        Text text = go.GetComponent<Text>();
        if (text == null)
        {
            throw new System.Exception("SetText异常");
        }
        if (text != null)
        {
            text.text = obj.ToString();
        }

        //
        return go;
    }

modify 左右切图和属性强化(星星)

InitAddBtn在切换飞机时变,点击 addBtn不变

UodateAllUI在切换飞机和点击addBtn都变

csharp 复制代码
            //中间的SwitchPlayer
            {
                //默认设置
                GameStateModel.Single.SelectedPlaneId = DataMgr.Single.Get<int>(DataKeys.PLANE_ID);
                int id, level;
                id = GameStateModel.Single.SelectedPlaneId;
                level = GameStateModel.Single.PlaneLevel;
                InitAddBtn(id);
                UpdateAllUI(id, level);
               
                //
                LeftBtn.onClick.AddListener(() => {
                    id = SwitchPlaneID( id, -1);
                    level = GameStateModel.Single.PlaneLevel;
                    InitAddBtn(GameStateModel.Single.SelectedPlaneId);
                    UpdateAllUI(id, level);
                });
                RightBtn.onClick.AddListener(() => {
                    id = SwitchPlaneID( id, 1);
                    level = GameStateModel.Single.PlaneLevel;
                    InitAddBtn(id);
                    UpdateAllUI(id, level);
                });
            }

modify 左右切图和飞机升级(钻石)

modify 强化界面的代码(未使用QF)

效果

StrengthenPanel : UIPanel (脚本生成的,功能自己加)

csharp 复制代码
using UnityEngine;
using UnityEngine.UI;
using QFramework;
using System.Collections.Generic;
using ItemKey = PropertyItem.ItemKey;
using LitJson;
using System.Numerics;
using Newtonsoft.Json.Linq;

namespace QFramework.AirCombat
{
    public class StrengthenPanelData : UIPanelData
    {
    }
    public partial class StrengthenPanel : UIPanel 
    {


        #region 字属

         List<RectTransform> _propertyItemLst=new List<RectTransform>();
        #endregion


        protected override void OnInit(IUIData uiData = null)
        {
            mData = uiData as StrengthenPanelData ?? new StrengthenPanelData();

            _propertyItemLst.Add(PropertyItem_Attack);
            _propertyItemLst.Add(PropertyItem_FireRate);
            _propertyItemLst.Add(PropertyItem_Life);

            #region 原文
            // please add init code here
            //if (false)
            //{
            //    Util.Get("Money").Go.AddComponent<Money>();
            //    Util.Get("Switchplayer").Go.AddComponent<SwitchPlayer>();
            //      Util.Get("Property").Go.AddComponent<PlaneProperty>(); //Slider
            //}
            //else
            //{

            //}
            //中间的飞机数据,3条Slider。Property做的事
            //PlaneProperty,PropertyItem
            {
                // _propertyItemLst = new List<PropertyItem>((int)Property.COUNT);

                //for (Property i = 0; i < Property.COUNT; i++)
                //{
                //    GameObject go = LoadMgr.Single.LoadPrefabAndInstantiate(Paths.PREFAB_PROPERTY_ITEM, transform);
                //    PropertyItem item = go.AddComponent<PropertyItem>();
                //    item.Init(i.ToString());
                //    _propertyItemLst.Add(item);
                //}
            }
            #endregion


            //上方的Money
            if (false)
            {
               // Util.Get("Star/BG/Text").SetText(DataMgr.Single.Get<int>(DataKeys.STAR));
                //Util.Get("Diamond/BG/Text").SetText(DataMgr.Single.Get<int>(DataKeys.DIAMOND));
            }
            else
            {
                UpdateMoneyText();
                //DataMgr.Single.Get<int>(DataKeys.STAR).LogInfo();
                //DataMgr.Single.Get<int>(DataKeys.STAR).LogInfo();
            }



            //中间的SwitchPlayer
            {
                //默认设置
                GameStateModel.Single.SelectedPlaneId = DataMgr.Single.Get<int>(DataKeys.PLANE_ID);
                int id, level;
                id = GameStateModel.Single.SelectedPlaneId;
                UpdateAllUI();
               
                //
                LeftBtn.onClick.AddListener(() => {
                    id = SwitchPlaneID( id, -1);
                    UpdateAllUI();
                });
                RightBtn.onClick.AddListener(() => {
                    id = SwitchPlaneID( id, 1);
                    UpdateAllUI();
                });
            }

            InitBackBtn();

        }


        #region 重写 其它
        protected override void OnOpen(IUIData uiData = null)
        {
        }

        protected override void OnShow()
        {
        }

        protected override void OnHide()
        {
        }

        protected override void OnClose()
        {
        }
        #endregion
        #region 辅助

        /// <summary>更细能改界面所有元素</summary>
        void UpdateAllUI()
        {
            int planeId = GameStateModel.Single.SelectedPlaneId;
            int level = GameStateModel.Single.PlaneLevel;
            //
            UpdateMoneyText();     
            UpdatePlaneSprite(planeId, level);
            foreach (var rect in _propertyItemLst)
            {
                InitAddButton(planeId, rect);
                UpdateText_Name_Value_Cost(planeId, rect);
                UpdateSlider_Value_MaxVaue(planeId, rect);
            }
            InitUpgradesBtn(planeId, level);
            UpdateUpgradesText(planeId);
        }

        void InitUpgradesBtn(int planeId, int level)
        {
            UpgradesBtn.onClick.RemoveAllListeners();
            UpgradesBtn.onClick.AddListener(() => {
                InitUpgradesBtnAction(planeId);
                UpdateAllUI();
            });
        }


        void InitBackBtn()
        {
            BackBtn.onClick.AddListener(() => {
                //SelectHeroPanel的push由SelectHeroPanel操作
                UIKit.Back<SelectHeroPanel>();
                CloseSelf();
            });
        }






         
         void UpdateMoneyText()
        {
            DiamondText.SetText(GameStateModel.Single.GetDiamond());
            StarText.SetText(GameStateModel.Single.GetStar());
        }




        private void InitUpgradesBtnAction(int planeId)
        {
            string pre = planeId + DataKeys.UPGRADES;
            //
            int curMoney = GameStateModel.Single.GetDiamond(); //自己的前不用加pre
            //
            int maxLevel = GameStateModel.Single.PlaneLevelMax;
            int curLevel = GameStateModel.Single.PlaneLevel;
            int afterLevel = curLevel;           
            //
            string costKey = KeysUtil.GetPropertyKeys(pre + curLevel);//0upgrades0:300 的冒号左边
            int costMoney = DataMgr.Single.Get<int>(costKey); //300
            int afterMoney = curMoney - costMoney;
            if (afterMoney >= 0 && curLevel < maxLevel-1)//有钻石&&上升空间 (0,1,2,3,max=4)
            {
                if (afterLevel >= maxLevel)//到顶了
                {
                    afterLevel = maxLevel;
                }
                else
                {
                    afterLevel = curLevel + 1;
                }
                GameStateModel.Single.SetDiamond(afterMoney);
                DataMgr.Single.SetObject(planeId + DataKeys.LEVEL, afterLevel);
                //
                var key = KeysUtil.GetPropertyKeysWithoutPlaneId(DataKeys.UPGRADES_RATIO);
                var level = GameStateModel.Single.PlaneLevel;
                level++;
                DataMgr.Single.Set(key, level);
            }
            else
            {
                Debug.Log("你没钻石了或者等级到顶了!");
                // UIManager.Single.ShowDialog("你没星星了!");
            }
        }



        private void InitAddButtonAction(int planeId, RectTransform rect)
        {     
           string property = (rect.name).TrimName(TrimNameType.DashAfter).LowerFirstLetter();//attack,fireRate,life
            string pre = planeId + property  ;
            int curMoney = GameStateModel.Single.GetStar(); //自己的前不用加pre
             //
            string  costKey = KeysUtil.GetPropertyKeys( pre + PropertyItem.ItemKey.cost);
            int costMoney = DataMgr.Single.Get<int>(costKey);
            int afterMoney = curMoney - costMoney;
            //
            int addValue= DataMgr.Single.Get<int>(pre + ItemKey.grouth);
            int maxValue= DataMgr.Single.Get<int>(pre + ItemKey.maxVaue);
            int curValue= DataMgr.Single.Get<int>(pre + ItemKey.value);
            int afterValue = curValue;
            if (afterMoney >= 0 && curValue< maxValue)//有钱&&上升空间
            {
                if (afterValue >= maxValue)//到顶了
                {
                    afterValue = maxValue;
                }
                else
                { 
                    afterValue = curValue + addValue;
                }
                GameStateModel.Single.SetStar(afterMoney);
                DataMgr.Single.SetObject(planeId + property + ItemKey.value, afterValue);
                UpdateAllUI();
            }
            else
            {
                Debug.Log("你没星星了或者数值到顶了!");   
                // UIManager.Single.ShowDialog("你没星星了!");
            }
        }


        void InitAddButton(int planeId,RectTransform rect)
        {
            Button addBtn = rect.GetComponentDeep<Button>("Add");
            addBtn.onClick.RemoveAllListeners();//切换不同id就重置
            addBtn.onClick.AddListener(() =>
            {
                InitAddButtonAction(planeId, rect);
            });

            Debug.Log( "AddBtn数量"+addBtn.onClick.GetPersistentEventCount())  ;
        }

        /// <summary>name,value,cost三个文本</summary>
        private void UpdateText_Name_Value_Cost(int planeId, RectTransform rect)
        {
            #region 说明
            /**
            {
                "planes": [
                  {
                    "planeId": 0,
                    "level": 0,
                    "attackTime":1,
                    "attack": { "name":"攻击","value":5,"cost":200,"costUnit":"star","grouth":10,"maxVaue": 500},
                    "fireRate": { "name":"攻速","value":80,"cost":200,"costUnit":"star","grouth":1,"maxVaue": 100},
                    "life": { "name":"生命","value":100,"cost":200,"costUnit":"star","grouth":50,"maxVaue": 1000},
                    "upgrades": { "name":"升级","coefficient": 2,"max":4,"0": 100,"1": 200,"2": 300,"3": 400,"costUnit":"diamond"}
                        },
            ......
                ],
                "planeSpeed": 1.2
            }
            **/
            #endregion
            // attack,fireRate,life 
            string property =  (rect.name).TrimName(TrimNameType.DashAfter).LowerFirstLetter();//attack,fireRate,life

            //name,value,cost三个文本
            for (ItemKey propertyKey = 0; propertyKey < PropertyItem.ItemKey.grouth; propertyKey++) 
            {
                string transName = PropertyItem.UpperFirstLetter(propertyKey);  //Name           
                //例如Name,Value,Cost,这个Transform
                Transform trans = rect.Find( transName );
                if (trans != null)
                {
                    string key = KeysUtil.GetPropertyKeys(planeId, property + propertyKey); //0attackname
                    Debug.Log("Text:" + key );
                    
                    
                    object obj = DataMgr.Single.GetObject(key);
                    string value = obj.ToString();
                    Debug.Log("Text:"+key+","+value);


                    trans.SetText(value);
                }
                else
                {
                    Debug.LogError("属性Slider预制名称错误,正确名称:" + transName);
                }
            }  
        }


        /// <summary>改成根据父节点,去更新子节点</summary>    
        private void UpdateSlider_Value_MaxVaue(int planeId, Transform trans)
        {
            string property = (trans.name).TrimName(TrimNameType.DashAfter).LowerFirstLetter();//attack
            Slider slider = trans.Find("Slider").GetComponent<Slider>();
            string maxVaueKey = KeysUtil.GetPropertyKeys(planeId, property+ ItemKey.maxVaue); //0attackmaxVaue(原文没加l)
            string valueKey = KeysUtil.GetPropertyKeys(planeId , property + ItemKey.value);//0attackvalue
            Debug.LogFormat("Slider:{0},{1}", valueKey, maxVaueKey);


            slider.minValue = 0;
            slider.maxValue = DataMgr.Single.Get<int>(maxVaueKey);
            slider.value = DataMgr.Single.Get<int>(valueKey);
            Debug.LogFormat("Slider:{0},{1},{2}", slider.minValue, slider.value,slider.maxValue) ;
        }

        void UpdateUpgradesText(int planeId)
        {
            string pre = planeId + DataKeys.UPGRADES;
            int curLevel = DataMgr.Single.Get<int>(planeId + DataKeys.LEVEL);
            //
            string costKey = KeysUtil.GetPropertyKeys(pre + curLevel);//0upgrades0:300 的冒号左边
            int costMoney = DataMgr.Single.Get<int>(costKey); //300
            CostText.SetText(costMoney);
        }


        private int SwitchPlaneID( int id, int direction)
        {
            string str = (direction == -1) ? "左" : "右";
            ( str+"键:" + id).LogInfo();   
            //
            var max = PlaneSpritesModel.Single.Count;
            id += direction;
            id = id.Clamp(0, max-1);                 
            // id = (id < 0 ) ? 0 : id >= max ? id = max - 1 : id;
            GameStateModel.Single.SelectedPlaneId = id;
            return  id;
        }


        void UpdatePlaneSprite(int planeID,int level)
        {
            Sprite planeSprite = PlaneSpritesModel.Single[planeID, level];
            IconImg.SetSprite(planeSprite);
        }
        #endregion
    }
}

StrengthenPanel(生成的)

csharp 复制代码
using System;
using UnityEngine;
using UnityEngine.UI;
using QFramework;

namespace QFramework.AirCombat
{
	// Generate Id:a3f593c2-800c-45b3-96af-41eaf4781fcc
	public partial class StrengthenPanel
	{
		public const string Name = "StrengthenPanel";
		
		[SerializeField]
		public UnityEngine.UI.Image BGImg;
		[SerializeField]
		public UnityEngine.UI.Text StarText;
		[SerializeField]
		public UnityEngine.UI.Text DiamondText;
		[SerializeField]
		public UnityEngine.UI.Button LeftBtn;
		[SerializeField]
		public UnityEngine.UI.Image IconImg;
		[SerializeField]
		public UnityEngine.UI.Button RightBtn;
		[SerializeField]
		public UnityEngine.UI.Button UpgradesBtn;
        [SerializeField]
        public UnityEngine.RectTransform PropertyItem_Attack;
        [SerializeField]
        public UnityEngine.RectTransform PropertyItem_FireRate;
        [SerializeField]
        public UnityEngine.RectTransform PropertyItem_Life;
        [SerializeField]
		public UnityEngine.UI.Text CostText;
		[SerializeField]
		public UnityEngine.UI.Button BackBtn;
		
		private StrengthenPanelData mPrivateData = null;
		
		protected override void ClearUIComponents()
		{
			BGImg = null;
			StarText = null;
			DiamondText = null;
			LeftBtn = null;
			IconImg = null;
			RightBtn = null;
			UpgradesBtn = null;
			PropertyItem_Attack= null;
			PropertyItem_FireRate= null;
			PropertyItem_Life= null;
			CostText = null;
			BackBtn = null;
			
			mData = null;
		}
		
		public StrengthenPanelData Data
		{
			get
			{
				return mData;
			}
		}
		
		StrengthenPanelData mData
		{
			get
			{
				return mPrivateData ?? (mPrivateData = new StrengthenPanelData());
			}
			set
			{
				mUIData = value;
				mPrivateData = value;
			}
		}
	}
}

watch 从QF的"点点点"入手改写强化界面的脚本

watch QF的"点点点"效果

watch QF的"点点点"脚本

csharp 复制代码
using UnityEngine;
using UnityEngine.UI;

namespace QFramework.Example
{
    class Framework
    {

        void All()
        {
            Model();
            System();
            Utility();
            Controller();
            Command();
            Architecture();
        }


        #region 辅助
       void Command()
        {
            IncreaseCountCommand increaseCountCommand;
            DecreaseCountCommand decreaseCountCommand;
        }
   
     void Model()
        {
            IModel iModel;
            AbstractModel abstractModel;
            IAirCombatAppModel iCounterAppModel;
            CounterAppModel counterAppModel;
        }

        void System()
        {
            ISystem iSystem;
            IAchievementSystem iAchievementSystem;
            AchievementSystem achievementSystem;

        }
        void Utility()
        {
            IUtility iIUtility;
            IStorage iStorage;
            Storage storage;

        }

        void Controller()
        {
            CounterAppController counterAppController;
        }

        void Architecture()
        {
            CounterApp counterApp;
        }
        #endregion
    }


    #region Model
    // 1. 定义一个 Model 对象
    public interface IAirCombatAppModel : IModel
    {
        BindableProperty<int> Star { get; }
        BindableProperty<int> Diamond
        { get; }
    }
    public class CounterAppModel : AbstractModel,IAirCombatAppModel
    {
        public BindableProperty<int> Star { get; } = new BindableProperty<int>();
        public BindableProperty<int> Diamond { get; } = new BindableProperty<int>();

        protected override void OnInit()
        {
            IStorage storage = this.GetUtility<IStorage>();
            
            // 设置初始值(不触发事件)
            Star.SetValueWithoutEvent(storage.LoadInt(nameof(Star)));
            Diamond.SetValueWithoutEvent(storage.LoadInt(nameof(Diamond)));

            // 当数据变更时 存储数据
            Star.Register(newCount =>
            {
                storage.SaveInt(nameof(Star),newCount);
            });
            Diamond.Register(newCount =>
            {
                storage.SaveInt(nameof(Diamond), newCount);
            });
        }
    }
    #endregion


    #region System
    public interface IAchievementSystem : ISystem
    {
        
    }

    public class AchievementSystem : AbstractSystem ,IAchievementSystem
    {
        protected override void OnInit()
        {
            this.GetModel<IAirCombatAppModel>() // -+
                .Star
                .Register(newCount =>
                {
                    if (newCount == 10)
                    {
                        Debug.Log("触发 点击达人 成就");
                    }
                    else if (newCount == 20)
                    {
                        Debug.Log("触发 点击专家 成就");
                    }
                    else if (newCount == -10)
                    {
                        Debug.Log("触发 点击菜鸟 成就");
                    }
                });
        }
    }
    #endregion


    #region Utility

    public interface IStorage : IUtility
    {
        void SaveInt(string key, int value);
        int LoadInt(string key, int defaultValue = 0);
    }
    
    public class Storage : IStorage
    {
        public void SaveInt(string key, int value)
        {
            PlayerPrefs.SetInt(key,value);
        }

        public int LoadInt(string key, int defaultValue = 0)
        {
            return PlayerPrefs.GetInt(key, defaultValue);
        }
    }
    #endregion


    #region Architecture
    // 2.定义一个架构(提供 MVC、分层、模块管理等)
    public class CounterApp : Architecture<CounterApp>
    {
        protected override void Init()
        {
            // 注册 System 
            this.RegisterSystem<IAchievementSystem>(new AchievementSystem()); 
             
            // 注册 Model
            this.RegisterModel<IAirCombatAppModel>(new CounterAppModel());
            
            // 注册存储工具的对象
            this.RegisterUtility<IStorage>(new Storage());
        }


        protected override void ExecuteCommand(ICommand command)
        {
            Debug.Log("Before " + command.GetType().Name + "Execute");
            base.ExecuteCommand(command);
            Debug.Log("After " + command.GetType().Name + "Execute");
        }

        protected override TResult ExecuteCommand<TResult>(ICommand<TResult> command)
        {
            Debug.Log("Before " + command.GetType().Name + "Execute");
            var result =  base.ExecuteCommand(command);
            Debug.Log("After " + command.GetType().Name + "Execute");
            return result;
        }
    }
    #endregion


    #region Command
    // 引入 Command
    public class IncreaseCountCommand : AbstractCommand 
    {
        protected override void OnExecute()
        {
            var model = this.GetModel<IAirCombatAppModel>();
                
            model.Star.Value++;
        }
    }
    
    public class DecreaseCountCommand : AbstractCommand
    {
        protected override void OnExecute()
        {
            this.GetModel<IAirCombatAppModel>().Star.Value--; // -+
        }
    }
    #endregion


    #region Controller
    // Controller
    public class CounterAppController : MonoBehaviour , IController /* 3.实现 IController 接口 */
    {
        // View
        private Button mBtnAdd;
        private Button mBtnSub;
        private Text mCountText;
        
        // 4. Model
        private IAirCombatAppModel mModel;

        void Start()
        {
            // 5. 获取模型
            mModel = this.GetModel<IAirCombatAppModel>();
            
            // View 组件获取
            mBtnAdd = transform.Find("BtnAdd").GetComponent<Button>();
            mBtnSub = transform.Find("BtnSub").GetComponent<Button>();
            mCountText = transform.Find("CountText").GetComponent<Text>();
            
            
            // 监听输入
            mBtnAdd.onClick.AddListener(() =>
            {
                // 交互逻辑
                this.SendCommand<IncreaseCountCommand>();
            });
            
            mBtnSub.onClick.AddListener(() =>
            {
                // 交互逻辑
                this.SendCommand(new DecreaseCountCommand(/* 这里可以传参(如果有) */));
            });

            // 表现逻辑
            mModel.Star.RegisterWithInitValue(newCount => // -+
            {
                UpdateView();

            }).UnRegisterWhenGameObjectDestroyed(gameObject);
        }
        
        void UpdateView()
        {
            mCountText.text = mModel.Star.ToString();
        }

        // 3.
        public IArchitecture GetArchitecture()
        {
            return CounterApp.Interface;
        }

        private void OnDestroy()
        {
            // 8. 将 Model 设置为空
            mModel = null;
        }
    }
    #endregion

}

modify 强化界面的代码(使用QF)

watch 效果

modify GameStart

大部分是复制原来的,修改了这个

csharp 复制代码
public class GameStart03 : MonoBehaviour ,IController
{......
            ResKit.Init();
            {
                this.SendCommand( new OpenStrengthenPanelCommand(() => 
                {
                    //DataMgr.Single.Set(DataKeys.STAR,666);                
                    //DataMgr.Single.Set(DataKeys.DIAMOND,1000);
                    //按他的设置来设置的话
                    //GameStateModel.Single.SetStar(800);//有的还没改,注释掉会报错
                    //GameStateModel.Single.SetDiamond(1000);
                    this.SendCommand(new SetStarCommand(1002)); //此时强化面板未打开
                    this.SendCommand(new SetDiamondCommand(1002));
                }));
            }
csharp 复制代码
using System;
using System.Collections;
using System.Linq;
using LitJson;
using QFramework;
using QFramework.Example;
using UnityEngine;

namespace QFramework.AirCombat
{ 
public class GameStart03 : MonoBehaviour ,IController
{
    // Use this for initialization
    /// <summary>首次登录,新手引导</summary>
    public bool IsFirst = false;


    private void Start()
    {
        if (FindObjectsOfType<LaunchGame>().Length > 1)
        {
            Destroy(gameObject);
            return;
        }
        
        StartCoroutine(Init());
    }

        private void Update()
        {
            if (Input.GetKeyDown(KeyCode.P)) //确定是json数据初始慢导致强化面板获取数据失败
            {
                OpenPanelFunc();
            }

        }



        private IEnumerator Init()
    {
        yield return TestMgr.Single.Init();
        yield return null;
        //
        Transform mgrTrans = transform.FindTop("Mgr");
        Transform canvasTrans = transform.FindTop("Canvas");
        //
        GameStateModel.Single.CurScene = SceneName.Main;
        LifeCycleMgr.Single.Init(mgrTrans);
        CoroutineMgr.Single.Init(mgrTrans);
        AudioMgr.Single.Init(mgrTrans);
        DataMgr.Single.ClearAll();
        if (IsFirst)
        { 
            GuideUiMgr.Single.Init(canvasTrans);//设置Canvas,因为我把启动脚本放外面,不放在Canvas下
            GuideMgr.Single.InitGuide();        
        }
        yield return new WaitForSeconds(5f);//延时让json数据初始化


        if (true)
        {
            ResKit.Init();
            {
                this.SendCommand( new OpenStrengthenPanelCommand(() => 
                {
                    //DataMgr.Single.Set(DataKeys.STAR,666);                
                    //DataMgr.Single.Set(DataKeys.DIAMOND,1000);
                    //按他的设置来设置的话
                    //GameStateModel.Single.SetStar(800);//有的还没改,注释掉会报错
                    //GameStateModel.Single.SetDiamond(1000);
                    this.SendCommand(new SetStarCommand(1002)); //此时强化面板未打开
                    this.SendCommand(new SetDiamondCommand(1002));
                }));
            }
        }
    }

        void OpenPanelFunc(  )
        {
            UIKit.OpenPanel<StrengthenPanel>();
        }

        public IArchitecture GetArchitecture()
        {
            return AirCombatApp.Interface;
        }
    }



}

modify StrengthenPanel.cs

csharp 复制代码
using UnityEngine;
using UnityEngine.UI;
using QFramework;
using System.Collections.Generic;
using ItemKey = PropertyItem.ItemKey;
using LitJson;
using System.Numerics;
using Newtonsoft.Json.Linq;
using UniRx.Triggers;
using System;
using System.Data;

namespace QFramework.AirCombat
{
    public class StrengthenPanelData : UIPanelData
    {
    }
    public partial class StrengthenPanel : UIPanel,IController 
    {


        #region 字属

         List<RectTransform> _propertyItemLst=new List<RectTransform>();
        #endregion


        protected override void OnInit(IUIData uiData = null)
        {
            mData = uiData as StrengthenPanelData ?? new StrengthenPanelData();

            _propertyItemLst.Add(PropertyItem_Attack);
            _propertyItemLst.Add(PropertyItem_FireRate);
            _propertyItemLst.Add(PropertyItem_Life);


            #region Top


            this.RegisterEvent<ChangeStarEvent>(e =>
            {
                UpdateStarText();
            });
            this.RegisterEvent<ChangeDiamondEvent>(e =>
            {
                UpdateDiamondText();
            });

            #endregion


            #region Center

           

            //UpdateUpgradesButton();
            LeftBtn.onClick.AddListener(() =>
            {
                this.SendCommand(new SwitchPlaneCommand(-1));
            });

            RightBtn.onClick.AddListener(() =>
            {
                this.SendCommand(new SwitchPlaneCommand(1));
            });



          

            this.RegisterEvent<SelectPlaneIDEvent>(eventId =>
            {
                UpdatePlaneSprite();
                //
                UpdatePropertyItems();
                UpdateUpgradesButton();
            });



            this.RegisterEvent<IncreasePlanePropertyEvent>(eventId =>//增加属性时
            {
                UpdateStarText();
                //
                UpdatePropertyItems();

            });

            this.RegisterEvent<UpgradesPlaneLevelEvent>(eventId =>//升级时
            {
                UpdateDiamondText();
                UpdatePlaneSprite();
                //
                UpdatePropertyItems();
                UpdateUpgradesButton();
            });
            #endregion  


            #region Bottom


            BackBtn.onClick.AddListener(() => 
            {
                //SelectHeroPanel的push由SelectHeroPanel操作
                UIKit.Back<SelectHeroPanel>();
                CloseSelf();
            });
            #endregion


            #region 初始化
            {

                this.SendCommand(new SetPlaneCommand(0));//在跑一次,去触发Register的
            }
            #endregion

        }
        #region 重写 其它
        protected override void OnOpen(IUIData uiData = null)
        {
        }

        protected override void OnShow()
        {
        }

        protected override void OnHide()
        {
        }

        protected override void OnClose()
        {
        }
        #endregion


        #region 辅助
        void UpdatePropertyItems()
        {
            UpdateValueText();
            UpdateSlider();
            UpdateAddButton();
            UpdateCostStarText();
        }

        private void UpdateAddButton()
        {
            foreach (var rect in _propertyItemLst)
            {
                string property = (rect.name).TrimName(TrimNameType.DashAfter).LowerFirstLetter();//attack,fireRate,life
                int planeID = this.GetModel<IAirCombatAppModel>().SelectPlaneID;
                string pre = planeID + property;
               //
                Button addBtn = rect.Find("Add").GetComponent<Button>();
                addBtn.onClick.RemoveAllListeners();//切换不同id就重置
                addBtn.onClick.AddListener(() =>
                {   
                    string costKey = KeysUtil.GetPropertyKeys(pre + PropertyItem.ItemKey.cost);
                    int cost = DataMgr.Single.Get<int>(costKey);
                                                                                    
                    bool can = this.SendCommand(new ChangeStarCommand( -cost) ); 
                    if (  can  )
                    {
                        this.SendCommand (new IncreasePlanePropertyCommand( property )  );
                    }
                });
            }
        }


        private void UpdateUpgradesText()
        {
            int planeID = this.GetModel<IAirCombatAppModel>().SelectPlaneID;
            int cueLevel = DataMgr.Single.Get<int>(planeID + DataKeys.LEVEL);
            string costKey = KeysUtil.GetPropertyKeys(planeID + DataKeys.UPGRADES + cueLevel);
            int cost = DataMgr.Single.Get<int>(costKey);
            //
            UpgradesBtn.SetButtonText(cost);
        }


        private void UpdateUpgradesButton()
        {

            #region 说明
            /**
            "planes": [
                "planeId": 0,
                  "level": 0,
                  "attackTime":1,
                  "attack": { "name":"攻击","value":5,"cost":180,"costUnit":"star","grouth":10,"maxVaue": 500},
                  "fireRate": { "name":"攻速","value":80,"cost":200,"costUnit":"star","grouth":1,"maxVaue": 100},
                  "life": { "name":"生命","value":100,"cost":200,"costUnit":"star","grouth":50,"maxVaue": 1000},
                  "upgrades": { "name":"升级","coefficient": 2,"max":4,"0": 100,"1": 200,"2": 300,"3": 400,"costUnit":"diamond"}
                        },
            **/
            #endregion

            int planeID = this.GetModel<IAirCombatAppModel>().SelectPlaneID;
            int cueLevel = DataMgr.Single.Get<int>(planeID + DataKeys.LEVEL);
            string costKey = KeysUtil.GetPropertyKeys(planeID + DataKeys.UPGRADES + cueLevel);
            int cost = DataMgr.Single.Get<int>(costKey);
            //
            UpdateUpgradesText();
            //
            UpgradesBtn.onClick.RemoveAllListeners();
            UpgradesBtn.onClick.AddListener(() =>
            {
                bool can = this.SendCommand(new ChangeDiamondCommand(-cost));
                if (can)
                {
                    this.SendCommand(new UpgradesPlaneLevelCommand());
                }
            });
        }

        private void UpdateCostStarText()
        {
            foreach (var rect in _propertyItemLst)
            {
                string property = (rect.name).TrimName(TrimNameType.DashAfter).LowerFirstLetter();//attack,fireRate,life
                int planeID = this.GetModel<IAirCombatAppModel>().SelectPlaneID;
                string pre = planeID + property;
                string key = KeysUtil.GetPropertyKeys(pre + PropertyItem.ItemKey.cost);
                int value = DataMgr.Single.Get<int>(key);
                //
                rect.Find("Cost").SetText(value);
            }
        }

        private void UpdateValueText()
        {


            #region 说明
            /*
               "planes": [
                {
                "planeId": 0,
                "level": 0,
                "attackTime":1,
                "attack": {"name":"攻击","value":5,"cost":180,"costUnit":"star","grouth":10,"maxVaue": 500},
                "fireRate": {"name":"攻速","value":80,"cost":200,"costUnit":"star","grouth":1,"maxVaue": 100},
                "life": {"name":"生命","value":100,"cost":200,"costUnit":"star","grouth":50,"maxVaue": 1000},
                "upgrades": {"name":"升级","coefficient": 2,"max":4,"0": 100,"1": 200,"2": 300,"3": 400,"costUnit":"diamond"}
                },
             */
            #endregion
            foreach (var rect in _propertyItemLst)
            {
                string property = (rect.name).TrimName(TrimNameType.DashAfter).LowerFirstLetter();//attack,fireRate,life
                int planeID = this.GetModel<IAirCombatAppModel>().SelectPlaneID;
                string valueKey = KeysUtil.GetPropertyKeys(planeID + property + PropertyItem.ItemKey.value);
                int value = DataMgr.Single.Get<int>(valueKey);
                //
                rect.Find("Value").SetText(value);
            }
        }

        void UpdatePlaneSprite()
        {
            int planeID = this.GetModel<IAirCombatAppModel>().SelectPlaneID;
            string levelKey = KeysUtil.GetPropertyKeys(planeID+DataKeys.LEVEL);
            int level=   DataMgr.Single.Get<int>(levelKey);
            Sprite planeSprite = PlaneSpritesModel.Single[planeID, level];
            IconImg.SetSprite(planeSprite);
        }


        void UpdateStarText()
        {
            int star = this.GetModel<IAirCombatAppModel>().Star;
            StarText.SetText(star);

        }

        void UpdateDiamondText()
        {
            int diamond = this.GetModel<IAirCombatAppModel>().Diamond;
            DiamondText.SetText(diamond);

        }


        /// <summary>改成根据父节点,去更新子节点</summary>    
        private void UpdateSlider()
        {
            foreach (var rect in _propertyItemLst)
            {
                int id = this.GetModel<IAirCombatAppModel>().SelectPlaneID;
                string property = (rect.name).TrimName(TrimNameType.DashAfter).LowerFirstLetter();//attack
                string pre = id + property;
                Slider slider = rect.Find("Slider").GetComponent<Slider>();
                string maxVaueKey = KeysUtil.GetPropertyKeys(pre + ItemKey.maxVaue); //0attackmaxVaue(原文没加l)
                string valueKey = KeysUtil.GetPropertyKeys(pre + ItemKey.value);//0attackvalue
                float maxValue=DataMgr.Single.Get<int>(maxVaueKey);
                float value= DataMgr.Single.Get<int>(valueKey);
                Debug.LogFormat("Slider:{0},{1}", valueKey, maxVaueKey);
                slider.minValue = 0;
                slider.maxValue = maxValue;
                slider.value = value;
                Debug.LogFormat("Slider:{0},{1},{2}", slider.minValue, slider.value, slider.maxValue);
            }
        }
        #endregion


        #region QF
        IArchitecture IBelongToArchitecture.GetArchitecture()
        {
            return AirCombatApp.Interface;
        }
        #endregion
    }
}

watch QF用到的主要方法

什么都不带的Command

带参数的Command

代返回值的Command

modify QF (增加的内容主要是Command)

总体框架是从PointGame(QF中的例子)中复制的

。。。

AirCombat_QFramework.All()汇总所有的"飞机大战"使用QF的方法,方便查找

。。。

主体是这个Command。Event是对Command后对应的操作。所以Event的命名很多对应这Command

csharp 复制代码
        void Command()
        {
            ChangeStarCommand increaseStarCommand;
            ChangeDiamondCommand increaseDiamondCommand;
            SetStarCommand setStarCommand;
            SetDiamondCommand setDiamondCommand;
            //
            IncreasePlanePropertyCommand increasePlanePropertyCommand;
            IncreasePlaneAttackCommand increasePlaneAttackCommand;
            IncreasePlaneFireRateCommand increasePlaneFireRateCommand;
            IncreasePlaneLifeCommand increasePlaneLifeCommand;
            UpgradesPlaneLevelCommand upgradesPlaneLevelCommand;
            //
            SetPlaneCommand initPlaneCommand;
            SwitchPlaneCommand changePlaneCommand;
            SwitchHeroCommand changeHeroCommand;
            //
            OpenStartGamePanelCommand openStartGamePanelCommand;
            OpenSelectHeroPanelCommand openSelectHeroCommand;
            OpenStrengthenPanelCommand openStrengthenPanel;
            ExitGameCommand exitGameCommand;
        }
csharp 复制代码
/****************************************************
    文件:QFramework_AirCombat.cs
	作者:lenovo
    邮箱: 
    日期:2023/8/8 20:53:33
	功能:
*****************************************************/

using JetBrains.Annotations;
using QFramework.Example;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Numerics;
using UnityEngine;
using static PropertyItem;
using Random = UnityEngine.Random;

namespace QFramework.AirCombat
{
    public class AirCombat_QFramework : MonoBehaviour ,IController
    {
        private void Start()
        {
        }
        void All()
        {   
            Architecture();
            //
            Model();
            System();
            Utility();
            Controller();
            //
            Command();
            Event();
            //
            Test();
        }
        #region 辅助 


        void Test()
        {
            if (Input.GetKeyDown(KeyCode.Q)) this.SendCommand(new ChangeStarCommand(100));
            if (Input.GetKeyDown(KeyCode.W)) this.SendCommand(new ChangeStarCommand(-100));
            if (Input.GetKeyDown(KeyCode.E)) this.SendCommand(new ChangeDiamondCommand(-100));
            if (Input.GetKeyDown(KeyCode.R)) this.SendCommand(new ChangeDiamondCommand(-100));
        }

        void Event()
        {
            ChangeStarEvent changeStarEvent;
            ChangeDiamondEvent changeDiamondEvent;
            //
            SelectHeroIDEvent switchHeroEvent;
            SelectPlaneIDEvent switchPlaneEvent;
            //
            IncreasePlanePropertyEvent increasePlanePropertyEvent;
            UpgradesPlaneLevelEvent upgradesPlaneLevelEvent; 
        }                      


        void Command()
        {
            ChangeStarCommand increaseStarCommand;
            ChangeDiamondCommand increaseDiamondCommand;
            SetStarCommand setStarCommand;
            SetDiamondCommand setDiamondCommand;
            //
            IncreasePlanePropertyCommand increasePlanePropertyCommand;
            IncreasePlaneAttackCommand increasePlaneAttackCommand;
            IncreasePlaneFireRateCommand increasePlaneFireRateCommand;
            IncreasePlaneLifeCommand increasePlaneLifeCommand;
            UpgradesPlaneLevelCommand upgradesPlaneLevelCommand;
            //
            SetPlaneCommand initPlaneCommand;
            SwitchPlaneCommand changePlaneCommand;
            SwitchHeroCommand changeHeroCommand;
            //
            OpenStartGamePanelCommand openStartGamePanelCommand;
            OpenSelectHeroPanelCommand openSelectHeroCommand;
            OpenStrengthenPanelCommand openStrengthenPanel;
            ExitGameCommand exitGameCommand;
        }


        void Model()
        {
            IModel iModel;
            AbstractModel abstractModel;
            IAirCombatAppModel iAirCombatAppModel;
            AirCombatAppModel airCombatAppModel;
            IPlaneModel iPlaneModel;
            PlaneModel planeModel;
        }

        void System()
        {
            ISystem iSystem;
            //IAchievementSystem iAchievementSystem;
            //AchievementSystem achievementSystem;

        }
        void Utility()
        {
            IUtility iIUtility;
            IStorage iStorage;
            PlayerPrefsStorage playerPrefsStorage;
            JsonStorage storage;

        }

        void Controller()
        {
            AirCombatAppController airCombatAppController;
        }

        void Architecture()
        {
            AirCombatApp counterApp;
        }

        public IArchitecture GetArchitecture()
        {
            return AirCombatApp.Interface;
        }



        #endregion

    }






    #region Model

    internal class PlaneModel
    {
    }

    internal interface IPlaneModel
    {
    }

    // 1. 定义一个 Model 对象
    public interface IAirCombatAppModel : IModel
    {
        BindableProperty<int> Star { get; }
        BindableProperty<int> Diamond { get; }
        BindableProperty<int> SelectPlaneID { get; }
        BindableProperty<int> SelectHeroID { get; }
    }
    public class AirCombatAppModel : AbstractModel, IAirCombatAppModel
    {
        public BindableProperty<int> Star { get; } = new BindableProperty<int>();
        public BindableProperty<int> Diamond { get; } = new BindableProperty<int>();
        public BindableProperty<int> SelectHeroID { get; } = new BindableProperty<int>();
        public BindableProperty<int> SelectPlaneID { get; } = new BindableProperty<int>();


        protected override void OnInit()
        {
            IStorage storage = this.GetUtility<IStorage>();

            // 设置初始值(不触发事件)
            Star.SetValueWithoutEvent(storage.LoadInt(nameof(Star)));
            Diamond.SetValueWithoutEvent(storage.LoadInt(nameof(Diamond)));
            SelectHeroID.SetValueWithoutEvent(storage.LoadInt(nameof(SelectHeroID)));
            SelectPlaneID.SetValueWithoutEvent(storage.LoadInt(nameof(SelectPlaneID)));

            // 当数据变更时 存储数据
            Star.Register(newCount =>
            {
                storage.SaveInt(nameof(Star), newCount);
            });
            Diamond.Register(newCount =>
            {
                storage.SaveInt(nameof(Diamond), newCount);
            });
            SelectHeroID.Register(newCount =>
            {
                storage.SaveInt(nameof(SelectHeroID), newCount);
            });
            SelectPlaneID.Register(newCount =>
            {
                storage.SaveInt(nameof(SelectPlaneID), newCount);
            });
        }

    }

    #endregion


    #region Command
    // 引入 Command
    public class ChangeStarCommand : AbstractCommand<bool>
    {
        int _change = 0;

        public ChangeStarCommand(int change)
        {
            _change = change;
        }



        protected override bool OnExecute()
        {
            var model = this.GetModel<IAirCombatAppModel>();
            int cur = this.GetModel<IAirCombatAppModel>().Star;
            int after = cur + _change;

            if (after >= 0 )//有钱&&上升空间
            {
                this.GetModel<IAirCombatAppModel>().Star.Value = after;     
                this.SendEvent<ChangeStarEvent>();
                "ChangeStarCommand".LogInfo();
                return true;
            }
            else
            {
                string str = "你没星星了!";
                str += "需要" + _change;
                str.LogInfo();
                // UIManager.Single.ShowDialog("你没星星了!");
                return false;
            }
        }
    }


    public class SetStarCommand : AbstractCommand
    {
        int _after = 0;

        public SetStarCommand(int after)
        {
            _after = after;
        }
        protected override void OnExecute()
        {
            this.GetModel<IAirCombatAppModel>().Star.Value = _after;
            this.SendEvent<ChangeStarEvent>();
        }
    }

    public class SetDiamondCommand : AbstractCommand
    {
        int _after = 0;


        public SetDiamondCommand(int change)
        {
            _after = change;
        }
        protected override void OnExecute()
        {
            this.GetModel<IAirCombatAppModel>().Diamond.Value = _after;
            this.SendEvent<ChangeDiamondEvent>();
        }
    }
                                                        
    public class ChangeDiamondCommand : AbstractCommand<bool>
    {
        int _change = 0;


        /// <summary>增加正,减少负</summary>
        public ChangeDiamondCommand(int change)
        {
            _change = change;
        }

        protected override bool OnExecute()
        {
            var model = this.GetModel<IAirCombatAppModel>();
            int cur = model.Diamond;
            int after = cur + _change;

            if (after >= 0)//有钱&&上升空间
            {
                model.Diamond.Value = after;
                this.SendEvent<ChangeDiamondEvent>();
                return true;
            }
            else
            {
                string str = "你没钻石了!";
                str += "需要" + _change;
                str.LogInfo();
                // UIManager.Single.ShowDialog("你没星星了!");
                return false;
            }
        }
    }

    public class IncreasePlanePropertyCommand : AbstractCommand
    {
        PlaneProperty.Property _planeProperty;

        public IncreasePlanePropertyCommand(PlaneProperty.Property planeProperty)
        {
            _planeProperty = planeProperty;
        }

        public IncreasePlanePropertyCommand(string planePropertyString)
        {
            _planeProperty = planePropertyString.String2Enum<PlaneProperty.Property>();
        }

        protected override void OnExecute()
        {
            string planeID = this.GetModel<IAirCombatAppModel>().SelectPlaneID.ToString();
            string property = "";
            switch (_planeProperty)
            {
                case PlaneProperty.Property.attack:
                    {
                        property  = DataKeys.ATTACK;
                    }
                    break;
                case PlaneProperty.Property.fireRate:
                    {
                        property  = DataKeys.FIRE_RATE;
                    }
                    break;
                case PlaneProperty.Property.life:
                    {
                        property  = DataKeys.LIFE;
                    }
                    break;
                default: break;
            }
            //
            string maxKey = KeysUtil.GetPropertyKeys(planeID + property + PropertyItem.ItemKey.maxVaue);
            string curKey = KeysUtil.GetPropertyKeys(planeID + property + PropertyItem.ItemKey.value);
            string grouthKey = KeysUtil.GetPropertyKeys(planeID + property + PropertyItem.ItemKey.grouth);
            int max = DataMgr.Single.Get<int>(maxKey);
            int cur = DataMgr.Single.Get<int>(curKey);
            int grouth = DataMgr.Single.Get<int>(grouthKey);
            int after = cur + grouth;


            if ( cur < max)//有上升空间
            {
                if (after >= max)//到顶了
                {                                                                  
                    after = max;
                }

                string valueKey = KeysUtil.GetPropertyKeys( planeID + property + ItemKey.value);
                DataMgr.Single.Set<int>(valueKey, after);

                Debug.LogFormat("IncreasePlanePropertyCommand {0}:{1} ",valueKey, after);
                this.SendEvent<IncreasePlanePropertyEvent>();
            }
            else
            {
                ("数值到顶了!").LogInfo();
                // UIManager.Single.ShowDialog("你没星星了!");
            }


        }
    }
    public class IncreasePlaneAttackCommand : AbstractCommand
    {
        int _change;

        public IncreasePlaneAttackCommand(int change)
        {
            _change = change;
        }

        protected override void OnExecute()
        {
            int id = this.GetModel<IAirCombatAppModel>().SelectPlaneID;
            string pre = id + DataKeys.ATTACK;
            //
            //
            int max = DataMgr.Single.Get<int>(pre + ItemKey.maxVaue);
            int cur = DataMgr.Single.Get<int>(pre + ItemKey.value);
            int after = cur+_change;


            if ( after >= 0 && cur < max)//有上升空间
            {
                if (after >= max)//到顶了
                {
                    after = max;
                }

                DataMgr.Single.SetObject(pre + ItemKey.value, after);
            }
            else
            {
                ("数值到顶了!").LogInfo();
                // UIManager.Single.ShowDialog("你没星星了!");
            }

        }
    }
    public class IncreasePlaneFireRateCommand : AbstractCommand
    {
        int _value;

        public IncreasePlaneFireRateCommand(int value)
        {
            _value = value;
        }

        protected override void OnExecute()
        {

       
        }
    }
    public class IncreasePlaneLifeCommand : AbstractCommand
    {
        int _value;

        public IncreasePlaneLifeCommand(int value)
        {
            _value = value;
        }

        protected override void OnExecute()
        {

          
        }
    }


    /// <summary>直接升级(费用判断另作处理)</summary>
    public class UpgradesPlaneLevelCommand : AbstractCommand
    {
        protected override void OnExecute()
        {
            ChangeLevel();    
            this.SendEvent<UpgradesPlaneLevelEvent>();
            ChangeData();
            this.SendEvent<IncreasePlanePropertyEvent>();
            //
          
        }

        private void ChangeData()
        {               
            //获取升级系数,修改数据
            var ratioKey = KeysUtil.GetPropertyKeysWithoutPlaneId(DataKeys.UPGRADES_RATIO);
            var ratio = DataMgr.Single.Get<int>(ratioKey);

            ChangeData(ratio, PropertyItem.ItemKey.value, PlaneProperty.Property.attack);
            ChangeData(ratio, PropertyItem.ItemKey.maxVaue, PlaneProperty.Property.attack);
            ChangeData(ratio, PropertyItem.ItemKey.grouth, PlaneProperty.Property.attack);
            //
            ChangeData(ratio, PropertyItem.ItemKey.value, PlaneProperty.Property.life);
            ChangeData(ratio, PropertyItem.ItemKey.maxVaue, PlaneProperty.Property.life);
            ChangeData(ratio, PropertyItem.ItemKey.grouth, PlaneProperty.Property.life);
        }

        void ChangeLevel()
        {
            int planeID = this.GetModel<IAirCombatAppModel>().SelectPlaneID;
            string curKey = KeysUtil.GetPropertyKeys(planeID + DataKeys.LEVEL);
            string maxKey = KeysUtil.GetPropertyKeys(planeID + DataKeys.LEVEL_MAX);
            int cur = DataMgr.Single.Get<int>(curKey);
            int max = DataMgr.Single.Get<int>(maxKey);
            int after = cur + 1;
            after = after.Clamp(0, max - 1);
            DataMgr.Single.SetObject(curKey, after);
        }


        /// <summary>暂时设定为 grouth和max乘以系数</summary>
        private void ChangeData(int ratio, PropertyItem.ItemKey itemKey, PlaneProperty.Property property)
        {
            int planeID = this.GetModel<IAirCombatAppModel>().SelectPlaneID;
            var key = KeysUtil.GetPropertyKeys(planeID + property.ToString() + itemKey);
            var value = DataMgr.Single.Get<int>(key);
            value *= ratio;
            DataMgr.Single.Set(key, value);
        }
    }

    public class SwitchHeroCommand : AbstractCommand
    {
        int _heroID = 0;

        public SwitchHeroCommand(int heroID)
        {
            _heroID = heroID;
        }

        protected override void OnExecute()
        {
            this.GetModel<AirCombatAppModel>().SelectHeroID.Value = _heroID;
            this.SendEvent<SelectHeroIDEvent>();

        }
    }

    public class SwitchPlaneCommand : AbstractCommand
    {
        int _change = 0;

        public SwitchPlaneCommand(int change)
        {
            _change = change;
        }

        protected override void OnExecute()
        {
            if (_change != 1 && _change != -1) //飞机的切换是顺序的
            {
                return;
            }
            int max = PlaneSpritesModel.Single.Count;
            int id = this.GetModel<IAirCombatAppModel>().SelectPlaneID;
            id += _change;                             
            id = id.Clamp(0, max - 1);
            // id = (id < 0 ) ? 0 : id >= max ? id = max - 1 : id;

            //
            string str = (_change == -1) ? "左" : "右";

            (str + "键:" + id).LogInfo();
            //
            this.GetModel<IAirCombatAppModel>().SelectPlaneID.Value = id;
            this.SendEvent<SelectPlaneIDEvent>();
        }
    }

    public class SetPlaneCommand : AbstractCommand
    {
        int _planeID = 0;

        public SetPlaneCommand(int planeID)
        {
            _planeID = planeID;
        }

        protected override void OnExecute()
        {
            int max = PlaneSpritesModel.Single.Count;
            if (_planeID < 0 || _planeID > max-1) 
            {
                return;
            }
            //
            this.GetModel<IAirCombatAppModel>().SelectPlaneID.Value = _planeID;
            GameStateModel.Single.SelectedPlaneId = _planeID;
            this.SendEvent<SelectPlaneIDEvent>();
        }
    }

    public class OpenSelectHeroPanelCommand : AbstractCommand
    {

        protected override void OnExecute()
        {
            UIKit.OpenPanel<SelectHeroPanel>();
        }
    }

    public class OpenStartGamePanelCommand : AbstractCommand
    {
        protected override void OnExecute()
        {
            UIKit.OpenPanel<StartGamePanel>();
        }
    }

    public class OpenStrengthenPanelCommand : AbstractCommand
    {
        Action _onComplete;

        /// <summary>比如设置了金币,但是对金币的监听是在打开面板是才注册的,所以onComplete才好触发</summary>
        public OpenStrengthenPanelCommand(Action onComplete)
        {
            _onComplete = onComplete;
        }

        public OpenStrengthenPanelCommand()
        {
        }

        protected override void OnExecute()
        {
            //这块是上个界面,选择飞机的界面来做的(如果再直接打开升级界面邮箱使用返回按钮的情况下可以用) 
            UIKit.OpenPanel<SelectHeroPanel>();
            UIKit.Stack.Push<SelectHeroPanel>();
            UIKit.HidePanel<SelectHeroPanel>();
            //UIManager.Single.Show(Paths.PREFAB_STRENGTHEN_VIEW);
            UIKit.OpenPanel<StrengthenPanel>();
            "打开升级面板".LogInfo();
            //
            _onComplete.DoIfNotNull();
        }
    }

    public class ExitGameCommand : AbstractCommand
    {
        protected override void OnExecute()
        {
            Application.Quit();
            "退出游戏".LogInfo();
        }
    }
    #endregion


    #region Utility

    public interface IStorage : IUtility
    {
        void SaveInt(string key, int value);
        int LoadInt(string key, int defaultValue = 0);
        void SaveString(string key, string value);
        string LoadString(string key, string defaultValue = "");
        void SaveFloat(string key, float value);
        float LoadFloat(string key, float defaultValue = 0.0f);
    }

    public class PlayerPrefsStorage : IStorage
    {
        public void SaveInt(string key, int value)
        {
            PlayerPrefs.SetInt(key, value);
        }

        public int LoadInt(string key, int defaultValue = 0)
        {
            return PlayerPrefs.GetInt(key, defaultValue);
        }
        public void SaveString(string key, string value)
        {
            PlayerPrefs.SetString(key, value);
        }

        public string LoadString(string key, string defaultValue = "")
        {
            return PlayerPrefs.GetString(key, defaultValue);
        }
        public void SaveFloat(string key, float value)
        {
            PlayerPrefs.SetFloat(key, value);
        }

        public float LoadFloat(string key, float defaultValue = 0.0f)
        {
            return PlayerPrefs.GetFloat(key, defaultValue);
        }
    }
    public class JsonStorage : IStorage
    {
        public void SaveInt(string key, int value)
        {
            PlayerPrefs.SetInt(key, value);
        }

        public int LoadInt(string key, int defaultValue = 0)
        {
            return PlayerPrefs.GetInt(key, defaultValue);
        }
        public void SaveString(string key, string value)
        {
            PlayerPrefs.SetString(key, value);
        }

        public string LoadString(string key, string defaultValue = "")
        {
            return PlayerPrefs.GetString(key, defaultValue);
        }
        public void SaveFloat(string key, float value)
        {
            PlayerPrefs.SetFloat(key, value);
        }

        public float LoadFloat(string key, float defaultValue = 0.0f)
        {
            return PlayerPrefs.GetFloat(key, defaultValue);
        }
    }

    #endregion



    #region Event

    public class UpgradesPlaneLevelEvent
    { 
    
    }
    public class IncreasePlanePropertyEvent
    {

    }

    public class ChangeStarEvent
    {

    }
    public class ChangeDiamondEvent
    {

    }

    public class SelectHeroIDEvent
    {

    }

    public class SelectPlaneIDEvent
    {

    }
    #endregion


    #region Architecture
    public class AirCombatApp : Architecture<AirCombatApp>
    {
        protected override void Init()
        {
            // 注册 System 
            //this.RegisterSystem<IAchievementSystem>(new AchievementSystem());

            // 注册 Model
            this.RegisterModel<IAirCombatAppModel>(new AirCombatAppModel());

            // 注册存储工具的对象
            this.RegisterUtility<IStorage>(new PlayerPrefsStorage());
            this.RegisterUtility<IStorage>(new JsonStorage());
        }


        protected override void ExecuteCommand(ICommand command)
        {
            Debug.Log("Before " + command.GetType().Name + "Execute");
            base.ExecuteCommand(command);
            Debug.Log("After " + command.GetType().Name + "Execute");
        }

        protected override TResult ExecuteCommand<TResult>(ICommand<TResult> command)
        {
            Debug.Log("Before " + command.GetType().Name + "Execute");
            var result = base.ExecuteCommand(command);
            Debug.Log("After " + command.GetType().Name + "Execute");
            return result;
        }
    }

    #endregion


}

---------------------------------------------------------

05 Scene 关卡选择

watch 原本的样式

4列乘以6行

这里乱了,是因为原始Panel拖到了QF的UIRoot,还没调整

modify 关卡选择面板

modify 关卡选择面板,关卡子项,加载面板

同上,创建预制体(复制旧的)

添加Bind

标记QF ABMark和New AB名字(_prefab的格式)

CreateUICode

modify 关卡选择面板

打开面板就发送实例Command(将操作从面板中分离开)

csharp 复制代码
using UnityEngine;
using UnityEngine.UI;
using QFramework;

namespace QFramework.AirCombat
{
	public class SelectLevelPanelData : UIPanelData
	{
	}
	public partial class SelectLevelPanel : UIPanel,IController
	{
		protected override void OnInit(IUIData uiData = null)
		{
			mData = uiData as SelectLevelPanelData ?? new SelectLevelPanelData();
            // please add init code here
            //LevelsTrans.transform ;

            //LevelRoot levelRoot;
            //LevelRootController levelRootController;
            //LevelsView levelsView;
            //LevelsController levelsController;
            //LevelItem levelItem;
            //LevelItemController levelItemController;
			this.SendCommand( new InstantiateLevelSelectableCommand(LevelsTrans));

			this.RegisterEvent<OpenLoadingPanelEvent>(_ => 
			{ 
				CloseSelf(); 
			});
													
			#region Bottom
			BackBtn.onClick.AddListenerAfterRemoveAll(() => 
			{
				UIKit.Back<SelectHeroPanel>();
				CloseSelf();
			});
			#endregion

		}


		#region 重写 其它
		protected override void OnOpen(IUIData uiData = null)
		{
		}
		
		protected override void OnShow()
		{
		}
		
		protected override void OnHide()
		{
		}
		
		protected override void OnClose()
		{
		}

        public IArchitecture GetArchitecture()
        {
			return AirCombatApp.Interface;
        }
        #endregion
    }
}

modify 实例关卡子项

这里看到Andy用了很多脚本。

所以先直接有用QF的ResLoader来达成效果

csharp 复制代码
        void ReaderConfig()
        {
            ReaderConfig readerConfig;
            ReaderMgr readerMgr;
            string str=Paths.CONFIG_LEVEL_CONFIG;
            //
            ILoader iLoader;
            ResourceLoader resourceLoader;
            LoadMgr loadMgr;
        }

。。。

_levelsTrans.LoadAndInstantiateGameObject("LevelSelectable",24);是自定义的拓展,看下一 #

csharp 复制代码
    public class InstantiateLevelSelectableCommand : AbstractCommand
    {
         RectTransform _levelsTrans;


        public InstantiateLevelSelectableCommand(RectTransform levelsTrans)
        {
            _levelsTrans = levelsTrans;
        }

        protected override void OnExecute()
        {

            #region Level
            /*
            {
              "levelCount": 24,
              "eachRow": 4
            }             
            */
            #endregion
            //原本用到的脚本,写方便切换看
            { 
                LevelRoot levelRoot;
                LevelRootController levelRootController;
                LevelsView levelsView;
                LevelsController levelsController;
                LevelItem levelItem;
                LevelItemController levelItemController;            
            }


            //数量和行列
            { 
            IReader reader = ReaderMgr.Single.GetReader(Paths.CONFIG_LEVEL_CONFIG);
           // int levelCount=  reader[LevelRoot.Name.levelCount.Enum2String()].Get<>;
            
            }
            //实例
            {
                //GameObject go = LoadMgr.Single.LoadPrefabAndInstantiate(Paths.PREFAB_LEVEL_ITEM, _levelsTrans);
                 _levelsTrans.LoadAndInstantiateGameObject("LevelSelectable",24);
            
            }
            // TODO 组件
            { 
                foreach (Transform trans in _levelsTrans)
                {
                    trans.AddComponent<LevelItem>();
                    trans.AddComponent<LevelItemController>();
                }            
            }
        }
    }

modify 关卡子项排列

csharp 复制代码
    public class InstantiateLevelSelectableCommand : AbstractCommand
    {
         RectTransform _levelsTrans;


        public InstantiateLevelSelectableCommand(RectTransform levelsTrans)
        {
            _levelsTrans = levelsTrans;
        }

        protected override void OnExecute()
        {

            #region Level
            /*
            {
              "levelCount": 24,
              "eachRow": 4
            }             
            */
            #endregion
            //原本用到的脚本
            { 

                LevelsView levelsView;
                LevelsController levelsController;               
                LevelRoot levelRoot;
                LevelRootController levelRootController;
                LevelItem levelItem;
                LevelItemController levelItemController;            
            }


            //数量和行列
            { 
            IReader reader = ReaderMgr.Single.GetReader(Paths.CONFIG_LEVEL_CONFIG);
           // int levelCount=  reader[LevelRoot.Name.levelCount.Enum2String()].Get<>;
            
            }

            int levelCount = 24;
            int eachRow = 4; //一行多少个
            //实例
            {
                //GameObject go = LoadMgr.Single.LoadPrefabAndInstantiate(Paths.PREFAB_LEVEL_ITEM, _levelsTrans);

                _levelsTrans.LoadAndInstantiateGameObject("LevelSelectable", levelCount);
            }
            //排列
            { 
                for (int idx = 0; idx < levelCount; idx++)
                {
                    Transform levelSelectable = _levelsTrans.GetChild(idx);
                    levelSelectable.name = "LevelSelectable_"+idx;
                    SetPos(levelSelectable, GetGrid(idx, eachRow)) ;
                }              
            }
            // TODO 组件
            { 
                foreach (Transform trans in _levelsTrans)
                {
                    trans.AddComponent<LevelItem>();
                    trans.AddComponent<LevelItemController>();
                }            
            }
        }


        #region 辅助 设置位置
        /// <summary>行,6行4列</summary>
        private Vector2 GetGrid(int id,int eachRow)
        {
            var y = id / eachRow; //id/4第几行 
            var x = id % eachRow; //id%4第几个
            return new Vector2(x, y);
        }

        private void SetPos( Transform t,Vector2 gridId)
        {
            int _leftOffet = 50; //上下行左右错开的值
            int _lineSpacing = 20;
            int _offset = 10;
            //
            var width = t.Rect().rect.width * t.localScale.x;
            var height = t.Rect().rect.height * t.localScale.y;

            var indention = gridId.y % 2 == 0 ? _leftOffet : 0;

            var x = indention + width * 0.5f + (_offset + width) * gridId.x;
            var y = height * 0.5f + (_lineSpacing + height) * gridId.y;
            t.Rect().anchoredPosition = new Vector2(x, -y);
        }

        #endregion
    }

modify 关卡子项解锁状态

关卡子项选择QF的UI生成代码,为了注册生成关卡子项时的,名字、位置和解锁状态

效果

LevelSelectable

csharp 复制代码
using UnityEngine;
using UnityEngine.UI;
using QFramework;
using QFramework.AirCombat;
using UnityEngine.Purchasing;

namespace QFramework.AirCombat
{
	public class LevelSelectableData : UIPanelData
	{
	}
	public partial class LevelSelectable : UIPanel, IController
    {
		public void Init()
		{
            int cur = transform.GetSiblingIndex();
			{ 
				gameObject.name = "LevelSelectable_" + (cur);
				LevelText.SetText(cur+1);//现实的话不从0开始
				EnterBtn.onClick.AddListenerAfterRemoveAll(() =>
				{
					Debug.Log("LevelSelectable 进入关卡" + cur);
					this.SendCommand(new OpenLoadingPanelCommand(transform));
				});
				MaskBtn.onClick.AddListenerAfterRemoveAll(() =>
				{
					Debug.Log("LevelSelectable 当前关卡未开放");
					// UIManager.Single.ShowDialog("当前关卡未开放");
				});			
			}
            //
            this.RegisterEvent<UpdateLevelSelectableLockStateEvent>(eventId =>
			{
				Debug.Log("LevelSelectable RegisterEvent<InstantiateLevelSelectableEvent>");
				//能挑战已经通关的下一关
				int tar = this.GetModel<IAirCombatAppModel>().PassedLevel + 1;
                Debug.Log("LevelSelectable tar "+ tar );

                bool can = (cur <= tar);
				if (can)
				{
					MaskBtn.Hide();
				}
				else
				{
					MaskBtn.Show();
				}
			});
		}



		#region 重写实现 
		protected override void OnInit(IUIData uiData = null)
		{
			mData = uiData as LevelSelectableData ?? new LevelSelectableData();
		}

		protected override void OnOpen(IUIData uiData = null)
		{
		}
		
		protected override void OnShow()
		{
		}
		
		protected override void OnHide()
		{
		}
		
		protected override void OnClose()
		{
		}

        public IArchitecture GetArchitecture()
        {
			return AirCombatApp.Interface;
        }
		#endregion

    }
}

InstantiateLevelSelectableCommand

csharp 复制代码
    public class InstantiateLevelSelectableCommand : AbstractCommand
    {
         RectTransform _levelsTrans;


        public InstantiateLevelSelectableCommand(RectTransform levelsTrans)
        {
            _levelsTrans = levelsTrans;
        }

        protected override void OnExecute()
        {

            #region Level
            /*
            {
              "levelCount": 24,
              "eachRow": 4
            }             
            */
            #endregion
            //原本用到的脚本
            { 

                LevelsView levelsView;
                LevelsController levelsController;               
                LevelRoot levelRoot;
                LevelRootController levelRootController;
                LevelItem levelItem;
                LevelItemController levelItemController;            
            }


            //数量和行列
            { 
            IReader reader = ReaderMgr.Single.GetReader(Paths.CONFIG_LEVEL_CONFIG);
           // int levelCount=  reader[LevelRoot.Name.levelCount.Enum2String()].Get<>;
            
            }

            int levelCount = 24;
            int eachRow = 4; //一行多少个
            //实例
            {
                //GameObject go = LoadMgr.Single.LoadPrefabAndInstantiate(Paths.PREFAB_LEVEL_ITEM, _levelsTrans);

                _levelsTrans.LoadAndInstantiateGameObject("LevelSelectable", levelCount);
            }
            //排列
            { 
                for (int idx = 0; idx < levelCount; idx++)
                {
                    Transform levelSelectable = _levelsTrans.GetChild(idx);
                    SetPos(levelSelectable, GetGrid(idx, eachRow)) ;
                }
              
            }
            // TODO 组件
            {
                for (int idx = 0; idx < levelCount; idx++)
                {
                    Transform levelSelectable = _levelsTrans.GetChild(idx);
                    levelSelectable.GetComponent<LevelSelectable>().Init();
                }
            }     
            this.SendEvent<UpdateLevelSelectableLockStateEvent>();
        }


        #region 辅助 设置位置
        /// <summary>行,6行4列</summary>
        private Vector2 GetGrid(int id,int eachRow)
        {
            var y = id / eachRow; //id/4第几行 
            var x = id % eachRow; //id%4第几个
            return new Vector2(x, y);
        }

        private void SetPos( Transform t,Vector2 gridId)
        {
            int _leftOffet = 50; //上下行左右错开的值
            int _lineSpacing = 20;
            int _offset = 10;
            //
            var width = t.Rect().rect.width * t.localScale.x;
            var height = t.Rect().rect.height * t.localScale.y;

            var indention = gridId.y % 2 == 0 ? _leftOffet : 0;

            var x = indention + width * 0.5f + (_offset + width) * gridId.x;
            var y = height * 0.5f + (_lineSpacing + height) * gridId.y;
            t.Rect().anchoredPosition = new Vector2(x, -y);
        }

        #endregion
    }

stars 拓展 QF的加载和示例

这种拓展用到了很多QF的脚本,建议直接放在QF的文件夹下

csharp 复制代码
namespace QFramework
{
    public static class ExtndQFramework
    {
        public static void Example(Transform parent)
        {
            LoadAndInstantiateGameObject("WhiteChess", parent);
        }

        public static GameObject LoadAndInstantiateGameObject(string likeWhiteChess
            ,Transform parent)
        {
            //在扫雷案例中测试的,用到WhiteChess
            ResKit.Init();
            ResLoader loader = ResLoader.Allocate();
            GameObject prefab = loader.LoadSync<GameObject>(likeWhiteChess);
            GameObject   go= GameObject.Instantiate(prefab, parent);
            return go;

        }

        public static Transform LoadAndInstantiateGameObject(this Transform parent
            , string likeWhiteChess
            , int count)
        {
            //在扫雷案例中测试的,用到WhiteChess
            ResKit.Init();
            ResLoader loader = ResLoader.Allocate();
            GameObject prefab = loader.LoadSync<GameObject>(likeWhiteChess);
            for (int i = 0; i < count; i++)
            {
                GameObject go = GameObject.Instantiate(prefab, parent);
            }
            return parent;

        }

        public static GameObject LoadGameObject(this string likeWhiteChess )
        {
            //在扫雷案例中测试的,用到WhiteChess
            ResKit.Init();
            ResLoader loader = ResLoader.Allocate();
            GameObject go = loader.LoadSync<GameObject>(likeWhiteChess);
            return go;

        }
    }
}

Modify 加载界面的进度条,加载完打开游戏界面

watch 效果

写死的时间是有点长,但不长看不出效果

stars 进度条

csharp 复制代码
    #region Smooth、Slider
    /// <summary>
    /// 以前看教程收集的一个例子
    /// </summary>
    /// <param name="mono">协程Mono</param>
    /// <param name="slider">滑动条</param>
    /// <param name="step">步进值</param>
    /// <param name="stepTime">步进时间</param>
    /// <param name="stepSmooth">步进平滑值</param>
    /// <exception cref="System.Exception"></exception>
    public static void PlayPrgSmooth(this MonoBehaviour mono
    , Slider slider
    , Text text
    , float step = 0.1f
    , float stepTime = 0.5f
    , float stepSmooth = 0.01f
    , Action onCompleted=null)
    {
        if (step <= stepSmooth)
        {
            throw new System.Exception("Prg step <= smooth异常");
        }
        mono.StartCoroutine(OnValueChangeSmooth(slider,text, step, stepTime, stepSmooth, onCompleted));


    }

    /// <summary>平滑点</summary>
    static IEnumerator OnValueChangeSmooth(Slider slider
       , Text text
       , float step
       , float stepTime
       , float stepSmooth
       , Action onCompleted)
    {
        slider.value = 0f;
        text.SetText(0.00f);
        //
        float process = 0f;
        while (process < 1f)
        {
            process += step;
            //类似帧循环,快点
            yield return new WaitUntil(() =>
            {
                float prg= Mathf.SmoothStep(from: slider.value
                    , to: process
                    , stepSmooth);
                text.SetText((prg).ToString("0.00%") ); //感觉要乘以100.0f,但不乘就有想要的效果
                slider.value = prg;
                return process - slider.value < stepSmooth;
            });

            yield return new WaitForSeconds(stepTime);//每0.5s会执行一遍循环体

        }
        slider.value = 1.0f;
        text.SetText("100.00%");
        onCompleted.DoIfNotNull();
    }
    #endregion

modify 打开加载界面时OpenLoadingPanelCommand

csharp 复制代码
    public class OpenLoadingPanelCommand : AbstractCommand
    {


        protected override void OnExecute()
        {
            this.SendEvent<OpenLoadingPanelEvent>();//关闭关卡选择界面会注册事件
            //TODO 设置场景状态
            LoadingPanel panel= UIKit.OpenPanel<LoadingPanel>();
            panel.PlayPrgSmooth(panel.Slider
             , panel.PrgText
             , 0.2f
             , 0.5f
             , 0.05f
             , () => {
                 this.SendCommand<OpenGamePanelCommand>();
                 this.SendEvent<CloseLoadingPanelEvent>();//关闭加载界面会注册事件 
             });
        }
    }

modify TODO 打开游戏界面 OpenGamePanelCommand

csharp 复制代码
    public class OpenGamePanelCommand : AbstractCommand
    {
        protected override void OnExecute()
        {

            Debug.Log("OpenGamePanelCommand");
        }
    }
相关推荐
Envyᥫᩣ2 小时前
C#语言:从入门到精通
开发语言·c#
晨欣5 小时前
Elasticsearch和Lucene之间是什么关系?(ChatGPT回答)
elasticsearch·chatgpt·lucene
小春熙子6 小时前
Unity图形学之Shader结构
unity·游戏引擎·技术美术
Footprint_Analytics7 小时前
Footprint Analytics 助力 Sei 游戏生态增长
游戏·web3·区块链
IT技术分享社区8 小时前
C#实战:使用腾讯云识别服务轻松提取火车票信息
开发语言·c#·云计算·腾讯云·共识算法
Sitarrrr9 小时前
【Unity】ScriptableObject的应用和3D物体跟随鼠标移动:鼠标放置物体在场景中
3d·unity
极梦网络无忧9 小时前
Unity中IK动画与布偶死亡动画切换的实现
unity·游戏引擎·lucene
半盏茶香12 小时前
【C语言】分支和循环详解(下)猜数字游戏
c语言·开发语言·c++·算法·游戏
△曉風殘月〆15 小时前
WPF MVVM入门系列教程(二、依赖属性)
c#·wpf·mvvm
逐·風17 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#