用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(this T t, Vector3 pos) where T:Component { t.transform.position = pos; return t; } public static T SetScale(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 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 DoIfNotNull(this Action 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 点击英雄选择时无效,写死是第一个
因为枚举和图像名字是对应的。我可能改动了枚举(头字母小写转成大写)
所以默认成第一个,就是第一张图总亮,也总是她的声音
/// 关联到人物语音的枚举。维持大小写 public enum Hero { Player_0, Player_1, Player_2 }
。。。。
需要改动两处
HeroItem(控制颜色)
HeroItemController(控制音效、SelectHero)
统一用这样的写法//String2Enum字符串转枚举,前面应该有提到过
string spriteName = transform.GetComponent().sprite.name; _hero = (spriteName.UpperFirstLetter()).String2Enum();
。。。。
暴露参数看效果
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().SelectPlaneID.Value = 0; this.GetModel().SelectHeroID.Value = 0; this.GetModel().PassedLevel.Value = 10; this.GetModel().SelectLevel.Value = 10; this.SendCommand(); }
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 /// 首次登录,新手引导 public bool IsFirst = false; private void Start() { if (FindObjectsOfType().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().SelectPlaneID.Value = 0; this.GetModel().SelectHeroID.Value = 0; this.GetModel().PassedLevel.Value = 10; this.GetModel().SelectLevel.Value = 10; this.SendCommand(); } #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() .Init(1f, Vector2.up); //Camera.main.GetOrAddComponent(); //Camera.main.GetOrAddComponent(); //Camera.main.GetOrAddComponent(); //尚未研究 } } }
相机移动脚本
后面在说玩家的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; } /// 首次载入且Go激活 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().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(); _curLevel = GameCurLevel(); _cloud = transform.GetChild(0).GetOrAddComponent(); SetSprite(_curLevel); } void Update() { if (JugdeUpdate(_offsetY, _camera)) { UpdatePos(_offsetY); UpdateSprite(); UpdateLevel(); } } #endregion #region 辅助 int GameCurLevel () { //return GameModel.Single.CurLevel; return this.GetModel().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(pre + level); if (sprite == null) { Debug.Log("MapItemCtrl 还没有所设关卡数的关卡图片=>"+ level); sprite = LoadMgr.Single.Load(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(); //Camera.main.GetOrAddComponent(); //Camera.main.GetOrAddComponent();//尚未研究 // Camera.main .GetOrAddComponent() .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().Init(offsetY, camera); map1.GetOrAddComponent().Init(offsetY, camera); // } } }
效果
按P运行
按W换图
stars GetOrAddComponentDeep
public static T GetOrAddComponentDeep(this Transform root, string childName) where T : Component { Transform result = null; result = root.FindChildDeep(childName); if (result != null) { if (result.GetComponent() != null) { return result.GetComponent(); } return result.AddComponent(); } 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((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(); _renderer = GetComponent(); 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 return _renderer.bounds.min.x 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 { Camera _camera; Vector2 _dir; SpriteRenderer _sR; public InCameraBorderCommand(Transform t,Vector2 dir) { _camera = t.AnyOneCameraFirstMain(); _sR = t.gameObject.GetComponent(); _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.CameraSizeMin().y; //down if (_dir== Vector2.right) return _sR.bounds.max.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(Tags.UICAMERA); } public static Camera MainCamera(this Transform t) { return GameObject .FindGameObjectWithTag(Tags.MAINCAMERA) .GetComponent(); } public static Camera AnyOneCamera(this Transform t) { Camera _camera = Object.FindObjectOfType(); 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 mObjectPool; GameObject _curGo; void Start() { mObjectPool = new SimpleObjectPool(() => { 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 _bulletPrefabDic = new Dictionary(); Dictionary _bulletModelDic = new Dictionary(); const int _preloadCnt=10; #endregion #region 生命 public void OnSingletonInit() { ResKit.Init(); ResLoader resLoader = ResLoader.Allocate(); // GameObject prefab = resLoader.LoadSync("Bullet"); SimpleObjectPool pool = new SimpleObjectPool(() => { 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 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 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.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((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((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(); //直接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((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 HeroLst = new List(); public override string ToString() { string str = ""; foreach (Hero hero in HeroLst) { str += "\n" + hero.ToString(); } return str; } } /// /// 01 里面的属性字段一定要有public /// System.Text.Json.JsonSerializer.Serialize就一定要有publc的属性 /// 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(); 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(); Debug.Log("02 " + cAfter.ToString()); } } public static JsonType GetJsonType_Common(this JsonData data) { return data.GetJsonType(); } /// /// https://blog.csdn.net/weixin_39562801/article/details/90410402 /// public static string Object2Json(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; } /// /// 01 File.ReadText(path) /// 需要一个无参构造函数 /// public static T Json2Object(this string str) { T t = JsonMapper.ToObject(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
/// /// LitJson /// File.ReadAllText /// public static T JsonPath2Object_JsonMapper(this string path) { string str = File.ReadAllText(path); Debug.Log("JsonPath2Object_JsonMapper\n" + str); T t = JsonMapper.ToObject(str); Debug.Log("JsonPath2Object_JsonMapper\n" + t.ToString()); return t; } /// /// Newtonsoft.Json /// File.ReadAllText /// public static T JsonPath2Object_JsonConvert(this string path) { string jsonStr = File.ReadAllText(path); Debug.Log("JsonPath2Object_JsonConvert\n" + jsonStr); T t = JsonConvert.DeserializeObject(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; /// 用来读取所有敌人信息json的一个类 public class AllEnemyData { /// Boss怪 public EnemyData[] Boss { get; set; } /// 精英怪 public EnemyData[] Elites {get; set; } /// 小怪 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; /// -1代表当前是随机轨迹,大于0的值,代表轨迹id public int trajectoryID; public int starNum; public int score; /// 掉落道具的可能性,例如值为10,就代表百分之十的概率 public int itemProbability; /// /// 掉落道具的数量,每个道具都在范围内随机 /// 例如:数量是2,范围是[0,1],那么可能会出一个0,一1.或者是两个1,或者是两个0 /// public int itemCount; // public double attackTime; public double fireRate; public double speed; // public PathType trajectoryType; public BulletType[] bulletType; //看json文件没有加s /// 掉落道具的范围,应该是长度为2的数组 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(Paths.CONFIG_ENEMY); AllEnemyData allEnemyData= (Paths.CONFIG_ENEMY).JsonPath2Object_JsonConvert(); Debug.Log(Common.Log_ClassFunction()+ allEnemyData.ToString()); Dictionary normalDic=new Dictionary(); Dictionary elitesDic = new Dictionary(); Dictionary bossDic=new Dictionary(); 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 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
/// /// ==null就New /// :IEnumerable返回的还是null /// public static T NewIfNull(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塞一个进去
/// /// ==null就New /// :IEnumerable返回的还是null /// public static T NewIfNull(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
/// 缺点Name等没有提示 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(this GameObject go) where T : Component { if (go.GetComponent() != null) { GameObject.DestroyImmediate(go.GetComponent()); } 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的意思是还在生成一波一波的敌人中
/// 生成队的敌人 public void SpawnEnemy() //原名Spawn { ITrajectoryData data = _trajectoryData.GetData(_enemyData.trajectoryType); if (Waving()) //_spawnedQueueNumbug 生成玩家的位置不对
因为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().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(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().Init(InitData); go.AddComponent();//加上的 }bug 导弹、子弹、打掉飞机掉的奖励出现MoveComponent缺失仍然调用的情况,并且此时不会移动
MoveComponent不存在
。。。
有存在过的情况
&& speed第一次等于0过
&& 玩家的子弹也静止了
解决 MoveComponent被多次添加到一个节点,区分问题
加了一个字符串字段,进行说明
解决导弹 t.NewIfNull()不行。需要 t=t.NewIfNull()
if是原版
_effect.NewIfNull();后_effect还是等于null,所以改成_effect=_effect.NewIfNull();
/// /// ==null就New /// 缺点 /// 01 t.NewIfNull()不行。需要 t=t.NewIfNull() /// 02 非结构,非值不能用ref /// public static T NewIfNull( 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 = (_speedbug 玩家不能吃掉奖励
还是这个图,ItemCollideMsgComponent的问题
。。。。。。
给对面传入自身,给自身传入对面
otherLst.ForEach(colliderMsg => colliderMsg.ColliderMsg(transform));//之前写错成lstusing System.Collections.Generic; using System.Linq; using UnityEngine; public class ColliderComponent : MonoBehaviour { private void Start() { gameObject.GetOrAddComponent().gravityScale = 0; } private void OnTriggerEnter2D(Collider2D other) { List lst = GetComponentsInChildren().ToList(); List otherLst = other.GetComponentsInChildren().ToList(); //需要判空 lst.ForEach(colliderMsg => colliderMsg.ColliderMsg(other.transform)); otherLst.ForEach(colliderMsg => colliderMsg.ColliderMsg(transform)); } }star GetComponentIfNull
if (false) { if (_renderer == null) { _renderer = GetComponent(); } } else { gameObject.GetComponentIfNull(ref _renderer); }/// ref存在值类型、结构约束泛型的问题 public static GameObject GetComponentIfNull( this GameObject go,ref T c) where T:Component { if (c == null) { c = go.GetComponent(); } return go; }----------------------------------------------------
bug 置顶
背景是需要记录运行时,物体上挂在的脚本名。
在用置顶(某个软件串口在最顶层)软件时,VS有时会抢掉,断掉当前的置顶操作
bug untiy 报错GameObject已被销毁
protected UiUtil UiUtil { get { if (_uiUtil == null) { try { _uiUtil = gameObject.GetOrAddComponent(); _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());所以在 UIManager.Single.Init里面才加上
public void Init(Canvas canvas) { Canvas = canvas; Canvas.AddComponentIfNull(); }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 InitCreator(EnemyType type , Transform parent , AllEnemyData allEnemyData , EnemyTrajectoryDataMgr trajectoryData , LevelData levelData) { List list = new List(); 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 InitCreator(EnemyType type , Transform parent , AllEnemyData allEnemyData , EnemyTrajectoryDataMgr trajectoryData , LevelData levelData) { List list = new List(); 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 { private AllEnemyData _allEnemyData; private EnemyTrajectoryDataMgr _enemyTrajectoryDataMgr; private EnemyCreaterConfigData _enemyCreaterConfigData; private Action _LoadConfigCallBack; public void Init(Action 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; /// /// 每死亡多少个普通怪生成一波精英怪 /// public int NormalDeadNumForSpawnElites; //public int NormalPerElitesNum; }json类改了字段名 PlaneCreatorData
public class PlaneCreatorData : ICreatorData //json数据没乱改字段名 { /// 找图用的。同等级下的不同Id敌人飞机 public int IdMax; /// 找图用的。同等级下的不同Id敌人飞机 public int IdMin; /// 每个飞机队列的飞机数量 public int QueuePlaneNum; /// 生成队列的数量 public int QueueNum; public EnemyType Type; // EnemyType json数据没乱改字段名 public double X; }增加一个接口,表示这是和json有关的,不要修改字段名
也是json,有一个拼写错误的字段
json文件也是这个字段名,要改得全改
SpwanCount
/// 导弹 public class MissileCreatorData : ICreatorData { /// 当前导弹的生成批次 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 creatorLst, IEnemyCreator enemyCreator) { var creater = GetValidCreater(creatorLst, enemyCreator); if (creater.IsNotNull()) { creater.Spawn(); } } /// 有效生成器 public static IEnemyCreator GetValidCreater(List creatorLst, IEnemyCreator enemyCreator) { return GetCreater(creatorLst, enemyCreator); } public static IEnemyCreator GetCreater(List 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 : 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(); } 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------------------------------------------------------
修改Model
------------------------------------------------------
stars ShootAt
每次生成文件跳一下
注意, 有可能新建,需要先 AssetDatabase.Refresh();检测到文件的存在
/// /// 名字形象, /// /// 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(path);//"Assets/Config/ABCfg.asset"; #endif }stars SubStringStartWith
public static void Selection_ActiveObject(string path) { #if UNITY_EDITOR Selection.activeObject = AssetDatabase.LoadAssetAtPath(path);//"Assets/Config/ABCfg.asset"; #endif }上面这生效需要路径以“Assets”开头的,而有的是以
public static readonly string CONFIG_FOLDER = Application.streamingAssetsPath + "/Config";等开头的(就是D:/),所以有这样一个方法
。。。。。。
/// 以tagStr为开头截取后面的字符串(包括tarString) public static string SubStringStartWith( this string str,string tagStr= StringMark.Assets) { List strs= str.Split('/').ToList(); int startIdx = strs.IndexOf(tagStr); string res = ""; for (int i = startIdx; ijson之类
音频、每关的敌人,敌人轨迹
提取路径,方便查找
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(); if (File.Exists(path)) { var data = JsonMapper.ToObject(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(); 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 list = new List(); list.Add(SetStraightData(10f)); list.Add(SetStraightData(90f)); list.Add(SetStraightData(80f)); return list.ToArray(); } private static ITrajectoryData[] InitWData(EnemyTrajectoryDataMgr data) { List list = new List(); list.Add(SetEllipseData()); return list.ToArray(); } private static ITrajectoryData[] InitEllipseData(EnemyTrajectoryDataMgr data) { List list = new List(); 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。跟链接中的好像是反着来的
添加链接描述