用QFramework重构飞机大战(Siki Andy的)(下01)(06-0? 游戏界面及之后的所有面板)

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

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

06 Scene 战斗

modify ItemCDEffect

stars SetPos、SetScale提取到拓展类

类似的可以延伸 (QF有类似的,为了链式编程没有加Set)
SetPosX
SetPosY
SetPosZ
SetPosXY
SetPosXZ
SetPosYZ
SetLocalPosX
SetLocalPosY
SetLocalPosZ
SetLocalPosXY
SetLocalPosXZ
SetLocalPosYZ
在这里插入图片描述

    public static T SetPos<T>(this T t, Vector3 pos) where T:Component
    {
        t.transform.position = pos;
        return t;
    }

    public static T SetScale<T>(this T t, Vector3 scale) where T : Component
    {
        t.transform.localScale = scale;
        return t;
    }

在这里插入图片描述

bug there is no implicit conversion between ‘method group’ and ‘method group’

在这里插入图片描述

在这里插入图片描述

stars DoIfNotNull

TaskQueue中出现多次含有两个参数的Action

    public void Execute(Action<object[]> complete)
    {
        _onComplete = complete;
        _values = new object[_tasks.Count];

        while (_tasks.Count > 0)
        {
            _id++;
            var task = _tasks.Dequeue();
            task.DoIfNotNull(this, _id);
        }

        ResetData();
    }
    public static Action<T1,T2> DoIfNotNull<T1, T2>(this Action<T1, T2> cb, T1 t1,T2 t2)
    {
        if (cb != null)
        {
            cb(t1,t2);
        }
        return cb;
    }

bug 未能实例出面板

回去打开初始工程看情况。
发现 第四季的到这都报了错,在LaunchGame中注释掉 新手引导Mgr的运行,能正常运行

在这里插入图片描述

watch 这里跳到前面的朴素分类法进行分解

最终得到Game场景的脚本分布
在这里插入图片描述

        }
        void All()
        {   
			......
            //
            {    //PoolMgr
                {
                    BulletPool();
                    PlanePool_Null();
                    ItemPool_Null();
                    FrameAniPool_Null();
                    MissilePool_Null ();
                    LightPool_Null();
                }
                //Game游戏中
                { 
                    GameRoot gameRoot;
                    GameLayerMgr gameLayerMgr;
                    GameEvent gameEvent;
                    {
                        MainCamera();
                        Map();
                        Effect();
                        PlaneEnemy();
                    }
                }
                {//DontDestroyOnLoad 
                    Mgr();
                    Canvas();
                }
            }
        }

        private void Canvas()
        {
            //GameUI
            {
                GameUIView gameUIView;
                GameUIController gameUIController;
                UiUtil uiUtil;
                //
                Life life;
                //
                Shield shield;
                ShieldController shieldController;
                ItemEffect itemEffect;
                ItemCDEffect itemCDEffect;
                //
                Power power;
                PowerController powerController;
            }
            //GameResultView
            {
                GameResultController gameResultController;
                GameResultView gameResultView;
                UiUtil uiUtil; 
                Empty4Raycast empty4Raycast;
            }
        }

        private void Mgr()
        {
            CoroutineMgr coroutineMgr;
            LifeCycleMgr lifeCycleMgr;
            AudioMgr audioMgr;
        }

        private void LightPool_Null()
        {
            throw new NotImplementedException();
        }

        private void MissilePool_Null()
        {
            throw new NotImplementedException();
        }

        private void FrameAniPool_Null()
        {
            throw new NotImplementedException();
        }

        private void ItemPool_Null()
        {
            throw new NotImplementedException();
        }

        private void PlanePool_Null()
        {
            throw new NotImplementedException();
        }

        private void BulletPool()
        {
            Bullet bullet;
            BulletEffectMgr bulletEffectMgr;
            MoveComponent moveComponent;
            BulletCollideMsgComponent bulletCollideMsgComponent;
            BulletBehaviour bulletBehaviour;
        }

        private void MainCamera()
        {
            CameraMove cameraMove;
            GameProcessMgr gameProcessMgr;
            MoveComponent moveComponent;
        }

        private void PlaneEnemy()
        {
            {//Plane节点 
                PlaneEnemyView planeEnemyView;
                RenderComponent renderComponent;
                CameraMove cameraMove;
                AutoDespawnComponent autoDespawnComponent;
                EnemyTypeComponent enemyTypeComponent;
                LifeComponent lifeComponent;
                SubMsgMgr subMsgMgr;
                EnemyBehaviour enemyBehaviour;
                MoveComponent moveComponent;
                ColliderComponent colliderComponent;
                PlaneCollideMsgComponent planeCollideMsgComponent;
            }
            { //Plane的子节点BulletRoot
                EnemyBulletMgr enemyBulletMgr;
                EmitBulletMgr emitBulletMgr;
                BossBulletEventComponent bossBulletEventComponent;
                SpawnBulletPointMgr spawnBulletPointMgr;
            }
            {Plane的子节点EnemyLife 
                EnemyLifeView enemyLifeView;
            }
            {EnemyLife的子节点Item_0,1,2,3
                EnemyLifeItem enemyLifeItem;
            }

        }

        private void Effect()
        {
            BulletDestroyAniView bulletDestroyAniView;
            PlaneDestroyAniView planeDestroyAniView;
            FrameAni frameAni;
        }

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

bug 点击英雄选择时无效,写死是第一个

因为枚举和图像名字是对应的。我可能改动了枚举(头字母小写转成大写)
所以默认成第一个,就是第一张图总亮,也总是她的声音

/// <summary>关联到人物语音的枚举。维持大小写</summary>
public enum Hero
{
    Player_0,
    Player_1,
    Player_2
}

。。。。
需要改动两处
HeroItem(控制颜色)
HeroItemController(控制音效、SelectHero)

统一用这样的写法//String2Enum字符串转枚举,前面应该有提到过

            string spriteName = transform.GetComponent<Image>().sprite.name;
            _hero = (spriteName.UpperFirstLetter()).String2Enum<Hero>();

。。。。
暴露参数看效果
在这里插入图片描述

bug 第二次点击相同的的英雄,音效会马上停止

自身逻辑问题,只考虑到不同英雄。
可以加上一个相同英雄的音效判定

HeroItemController

    private void Selected()
    {
        GameStateModel.Single.SelectedHero = _hero;
        if (AudioMgr.Single.CurAudioName() == _hero.ToString())
        {
            AudioMgr.Single.Replay(_hero.ToString());
            return;
        }
        AudioMgr.Single.Play(_hero.ToString());
        Debug.Log("HeroItemController "+ _hero.ToString());
    }

AudioMgr

    public string CurAudioName()
    {
        if (GetSource().clip != null)
        {
           return GetSource().clip.name;
        }
        return String.Empty ;
    }

modify 地图之相机

初始项目

移动的是相机,图是不动的
在这里插入图片描述

启动脚本 GameStart05

主要是这块,其它是复制之前的


        void OpenPanelFunc(  )
        {
            this.GetModel<IAirCombatAppModel>().SelectPlaneID.Value = 0;
            this.GetModel<IAirCombatAppModel>().SelectHeroID.Value = 0;
            this.GetModel<IAirCombatAppModel>().PassedLevel.Value = 10;
            this.GetModel<IAirCombatAppModel>().SelectLevel.Value = 10;
            this.SendCommand<OpenGamePanelCommand>();
        }
using System;
using System.Collections;
using System.Linq;
using LitJson;
using QFramework;
using QFramework.Example;
using UnityEngine;

namespace QFramework.AirCombat
{ 
public class GameStart05 : 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.Game;
        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(3f);//延时让json数据初始化



        if (true)
        {
            ResKit.Init();
            {
                   // OpenPanelFunc();
            }
        }
    }

        void OpenPanelFunc(  )
        {

            this.GetModel<IAirCombatAppModel>().SelectPlaneID.Value = 0;
            this.GetModel<IAirCombatAppModel>().SelectHeroID.Value = 0;
            this.GetModel<IAirCombatAppModel>().PassedLevel.Value = 10;
            this.GetModel<IAirCombatAppModel>().SelectLevel.Value = 10;
            this.SendCommand<OpenGamePanelCommand>();
        }



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



}

相机移动 OpenGamePanelCommand

地图是已有的预制体解除脚本了的,

    public class OpenGamePanelCommand : AbstractCommand
    {
        protected override void OnExecute()
        {
            GameStateModel.Single.CurScene = SceneName.Game;
            Debug.Log("OpenGamePanelCommand");
            {// 
                Camera.main
                    .GetOrAddComponent<MainCameraCtrl>()
                    .Init(1f, Vector2.up);

                //Camera.main.GetOrAddComponent<CameraMove>();
                //Camera.main.GetOrAddComponent<MoveComponent>();
                //Camera.main.GetOrAddComponent<GameProcessMgr>(); //尚未研究           
            }
        }
    }

相机移动脚本

后面在说玩家的MoveCommand时,可以尝试改成MoveUpCommand

/****************************************************
    文件:MainCameraCtrl.cs
	作者:lenovo
    邮箱: 
    日期:2023/8/16 21:11:50
	功能:
*****************************************************/

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
 
namespace QFramework.AirCombat
{
    public class MainCameraCtrl : MonoBehaviour  ,IController
    {
        #region 属性
       public float _speed;
       public Vector2 _dir;
        #endregion

        #region 生命



        public void Init(float speed, Vector2 dir)
        {
            _speed = speed;
            _dir = dir;
        }

        /// <summary>首次载入且Go激活</summary>
        void Start()
        {
          //  CameraMove cameraMove;
            GameProcessMgr gameProcessMgr;
            MoveComponent moveComponent;
        }


        void Update()
        {
            if (_speed != 0 && _dir != null)
            {
                transform.Translate( _dir * _speed * Time.deltaTime, Space.World );
            }

        }

   
        #endregion



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

    }
}




效果

按下P
在这里插入图片描述

modify 地图之图片

GameStart05

改一部分,其它照抄前面的

        private void Update()
        {
            if (Input.GetKeyDown(KeyCode.Q)) //确定是json数据初始慢导致强化面板获取数据失败
            {
                OpenPanelFunc();
            }
            if (Input.GetKeyDown(KeyCode.W))
            {
                this.GetModel<IAirCombatAppModel>().SelectLevel.Value ++;
            }
        }

MapItemCtrl

/****************************************************
    文件:MainCameraCtrl.cs
	作者:lenovo
    邮箱: 
    日期:2023/8/16 21:11:50
	功能:
*****************************************************/

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
 
namespace QFramework.AirCombat
{
    public class MapItemCtrl : MonoBehaviour,IController
    {
        #region 属性
        private float _offsetY;
        private MapCloud _cloud;
        private Transform _camera;
        private SpriteRenderer _renderer;
        private static int _curLevel;
        #endregion

        #region 生命



        public void Init(float offsetY, Transform camera)
        {
            _offsetY = offsetY;
            _camera = camera;
            _renderer = GetComponent<SpriteRenderer>();
            _curLevel = GameCurLevel();
            _cloud = transform.GetChild(0).GetOrAddComponent<MapCloud>();
            SetSprite(_curLevel);
        }




        void Update()
        {
            if (JugdeUpdate(_offsetY, _camera))
            {
                UpdatePos(_offsetY);
                UpdateSprite();
                UpdateLevel();
            }

        }


        #endregion

        #region 辅助

        int GameCurLevel ()
        {
            //return GameModel.Single.CurLevel;
            return this.GetModel<IAirCombatAppModel>().SelectLevel;
        }
        private bool JugdeUpdate(float offset, Transform camera)
        {
            return (camera.position.y - transform.position.y) >= offset;
        }

        private void UpdateLevel()
        {
            bool isActive = (_curLevel != GameCurLevel());
            _cloud.SetActive(isActive);
            if (isActive)
            {
                _curLevel = GameCurLevel();
            }
        }

        private void UpdatePos(float offset)
        {
            transform.SetPosY(transform.position.y + offset * 2);
        }

        private void UpdateSprite()
        {
            SetSprite(GameCurLevel());
        }

        private void SetSprite(int level)
        {
            var pre = Paths.PICTURE_MAP_FOLDER + Const.MAP_PREFIX;
            var sprite = LoadMgr.Single.Load<Sprite>(pre + level);
            if (sprite == null)
            {

                Debug.Log("MapItemCtrl 还没有所设关卡数的关卡图片=>"+ level);
                sprite = LoadMgr.Single.Load<Sprite>(pre + 0);
            }

            _renderer.sprite = sprite;
        }
        #endregion


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

    }
}




MapCloud不改

OpenGamePanelCommand

    public class OpenGamePanelCommand : AbstractCommand
    {
        protected override void OnExecute()
        {
            GameStateModel.Single.CurScene = SceneName.Game;
            Debug.Log("OpenGamePanelCommand");
            
            {// Camera.main
                //Camera.main.GetOrAddComponent<CameraMove>();
                //Camera.main.GetOrAddComponent<MoveComponent>();
                //Camera.main.GetOrAddComponent<GameProcessMgr>();//尚未研究
                //
                Camera.main
                    .GetOrAddComponent<MainCameraCtrl>()
                    .Init(1f, Vector2.up);
            }
            {//Map图片
                Transform mapMgrTrans = Camera.main.transform.FindTop("MapMgr");
                MapMgr mapMgr;
                MapItem mapItem;
                MapCloud mapCloud;
                //
                var map0 = mapMgrTrans.Find("map_0");
                var map1 = mapMgrTrans.Find("map_1");
                var offsetY = Mathf.Abs(map1.position.y - map0.position.y);
                Transform camera = Camera.main.transform;
                map0.GetOrAddComponent<MapItemCtrl>().Init(offsetY, camera);
                map1.GetOrAddComponent<MapItemCtrl>().Init(offsetY, camera);
                //
            }

        }
    }

效果

按P运行
按W换图
在这里插入图片描述

stars GetOrAddComponentDeep

    public static T GetOrAddComponentDeep<T>(this Transform root, string childName) where T : Component
    {
        Transform result = null;
        result = root.FindChildDeep(childName);
        if (result != null)
        {
            if (result.GetComponent<T>() != null)
            { 
                 return result.GetComponent<T>();
            }
            return  result.AddComponent<T>();
        }
        else
        {
            Debug.LogErrorFormat("{0}未找到子节点{1}", root.name, childName);
            return null;
        }
    }

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

watch 这里同一个Scene也升一下目录,内容比较多

06 Scene 战斗 modify 玩家

在场景中的位置

GameRoot/PLANE/第一个子节点
在这里插入图片描述
在这里插入图片描述

脚本

挂了两个MoveComponent,盲猜两个方向?
void Plane() { PlayerView playerView; RenderComponent renderComponent; PlayerEnterAni playerEnterAni; CameraMove cameraMove; PlayerBehaviour playerBehaviour; MoveComponent moveComponent; PlayerController playerController; ColliderComponent colliderComponent; PlaneCollideMsgComponent planeCollideMsgComponent; PlayerBuffMgr playerBuffMgr; {//BulletRoot EmitBulletMgr emitBulletMgr; BossBulletEventComponent bossBulletEventComponent; SpawnBulletPointMgr spawnBulletPointMgr; } }在这里插入图片描述

modify 基础的四向Move

把MoveComponent改成MoveCommand,少去添加一个组件

MoveComponent

using UnityEngine;

public class MoveComponent : MonoBehaviour
{
    private float _speed;

    public void Init(float speed)
    {
        _speed = speed;
    }

    public void Move(Vector2 direction)
    {
        if (_speed != 0)
        { 
            transform.Translate(direction * _speed * Time.deltaTime,Space.World);
        }
    }
}

MoveCommand

类似的上面相机可以改一个名叫MoveUpCommand的

    public class MoveCommand : AbstractCommand
    {
        Transform _t;
        Vector2 _dir;
        float _speed;

        public MoveCommand(Transform t, Vector2 dir, float speed)
        {
            _t = t;
            _dir = dir;
            _speed = speed;
        }

        protected override void OnExecute()
        {
            _t.Translate(_dir * _speed * Time.deltaTime, Space.World);
        }
    }

PlayerCtrl

/****************************************************
    文件:MainCameraCtrl.cs
	作者:lenovo
    邮箱: 
    日期:2023/8/16 21:11:50
	功能:
*****************************************************/

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
 
namespace QFramework.AirCombat
{
    public class PlayerCtrl : MonoBehaviour,IController
    {
        #region 属性
        private float _speed=2f;
        #endregion

        #region 生命



        void Update()
        {
            // this.SendCommand(new MoveCommand(transform, _speed));//这种将dir交给外面判断的不行
            if (Input.GetKey(KeyCode.W))
            {
                this.SendCommand(new MoveCommand(transform,Vector2.up,_speed));
            }
            if (Input.GetKey(KeyCode.S))
            {
                this.SendCommand(new MoveCommand(transform, Vector2.down, _speed));
            }
            if (Input.GetKey(KeyCode.A))
            {
                this.SendCommand(new MoveCommand(transform, Vector2.left, _speed));
            }
            if (Input.GetKey(KeyCode.D))
            {
                this.SendCommand(new MoveCommand(transform, Vector2.right, _speed));
            }

        }


        #endregion

        #region 辅助

        #endregion


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

    }
}




效果

在这里插入图片描述

modify 加上相机的基础移动

就是CameraMove里面调用了MoveComponent,Update里面同步了相机的向上移动,使得两者相对静止

相机SendCommand里面有CameraMoveUpEvent

原本是相机只是MoveUpCommand。
现在需要玩家监听相机的移动,所以新建CameraMoveUpCommand 。
CameraMoveUpCommand 里面有CameraMoveUpEvent,让玩家去监听

    public class CameraMoveUpCommand : AbstractCommand
    {
        Transform _t;
        float _speed;


        public CameraMoveUpCommand(Transform t, float speed)
        {
            _t = t;
            _speed = speed;
        }


        protected override void OnExecute()
        {
            _t.Translate(Vector2.up* _speed * Time.deltaTime, Space.World);
            this.SendEvent(new CameraMoveUpEvent( _speed));
        }
    }

玩家去Register这个Event

/****************************************************
    文件:PlayerCtrl.cs
	作者:lenovo
    邮箱: 
    日期:2023/8/16 21:11:50
	功能:
*****************************************************/

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
 
namespace QFramework.AirCombat
{
    public class PlayerCtrl : MonoBehaviour,IController
    {
        #region 属性
        private float _speed=2f;
        #endregion



        #region 生命
        private void Start()
        {
            this.RegisterEvent<CameraMoveUpEvent>((e) => 
            {
                this.SendCommand(new MoveUpCommand(transform,e.Speed));
            });
        }
        ......

效果

可以看到没有原本项目的CameraMove脚本也在跑了
在这里插入图片描述

到现在完成玩家的两个脚本

            {//玩家
                //CameraMove cameraMove;
                //MoveComponent moveComponent;
                PlayerView playerView;
                RenderComponent renderComponent;
                PlayerEnterAni playerEnterAni;
                PlayerBehaviour playerBehaviour;
                PlayerController playerController;
                ColliderComponent colliderComponent;
                PlaneCollideMsgComponent planeCollideMsgComponent;
                PlayerBuffMgr playerBuffMgr;
            }

限制移动(边界检测)

原版

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    private MoveComponent _move;

    //飞机中心点到边界的差值
    private Vector2 _offset;
    private SpriteRenderer _renderer;



    #region 生命
    // Use this for initialization
    private void Start()
    {
        _move = GetComponent<MoveComponent>();
        _renderer = GetComponent<SpriteRenderer>();
        InputMgr.Single.AddListener(KeyCode.W);
        InputMgr.Single.AddListener(KeyCode.A);
        InputMgr.Single.AddListener(KeyCode.S);
        InputMgr.Single.AddListener(KeyCode.D);

        MessageMgr.Single.AddListener(KeyCode.W, InputState.PREE, ReveiveW);
        MessageMgr.Single.AddListener(KeyCode.A, InputState.PREE, ReveiveA);
        MessageMgr.Single.AddListener(KeyCode.S, InputState.PREE, ReveiveS);
        MessageMgr.Single.AddListener(KeyCode.D, InputState.PREE, ReveiveD);
        InitData();
    }

    private void InitData()
    {
        _offset = transform.position - _renderer.bounds.min;
    }

    private void OnDestroy()
    {
        InputMgr.Single.RemoveListener(KeyCode.W);
        InputMgr.Single.RemoveListener(KeyCode.A);
        InputMgr.Single.RemoveListener(KeyCode.S);
        InputMgr.Single.RemoveListener(KeyCode.D);

        MessageMgr.Single.RemoveListener(KeyCode.W, InputState.PREE, ReveiveW);
        MessageMgr.Single.RemoveListener(KeyCode.A, InputState.PREE, ReveiveA);
        MessageMgr.Single.RemoveListener(KeyCode.S, InputState.PREE, ReveiveS);
        MessageMgr.Single.RemoveListener(KeyCode.D, InputState.PREE, ReveiveD);
    }
    #endregion  




    #region 辅助 pub
    public void ReveiveW(params object[] args)
    {
        if (!JudgeUpBorder()) _move.Move(Vector2.up);
    }

    public void ReveiveA(params object[] args)
    {
        if (!JudgeLeftBorder()) _move.Move(Vector2.left);
    }

    public void ReveiveS(params object[] args)
    {
        if (!JudgeDownBorder()) _move.Move(Vector2.down);
    }

    public void ReveiveD(params object[] args)
    {
        if (!JudgeRightBorder()) _move.Move(Vector2.right);
    }
    #endregion




    #region pri
    private bool JudgeUpBorder()
    {
        return _renderer.bounds.max.y >= GameUtil.GetCameraMax().y;
    }

    private bool JudgeDownBorder()
    {
        return _renderer.bounds.min.y <= GameUtil.GetCameraMin().y;
    }

    private bool JudgeLeftBorder()
    {
        return _renderer.bounds.min.x <= GameUtil.GetCameraMin().x;
    }

    private bool JudgeRightBorder()
    {
        return _renderer.bounds.max.x >= GameUtil.GetCameraMax().x;
    }

    private void ResetPosX(Vector2 border, Vector2 direction)
    {
        var pos = transform.localPosition;
        pos.z = 0;
        pos.x = border.x - Vector2.Dot(_offset, direction);
        transform.localPosition = pos;
    }

    private void ResetPosY(Vector2 border, Vector2 direction)
    {
        var pos = transform.localPosition;
        pos.z = 0;
        pos.y = border.y - Vector2.Dot(_offset, direction);
        transform.localPosition = pos;
    }

    private void Drag(Vector3 screenPos)
    {
        var pos = Camera.main.ScreenToWorldPoint(screenPos);
        pos.z = 0;
        transform.localPosition = pos;
    }
    #endregion



    #region 系统
    private void OnMouseDrag()
    {
#if UNITY_EDITOR
        Drag(Input.mousePosition);
#else
		if (Input.touches.Length > 0)
		{
			Drag(Input.touches[0].position);
		}
#endif

        if (JudgeUpBorder())
            ResetPosY(GameUtil.GetCameraMax(), Vector2.up);
        else if (JudgeDownBorder()) ResetPosY(GameUtil.GetCameraMin(), Vector2.down);

        if (JudgeLeftBorder())
            ResetPosX(GameUtil.GetCameraMin(), Vector2.left);
        else if (JudgeRightBorder()) ResetPosX(GameUtil.GetCameraMax(), Vector2.right);
    }
    #endregion






}

改成InCameraBorderCommand

MoveCommand中判断时候用到InCameraBorderCommand
。。。
Camera的拓展看后面的

    public class MoveCommand : AbstractCommand
    {
        Transform _t;
        Vector2 _dir;
        float _speed;


        public MoveCommand(Transform t, Vector2 dir, float speed)
        {
            _t = t;
            _dir = dir;
            _speed = speed;
        }


        protected override  void OnExecute()
        {
            if (this.SendCommand(new InCameraBorderCommand(_t, _dir)))
            { 
                _t.Translate(_dir * _speed * Time.deltaTime, Space.World);
            
            }
        }
    }

    public class InCameraBorderCommand : AbstractCommand<bool>
    {
        Camera _camera;
        Vector2 _dir;
        SpriteRenderer _sR;



        public InCameraBorderCommand(Transform t,Vector2 dir)
        {
            _camera = t.AnyOneCameraFirstMain();
            _sR = t.gameObject.GetComponent<SpriteRenderer>();
            _dir = dir;

            Debug.LogFormat(Common.Log_ClassFunction()+"{0},{1},{2}", _camera,_sR,_dir);
        }


        protected override bool OnExecute()
        {           
            if (_dir== Vector2.up) return _sR.bounds.max.y <= _camera.CameraSizeMax().y; //up
            if (_dir== Vector2.down) return _sR.bounds.min.y >= _camera.CameraSizeMin().y; //down
            if (_dir== Vector2.right) return _sR.bounds.max.x <= _camera.CameraSizeMax().x; //right
            if (_dir== Vector2.left) return _sR.bounds.min.x >= _camera.CameraSizeMin().x; //left
            return true;
        }
    }

效果

可以看到 InCameraBorderCommand在跑
在这里插入图片描述

stars Camera

将GameUtils中的方法改成this拓展。
前面的边界检测用到

public static partial class Tags 
{
    public const string MAINCAMERA = "MainCamera";
    public const string UICAMERA = "UICamera";
}
/****************************************************
    文件:ExtendCamera.cs
	作者:lenovo
    邮箱: 
    日期:2023/7/9 19:31:26
	功能:
*****************************************************/

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


public static class ExtendCamera
{
    #region Camera
    public static Camera UICamera(this Transform t)
    {
        return t.gameObject.FindComponentWithTag<Camera>(Tags.UICAMERA);
    }
    public static Camera MainCamera(this Transform t)
    {
        return GameObject
            .FindGameObjectWithTag(Tags.MAINCAMERA)
            .GetComponent<Camera>();
    }


    public static Camera AnyOneCamera(this Transform t)
    {
        Camera _camera = Object.FindObjectOfType<Camera>();
        if (_camera == null)
        {
            if (_camera == null)
                Debug.LogError("当前场景中没有相机");

            return _camera;
        }
        else
        {
            return _camera;
        }
    }

    public static Camera AnyOneCameraFirstMain(this Transform t)
    {
        Camera camera = t.MainCamera();
        if (camera == null)
        {
            return t.AnyOneCamera();
        }
        return camera;

    }
    #endregion



    #region CameraSize
    public static Vector2 CameraSize(this Camera camera)
    {
        Vector2 size = Vector2.zero;
        if (size == Vector2.zero)
        {
            var heigth = camera.orthographicSize * 2;
            var width = heigth * camera.aspect;
            size = new Vector2(width, heigth);
        }
        return size;
    }

    public static Vector2 CameraSize(this Transform t)
    {
        Camera camera = t.AnyOneCameraFirstMain();
        Vector2 size = Vector2.zero;
        if (camera != null && size == Vector2.zero)
        {
            var heigth = camera.orthographicSize * 2;
            var width = heigth * camera.aspect;
            size = new Vector2(width, heigth);
        }
        return Vector2.zero;
    }

    public static Vector2 CameraSizeMin(this Transform t)
    {
        Camera camera = t.AnyOneCameraFirstMain();

        return camera.CameraSizeMin();
    }

    public static Vector2 CameraSizeMin(this Camera camera)
    {
        if (camera != null)
        {
            var pos = camera.transform.position;
            var size = camera.CameraSize();
            return new Vector3(
                  pos.x - size.x * 0.5f
                , pos.y - size.y * 0.5f
                , pos.z);
        }

        return Vector2.zero;
    }

    public static Vector2 CameraSizeMax(this Transform t)
    {
        Camera camera = t.AnyOneCameraFirstMain();
        return camera.CameraSizeMax();
    }

    public static Vector2 CameraSizeMax(this Camera camera)
    {
        if (camera != null)
        {
            var pos = camera.transform.position;
            var size = camera.CameraSize();
            return new Vector3(
                  pos.x + size.x * 0.5f
                , pos.y + size.y * 0.5f
                , pos.z);
        }

        return Vector2.zero;
    }


    #endregion  


}


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

watch QF对象池

在这里插入图片描述

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

namespace QFramework
{
    public class SimpleObjectPoolExample : MonoBehaviour
    {
        private SimpleObjectPool<GameObject> mObjectPool;
        GameObject _curGo;

        void Start()
        {
            mObjectPool = new SimpleObjectPool<GameObject>(() =>
            {
                var gameObj = new GameObject();
                gameObj.Hide();
                gameObj.transform.SetParent(transform);
                return gameObj;
            }, gameObj => 
            { 
                gameObj.Hide(); 
            }, 5);
        }

        // Update is called once per frame
        void Update()
        {
            if (Input.GetKeyDown(KeyCode.Q))
            {
                _curGo = mObjectPool.Allocate();
                _curGo.Show();
                _curGo.transform.SetParent(transform);
            }

            if (Input.GetKeyDown(KeyCode.W))
            {
                mObjectPool.Recycle(_curGo);
                foreach (Transform t in transform)
                {
                    if (t.gameObject.activeInHierarchy == true)
                    {
                        _curGo = t.gameObject;
                        break;
                    }
                }
            }
            if (Input.GetKeyDown(KeyCode.E))
            {
                mObjectPool.Clear(go => 
                { 
                    Destroy(go); 
                });
            }

        }
    }
}

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

modify 子弹

可以观察到子弹是间歇性自动发射的

位置

父节点在这里,但是根据设置未知的节点挂在对应的飞机下面
在这里插入图片描述

脚本

        private void Bullet()
        {
            {//PoolMgr 
                {//BulletPool 
                    {//Bullet 
                        Bullet bullet;
                        BulletEffectMgr bulletEffectMgr;
                        BulletBehaviour bulletBehaviour;
                        MoveComponent moveComponent;
                        BulletCollideMsgComponent bulletCollideMsgComponent;
                    }
                }
            }
        }
        

新建一个EmitBulletMgr

给飞机需要实例子弹时,有对应的Pool给GameObject

/****************************************************
    文件:MainCameraCtrl.cs
	作者:lenovo
    邮箱: 
    日期:2023/8/16 21:11:50
	功能:
*****************************************************/

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UniRx.Triggers;
using UnityEngine;
using static ResourcesName;
using Random = UnityEngine.Random;
 
namespace QFramework.AirCombat
{
    public class EmitBulletMgr : MonoBehaviour,ISingleton
    {
        #region 属性
        Dictionary<BulletType, SimpleObjectPool<GameObject>> _bulletPrefabDic
            = new Dictionary<BulletType, SimpleObjectPool<GameObject>>();
        Dictionary<BulletType, IBulletModel> _bulletModelDic
            = new Dictionary<BulletType, IBulletModel>();
        const int _preloadCnt=10;
        #endregion




    #region 生命
    public void OnSingletonInit()
        {
            ResKit.Init();
            ResLoader resLoader = ResLoader.Allocate();
            //
            GameObject prefab = resLoader.LoadSync<GameObject>("Bullet");
            SimpleObjectPool<GameObject> pool = new SimpleObjectPool<GameObject>(() =>
            {
                GameObject go = GameObject.Instantiate(prefab, Vector2.zero, Quaternion.identity);
                go.Identity();
                go.Hide();

                return go;
            }, go =>
            {
                go.Identity();
                go.Hide();
            }, _preloadCnt);
            _bulletPrefabDic.Add(BulletType.PLAYER, pool);
        }
        #endregion

        #region 辅助
         public  GameObject SpawnBullet(BulletType bulletType)
        {
            SimpleObjectPool<GameObject> pool;
            _bulletPrefabDic.TryGetValue(bulletType, out  pool);
            if (pool!= null)
            {
                GameObject go = pool.Allocate();
                return go;
            }
            return null;
        }

        public void RecycleBullet(BulletType bulletType,GameObject old)
        {
            SimpleObjectPool<GameObject> pool;
            _bulletPrefabDic.TryGetValue(bulletType, out pool);
            if (pool != null)
            {
                pool.Recycle(old);
            }
        }
        #endregion


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

        public static EmitBulletMgr Instance
        {
            get { return MonoSingletonProperty<EmitBulletMgr>.Instance; }
        }



        #endregion

    }
}




玩家 PlayerCtrl (只有子弹间隔,还没有装弹间隔)

使用了UniRX

/****************************************************
    文件:MainCameraCtrl.cs
	作者:lenovo
    邮箱: 
    日期:2023/8/16 21:11:50
	功能:
*****************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using UniRx;
using UniRx.Triggers;
using UnityEngine;
using Random = UnityEngine.Random;
 
namespace QFramework.AirCombat
{
    public class PlayerCtrl : MonoBehaviour,IController
    {
        #region 属性
        private float _bulletSpeed;    //子弹速度
        Transform _bulletPoolTrans;       //子弹父节点。固定不动的。就是防乱而已
        Transform _shooterlTrans;       //子弹射出时的初始位置,随飞机变化
        private float _shootSpanTime; //射击间隔
        private float _bulletSpanTime; //射击时子弹间隔
        #endregion



        #region 生命
        private void Start()
        {
            _bulletPoolTrans = transform.FindTop("Pool").Find("BulletPool");
            _shooterlTrans = transform.GetChild(0);
            _bulletSpeed = 2f;
            _shootSpanTime = 0.5f;
            _bulletSpanTime = 0.2f;
            this.RegisterEvent<CameraMoveUpEvent>((e) => 
            {
                this.SendCommand(new MoveUpCommand(transform,e.Speed));
            });

           //
            gameObject.UpdateAsObservable()
                .Sample(TimeSpan.FromSeconds(_bulletSpanTime))
                .Subscribe(_ =>
                { 
                    this.SendCommand(new FireCommand(_bulletPoolTrans, _shooterlTrans.position));
                })
                .AddTo(gameObject);

        }

        void Update()
        {

            UpdateMove();


        }
        #endregion

        #region 辅助  

        void UpdateMove()
        { 
           // this.SendCommand(new MoveCommand(transform, _speed));//这种将dir交给外面判断的不行
            if (Input.GetKey(KeyCode.W))
            {
                this.SendCommand(new MoveCommand(transform,Vector2.up,_bulletSpeed));
            }
            if (Input.GetKey(KeyCode.S))
            {
                this.SendCommand(new MoveCommand(transform, Vector2.down, _bulletSpeed));
            }
            if (Input.GetKey(KeyCode.A))
            {
                this.SendCommand(new MoveCommand(transform, Vector2.left, _bulletSpeed));
            }
            if (Input.GetKey(KeyCode.D))
            {
                this.SendCommand(new MoveCommand(transform, Vector2.right, _bulletSpeed));
            }        
        }

        #endregion


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

    }
}




子弹 PlayerBulletCtrl

/****************************************************
    文件:MainCameraCtrl.cs
	作者:lenovo
    邮箱: 
    日期:2023/8/16 21:11:50
	功能:
*****************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using UniRx;
using UnityEngine;
using Random = UnityEngine.Random;
 
namespace QFramework.AirCombat
{
    public class PlayerBulletCtrl : MonoBehaviour,IController
    {
        #region 属性
        private float _bulletSpeed=3f;//需要大于飞机速度,不然与飞机一起方向事相对静止很难看
        float _destroyTime = 2f;
        IBulletModel _model;

        #endregion



        #region 生命


        private void OnEnable()
        {

            if (true)  //不确定要不要给加上相机的基础运动矢量
            { 
                this.RegisterEvent<CameraMoveUpEvent>((e) => 
                {
                    this.SendCommand(new MoveUpCommand(transform,e.Speed));
                });            
            }
            //
            Observable
                .Timer(TimeSpan.FromSeconds(_destroyTime))
                .RepeatUntilDisable(gameObject)
                .Subscribe(_ =>
                {
                    EmitBulletMgr.Instance.RecycleBullet(BulletType.PLAYER, gameObject);
                })
                .AddTo(gameObject);
        }


        void Update()
        {

            this.SendCommand(new MoveCommand(transform,Vector2.up,_bulletSpeed));
        }
        #endregion

        #region 系统



        #endregion


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

    }
}




发射命令 FireComand

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

    public class FireCommand : AbstractCommand
    {
        Transform _parent;
        UnityEngine.Vector3 _initPos;

        public FireCommand(Transform parent, UnityEngine.Vector3 initPos)
        {
            _parent = parent;
            _initPos = initPos;
        }

        protected override void OnExecute()
        {
            GameObject go = EmitBulletMgr.Instance.SpawnBullet(BulletType.PLAYER);
            go.SetParent(_parent);
            go.Identity();
            go.transform.position = _initPos;
            go. GetOrAddComponent<PlayerBulletCtrl>(); //直接Add防止多个
            go.Show();
            //InitComponent();
            //InitPos(_bulletModel);
        }
    }

效果

在这里插入图片描述

换弹时间

效果

在这里插入图片描述

脚本

/****************************************************
    文件:MainCameraCtrl.cs
	作者:lenovo
    邮箱: 
    日期:2023/8/16 21:11:50
	功能:
*****************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using UniRx;
using UniRx.Triggers;
using UnityEngine;
using Random = UnityEngine.Random;
 
namespace QFramework.AirCombat
{
    public class PlayerCtrl : MonoBehaviour,IController
    {
        #region 属性
        private float _bulletSpeed;    //子弹速度
        Transform _bulletPoolTrans;       //子弹父节点。就是防乱而已
        Transform _reloadBulletTrans;       //子弹射出时的初始位置,随飞机变化。不是子弹的父节点
        private float _reloadBulletTime; //射击间隔。也就是装弹间隔
        public bool _loadingBullet;
        private float _bulletSpanTime; //射击时子弹间隔
        private int _loadBulletCnt;  //子弹容量
        private int _loadBulletCnter;   //子弹存量
       // private float _fireSpanTime;//这种卡时间有时多一个少一个

        #endregion


        #region 生命
        private void Start()
        {
            _bulletPoolTrans = transform.FindTop("Pool").Find("BulletPool");
            _reloadBulletTrans = transform.GetChild(0);
            _bulletSpeed = 3f;
            _bulletSpanTime = 0.2f;
           // _fireSpanTime = 1f;
            _loadBulletCnt = 5;
            _loadBulletCnter = _loadBulletCnt;
            _reloadBulletTime = 0.5f;
            _loadingBullet = false;
            this.RegisterEvent<CameraMoveUpEvent>((e) => 
            {
                this.SendCommand(new MoveUpCommand(transform,e.Speed));
            });

            //
            {//射速 
                _reloadBulletTrans.UpdateAsObservable()
                    .Where(_=>_loadingBullet==false)
                    .Sample(TimeSpan.FromSeconds(_bulletSpanTime))
                    .Subscribe( _=>
                    {
                        if (_loadBulletCnter >0)
                        {
                            _loadBulletCnter--;
                            this.SendCommand(new FireCommand(_bulletPoolTrans
                                , _reloadBulletTrans.position));
                        }
                        else
                        {
                            _loadingBullet = true;
                        }

                    })
                    .AddTo(_reloadBulletTrans);            
            }
            {  //换弹
                _reloadBulletTrans.UpdateAsObservable()
                    .Where(_ => _loadingBullet == true)
                    .Sample(TimeSpan.FromSeconds(_reloadBulletTime))
                    .Subscribe(_ =>
                    {
                        _loadBulletCnter=_loadBulletCnt;
                        _loadingBullet = false;

                    })
                    .AddTo(_reloadBulletTrans);            
            }


        }

        void Update()
        {

            UpdateMove();


        }
        #endregion


        #region 辅助  

        void UpdateMove()
        { 
           // this.SendCommand(new MoveCommand(transform, _speed));//这种将dir交给外面判断的不行
            if (Input.GetKey(KeyCode.W))
            {
                this.SendCommand(new MoveCommand(transform,Vector2.up,_bulletSpeed));
            }
            if (Input.GetKey(KeyCode.S))
            {
                this.SendCommand(new MoveCommand(transform, Vector2.down, _bulletSpeed));
            }
            if (Input.GetKey(KeyCode.A))
            {
                this.SendCommand(new MoveCommand(transform, Vector2.left, _bulletSpeed));
            }
            if (Input.GetKey(KeyCode.D))
            {
                this.SendCommand(new MoveCommand(transform, Vector2.right, _bulletSpeed));
            }        
        }

        #endregion


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




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

大飞弹

在这里插入图片描述

大飞弹预警线

在这里插入图片描述

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

爆炸特效

位置

在这里插入图片描述

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

爆金币

位置

在这里插入图片描述

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

06 Scene 战斗 modify 敌人

在场景中的位置

GameRoot/PLANE除了第一个子节点(第一个是玩家)
子节点有血条。
在这里插入图片描述

在这里插入图片描述

脚本

跟玩家不同,这里只有一个MoveComponent

        void Enemy()
        {
            PlaneEnemyView planeEnemyView;
            RenderComponent renderComponent;
            PlayerEnterAni playerEnterAni;
            CameraMove cameraMove; //没激活
            AutoDespawnComponent autoDespawnComponent;
            EnemyTypeComponent enemyTypeComponent;
            LifeComponent lifeComponen;
            SubMsgMgr subMsgMgr;
            EnemyBehaviour enemyBehaviour;
            MoveComponent moveComponent;
            ColliderComponent colliderComponent;
            PlaneCollideMsgComponent planeCollideMsgComponent;
            {//BulletRoot 
                EnemyBulletMgr enemyBulletMgr;
                EmitBulletMgr emitBulletMgr;
                BossBulletEventComponent bossBulletEventComponent;
                SpawnBulletPointMgr spawnBulletPointMgr;
            }
            {//EnemyLife 
                EnemyLifeView enemyLifeView;
                {//Item 
                    EnemyLifeItem enemyLifeItem;
                }       
            }
        }

watch 第一关敌人的生成情况

左5
右5
左5+从左到右的飞机
右5+2个导弹
2个导弹
左5+从左到右的飞机
右5
左5
右5
警告
Boss
也就是LevelDatas[0]的数据
LevelEnemyDataConfig.json

{
  "LevelDatas": [
    {
      "PlaneCreaterDatas": [
        {
          "IdMax": 1,
          "IdMin": 0,
          "QueuePlaneNum": 5,
          "QueueNum": 4,
          "Type": 0,
          "X": -1.0
        },
        {
          "IdMax": 5,
          "IdMin": 2,
          "QueuePlaneNum": 5,
          "QueueNum": 4,
          "Type": 0,
          "X": 1.0
        },
        {
          "IdMax": 2,
          "IdMin": 0,
          "QueuePlaneNum": 1,
          "QueueNum": 2,
          "Type": 1,
          "X": 0.0
        },
        {
          "IdMax": 0,
          "IdMin": 0,
          "QueuePlaneNum": 1,
          "QueueNum": 1,
          "Type": 2,
          "X": 0.0
        }
      ],
      "MissileCreaterDatas": [
        {
          "Batch": 0,
          "X": 1,
          "NumOfWarning": 2,
          "EachWarningTime": 0.8,
          "SpwanCount": 2,
          "Speed": 4
        }
        ,
        {
          "Batch": 0,
          "X": -1,
          "NumOfWarning": 2,
          "EachWarningTime": 0.8,
          "SpwanCount": 2,
          "Speed": 4
        }
      ],
      "EnemyNumMax": 5,
      "EnemyNumMin": 5,
      "NormalDeadNumForSpawnElites": 20
    },
    {
      "PlaneCreaterDatas": [
        {
          "IdMax": 1,
          "IdMin": 0,
          "QueuePlaneNum": 5,
          "QueueNum": 4,
          "Type": 0,
          "X": -1.0
        },
        {
          "IdMax": 5,
          "IdMin": 2,
          "QueuePlaneNum": 5,
          "QueueNum": 4,
          "Type": 0,
          "X": 1.0
        },
        {
          "IdMax": 2,
          "IdMin": 0,
          "QueuePlaneNum": 1,
          "QueueNum": 2,
          "Type": 1,
          "X": 0.0
        },
        {
          "IdMax": 0,
          "IdMin": 0,
          "QueuePlaneNum": 1,
          "QueueNum": 1,
          "Type": 2,
          "X": 0.0
        }
      ],
      "MissileCreaterDatas": [
        {
          "Batch": 0,
          "X": 1,
          "NumOfWarning": 2,
          "EachWarningTime": 0.8,
          "SpwanCount": 2,
          "Speed": 4
        }
      ,
        {
          "Batch": 0,
          "X": -1,
          "NumOfWarning": 2,
          "EachWarningTime": 0.8,
          "SpwanCount": 2,
          "Speed": 4
        }
      ],
      "EnemyNumMax": 5,
      "EnemyNumMin": 5,
      "NormalDeadNumForSpawnElites": 20
    }
  ]
}

stars 解析json ExtendLitJson

主要是json和Object互转
。。。
这里是用拓展的方式,以及摸清更加细节的注意事项

/****************************************************
    文件:ExtendLitJson.cs
	作者:lenovo
    邮箱: 
    日期:2023/7/24 22:32:11
	功能:
*****************************************************/

using LitJson;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using UnityEngine;
using Random = UnityEngine.Random;
using System.IO;

public static class ExtendLitJson
{

    #region 内部类

    public class Heros
    {
        public List<Hero> HeroLst = new List<Hero>();

        public override string ToString()
        {
            string str = "";
            foreach (Hero hero in HeroLst)
            {
                str += "\n" + hero.ToString();
            }
            return str;
        }
    }

    /// <summary>
    /// 01 里面的属性字段一定要有public
    /// <para />System.Text.Json.JsonSerializer.Serialize就一定要有publc的属性
    /// </summary>
    public class Hero
    {
        public Hero(string name, int age)
        {
            Name = name;
            Age = age;
        }

        public string Name { get; set; }
        public int Age { get; set; }
        //或者
        //string _name;
        //int _age;
        //public string Name { get => _name; set => _name = value; }
        //public int Age { get => _age; set => _age = value; }


        //public Hero(string name, int age)
        //{
        //    this.Name = name;
        //    this.Age = age;
        //}



        public override string ToString()
        {
            string str = "";
            str += "\t" + Name;
            str += "\t" + Age;

            return str;
        }

    }

    public class Person
    {


        public string Name { get; set; }
        public int Age { get; set; }
        //或者
        //string _name;
        //int _age;
        //public string Name { get => _name; set => _name = value; }
        //public int Age { get => _age; set => _age = value; }




        public override string ToString()
        {
            string str = "";
            str += "\t" + Name;
            str += "\t" + Age;

            return str;
        }


    }

    public class Character
    {
        public Character()
        {

        }
        public Character(string name, int age)
        {
            Name = name;
            Age = age;
        }

        public string Name { get; set; }
        public int Age { get; set; }
        //或者
        //string _name;
        //int _age;
        //public string Name { get => _name; set => _name = value; }
        //public int Age { get => _age; set => _age = value; }




        public override string ToString()
        {
            string str = "";
            str += "\t" + Name;
            str += "\t" + Age;

            return str;
        }


    }


    #endregion


    public static void Example()
    {
        if (false)
        {
            JsonData data = new JsonData();
            JsonType type = data.GetJsonType();
        }
        if (false)
        {
            ExtendLitJson.Hero hero = new ExtendLitJson.Hero("刘备", 18);
            Debug.Log(hero.Object2Json());
        }
        if (false)
        {

            ExtendLitJson.Hero hero1 = new ExtendLitJson.Hero("刘备", 18);
            ExtendLitJson.Hero hero2 = new ExtendLitJson.Hero("关羽", 18);
            ExtendLitJson.Hero hero3 = new ExtendLitJson.Hero("张飞", 18);
            ExtendLitJson.Hero[] heroArr = new ExtendLitJson.Hero[3] { hero1, hero2, hero3 };
            Debug.Log(heroArr.Object2Json());
        }
        if (false)
        {
            ExtendLitJson.Hero hero1 = new ExtendLitJson.Hero("刘备", 18);
            ExtendLitJson.Hero hero2 = new ExtendLitJson.Hero("关羽", 18);
            ExtendLitJson.Hero hero3 = new ExtendLitJson.Hero("张飞", 18);
            ExtendLitJson.Heros heros = new ExtendLitJson.Heros();
            heros.HeroLst.Add(hero1);
            heros.HeroLst.Add(hero2);
            heros.HeroLst.Add(hero3);
            Debug.Log(heros.Object2Json());

        }
        if (false) //想采用 System.Text.Json.JsonSerializer.Serialize。但是引用不到
        {
            //string str1 = @"{"Name":"刘备","Age":18}";
            //string str2 = "  /{/"Name/":/"刘备/",/"Age/":18/}  ";
            var stream = new ExtendLitJson.Person { Name ="刘备",Age=18 };
            // string str = System.Text.Json.JsonSerializer.Serialize(stream);
            string str = "";
            //
            ExtendLitJson.Person person;
            person = str.Json2Object<ExtendLitJson.Person>();
            Debug.Log(person.ToString());
        }
        if (true)
        {
            ExtendLitJson.Character cPre=new ExtendLitJson.Character("刘备",18);
            string json = cPre.Object2Json() ;
            Debug.Log("01 "+json);
            ExtendLitJson.Character cAfter = json.Json2Object<ExtendLitJson.Character>();
            Debug.Log("02 " + cAfter.ToString());
        }
    }

    public static JsonType GetJsonType_Common(this JsonData data)
    {
        return data.GetJsonType();
    }

    /// <summary>
    /// https://blog.csdn.net/weixin_39562801/article/details/90410402
    /// </summary> 
    public static string Object2Json<T>(this T t)
    {
        string str = JsonMapper.ToJson(t);
        Regex reg = new Regex(@"(?i)\\[uU]([0-9a-f]{4})");
        str = reg.Replace(str, delegate (Match m)
        {
            return ((char)Convert.ToInt32(m.Groups[1].Value, 16)).ToString();
        });

        return str;
    }



    /// <summary>
    /// 01 File.ReadText(path)
    /// 需要一个无参构造函数
    /// </summary>
    public static T Json2Object<T>(this string str)
    {
        T t = JsonMapper.ToObject<T>(str);
        return t;
    }
}





【未完成】用System.Text.Json解析Json

用的原因

手动敲json数据是不用注意转义、两个双引号之类。
还有new{}

bug [未解决] 尝试使用 System.Text.Json,实现匿名new{}的效果

这个界面安装后,对于Common项目还需要去 “添加引用”才有 System.Text.Json
在这里插入图片描述

bug 需要net6

需要net 6.0 及以上版本

bug net6要过时了

支持的版本
在这里插入图片描述

bug name ‘Json’ does not exist in the namespace ‘System.Text’

System.Text.Json下载的是最新的70003,介绍有NetFrame462
所以项目改成462,不报错了
。。。
安装的是System.Text.Json,但是引用显示的Newtosoft.Json。而Newtosoft.Json里面确实没有System.Text.Json
在这里插入图片描述

bug 到文件夹查找

然后“添加引用”,添加了但是没看到。
在这里插入图片描述

bug Vs项目 属性 不能设置版本

之前还可以调出的(从471改成462)
在这里插入图片描述

bug 总结

不管是462还是471,你安装引用了System.Text.Json,实际只显示Newtosoft.Json

用Newtonsoft.Json解析json

modify 用JsonConvert

在这里插入图片描述

    /// <summary>
    /// LitJson
    /// <para />File.ReadAllText
    /// </summary> 
    public static T JsonPath2Object_JsonMapper<T>(this string path)
    {
        string str = File.ReadAllText(path);
        Debug.Log("JsonPath2Object_JsonMapper\n" + str);

        T t = JsonMapper.ToObject<T>(str);
        Debug.Log("JsonPath2Object_JsonMapper\n" + t.ToString());
        return t;
    }

    /// <summary>
    /// Newtonsoft.Json
    /// <para />File.ReadAllText
    /// </summary> 
    public static T JsonPath2Object_JsonConvert<T>(this string path)
    {
        string jsonStr = File.ReadAllText(path);
        Debug.Log("JsonPath2Object_JsonConvert\n" + jsonStr);

        T t = JsonConvert.DeserializeObject<T>(jsonStr);
        Debug.Log("JsonPath2Object_JsonConvert\n" + t.ToString());
        return t;
    }

bug bulletType

json和EnemyData不一样,可能是被我改了

watch EnemyData的字典加载

EnemyData、AllEnemyData

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

/// <summary>用来读取所有敌人信息json的一个类</summary>

public class AllEnemyData
{

    /// <summary>Boss怪</summary>
    public EnemyData[] Boss { get; set; }
    /// <summary>精英怪</summary>
    public EnemyData[] Elites {get; set; }
    /// <summary>小怪</summary>
    public EnemyData[] Normal {get; set; }




    #region 构造
    public AllEnemyData()
    {
    }

    public AllEnemyData(EnemyData[] boss, EnemyData[] elites, EnemyData[] normal)
    {
        Boss = boss;
        Elites = elites;
        Normal = normal;
    }
    #endregion


    public EnemyData[] GetData(EnemyType type)
    {
        switch (type)
        {
            case EnemyType.NORMAL:
                return Normal;
            case EnemyType.ELITES:
                return Elites;
            case EnemyType.BOSS:
                return Boss;
            default:
                return null;
        }
    }



    public override string ToString()
    {
        string str = "";
        if (Normal == null) Debug.LogError("EnemyData Normal为null");
        if (Elites == null) Debug.LogError("EnemyData Elites为null");
        if (Boss == null)   Debug.LogError("EnemyData Boss为null");
        foreach (EnemyData item in Normal)
        {
            str+= item.ToString();
        }
        foreach (EnemyData item in Elites)
        {
            str += item.ToString();
        }
        foreach (EnemyData item in Boss)
        {
            str += item.ToString();
        }

        return str;
    }
}

public class EnemyData
{
    public int id;
    public int attack;
    public int life;
    /// <summary>-1代表当前是随机轨迹,大于0的值,代表轨迹id</summary>
    public int trajectoryID;
    public int starNum;
    public int score;
    /// <summary> 掉落道具的可能性,例如值为10,就代表百分之十的概率 </summary>
    public int itemProbability;
    /// <summary>
    /// 掉落道具的数量,每个道具都在范围内随机
    /// <para />例如:数量是2,范围是[0,1],那么可能会出一个0,一1.或者是两个1,或者是两个0
    /// </summary>
    public int itemCount;
    //
    public double attackTime;
    public double fireRate;
    public double speed;
    //
    public PathType trajectoryType;
    public BulletType[] bulletType;   //看json文件没有加s
    /// <summary> 掉落道具的范围,应该是长度为2的数组 </summary>
    public ItemType[] itemRange;

   public override string ToString()
    {
        string str = "";
        str += "\t" + id;
        str += "\t" + attack;
        str += "\t" + life;
        str += "\t" + trajectoryID;
        str += "\t" + starNum;
        str += "\t" + score;
        str += "\t" + itemCount;
        str += "\t" + attackTime;
        str += "\t" + fireRate;
        str += "\t" + speed;
        str += "\t" + trajectoryType.ToString();
        str += "\t" ;
        foreach (var item in bulletType)
        {
            str += item.ToString()+",";
        }
        str += "\t";
        foreach (var item in itemRange)
        {
            str += item.ToString() + ",";
        }

        return str;
    }

}

解析json部分(放在SpawnPlaneMgr)


            {//获取数据AllEnemyData 

                //AllEnemyData allEnemyData= LoadByJson<AllEnemyData>(Paths.CONFIG_ENEMY);
                AllEnemyData allEnemyData= (Paths.CONFIG_ENEMY).JsonPath2Object_JsonConvert<AllEnemyData>();

                Debug.Log(Common.Log_ClassFunction()+ allEnemyData.ToString());
                Dictionary<int,EnemyData> normalDic=new Dictionary<int,EnemyData>();
                Dictionary<int,EnemyData> elitesDic = new Dictionary<int,EnemyData>();
                Dictionary<int,EnemyData> bossDic=new Dictionary<int,EnemyData>();
                foreach (EnemyData item in allEnemyData.Normal)
                {
                    normalDic.Add(item.id,item);
                }
                foreach (EnemyData item in allEnemyData.Elites)
                {
                     elitesDic.Add(item.id, item);
                }
                foreach (EnemyData item in allEnemyData.Boss)
                {
                      bossDic.Add(item.id, item);
                }
                _enemyDataDic.Add(EnemyType.NORMAL, normalDic);
                _enemyDataDic.Add(EnemyType.ELITES, elitesDic);
                _enemyDataDic.Add(EnemyType.BOSS, bossDic);


                foreach (Dictionary<int,EnemyData> i in _enemyDataDic.Values)
                {
                    foreach (EnemyData j in i.Values)
                    {

                        Debug.Log("****"+j.ToString());
                    }
                }            
            }

效果

在这里插入图片描述

watch 原版敌人飞机挂载的脚本

飞机

在这里插入图片描述

发射子弹

在这里插入图片描述

生命

在这里插入图片描述

在这里插入图片描述

bug 只有一个编译单元可具有顶级语句

想有以下效果。
但是仅能有一个脚本有顶级语句
在这里插入图片描述

bug 匿名 error CS0656: Missing compiler required member ‘Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create’

Unity 报错error CS0656: Missing compiler required member ‘Microsoft.CSharp.RuntimeBinder.CSharpArgumen

stars NewIfNull:new

    /// <summary>
    /// ==null就New
    /// <para />:IEnumerable返回的还是null
    /// </summary>
    public static T NewIfNull<T>(this T t ) where T :new()
    {
        if (t == null)
        {
            t = new T();
        }
        return t;
    }

bug NewIfNull不支持IEnumerable

下图的_keys的类:IEnumerable ,IEnumerable 返回为null
。。。
因为实现的 public IEnumerator GetEnumerator() 需要至少一个参数
如下图如果传入 keyData,实例时也无法将 keyData塞一个进去
在这里插入图片描述

    /// <summary>
    /// ==null就New
    /// <para />:IEnumerable返回的还是null
    /// </summary>
    public static T NewIfNull<T>(this T t ) where T :IEnumerable ,new()
    {
        if (t == null)
        {
            t = new T();
        }
        return t;
    }

watch 两种枚举、一个类、配置文件

四者要统一。因为想全改成大写。不统一会导致读取配置文件(敌人啊,子弹啊)报错

public enum BulletType
{
    PLAYER,
    ENEMY_NORMAL_0,
    ENEMY_BOSS_0,
    ENEMY_BOSS_1,
    POWER,
    COUNT
}


public enum BulletName
{
    ENEMY_NORMAL_0,
    ENEMY_BOSS_0,
    ENEMY_BOSS_1,
    COUNT
}

public class AllBulletData
{
    public NormalBulletData PLAYER;
    //public NormalBulletData Enemy_Normal_0;
    //public Boss0BulletData Enemy_Boss_0;
    //public Boss1BulletData Enemy_Boss_1;
    public NormalBulletData ENEMY_NOAMAL_0;
    public Boss0BulletData  ENEMY_BOSS_0;
    public Boss1BulletData  ENEMY_BOSS_1;
}

bug QF打包时 error CS0012

error CS0012: The type ‘Quaternion’ is defined in an assembly that is not referenced. You must add a reference to assembly ‘UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null’.
。。。
如下的问题。和我学习 $“” 时的冲突了。二选一
Net 4x + IL2CPP
Net 2.0+Mono

    /// <summary>缺点Name等没有提示</summary>
    static void 动态类型处理匿名()
    {
        dynamic person = PersonInfo();
        //Unityerror CS0656: Missing compiler required member 'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'
        //Net 4x + IL2CPP   //QF在这种情况下打包报错,所以先 Net2.0+Mono打包先。所以先注释下面
        //https://blog.csdn.net/VAR_720/article/details/132085575
       // Log($"姓名:{person.Name},年龄:{person.Age}");
    }

stars RemoveIfExistComponent

    public static GameObject RemoveIfExistComponent<T>(this GameObject go) where T : Component
    {
        if (go.GetComponent<T>() != null)
        {
            GameObject.DestroyImmediate(go.GetComponent<T>());
        }
        return go;
    } 

stars LocalRotation

    public static Transform LocalRotationDown(this Transform t)
    {
        t.SetLocalRotattionZ(180);
        return t;
    }

    public static Transform SetLocalRotattionX(this Transform t, float x)
    {
        Quaternion quaternion = t.localRotation;
        quaternion.x = x;
        t.localRotation = quaternion;
        return t;
    }

    public static Transform SetLocalRotattionY(this Transform t, float y)
    {
        Quaternion quaternion = t.localRotation;
        quaternion.y = y;
        t.localRotation = quaternion;
        return t;
    }
    public static Transform SetLocalRotattionZ(this Transform t,float z)
    {
        Quaternion quaternion = t.localRotation;
        quaternion.z = z;
        t.localRotation = quaternion;
        return t;
    }

stars Vector3Add Vector3Sub

在这里插入图片描述

在这里插入图片描述

    public static void Example()
    {
        {
            Vector3 v1 = new Vector3(1, 1, 1);
            Vector3 v2 = new Vector3(2, 2, 2);
            Debug.Log($"{v1}+{v2}={v1.Vector3Add(v2)}");
            Debug.Log($"v1={v1},v2={v2}");
            Debug.Log($"{v1}-{v2}={v1.Vector3Sub(v2)}");
            Debug.Log($"v1={v1},v2={v2}");
        }
        {
            Vector3 v1 = new Vector3(1, 1, 1);
            Vector3 v2 = new Vector3(2, 2, 2);
            Debug.Log($"{v1}+{v2}={ExtendVector3.Vector3Add(v1, v2)}");
            Debug.Log($"v1={v1},v2={v2}");
            Debug.Log($"{v1}-{v2}={ExtendVector3.Vector3Sub(v1, v2)}");
            Debug.Log($"v1={v1},v2={v2}");
        }
    }


    #region Add Sub (没有ref的)
    public static Vector3 Vector3Add(  Vector3 v1, Vector3 v2)
    {
        return new Vector3(
            v1.x + v2.x,
            v1.y + v2.y,
            v1.z + v2.z
            );
    }

    public static Vector3 Vector3Sub( Vector3 v1, Vector3 v2)
    {
        return new Vector3(
            v1.x - v2.x,
            v1.y - v2.y,
            v1.z - v2.z
            );
    }
    #endregion  
    #region Add Sub (ref的,不加ref,v1仍然不变)
    public static Vector3 Vector3Add(ref this Vector3 v1,Vector3 v2)
    {
        v1= new Vector3(
            v1.x + v2.x,
            v1.y + v2.y,
            v1.z + v2.z
            );
        return v1;
    }

    public static Vector3 Vector3Sub(ref this Vector3 v1, Vector3 v2)
    {
        v1= new Vector3(
            v1.x - v2.x,
            v1.y - v2.y,
            v1.z - v2.z
            );
        return v1;
    }
    #endregion  

bug 一个敌人都没有实例

PlaneEnemyView.Init
LoadCreatorData.Spawn
if (Waving())这里的条件没理解透,写反了
Waving的意思是还在生成一波一波的敌人中

    /// <summary>生成队的敌人</summary>
    public void SpawnEnemy() //原名Spawn
    {
        ITrajectoryData data = _trajectoryData.GetData(_enemyData.trajectoryType);
        if (Waving()) //_spawnedQueueNum < _planeCreatorData.QueueNum;
        {
            _spawnedQueueNum++;
            for (int i = 0; i < _planeCreatorData.QueuePlaneNum; i++)
            {
                _lastEnemy = InitPlaneEnemyView(i,data);
            }
        }
    }

bug 生成玩家的位置不对

因为BoundsSizeY();写成BoundsMinY();里面是什么代码见名知意
//
用了临时变量多是为方便看打点

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

public class PlayerEnterAni : MonoBehaviour 
{
	// Use this for initialization
	void Start ()
	{
		
		Camera camera = transform.AnyOneCameraFirstMain();
		Vector2 minSize = camera.CameraSizeMin();
        float yMin = minSize.y;
		float boundsSizeY = GetComponent<SpriteRenderer>().BoundsSizeY();
        float fromY = yMin - boundsSizeY * 0.5f;
        //
         float toY	= yMin + boundsSizeY; //0.5刚刚贴着,再加0.5多一小段距离,也就是1	
		Vector3 pos = transform.position;
		Vector3 toPos = pos.SetY(toY);
        //
        transform.SetPosY(fromY); //开始在下面刚刚看不到的地方
		//
		var reader = ReaderMgr.Single.GetReader(ResourcesPath.CONFIG_Game_CONFIG);
		reader[ReaderKey.cameraSpeed].Get<float>(cameraSpeed =>
		{
			float time = 1;
			transform
			.DOMove(toPos + Vector3.up * cameraSpeed * time, time)//玩家、相机相对静止
			.OnComplete(()=>
			{
				GameStateModel.Single.GameState = GameState.START;
			} );
		});
	}
}

效果

可以看到,玩家位置对了,敌人的位置还不对
在这里插入图片描述

bug 敌人的生成位置的Y值不对

一开始生成就在这位置是不对的
在这里插入图片描述

位置由挂载EnemyCreator的节点提供

PlaneEnemyCreator.InitPos
如下,那就是yMax

    private void InitPos(float x)
    {
        var yMax = GameUtil.GetCameraMax().y;
        var xMin = GameUtil.GetCameraMin().x;
        var xMax = GameUtil.GetCameraMax().x;

        var pos = new Vector3();
        pos.x = x;
        pos.y = yMax;
        pos.z = GameLayer.PLANE.Enum2Int(); //按不同类型做了一个分层
        pos.x =(pos.x).Clamp(xMin,xMax);
        transform.position = pos;
    }

原版挂在MainCamera下

所以会自动改变位置
。。。
而我把它们拆分出来了
那就加上CameraMove

	//GameProgressMgr
    private void InitCurCreatorMgrGo(out GameObject go)
    {
        go = new GameObject(GameObjectName.CreatorMgr);
        go.SetParent(GameObject.Find(GameObjectName.Mgr));
        go.AddComponent<EnemyCreaterMgr>().Init(InitData);
		go.AddComponent<CameraMove>();//加上的
    }

在这里插入图片描述
在这里插入图片描述

bug 导弹、子弹、打掉飞机掉的奖励出现MoveComponent缺失仍然调用的情况,并且此时不会移动

MoveComponent不存在
。。。
有存在过的情况
&& speed第一次等于0过
&& 玩家的子弹也静止了

解决 MoveComponent被多次添加到一个节点,区分问题

加了一个字符串字段,进行说明

解决导弹 t.NewIfNull()不行。需要 t=t.NewIfNull()

if是原版
_effect.NewIfNull();后_effect还是等于null,所以改成_effect=_effect.NewIfNull();

我

    /// <summary>
    /// ==null就New
    /// <para />缺点 
    /// <para />01 t.NewIfNull()不行。需要 t=t.NewIfNull()
    /// <para />02 非结构,非值不能用ref
    /// </summary>
    public static T NewIfNull<T>( this T t) where T : new ()
    {
        if (t == null)
        {
            t = new T();
        }
        return t;
    }

bug 奖励不会移动

对比,少了一个MoveComponent

那就是GetOrAdd的事
。。。
GetOrAdd保证的是节点总会有一个该组件
AddIfISNull(ref _move),保证_move为空时会为_move赋值,哪怕节点有多个该组件,只管_move是否为空
在这里插入图片描述
在这里插入图片描述

速度对不上

左边正常,右边有问题
两个组件一个是水平小小的偏移,一个是竖直的的大幅度运动
。。。
通过加Despection, _move.Description = GameObjectName.SlowSpeedEffect;,其它的地方也加了。
发现两个MoveComponent的赋值位置在SlowSpeedEffect
if (false)可以看到是-=的错误
。。。
//SlowSpeedEffect

    public void UpdateFunc()
    {
        if (_move == null)
        { 
            return;
        }
        if (false)
        {
            _speed = (_speed < _speedLimit)
                ? _speedLimit 
                : (_slowSpeed * UnityEngine.Time.deltaTime);
        }
        else
        {
            if (_speed < _speedLimit)
            {
                _speed = _speedLimit;
            }
            else
            {
                _speed -= _slowSpeed * UnityEngine.Time.deltaTime;
            }
        }

        
        _move.Init(_speed);
        _move.Move(_axis);
        _move.Description = GameObjectName.SlowSpeedEffect;
    }

在这里插入图片描述

bug 玩家不能吃掉奖励

还是这个图,ItemCollideMsgComponent的问题
。。。。。。
给对面传入自身,给自身传入对面

  otherLst.ForEach(colliderMsg => colliderMsg.ColliderMsg(transform));//之前写错成lst

在这里插入图片描述

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

public class ColliderComponent : MonoBehaviour
{
    private void Start()
    {
        gameObject.GetOrAddComponent<Rigidbody2D>().gravityScale = 0;
    }

    private void OnTriggerEnter2D(Collider2D other)
    {
        List< IColliderMsg > lst =  GetComponentsInChildren<IColliderMsg>().ToList();
        List< IColliderMsg > otherLst =  other.GetComponentsInChildren<IColliderMsg>().ToList();
        //需要判空
        lst.ForEach(colliderMsg => colliderMsg.ColliderMsg(other.transform));
        otherLst.ForEach(colliderMsg => colliderMsg.ColliderMsg(transform));

    }
}

star GetComponentIfNull

        if (false)
        {
            if (_renderer == null)
            {
                _renderer = GetComponent<SpriteRenderer>();
            }
        }
        else
        { 
            gameObject.GetComponentIfNull(ref _renderer);
        }
    /// <summary>ref存在值类型、结构约束泛型的问题</summary>
    public static GameObject GetComponentIfNull<T>( this GameObject go,ref T c) where T:Component
    {
        if (c == null)
        {
            c = go.GetComponent<T>();
        }
        return go;
    }

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

bug 置顶

背景是需要记录运行时,物体上挂在的脚本名。
在用置顶(某个软件串口在最顶层)软件时,VS有时会抢掉,断掉当前的置顶操作

bug untiy 报错GameObject已被销毁

    protected UiUtil UiUtil
    {
        get
        {
            if (_uiUtil == null)
            {
                try
                {
                    _uiUtil = gameObject.GetOrAddComponent<UiUtil>();
                    _uiUtil.Init();
                }
                //MissingReferenceException: The object of type 'Life'
                //has been destroyed but you are still trying to access it.
                catch (System.Exception e)
                {
                    throw new System.Exception("UiUtil异常:"+e.ToString());
                }

            }

            return _uiUtil;
        }
    }

位置GameRoot/GameUI/Life

正常的
在这里插入图片描述
报错的
在这里插入图片描述

原因 脚本移动

Canvas挂了DontDestroyOnLoad(在LaunchGame里面)的脚本
我在移动了LaunchGame时,没有补回去

直接加Canvas会马上归到DontDestroyOnLoad那边,导致canvasTrans找不到,为空

        UIManager.Single.Init(canvasTrans.GetComponent<Canvas>());

所以在 UIManager.Single.Init里面才加上

    public void Init(Canvas canvas)
    {
        Canvas = canvas;
        Canvas.AddComponentIfNull<DontDestroyOnLoad>();
    }

bug[未解决] The type or namespace name ‘Mapster’ could not be found

mapster.tool version 8.2.0

bug 包“Mapster.Tool 8.2.0”具有一个包类型“DotnetTool”,项目“XXX”不支持该类型。

stars Clamp

        float res = para.Clamp(0.0f, 10.0f);

    public static float Clamp(ref this float para,float min,float max)
    {
        return Mathf.Clamp(para,min,max);
    }

bug 脚本命名只有一个字母大小不同

不支持,
比如
Extendobject,ExtendObject,这样的命名unity不会允许同时存在

bug 某些类型敌人生成器初始化失败

排查是createrData.EnemyType == type//Normal的
深挖是levelData.PlaneCreaterDatas
深挖是我点击了 Tools下的两个Generate,其中一个的动态生成只有一种EnemyType
总结就是动态生成Config的代码没写好
后面讲json有详细讲到

在这里插入图片描述

    public static List<IEnemyCreator> InitCreator(EnemyType type
        , Transform parent
        , AllEnemyData allEnemyData
        , EnemyTrajectoryDataMgr trajectoryData
        , LevelData levelData)
    {
        List<IEnemyCreator> list = new List<IEnemyCreator>();
        foreach (var createrData in levelData.PlaneCreaterDatas)
        {
            if (createrData.EnemyType == type)
            { 
                list.Add(SpawnCreator( parent
                    ,createrData
                    ,allEnemyData
                    ,trajectoryData));            
            }

        }
        if (list.Count == 0)
        {
            Debug.LogError($"EnemyCreator失败,类型:{type}");
        }

        return list;
    }

json LevelEnemyDataConfiga点击更新后发现变化,里面的类型都是0(Normal)

我修改了字段名,直接贴过来麻烦

bug unity调试总是 busy for stackover

bug untiy 更换版本后场景损坏

就是场景里面大部分节点都没了

bug 获取数据长度为负数

json数据被破坏,导致没有该类型的敌人数据,所以l返回的list为空
//GameUtil

    public static List<IEnemyCreator> InitCreator(EnemyType type
        , Transform parent
        , AllEnemyData allEnemyData
        , EnemyTrajectoryDataMgr trajectoryData
        , LevelData levelData)
    {
        List<IEnemyCreator> list = new List<IEnemyCreator>();
        foreach (PlaneCreatorData data in levelData.PlaneCreaterDatas)
        {
            if (data.EnemyType == type) //ever error;data都是normal
            { 
                list.Add(SpawnCreator( parent,data,allEnemyData,trajectoryData));            
            }
            Debug.Log($"data.EnemyType == type=>{data.EnemyType}=={type}");

        }
        if (list.IsNotNull() && list.Count > 0)
        {  
            return list;
        }
        else
        {
            throw new System.Exception($"Creater初始化失败:{type}");
        }
    }

learn 直通过方法名就调用有参方法,怎么传的参数

01 只有一个引用,就是上面01在引用
02 方法中有参数
03 而且参数中是有数据的
所以不清楚这种是怎么把实参传过来的
在这里插入图片描述

Action

public class LoadCreaterData : NormalSingleton<LoadCreaterData> {
    
    private AllEnemyData _allEnemyData;
    private EnemyTrajectoryDataMgr _enemyTrajectoryDataMgr;
    private EnemyCreaterConfigData _enemyCreaterConfigData;
    private Action<AllEnemyData, EnemyTrajectoryDataMgr, EnemyCreaterConfigData> _LoadConfigCallBack;

    public void Init(Action<AllEnemyData,EnemyTrajectoryDataMgr,EnemyCreaterConfigData> callBack)
    {
        if (_allEnemyData != null
            && _enemyTrajectoryDataMgr != null 
            && _enemyCreaterConfigData != null)
        {

            callBack.DoIfNotNull(_allEnemyData,_enemyTrajectoryDataMgr,_enemyCreaterConfigData);
            return;
        }
        _LoadConfigCallBack = callBack;
        InitTrajectoryData();
        InitEnemyData();
        InitCreaterData();
    }

json类改了字段名 LevelData

public class LevelData  //有关json,不要随便改字段名
{
    public PlaneCreatorData[] PlaneCreaterDatas; //敌我飞机
    public MissileCreatorData[] MissileCreaterDatas; //导弹
    public int EnemyNumMax;
    public int EnemyNumMin;
    /// <summary>
    /// 每死亡多少个普通怪生成一波精英怪
    /// </summary>
    public int  NormalDeadNumForSpawnElites;  //public int NormalPerElitesNum;
}

json类改了字段名 PlaneCreatorData

public class PlaneCreatorData : ICreatorData  //json数据没乱改字段名
{
    /// <summary>找图用的。同等级下的不同Id敌人飞机</summary>
    public int IdMax;
    /// <summary>找图用的。同等级下的不同Id敌人飞机</summary>
    public int IdMin;
    /// <summary> 每个飞机队列的飞机数量  </summary>
    public int QueuePlaneNum;
    /// <summary>  生成队列的数量 </summary>
    public int QueueNum;
    public EnemyType Type; // EnemyType  json数据没乱改字段名
    public double X;
}

增加一个接口,表示这是和json有关的,不要修改字段名

也是json,有一个拼写错误的字段

json文件也是这个字段名,要改得全改
SpwanCount

/// <summary>导弹</summary>
public class MissileCreatorData : ICreatorData
{
    /// <summary> 当前导弹的生成批次 </summary>
    public int Batch;
    public double X;
    public int NumOfWarning;
    public double EachWarningTime;
    public int SpwanCount; //这里是错了,但是json文件就是这个,先不动
    public double Speed;
}

modify 两个类NormalCreaterMgr、ElitesCreaterMgr有相同的方法。静态类

public static class CreatorMgrUtil
{
    public static void SpawnAction(List<IEnemyCreator> creatorLst, IEnemyCreator enemyCreator)
    {
        var creater = GetValidCreater(creatorLst, enemyCreator);
        if (creater.IsNotNull())
        {
            creater.Spawn();
        }
    }

    /// <summary>有效生成器</summary>
    public static IEnemyCreator GetValidCreater(List<IEnemyCreator> creatorLst, IEnemyCreator enemyCreator)
    {
        return GetCreater(creatorLst, enemyCreator);
    }

    public static IEnemyCreator GetCreater(List<IEnemyCreator> list, IEnemyCreator enemyCreator)
    {
        enemyCreator = null;
        foreach (IEnemyCreator creater in list)
        {
            if (enemyCreator == null || enemyCreator.GetSpawnRatio() > creater.GetSpawnRatio())
            {
                if (!creater.IsSpawning())
                {
                    enemyCreator = creater;
                }
            }
        }

        return enemyCreator;
    }
}

bug 有物体没有清除干净

Some objects were not cleaned up when closing the scene. (Did you spawn new GameObjects from OnDestroy?)
The following scene GameObjects were found:
LifeCycleMgr
CoroutineMgr
AudioMgr
。。。。
后面也发生了(概率小),但比没有的强

在这里插入图片描述

无效 UnityEngine.Object.Destroy

public class LaunchGame : MonoBehaviour
{
    private void OnApplicationQuit()
    {
        //Destroy(AudioMgr.Single.gameObject);
        //Destroy(CoroutineMgr.Single.gameObject);
        //Destroy(LifeCycleMgr.Single.gameObject);
        //DestroyImmediate(AudioMgr.Single.gameObject);
        //DestroyImmediate(CoroutineMgr.Single.gameObject);
        //DestroyImmediate(LifeCycleMgr.Single.gameObject);
        //以上不起作用
        //后面发现,以下也没起作用
        //UnityEngine.Object.Destroy(AudioMgr.Single.gameObject);
        //UnityEngine.Object.Destroy(CoroutineMgr.Single.gameObject);
        //UnityEngine.Object.Destroy(LifeCycleMgr.Single.gameObject);

    }

01 解决部分 增加OnDestroy

对对其他人的单例,增加这一个

    protected virtual void OnDestroy()
    {
        _single = null;
    }
public class MonoSingletonSimple<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T _single;

    public static T Single
    {
        get
        {
            if (_single == null)
            {
                var go = new GameObject(typeof(T).Name);
                DontDestroyOnLoad(go);
                _single = go.AddComponent<T>();
            }

            return _single;
        }
    }
    protected virtual void OnDestroy()
    {
        _single = null;
    }
}

02 未解决 LifeCycleMgr

盲猜这部分
在这里插入图片描述

stars 条件判断

public static partial class ExtendJugde
{
    public static bool IsAllNull(params object[] paras)
    {
        bool res = paras[0].IsNull();

        for (int i = 0; i < paras.Length; i++)
        {
              res = res && paras[i].IsNull();
        }
        return res;
    }
    public static bool IsAllNotNull(params object[] paras)
    {
        bool res = paras[0].IsNotNull();

        for (int i = 0; i < paras.Length; i++)
        {
            res = res && paras[i].IsNotNull();
        }
        return res;
    }


    public static bool IsEitherNull(params object[] paras)
    {
        bool res = paras[0].IsNull();

        for (int i = 0; i < paras.Length; i++)
        {
            res = res || paras[i].IsNull();
        }
        return res;
    }

    public static bool IsEitherNotNull(params object[] paras)
    {
        bool res = paras[0].IsNotNull();

        for (int i = 0; i < paras.Length; i++)
        {
            res = res || paras[i].IsNotNull();
        }
        return res;
    }

}

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

修改Model

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

stars ShootAt

每次生成文件跳一下
注意, 有可能新建,需要先 AssetDatabase.Refresh();检测到文件的存在

	/// <summary>
	/// 名字形象,
	/// </summary>
	/// <param name="path"></param>

    public static void ShootAt(this string path)
    {
#if UNITY_EDITOR
		AssetDatabase.Refresh();// 有可能新建,需要先  AssetDatabase.Refresh();检测到文件的存在
        Selection_ActiveObject(path);
#endif
    }


    public static void Selection_ActiveObject(string path)
    {
#if UNITY_EDITOR
        Selection.activeObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path);//"Assets/Config/ABCfg.asset";
#endif
    }

stars SubStringStartWith

    public static void Selection_ActiveObject(string path)
    {
#if UNITY_EDITOR
        Selection.activeObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path);//"Assets/Config/ABCfg.asset";
#endif
    }

上面这生效需要路径以“Assets”开头的,而有的是以

    public static readonly string CONFIG_FOLDER = Application.streamingAssetsPath + "/Config";

等开头的(就是D:/),所以有这样一个方法

。。。。。。

    /// <summary>以tagStr为开头截取后面的字符串(包括tarString)</summary>
    public static string SubStringStartWith( this string str,string  tagStr= StringMark.Assets)
    {
        List<string> strs=  str.Split('/').ToList();
        int startIdx = strs.IndexOf(tagStr);
        string res = "";
        for (int i = startIdx; i < strs.Count; i++)
        {
            res += strs[i]+"/"; 
        }
       res= res.Remove( res.LastIndexOf('/'));
        return res;
    }

json之类

音频、每关的敌人,敌人轨迹

提取路径,方便查找

public static class MenuItemPath
{
    public const string Assets_CreateAudioColumeJson="Assets/CreateAudioColumeJson";
    public const string Tools_GenerateLevelEnemyConfig= "Tools/GenerateLevelEnemyConfig";
    public const string Tools_GenerateEnemyTrajectoryData= "Tools/GenerateEnemyTrajectoryData";

}

bug cfg错误

如果你点击了这两个,运行游戏后会报错。因为新生成的config内容少了很多。
比如LevelEnemyDataConfig里面的EnemyType只有0,会导致“# bug 某些类型敌人生成器初始化失败”
在这里插入图片描述

音频

主要是 (path.SubStringStartWith(StringMark.Assets)).ShootAt(); //上面临近的stars有介绍到两个方法
在这里插入图片描述

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

public class JsonDataTool
{
    [MenuItem(MenuItemPath.Assets_CreateAudioColumeJson)]
    private static void CreateJson()
    {
        var ids = Selection.assetGUIDs;
        var path = AssetDatabase.GUIDToAssetPath(ids[0]);
        AudioJson(path);
    }

    private static void AudioJson(string selectedPath)
    {
        if (!selectedPath.EndsWith(ResourcesPath.AUDIO_FOLDER))
            return;

        var info = new DirectoryInfo(selectedPath);
        var fileInfos = info.GetFiles( StringMark.Star , SearchOption.AllDirectories);


        var path = ResourcesPath.CONFIG_AUDIO_VOLUME_CONFIG;
        var volumes = new List<AudioVolume>();

        if (File.Exists(path))
        {
            var data = JsonMapper.ToObject<AudioVolume[]>(File.ReadAllText(path));


            foreach (var fileInfo in fileInfos)
            {
                if (fileInfo.Name.EndsWith(Affixes.Meta))
                { 
                    continue;
                }
                var name = Path.GetFileNameWithoutExtension(fileInfo.Name);
                var temp = new AudioVolume()
                {
                    Name = name,
                    Volume = GetVolume(data, name)
                };
                volumes.Add(temp);
            }
        }
        else
        {
            foreach (var fileInfo in fileInfos)
            {
                if (fileInfo.Name.EndsWith(Affixes.Meta))
                    continue;
                var name = Path.GetFileNameWithoutExtension(fileInfo.Name);
                var temp = new AudioVolume()
                {
                    Name = name,
                    Volume = 0.5f
                };
                volumes.Add(temp);
            }
        }

        var json = JsonMapper.ToJson(volumes);
        File.WriteAllText(path, json);
        Debug.Log("成功生成AudioVolume配置文件");
        (path.SubStringStartWith(StringMark.Assets)).ShootAt(); 
       
    }

    private static double GetVolume(AudioVolume[] data, string key)
    {
        var item = data.Where(u => u.Name == key).FirstOrDefault();
        if (item != null)
            return item.Volume;
        return 0.5f;
    }
}

public class AudioVolume
{
    public string Name { get; set; }
    public double Volume { get; set; }
}

轨迹

其它两个类型数据是抄原始config,方便复原
问题是轨迹数据Data没有W,Config文件又有W

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

public class GenerateEnemyTrajectoryData {

	[MenuItem(MenuItemPath.Tools_GenerateEnemyTrajectoryData)]
	private static void Execute()
	{
		EnemyTrajectoryDataMgr data =  new EnemyTrajectoryDataMgr();
		data.TrajectoryDataDic = new Dictionary<TrajectoryType, ITrajectoryData[]>();
		data.TrajectoryDataDic[TrajectoryType.STRAIGHT] = InitStraightData(data);
		data.TrajectoryDataDic[TrajectoryType.W] = InitWData(data);
		data.TrajectoryDataDic[TrajectoryType.ELLIPSE] = InitEllipseData(data);
        //
		string json = JsonUtil.Dic2Json(data.TrajectoryDataDic);
		string path = ResourcesPath.CONFIG_ENEMY_TRAJECTORY;

        File.WriteAllText(path,json);
		(path.SubStringStartWith(StringMark.Assets)).ShootAt();

	}

    private static ITrajectoryData[] InitStraightData(EnemyTrajectoryDataMgr data)
    {
        List<ITrajectoryData> list = new List<ITrajectoryData>();
        list.Add(SetStraightData(10f));
        list.Add(SetStraightData(90f));
        list.Add(SetStraightData(80f));
        return list.ToArray();
    }

    private static ITrajectoryData[] InitWData(EnemyTrajectoryDataMgr data)
	{
		List<ITrajectoryData> list = new List<ITrajectoryData>();
		list.Add(SetEllipseData());
		return list.ToArray();
	}

    private static ITrajectoryData[] InitEllipseData(EnemyTrajectoryDataMgr data)
    {
        List<ITrajectoryData> list = new List<ITrajectoryData>();
        list.Add(SetWData(20f));
        return list.ToArray();
    }

    #region pri
    private static StraightTrajectoryData SetStraightData(float angle)
	{
		StraightTrajectoryData data = new StraightTrajectoryData();
		data.Angle = angle;
		return data;
	}
    private static WTrajectoryData SetWData(float angle)
    {
        WTrajectoryData data = new WTrajectoryData();
        data.Angle = angle;
        return data;
    }

    private static EllipseTrajectoryData SetEllipseData()
    {
        EllipseTrajectoryData data = new EllipseTrajectoryData()
        {
            YRatioInScreen = 0.8f,
            XRadius = 1,
            YRadius = 0.5f,
            Precision = 20
        };
        return data;
    }
    #endregion


}

bug 轨迹数据为空

轨迹字典只有W类型
字典是json读取的
json读取没对接好大小写(枚举是全大写),如下
在这里插入图片描述

bug unity thread loop failded

以前是先打断点,再运行
现在我是先运行起来,因为要断点的部分还不会运行到,再去打断点,再去运行
有局限性,但也好用

star untiy全局宏定义

我的是net4.x,mcs.rsp。跟链接中的好像是反着来的

添加链接描述
在这里插入图片描述

最近更新

  1. redis+spring面试题

    2024-07-10 22:18:05       0 阅读
  2. 线段树分治+可撤销并查集 学习笔记

    2024-07-10 22:18:05       0 阅读

热门阅读

  1. mac上修改jupyterlab工作目录

    2024-07-10 22:18:05       8 阅读
  2. mongoexport导出聚合查询的mongo数据

    2024-07-10 22:18:05       5 阅读
  3. k8s离线安装单节点elasticsearch7.x

    2024-07-10 22:18:05       7 阅读
  4. d3tree树控件,点击动态加载,默认展开三层

    2024-07-10 22:18:05       5 阅读
  5. ontape备份异机还原的样例

    2024-07-10 22:18:05       6 阅读
  6. [PaddlePaddle飞桨] PaddleSpeech语言小模型部署

    2024-07-10 22:18:05       6 阅读
  7. vivado DQS_BIAS

    2024-07-10 22:18:05       6 阅读
  8. PHP框架详解-symfony框架

    2024-07-10 22:18:05       7 阅读