用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的子节点BulletRootEnemyBulletMgr enemyBulletMgr;EmitBulletMgr emitBulletMgr;BossBulletEventComponent bossBulletEventComponent;SpawnBulletPointMgr spawnBulletPointMgr;}{Plane的子节点EnemyLife EnemyLifeView enemyLifeView;}{EnemyLife的子节点Item_0,1,2,3EnemyLifeItem 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 initializationprivate 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 辅助 pubpublic 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 priprivate 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_EDITORDrag(Input.mousePosition);
#elseif (Input.touches.Length > 0){Drag(Input.touches[0].position);}
#endifif (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; //upif (_dir== Vector2.down) return _sR.bounds.min.y >= _camera.CameraSizeMin().y; //downif (_dir== Vector2.right) return _sR.bounds.max.x <= _camera.CameraSizeMax().x; //rightif (_dir== Vector2.left) return _sR.bounds.min.x >= _camera.CameraSizeMin().x; //leftreturn 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 Camerapublic 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 CameraSizepublic 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 framevoid 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;}}#endregionpublic 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;}#endregionpublic 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 initializationvoid 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

	//GameProgressMgrprivate 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_EDITORAssetDatabase.Refresh();// 有可能新建,需要先  AssetDatabase.Refresh();检测到文件的存在Selection_ActiveObject(path);
#endif}public static void Selection_ActiveObject(string path){
#if UNITY_EDITORSelection.activeObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path);//"Assets/Config/ABCfg.asset";
#endif}

stars SubStringStartWith

    public static void Selection_ActiveObject(string path){
#if UNITY_EDITORSelection.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 priprivate 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。跟链接中的好像是反着来的

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/868086.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nginx七层(应用层)反向代理:HTTP反向代理proxy_pass篇

Nginx七层&#xff08;应用层&#xff09;反向代理 HTTP反向代理proxy_pass篇 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of thi…

Python3极简教程(一小时学完)中

异常 在这个实验我们学习 Python 的异常以及如何在你的代码中处理它们。 知识点 NameErrorTypeError异常处理&#xff08;try..except&#xff09;异常抛出&#xff08;raise&#xff09;finally 子句 异常 在程序执行过程中发生的任何错误都是异常。每个异常显示一些相关…

fasttext工具介绍

fastText是由Facebook Research团队于2016年开源的一个词向量计算和文本分类工具。尽管在学术上并未带来巨大创新&#xff0c;但其在实际应用中的表现却非常出色&#xff0c;特别是在文本分类任务中&#xff0c;fastText往往能以浅层网络结构取得与深度网络相媲美的精度&#x…

Maven一键配置阿里云远程仓库,让你的项目依赖飞起来!

文章目录 引言一、为什么选择阿里云Maven仓库&#xff1f;二、如何设置Maven阿里云远程仓库&#xff1f;三、使用阿里云Maven仓库的注意事项总结 引言 在软件开发的世界里&#xff0c;Maven无疑是一个强大的项目管理工具&#xff0c;它能够帮助我们自动化构建、依赖管理和项目…

QT5.14.2与Mysql8.0.16配置笔记

1、前言 我的QT版本为 qt-opensource-windows-x86-5.14.2。这是QT官方能提供的自带安装包的最近版本&#xff0c;更新的版本需要自己编译源代码&#xff0c;可点击此链接进行下载&#xff1a;Index of /archive/qt/5.14/5.14.2&#xff0c;选择下载 qt-opensource-windows-x86…

为什么固定尺寸 AdSense 广告依旧会出现并非指定的尺寸广告?

经常在网站上投放谷歌 AdSense广告的站长应该都碰到过&#xff0c;明明投放的是固定尺寸的广告位里旧会出现并非指定尺寸的AdSense 广告&#xff0c;很诡异的感觉。其实这都是因为你的 AdSense 账号广告优化造成的&#xff0c;其中里面就包含了广告尺寸优化&#xff0c;只需要在…

Spring源码十七:Bean实例化入口探索

上一篇Spring源码十六&#xff1a;Bean名称转化我们讨论doGetBean的第一个方法transformedBeanName方法&#xff0c;了解Spring是如何处理特殊的beanName&#xff08;带&符号前缀&#xff09;与Spring的别名机制。今天我们继续往方法下面看&#xff1a; doGetBean 这个方法…

Vue笔记11-Composition API的优势

Options API存在的问题 使用传统Options API中&#xff0c;新增或者修改一个需求&#xff0c;就需要分别在data&#xff0c;methods&#xff0c;computed里修改&#xff0c;而这些选项分布在代码的各个地方&#xff0c;中间还穿插着其他Optional API&#xff0c;如果代码量上来…

JAVA之(方法的重载与重写、this关键字、super关键字)

方法的重载与重写 一、方法的重载与重写1、回顾方法的定义2、重载的概念3、重写 二、this关键字1、何为this方法2、使用方法&#xff08;1&#xff09;在构造方法中指构造器所创建的新对象&#xff08;2&#xff09; 方法中指调用该方法的对象&#xff08;3&#xff09; 在类本…

centos7.9 rpm包安装mysql8.2.0数据库、root设置客户端登录、配置并发、表名大小写敏感、启动重启指令等记录

centos安装mysql8数据库,下载的是rpm-bundle.tar包,这样可以在内网环境离线安装,工作中医院的服务器很多也是内网的,所以这里记录下rpm-bundle.tar包安装的步骤。 lscpu 查看处理器是x86还是arm 下载对应的版本 bundle tar包 ((mysql-8.2.0-1.el7.x86_64.rpm-bundle.tar))…

2023年了,还在手动px转rem吗?

px-to-rem 使用amfe-flexible和postcss-pxtorem在webpack中配置px转rem npm i amfe-flexible -Snpm i postcss-pxtorem -D在main.js中 import flexible from amfe-flexible Vue.use(flexible);index.html中 <meta name"viewport" content"widthdevice-w…

Web3D引擎,three.js堪称扛把子,Babylon.js差点意思。

涉及到Web3D开发&#xff0c;Three.js和Babylon.js是两个备受推崇的引擎。它们都是基于WebGL的开源3D引擎&#xff0c;用于创建交互式的3D图形应用程序&#xff0c;但要细论起来&#xff0c;three.js普及度远超Babylon .js. 一、二者的介绍 Three.js&#xff1a; Three.js 是一…

Socket编程用到的函数TCP UDP实例

最基本的 Socket 模型 参考这次答应我&#xff0c;一举拿下 I/O 多路复用&#xff01; (qq.com) Socket编程详解-CSDN博客 Socket是一种通信机制&#xff0c;通过它可以在不同主机之间进行数据交换。在Socket编程中&#xff0c;有两种常见的通信模式&#xff1a;客户端-服务…

PIP换源的全面指南

##概述 在Python的世界里&#xff0c;pip是不可或缺的包管理工具&#xff0c;它帮助开发者安装和管理Python软件包。然而&#xff0c;由于网络条件或服务器位置等因素&#xff0c;直接使用默认的pip源有时会遇到下载速度慢或者连接不稳定的问题。这时&#xff0c;更换pip源到一…

在Linux操作系统中去修复/etc/fstab文件引起的系统故障。

如果/etcfstab文件中发生错误&#xff0c;有可能导致系统无法正常启动。 比如&#xff1a;系统里的一块磁盘被删除&#xff0c;但是/etc/fstab中关于这块磁盘的信息依然被保存在文件/etc/fstab中。 主要看倒数后两行&#xff0c;系统提示&#xff0c;敲ctrlD或者是直接输入密码…

Linux muduo 网络库

主要记录示意图和知识点框架&#xff1a; 1、阻塞、非阻塞、同步、异步 在处理IO的时候&#xff0c;阻塞和非阻塞都是同步IO&#xff0c;只有使用了特殊的API才是异步IO。 2、五种IO模型&#xff1a; 阻塞、非阻塞、IO复用、信号驱动、异步IO 3、muduo网络库 muduo网络库给用…

【Java】垃圾回收学习笔记(一):Root Search 根可达算法+垃圾回收的起点

文章目录 1. 引用计数法优点缺点 2. 可达性分析 Root Search2.1 那些对象是GC Roots2.2 引用的分类2.3 回收方法区 3. 实现细节3.1 GC的起点&#xff1a;节点枚举OopMap&#xff1a;帮助高效的根节点枚举 3.2 何时开始GC&#xff1a;安全点与安全区域如何选取安全点如何让程序进…

rocketmq-console可视化界面功能说明

rocketmq-console可视化界面功能说明 登录界面OPS(运维)Dashboard(驾驶舱)Cluster(集群)Topic(主题)Consumer(消费者)Producer(生产者)Message(消息)MessageTrace(消息轨迹) rocketmq-console是rocketmq的一款可视化工具&#xff0c;提供了mq的使用详情等功能。 本章针对于rock…

玫瑰千层烤饼:味蕾的芬芳盛宴

在美食的缤纷世界里&#xff0c;有一种独特的存在&#xff0c;它融合了玫瑰的芬芳与烤饼的酥脆&#xff0c;那便是令人陶醉的甘肃美食玫瑰千层烤饼。食家巷玫瑰千层烤饼&#xff0c;宛如一件精心雕琢的艺术品。每一层薄如纸张的面皮&#xff0c;都承载着制作者的细腻与用心。层…

【qt】TCP 服务端怎么收到信息?

上一节,我已经讲了,TCP的监听,是基于上一节的,不知道的可以看看. 当我们的TCP 服务器 有 客户端请求连接的时候,会发出一个信号newConnection(). 在TCP服务端与客户端的通信中,我们需要使用到套接字 QTcpSocket类. 套接字相当于是网络通信的接口,服务段和客户端都要通过它进行通…