//
官网的
全民飞机大战(第一季)-----框架设计篇(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
csharp
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
csharp
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();
}
csharp
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场景的脚本分布
csharp
}
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 点击英雄选择时无效,写死是第一个
因为枚举和图像名字是对应的。我可能改动了枚举(头字母小写转成大写)
所以默认成第一个,就是第一张图总亮,也总是她的声音
csharp
/// <summary>关联到人物语音的枚举。维持大小写</summary>
public enum Hero
{
Player_0,
Player_1,
Player_2
}
。。。。
需要改动两处
HeroItem(控制颜色)
HeroItemController(控制音效、SelectHero)
统一用这样的写法//String2Enum字符串转枚举,前面应该有提到过
csharp
string spriteName = transform.GetComponent<Image>().sprite.name;
_hero = (spriteName.UpperFirstLetter()).String2Enum<Hero>();
。。。。
暴露参数看效果
bug 第二次点击相同的的英雄,音效会马上停止
自身逻辑问题,只考虑到不同英雄。
可以加上一个相同英雄的音效判定
HeroItemController
csharp
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
csharp
public string CurAudioName()
{
if (GetSource().clip != null)
{
return GetSource().clip.name;
}
return String.Empty ;
}
modify 地图之相机
初始项目
移动的是相机,图是不动的
启动脚本 GameStart05
主要是这块,其它是复制之前的
csharp
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>();
}
csharp
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
地图是已有的预制体解除脚本了的,
csharp
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
csharp
/****************************************************
文件: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
改一部分,其它照抄前面的
csharp
private void Update()
{
if (Input.GetKeyDown(KeyCode.Q)) //确定是json数据初始慢导致强化面板获取数据失败
{
OpenPanelFunc();
}
if (Input.GetKeyDown(KeyCode.W))
{
this.GetModel<IAirCombatAppModel>().SelectLevel.Value ++;
}
}
MapItemCtrl
csharp
/****************************************************
文件: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
csharp
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
csharp
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
csharp
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的
csharp
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
csharp
/****************************************************
文件: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,让玩家去监听
csharp
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
csharp
/****************************************************
文件: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脚本也在跑了
到现在完成玩家的两个脚本
csharp
{//玩家
//CameraMove cameraMove;
//MoveComponent moveComponent;
PlayerView playerView;
RenderComponent renderComponent;
PlayerEnterAni playerEnterAni;
PlayerBehaviour playerBehaviour;
PlayerController playerController;
ColliderComponent colliderComponent;
PlaneCollideMsgComponent planeCollideMsgComponent;
PlayerBuffMgr playerBuffMgr;
}
限制移动(边界检测)
原版
csharp
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private MoveComponent _move;
//飞机中心点到边界的差值
private Vector2 _offset;
private SpriteRenderer _renderer;
#region 生命
// Use this for initialization
private void Start()
{
_move = GetComponent<MoveComponent>();
_renderer = GetComponent<SpriteRenderer>();
InputMgr.Single.AddListener(KeyCode.W);
InputMgr.Single.AddListener(KeyCode.A);
InputMgr.Single.AddListener(KeyCode.S);
InputMgr.Single.AddListener(KeyCode.D);
MessageMgr.Single.AddListener(KeyCode.W, InputState.PREE, ReveiveW);
MessageMgr.Single.AddListener(KeyCode.A, InputState.PREE, ReveiveA);
MessageMgr.Single.AddListener(KeyCode.S, InputState.PREE, ReveiveS);
MessageMgr.Single.AddListener(KeyCode.D, InputState.PREE, ReveiveD);
InitData();
}
private void InitData()
{
_offset = transform.position - _renderer.bounds.min;
}
private void OnDestroy()
{
InputMgr.Single.RemoveListener(KeyCode.W);
InputMgr.Single.RemoveListener(KeyCode.A);
InputMgr.Single.RemoveListener(KeyCode.S);
InputMgr.Single.RemoveListener(KeyCode.D);
MessageMgr.Single.RemoveListener(KeyCode.W, InputState.PREE, ReveiveW);
MessageMgr.Single.RemoveListener(KeyCode.A, InputState.PREE, ReveiveA);
MessageMgr.Single.RemoveListener(KeyCode.S, InputState.PREE, ReveiveS);
MessageMgr.Single.RemoveListener(KeyCode.D, InputState.PREE, ReveiveD);
}
#endregion
#region 辅助 pub
public void ReveiveW(params object[] args)
{
if (!JudgeUpBorder()) _move.Move(Vector2.up);
}
public void ReveiveA(params object[] args)
{
if (!JudgeLeftBorder()) _move.Move(Vector2.left);
}
public void ReveiveS(params object[] args)
{
if (!JudgeDownBorder()) _move.Move(Vector2.down);
}
public void ReveiveD(params object[] args)
{
if (!JudgeRightBorder()) _move.Move(Vector2.right);
}
#endregion
#region pri
private bool JudgeUpBorder()
{
return _renderer.bounds.max.y >= GameUtil.GetCameraMax().y;
}
private bool JudgeDownBorder()
{
return _renderer.bounds.min.y <= GameUtil.GetCameraMin().y;
}
private bool JudgeLeftBorder()
{
return _renderer.bounds.min.x <= GameUtil.GetCameraMin().x;
}
private bool JudgeRightBorder()
{
return _renderer.bounds.max.x >= GameUtil.GetCameraMax().x;
}
private void ResetPosX(Vector2 border, Vector2 direction)
{
var pos = transform.localPosition;
pos.z = 0;
pos.x = border.x - Vector2.Dot(_offset, direction);
transform.localPosition = pos;
}
private void ResetPosY(Vector2 border, Vector2 direction)
{
var pos = transform.localPosition;
pos.z = 0;
pos.y = border.y - Vector2.Dot(_offset, direction);
transform.localPosition = pos;
}
private void Drag(Vector3 screenPos)
{
var pos = Camera.main.ScreenToWorldPoint(screenPos);
pos.z = 0;
transform.localPosition = pos;
}
#endregion
#region 系统
private void OnMouseDrag()
{
#if UNITY_EDITOR
Drag(Input.mousePosition);
#else
if (Input.touches.Length > 0)
{
Drag(Input.touches[0].position);
}
#endif
if (JudgeUpBorder())
ResetPosY(GameUtil.GetCameraMax(), Vector2.up);
else if (JudgeDownBorder()) ResetPosY(GameUtil.GetCameraMin(), Vector2.down);
if (JudgeLeftBorder())
ResetPosX(GameUtil.GetCameraMin(), Vector2.left);
else if (JudgeRightBorder()) ResetPosX(GameUtil.GetCameraMax(), Vector2.right);
}
#endregion
}
改成InCameraBorderCommand
MoveCommand中判断时候用到InCameraBorderCommand
。。。
Camera的拓展看后面的
csharp
public class MoveCommand : AbstractCommand
{
Transform _t;
Vector2 _dir;
float _speed;
public MoveCommand(Transform t, Vector2 dir, float speed)
{
_t = t;
_dir = dir;
_speed = speed;
}
protected override void OnExecute()
{
if (this.SendCommand(new InCameraBorderCommand(_t, _dir)))
{
_t.Translate(_dir * _speed * Time.deltaTime, Space.World);
}
}
}
public class InCameraBorderCommand : AbstractCommand<bool>
{
Camera _camera;
Vector2 _dir;
SpriteRenderer _sR;
public InCameraBorderCommand(Transform t,Vector2 dir)
{
_camera = t.AnyOneCameraFirstMain();
_sR = t.gameObject.GetComponent<SpriteRenderer>();
_dir = dir;
Debug.LogFormat(Common.Log_ClassFunction()+"{0},{1},{2}", _camera,_sR,_dir);
}
protected override bool OnExecute()
{
if (_dir== Vector2.up) return _sR.bounds.max.y <= _camera.CameraSizeMax().y; //up
if (_dir== Vector2.down) return _sR.bounds.min.y >= _camera.CameraSizeMin().y; //down
if (_dir== Vector2.right) return _sR.bounds.max.x <= _camera.CameraSizeMax().x; //right
if (_dir== Vector2.left) return _sR.bounds.min.x >= _camera.CameraSizeMin().x; //left
return true;
}
}
效果
可以看到 InCameraBorderCommand在跑
stars Camera
将GameUtils中的方法改成this拓展。
前面的边界检测用到
csharp
public static partial class Tags
{
public const string MAINCAMERA = "MainCamera";
public const string UICAMERA = "UICamera";
}
csharp
/****************************************************
文件:ExtendCamera.cs
作者:lenovo
邮箱:
日期:2023/7/9 19:31:26
功能:
*****************************************************/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public static class ExtendCamera
{
#region Camera
public static Camera UICamera(this Transform t)
{
return t.gameObject.FindComponentWithTag<Camera>(Tags.UICAMERA);
}
public static Camera MainCamera(this Transform t)
{
return GameObject
.FindGameObjectWithTag(Tags.MAINCAMERA)
.GetComponent<Camera>();
}
public static Camera AnyOneCamera(this Transform t)
{
Camera _camera = Object.FindObjectOfType<Camera>();
if (_camera == null)
{
if (_camera == null)
Debug.LogError("当前场景中没有相机");
return _camera;
}
else
{
return _camera;
}
}
public static Camera AnyOneCameraFirstMain(this Transform t)
{
Camera camera = t.MainCamera();
if (camera == null)
{
return t.AnyOneCamera();
}
return camera;
}
#endregion
#region CameraSize
public static Vector2 CameraSize(this Camera camera)
{
Vector2 size = Vector2.zero;
if (size == Vector2.zero)
{
var heigth = camera.orthographicSize * 2;
var width = heigth * camera.aspect;
size = new Vector2(width, heigth);
}
return size;
}
public static Vector2 CameraSize(this Transform t)
{
Camera camera = t.AnyOneCameraFirstMain();
Vector2 size = Vector2.zero;
if (camera != null && size == Vector2.zero)
{
var heigth = camera.orthographicSize * 2;
var width = heigth * camera.aspect;
size = new Vector2(width, heigth);
}
return Vector2.zero;
}
public static Vector2 CameraSizeMin(this Transform t)
{
Camera camera = t.AnyOneCameraFirstMain();
return camera.CameraSizeMin();
}
public static Vector2 CameraSizeMin(this Camera camera)
{
if (camera != null)
{
var pos = camera.transform.position;
var size = camera.CameraSize();
return new Vector3(
pos.x - size.x * 0.5f
, pos.y - size.y * 0.5f
, pos.z);
}
return Vector2.zero;
}
public static Vector2 CameraSizeMax(this Transform t)
{
Camera camera = t.AnyOneCameraFirstMain();
return camera.CameraSizeMax();
}
public static Vector2 CameraSizeMax(this Camera camera)
{
if (camera != null)
{
var pos = camera.transform.position;
var size = camera.CameraSize();
return new Vector3(
pos.x + size.x * 0.5f
, pos.y + size.y * 0.5f
, pos.z);
}
return Vector2.zero;
}
#endregion
}
----------------------------------------------------
watch QF对象池
csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace QFramework
{
public class SimpleObjectPoolExample : MonoBehaviour
{
private SimpleObjectPool<GameObject> mObjectPool;
GameObject _curGo;
void Start()
{
mObjectPool = new SimpleObjectPool<GameObject>(() =>
{
var gameObj = new GameObject();
gameObj.Hide();
gameObj.transform.SetParent(transform);
return gameObj;
}, gameObj =>
{
gameObj.Hide();
}, 5);
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Q))
{
_curGo = mObjectPool.Allocate();
_curGo.Show();
_curGo.transform.SetParent(transform);
}
if (Input.GetKeyDown(KeyCode.W))
{
mObjectPool.Recycle(_curGo);
foreach (Transform t in transform)
{
if (t.gameObject.activeInHierarchy == true)
{
_curGo = t.gameObject;
break;
}
}
}
if (Input.GetKeyDown(KeyCode.E))
{
mObjectPool.Clear(go =>
{
Destroy(go);
});
}
}
}
}
----------------------------------------------------
modify 子弹
可以观察到子弹是间歇性自动发射的
位置
父节点在这里,但是根据设置未知的节点挂在对应的飞机下面
脚本
csharp
private void Bullet()
{
{//PoolMgr
{//BulletPool
{//Bullet
Bullet bullet;
BulletEffectMgr bulletEffectMgr;
BulletBehaviour bulletBehaviour;
MoveComponent moveComponent;
BulletCollideMsgComponent bulletCollideMsgComponent;
}
}
}
}
新建一个EmitBulletMgr
给飞机需要实例子弹时,有对应的Pool给GameObject
csharp
/****************************************************
文件: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
csharp
/****************************************************
文件: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
csharp
/****************************************************
文件: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
csharp
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);
}
}
效果
换弹时间
效果
脚本
csharp
/****************************************************
文件: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
csharp
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
csharp
{
"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互转
。。。
这里是用拓展的方式,以及摸清更加细节的注意事项
csharp
/****************************************************
文件:ExtendLitJson.cs
作者:lenovo
邮箱:
日期:2023/7/24 22:32:11
功能:
*****************************************************/
using LitJson;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using UnityEngine;
using Random = UnityEngine.Random;
using System.IO;
public static class ExtendLitJson
{
#region 内部类
public class Heros
{
public List<Hero> HeroLst = new List<Hero>();
public override string ToString()
{
string str = "";
foreach (Hero hero in HeroLst)
{
str += "\n" + hero.ToString();
}
return str;
}
}
/// <summary>
/// 01 里面的属性字段一定要有public
/// <para />System.Text.Json.JsonSerializer.Serialize就一定要有publc的属性
/// </summary>
public class Hero
{
public Hero(string name, int age)
{
Name = name;
Age = age;
}
public string Name { get; set; }
public int Age { get; set; }
//或者
//string _name;
//int _age;
//public string Name { get => _name; set => _name = value; }
//public int Age { get => _age; set => _age = value; }
//public Hero(string name, int age)
//{
// this.Name = name;
// this.Age = age;
//}
public override string ToString()
{
string str = "";
str += "\t" + Name;
str += "\t" + Age;
return str;
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
//或者
//string _name;
//int _age;
//public string Name { get => _name; set => _name = value; }
//public int Age { get => _age; set => _age = value; }
public override string ToString()
{
string str = "";
str += "\t" + Name;
str += "\t" + Age;
return str;
}
}
public class Character
{
public Character()
{
}
public Character(string name, int age)
{
Name = name;
Age = age;
}
public string Name { get; set; }
public int Age { get; set; }
//或者
//string _name;
//int _age;
//public string Name { get => _name; set => _name = value; }
//public int Age { get => _age; set => _age = value; }
public override string ToString()
{
string str = "";
str += "\t" + Name;
str += "\t" + Age;
return str;
}
}
#endregion
public static void Example()
{
if (false)
{
JsonData data = new JsonData();
JsonType type = data.GetJsonType();
}
if (false)
{
ExtendLitJson.Hero hero = new ExtendLitJson.Hero("刘备", 18);
Debug.Log(hero.Object2Json());
}
if (false)
{
ExtendLitJson.Hero hero1 = new ExtendLitJson.Hero("刘备", 18);
ExtendLitJson.Hero hero2 = new ExtendLitJson.Hero("关羽", 18);
ExtendLitJson.Hero hero3 = new ExtendLitJson.Hero("张飞", 18);
ExtendLitJson.Hero[] heroArr = new ExtendLitJson.Hero[3] { hero1, hero2, hero3 };
Debug.Log(heroArr.Object2Json());
}
if (false)
{
ExtendLitJson.Hero hero1 = new ExtendLitJson.Hero("刘备", 18);
ExtendLitJson.Hero hero2 = new ExtendLitJson.Hero("关羽", 18);
ExtendLitJson.Hero hero3 = new ExtendLitJson.Hero("张飞", 18);
ExtendLitJson.Heros heros = new ExtendLitJson.Heros();
heros.HeroLst.Add(hero1);
heros.HeroLst.Add(hero2);
heros.HeroLst.Add(hero3);
Debug.Log(heros.Object2Json());
}
if (false) //想采用 System.Text.Json.JsonSerializer.Serialize。但是引用不到
{
//string str1 = @"{"Name":"刘备","Age":18}";
//string str2 = " /{/"Name/":/"刘备/",/"Age/":18/} ";
var stream = new ExtendLitJson.Person { Name ="刘备",Age=18 };
// string str = System.Text.Json.JsonSerializer.Serialize(stream);
string str = "";
//
ExtendLitJson.Person person;
person = str.Json2Object<ExtendLitJson.Person>();
Debug.Log(person.ToString());
}
if (true)
{
ExtendLitJson.Character cPre=new ExtendLitJson.Character("刘备",18);
string json = cPre.Object2Json() ;
Debug.Log("01 "+json);
ExtendLitJson.Character cAfter = json.Json2Object<ExtendLitJson.Character>();
Debug.Log("02 " + cAfter.ToString());
}
}
public static JsonType GetJsonType_Common(this JsonData data)
{
return data.GetJsonType();
}
/// <summary>
/// https://blog.csdn.net/weixin_39562801/article/details/90410402
/// </summary>
public static string Object2Json<T>(this T t)
{
string str = JsonMapper.ToJson(t);
Regex reg = new Regex(@"(?i)\\[uU]([0-9a-f]{4})");
str = reg.Replace(str, delegate (Match m)
{
return ((char)Convert.ToInt32(m.Groups[1].Value, 16)).ToString();
});
return str;
}
/// <summary>
/// 01 File.ReadText(path)
/// 需要一个无参构造函数
/// </summary>
public static T Json2Object<T>(this string str)
{
T t = JsonMapper.ToObject<T>(str);
return t;
}
}
【未完成】用System.Text.Json解析Json
用的原因
手动敲json数据是不用注意转义、两个双引号之类。
还有new{}
bug [未解决] 尝试使用 System.Text.Json,实现匿名new{}的效果
这个界面安装后,对于Common项目还需要去 "添加引用"才有 System.Text.Json
bug 需要net6
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
csharp
/// <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
csharp
using System;
using System.Collections.Generic;
using UnityEngine;
/// <summary>用来读取所有敌人信息json的一个类</summary>
public class AllEnemyData
{
/// <summary>Boss怪</summary>
public EnemyData[] Boss { get; set; }
/// <summary>精英怪</summary>
public EnemyData[] Elites {get; set; }
/// <summary>小怪</summary>
public EnemyData[] Normal {get; set; }
#region 构造
public AllEnemyData()
{
}
public AllEnemyData(EnemyData[] boss, EnemyData[] elites, EnemyData[] normal)
{
Boss = boss;
Elites = elites;
Normal = normal;
}
#endregion
public EnemyData[] GetData(EnemyType type)
{
switch (type)
{
case EnemyType.NORMAL:
return Normal;
case EnemyType.ELITES:
return Elites;
case EnemyType.BOSS:
return Boss;
default:
return null;
}
}
public override string ToString()
{
string str = "";
if (Normal == null) Debug.LogError("EnemyData Normal为null");
if (Elites == null) Debug.LogError("EnemyData Elites为null");
if (Boss == null) Debug.LogError("EnemyData Boss为null");
foreach (EnemyData item in Normal)
{
str+= item.ToString();
}
foreach (EnemyData item in Elites)
{
str += item.ToString();
}
foreach (EnemyData item in Boss)
{
str += item.ToString();
}
return str;
}
}
public class EnemyData
{
public int id;
public int attack;
public int life;
/// <summary>-1代表当前是随机轨迹,大于0的值,代表轨迹id</summary>
public int trajectoryID;
public int starNum;
public int score;
/// <summary> 掉落道具的可能性,例如值为10,就代表百分之十的概率 </summary>
public int itemProbability;
/// <summary>
/// 掉落道具的数量,每个道具都在范围内随机
/// <para />例如:数量是2,范围是[0,1],那么可能会出一个0,一1.或者是两个1,或者是两个0
/// </summary>
public int itemCount;
//
public double attackTime;
public double fireRate;
public double speed;
//
public PathType trajectoryType;
public BulletType[] bulletType; //看json文件没有加s
/// <summary> 掉落道具的范围,应该是长度为2的数组 </summary>
public ItemType[] itemRange;
public override string ToString()
{
string str = "";
str += "\t" + id;
str += "\t" + attack;
str += "\t" + life;
str += "\t" + trajectoryID;
str += "\t" + starNum;
str += "\t" + score;
str += "\t" + itemCount;
str += "\t" + attackTime;
str += "\t" + fireRate;
str += "\t" + speed;
str += "\t" + trajectoryType.ToString();
str += "\t" ;
foreach (var item in bulletType)
{
str += item.ToString()+",";
}
str += "\t";
foreach (var item in itemRange)
{
str += item.ToString() + ",";
}
return str;
}
}
解析json部分(放在SpawnPlaneMgr)
csharp
{//获取数据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
csharp
/// <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塞一个进去
csharp
/// <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 两种枚举、一个类、配置文件
四者要统一。因为想全改成大写。不统一会导致读取配置文件(敌人啊,子弹啊)报错
csharp
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
csharp
/// <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
csharp
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
csharp
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
csharp
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的意思是还在生成一波一波的敌人中
csharp
/// <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();里面是什么代码见名知意
//
用了临时变量多是为方便看打点
csharp
using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using UnityEngine;
public class PlayerEnterAni : MonoBehaviour
{
// Use this for initialization
void Start ()
{
Camera camera = transform.AnyOneCameraFirstMain();
Vector2 minSize = camera.CameraSizeMin();
float yMin = minSize.y;
float boundsSizeY = GetComponent<SpriteRenderer>().BoundsSizeY();
float fromY = yMin - boundsSizeY * 0.5f;
//
float toY = yMin + boundsSizeY; //0.5刚刚贴着,再加0.5多一小段距离,也就是1
Vector3 pos = transform.position;
Vector3 toPos = pos.SetY(toY);
//
transform.SetPosY(fromY); //开始在下面刚刚看不到的地方
//
var reader = ReaderMgr.Single.GetReader(ResourcesPath.CONFIG_Game_CONFIG);
reader[ReaderKey.cameraSpeed].Get<float>(cameraSpeed =>
{
float time = 1;
transform
.DOMove(toPos + Vector3.up * cameraSpeed * time, time)//玩家、相机相对静止
.OnComplete(()=>
{
GameStateModel.Single.GameState = GameState.START;
} );
});
}
}
效果
可以看到,玩家位置对了,敌人的位置还不对
bug 敌人的生成位置的Y值不对
一开始生成就在这位置是不对的
位置由挂载EnemyCreator的节点提供
PlaneEnemyCreator.InitPos
如下,那就是yMax
csharp
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
csharp
//GameProgressMgr
private void InitCurCreatorMgrGo(out GameObject go)
{
go = new GameObject(GameObjectName.CreatorMgr);
go.SetParent(GameObject.Find(GameObjectName.Mgr));
go.AddComponent<EnemyCreaterMgr>().Init(InitData);
go.AddComponent<CameraMove>();//加上的
}
bug 导弹、子弹、打掉飞机掉的奖励出现MoveComponent缺失仍然调用的情况,并且此时不会移动
MoveComponent不存在
。。。
有存在过的情况
&& speed第一次等于0过
&& 玩家的子弹也静止了
解决 MoveComponent被多次添加到一个节点,区分问题
加了一个字符串字段,进行说明
解决导弹 t.NewIfNull()不行。需要 t=t.NewIfNull()
if是原版
_effect.NewIfNull();后_effect还是等于null,所以改成_effect=_effect.NewIfNull();
csharp
/// <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
csharp
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的问题
。。。。。。
给对面传入自身,给自身传入对面
csharp
otherLst.ForEach(colliderMsg => colliderMsg.ColliderMsg(transform));//之前写错成lst
csharp
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
csharp
if (false)
{
if (_renderer == null)
{
_renderer = GetComponent<SpriteRenderer>();
}
}
else
{
gameObject.GetComponentIfNull(ref _renderer);
}
csharp
/// <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已被销毁
csharp
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找不到,为空
csharp
UIManager.Single.Init(canvasTrans.GetComponent<Canvas>());
所以在 UIManager.Single.Init里面才加上
csharp
public void Init(Canvas canvas)
{
Canvas = canvas;
Canvas.AddComponentIfNull<DontDestroyOnLoad>();
}
bug[未解决] The type or namespace name 'Mapster' could not be found
bug 包"Mapster.Tool 8.2.0"具有一个包类型"DotnetTool",项目"XXX"不支持该类型。
stars Clamp
csharp
float res = para.Clamp(0.0f, 10.0f);
csharp
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有详细讲到
csharp
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
csharp
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
csharp
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
csharp
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
csharp
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
csharp
/// <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有相同的方法。静态类
csharp
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
csharp
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
对对其他人的单例,增加这一个
csharp
protected virtual void OnDestroy()
{
_single = null;
}
csharp
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 条件判断
csharp
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();检测到文件的存在
csharp
/// <summary>
/// 名字形象,
/// </summary>
/// <param name="path"></param>
public static void ShootAt(this string path)
{
#if UNITY_EDITOR
AssetDatabase.Refresh();// 有可能新建,需要先 AssetDatabase.Refresh();检测到文件的存在
Selection_ActiveObject(path);
#endif
}
public static void Selection_ActiveObject(string path)
{
#if UNITY_EDITOR
Selection.activeObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path);//"Assets/Config/ABCfg.asset";
#endif
}
stars SubStringStartWith
csharp
public static void Selection_ActiveObject(string path)
{
#if UNITY_EDITOR
Selection.activeObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(path);//"Assets/Config/ABCfg.asset";
#endif
}
上面这生效需要路径以"Assets"开头的,而有的是以
csharp
public static readonly string CONFIG_FOLDER = Application.streamingAssetsPath + "/Config";
等开头的(就是D:/),所以有这样一个方法
。。。。。。
csharp
/// <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之类
音频、每关的敌人,敌人轨迹
提取路径,方便查找
csharp
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有介绍到两个方法
csharp
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
csharp
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using LitJson;
using UnityEditor;
using UnityEngine;
public class GenerateEnemyTrajectoryData {
[MenuItem(MenuItemPath.Tools_GenerateEnemyTrajectoryData)]
private static void Execute()
{
EnemyTrajectoryDataMgr data = new EnemyTrajectoryDataMgr();
data.TrajectoryDataDic = new Dictionary<TrajectoryType, ITrajectoryData[]>();
data.TrajectoryDataDic[TrajectoryType.STRAIGHT] = InitStraightData(data);
data.TrajectoryDataDic[TrajectoryType.W] = InitWData(data);
data.TrajectoryDataDic[TrajectoryType.ELLIPSE] = InitEllipseData(data);
//
string json = JsonUtil.Dic2Json(data.TrajectoryDataDic);
string path = ResourcesPath.CONFIG_ENEMY_TRAJECTORY;
File.WriteAllText(path,json);
(path.SubStringStartWith(StringMark.Assets)).ShootAt();
}
private static ITrajectoryData[] InitStraightData(EnemyTrajectoryDataMgr data)
{
List<ITrajectoryData> list = new List<ITrajectoryData>();
list.Add(SetStraightData(10f));
list.Add(SetStraightData(90f));
list.Add(SetStraightData(80f));
return list.ToArray();
}
private static ITrajectoryData[] InitWData(EnemyTrajectoryDataMgr data)
{
List<ITrajectoryData> list = new List<ITrajectoryData>();
list.Add(SetEllipseData());
return list.ToArray();
}
private static ITrajectoryData[] InitEllipseData(EnemyTrajectoryDataMgr data)
{
List<ITrajectoryData> list = new List<ITrajectoryData>();
list.Add(SetWData(20f));
return list.ToArray();
}
#region pri
private static StraightTrajectoryData SetStraightData(float angle)
{
StraightTrajectoryData data = new StraightTrajectoryData();
data.Angle = angle;
return data;
}
private static WTrajectoryData SetWData(float angle)
{
WTrajectoryData data = new WTrajectoryData();
data.Angle = angle;
return data;
}
private static EllipseTrajectoryData SetEllipseData()
{
EllipseTrajectoryData data = new EllipseTrajectoryData()
{
YRatioInScreen = 0.8f,
XRadius = 1,
YRadius = 0.5f,
Precision = 20
};
return data;
}
#endregion
}
bug 轨迹数据为空
轨迹字典只有W类型
字典是json读取的
json读取没对接好大小写(枚举是全大写),如下
bug unity thread loop failded
以前是先打断点,再运行
现在我是先运行起来,因为要断点的部分还不会运行到,再去打断点,再去运行
有局限性,但也好用
star untiy全局宏定义
我的是net4.x,mcs.rsp。跟链接中的好像是反着来的