unityzgy/Assets/Scripts/Managers/GameObjectManager.cs
ayuan9957 bf12e02276 feat: 多语言本地化系统 - 支持中/英/法/俄实时切换
- LocalizationManager: 新增翻译表(SetFormatted/SetDuration/LocalizeOperateMsg)
- LoginPanel: InputField placeholder本地化、字体颜色保持
- HistoryPanel: 用时数据本地化、placeholder本地化
- RecordDetailPanel: 操作详情消息本地化(LanguageChanged重建)
- AppraiseWindowBase: 评价等级本地化、操作消息重建
- EditorAppraiser: 所有评估消息改用InjectOperateMsgLocalized
- StudentOperateRecorder: 新增InjectOperateMsgLocalized方法
- LocalizationLanguageTestBar: 单例模式、ScreenSpaceOverlay筛选
- 字体切换时保留颜色和verticalOverflow
2026-07-16 10:05:59 +08:00

409 lines
13 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DevelopEngine;
using XFramework;
using System;
using UnityEngine.EventSystems;
//场景物体控制器 用于引用存储、获取、物体外部控制、厂区分配、统计需同步物体的位置并发送等逻辑
public class GameObjectManager : MonoSingleton<GameObjectManager>
{
List<CarCtrl> Cars = new List<CarCtrl>();
List<AreaFlageCtrl> areaFlages = new List<AreaFlageCtrl>();//规划完成的一区九场
Dictionary<string, List<CarCtrl>> inAreaCarDic = new Dictionary<string, List<CarCtrl>>();//分配完所属区域的车辆
bool onLooked;//教员监视状态
float timer;
Dictionary<ManagerGOTypes, Dictionary<int,GameObject>> allGOs = new Dictionary<ManagerGOTypes, Dictionary<int,GameObject>>();//所有待管理的对象
CarCtrl currentSelectCar;//当前选中的车辆 用于后期控制
public CarCtrlType ctrlType = CarCtrlType.AI;
AdjustingCarIconData[] carIconMsgs;//待发送的车辆图标数据类
Surveyor rulerManager;
private void Update()
{
if (onLooked) //同步模式下 同步位置信息
{
GetCarsPosAndSend();
}
RayHitMoveCar();
rulerManager?.Update();
}
//获取点击事件 控制车辆移动
private void RayHitMoveCar()
{
if (EventSystem.current != null && EventSystem.current.IsPointerOverGameObject()) return; //射线检测时屏蔽UI部分
if (Cars.Count < 0) return; //无车辆时 不需要射线检测
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;//鼠标射线检测
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, 2000) && hit.collider.gameObject.layer == LayerMask.NameToLayer("Floor"))//只有点击到地面时才设置车辆位移
{
SetCarMovePoints(hit.point);
}
}
}
//梳理车辆位置信息 发送到服务端 每0.5f发送一次同步位置信息
private void GetCarsPosAndSend()
{
if (Cars.Count < 0) return;
timer += Time.unscaledTime;
if (timer < 0.5f) return;
timer = 0;
if (carIconMsgs == null || carIconMsgs.Length != Cars.Count)
{
carIconMsgs = new AdjustingCarIconData[Cars.Count];
}
for (int i = 0; i < Cars.Count; i++)
{
if (carIconMsgs[i] == null)
carIconMsgs[i] = new AdjustingCarIconData();
carIconMsgs[i].carName = Cars[i].transform.name;
Vector2 pos = Cars[i].iconBase_thisIcon.GetComponent<RectTransform>().anchoredPosition;
carIconMsgs[i].anchorPos = new VectorTwo(pos);
carIconMsgs[i].angle = ((CarIconInstanceCtrl)Cars[i].iconBase_thisIcon).dir.transform.localEulerAngles.z;
}
TCPManager.Instance.SendNetMsg<AdjustingCarIconData[]>(carIconMsgs, NetMsgName.Syn);
}
//改变选中装备的展开状态
internal void ChangeCarOpening()
{
//throw new NotImplementedException();
currentSelectCar?.ChangeOpening();
}
//修理所展开时 改变车辆控制方式为人员控制 注册展开事件 展开时分配所属分区
public void StartPersonCtrl()
{
ctrlType = CarCtrlType.Person;
for (int i = 0; i < Cars.Count; i++)
{
Cars[i].ChangeCtrlType(CarCtrlType.Person);
Cars[i].carOpenedEvent += CarOpened;
Cars[i].transform.GetOrAddComponent<Cam_InteractionObject>().Event_ParamClick += Event_ParamClick;
}
CameraCtrl.Instance.AddGOEventSystem();
CameraCtrl.Instance.rightButtonEvent += RightButtonEvent;
}
//右键取消当前选中车辆的选中状态
private void RightButtonEvent()
{
if (currentSelectCar != null)
{
currentSelectCar.carStateChangedEvent -= ShowCarState;//取消车辆状态监视
currentSelectCar.IsSelect = false;//关闭车辆选中状态
ShowCarState(CarState.none);//情况车辆状态显示UI
currentSelectCar = null;//清空选中车辆
}
}
//修理所展开科目时的 车辆选取事件
private void Event_ParamClick(GOEventSystem.GOPointerEventData data)
{
CarCtrl pointHitCar = data.pointerPress.GetComponentInParent<CarCtrl>();
if (pointHitCar == null) return;
if (pointHitCar.OtherUserCtrl) return;//协同模式下他人正在控制的物体无法获取
if (currentSelectCar != pointHitCar)//选了一个新的车辆
{
if (currentSelectCar != null)//当前已经选了一个车辆
{
currentSelectCar.IsSelect = false;
currentSelectCar.carStateChangedEvent -= ShowCarState;//取消对上一个选中车辆的状态监视事件
}
currentSelectCar = pointHitCar;
currentSelectCar.IsSelect = true;
currentSelectCar.carStateChangedEvent += ShowCarState;
ShowCarState(currentSelectCar.CarState);//选中车辆时显示车辆状态
Debug.Log(currentSelectCar.gameObject.name);
}
}
//当前选中车辆的状态发生变化
private void ShowCarState(CarState carState)
{
GameMain.Instance.UIManager.ChangePanel("CarState",carState);
}
//车辆展开完毕时执行 为车辆分配所属区域
private void CarOpened(CarCtrl car)
{
DistributionAreaForOneCar(car);
}
//为当前选择车辆设置移动路点
private void SetCarMovePoints(Vector3 vector3)
{
if (currentSelectCar != null)//选择车辆后点击地面时 移动车辆
{
currentSelectCar.SetWayPoints(new List<Vector3>() { vector3 });
}
}
//获取首车Trans
public Transform GetFirstCar()
{
for (int i = 0; i < Cars.Count; i++)
{
if (Cars[i].carType == CarPosType.firstCar)
return Cars[i].transform;
}
return null;
}
/// <summary>
/// 教员开始监视或取消监视时执行
/// </summary>
/// <param name="isLooked"></param>
public void SetAdjustingState(bool isLooked)
{
this.onLooked = isLooked;
}
/// <summary>
/// 注入待管理的对象
/// </summary>
/// <param name="type">待管理对象的类型</param>
/// <param name="t">带管理的对象</param>
public void InjeectGO(ManagerGOTypes type, GameObject t,int id)
{
if (!allGOs.ContainsKey(type))
allGOs.Add(type, new Dictionary<int,GameObject>());
allGOs[type][id] = t;
}
///// <summary>
///// 获取某类型的所有对象
///// </summary>
///// <param name="type">对象类型</param>
//public GameObject[] GetGOs(ManagerGOTypes type)
//{
// if (allGOs.ContainsKey(type))
// return allGOs[type].ToArray();
// return null;
//}
/// <summary>
/// 删除突发事件检测器
/// </summary>
public void DestoryTuFaGO(int id)
{
if (allGOs[ManagerGOTypes.tufa].ContainsKey(id))
{
Destroy(allGOs[ManagerGOTypes.tufa][id].gameObject);
allGOs[ManagerGOTypes.tufa].Remove(id);
}
else
{
Debug.Log($"不存在id为{id}的触发器,无需删除!");
}
}
//存储新建车辆 便于后续调用 按照排列顺序添加
public void SetNewCar(CarCtrl carCtrl)
{
if (Cars.Count > 0)//设置前一辆车为后车跟随对象
{
carCtrl.SetFollowTarget(Cars[Cars.Count - 1].transform);
}
Cars.Add(carCtrl);
}
//重新为所有车辆规划所属厂区
public void DistributionAreaForCars()
{
for (int i = 0; i < Cars.Count; i++)
{
if (Cars[i].CarState == CarState.opened || Cars[i].CarState == CarState.openingAnim)//完成展开的/展开过程中的车辆规划厂区 未展开的不进行规划
{
DistributionAreaForOneCar(Cars[i]);
}
}
}
//按照最近距离原则 为车辆分配所属厂区
private void DistributionAreaForOneCar(CarCtrl car)
{
string areaName = GetNearestDistanceArea(car.transform.position);
if (!inAreaCarDic.ContainsKey(areaName))
inAreaCarDic.Add(areaName, new List<CarCtrl>());
inAreaCarDic[areaName].Add(car);
car.SetArea(areaName);
}
//获取与车辆最近的厂区名称
private string GetNearestDistanceArea(Vector3 carPos)
{
float nearDistance = float.MaxValue;
string nearFlageName = "";
for (int i = 0; i < areaFlages.Count; i++)
{
float currentDis = Vector3.Distance(carPos, areaFlages[i].transform.position);
if (currentDis < nearDistance)
{
nearDistance = currentDis;
nearFlageName = areaFlages[i].AreaName;
}
}
return nearFlageName;
}
//记录完成位置规划的厂区
public void AddPlannedArea(AreaFlageCtrl area)
{
areaFlages.Add(area);
inAreaCarDic.Add(area.AreaName, new List<CarCtrl>());
if (StudentStateManager.Instance.LearnState == LearnState.learning)//训练模式提示厂区规划距离是否合适
{
string checkResult = EditorAppraiser.CheckAreaDistance(areaFlages);//先检查厂区之间的距离
if (!string.IsNullOrEmpty(checkResult))//厂区之间距离不符合要求
{
GameMain.Instance.UIManager.ShowPromptText(checkResult, Color.blue);
}
else if(area.AreaName== AreaNameEnum..ToString())//厂区间距符合要求 在继续检查是否是洗消场
{
checkResult = EditorAppraiser.CheckAreaToWaterDistance(area.transform);//若是洗消场则继续检查距离水源的间距
if (!string.IsNullOrEmpty(checkResult))//洗消场与水源距离不符合要求
{
GameMain.Instance.UIManager.ShowPromptText(checkResult, Color.blue);
}
}
}
}
//重新规划时移除
public void RemoveArea(AreaFlageCtrl area)
{
if (areaFlages.Contains(area))
{
areaFlages.Remove(area);
inAreaCarDic.Remove(area.AreaName);
}
}
//获取当前所有完成规划的厂区
public List<AreaFlageCtrl> GetAllAreas()
{
return areaFlages;
}
//改变当前棋子是否能够 捡起
public void ChangeAreaFlagePick(bool can)
{
Debug.Log($"当前旗子交互状态:{can}");
for (int i = 0; i < areaFlages.Count; i++)
{
areaFlages[i].ChangePicked(can);
}
}
//获取当前所有厂区的车辆Trans规划汇总 若无车辆时存入区域标志Trans
public Dictionary<string, List<Transform>> GetAreaAndCars()
{
Dictionary<string, List<Transform>> areaAndTransDic = new Dictionary<string, List<Transform>>();
foreach (var item in inAreaCarDic)
{
List<Transform> inAreaTrans = new List<Transform>();
for (int i = 0; i < item.Value.Count; i++)
{
inAreaTrans.Add(item.Value[i].transform);
}
if (inAreaTrans.Count == 0)//未在厂区内部规划车辆
{
inAreaTrans.Add(ArrayHelper.Find(areaFlages, (P) => P.AreaName == item.Key).transform);//将厂区Trans 加入 用于计算距离
}
areaAndTransDic.Add(item.Key, inAreaTrans);
}
return areaAndTransDic;
}
//车队开始移动
public void StartAIMove()
{
for (int i = 0; i < Cars.Count; i++)
{
Cars[i].StartMove();
}
}
//车队结束移动
public void StopMove()
{
for (int i = 0; i < Cars.Count; i++)
{
Cars[i].StopMove();
}
}
//设置行军路径 车队首个元素为首车
public void SetWayPoints(List<Vector3> points)
{
Cars[0].SetWayPoints(points);
}
//获取所有的车辆
public List<CarCtrl> GetCarList()
{
return Cars;
}
///// <summary>
///// 硬件控制当前选中的车辆
///// </summary>
//public void CtrlCarMove(float hor,float ver,float handBreak)
//{
// currentSelectCar?.CarMove(hor, ver, handBreak);
//}
/// <summary>
/// 改变测量状态
/// </summary>
/// <param name="isOn">true 开始测量模式 false 关闭测量模式</param>
public void ChangeCLState(bool isOn)
{
if (isOn)//开时测量 创建测量员
{
rulerManager = new Surveyor(Resources.Load<GameObject>(ResourcesManager.GetPath("Ruler")));
}
else//结束测量 销毁测量员
{
rulerManager?.StopCL();
rulerManager = null;
}
}
}
/// <summary>
/// 待管理的物体类型
/// </summary>
public enum ManagerGOTypes
{
//行军车辆
car,
//修理厂区域标志
areaFlag,
//突发事件触发器
tufa,
//VR模式下能看到的提示文字
vr3DText,
}