unityzgy/Assets/Scripts/UIScripts/Student/StudentSub1EditorPanel.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

562 lines
24 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
using XFramework;
public class StudentSub1EditorPanel : BasePanel
{
const string panelName = "Panel_studentSub1Editor";
Text subName;
Text terrainName;
RectTransform image_PermissibleArea;//允许摆放区域
EditorContentCtrl contentCtrl_cars;
EditorContentCtrl contentCtrl_person;
// public EditorContentCtrl contentCtrl_childEquip;
MyDragMap map;
GameObject carInstacnePrefab;
GameObject onboardInstancePrefab;
Button button_quitLearn;
Button button_lookTerrain;
Button button_editorOver;
PromptTextCtrl editorButtonPrompt;
Slider slider_Speed;
Text text_Speed;
Dropdown dropdown_carsGroup;//下拉菜单规划当前编辑的组别 连续训练时可设置
InputField inputField_taskDetail;//任务简介
//bool isOn;//观察模式是否开启 开启时启用相机控制脚本
CarDetailCtrl carDetail;
public List<CarIconInstanceCtrl> onMapCars = new List<CarIconInstanceCtrl>();//完成摆放的车辆
public Dictionary<FormationGroupingEnum, List<CarIconInstanceCtrl>> onMapCarsGroups = new Dictionary<FormationGroupingEnum, List<CarIconInstanceCtrl>>();//按照分组记录当前的编队
public Dictionary<string, string> onBoardEquip = new Dictionary<string, string>(); //教员端数据库中设置的默认车载装备关系 即该类型车辆必定带有一个该装备
List<string> allCarNames = new List<string>();//记录所有已生成车辆的名称 避免车辆名称重复
CarIconInstanceCtrl firstCar;//设置的首车
CarIconInstanceCtrl lastCar;//设置的尾车
Image firstCarMarkIcon;
Image lastCarMarkIcon;
private IconBase tempIcon;//当前待编辑的 装备/人员图标 不为空时代表存在待编辑的GO 此时无法结束编辑
//string equipPath = Application.streamingAssetsPath + "/Config/装备库.txt";
//string personPath = Application.streamingAssetsPath + "/Config/人员库.txt";
//string childEquipPath = Application.streamingAssetsPath + "/Config/附属装备库.txt";
NetSub1Msg subMsg;
PlanningPlaneCtrl planningCtrl;//线路规划
Toggle drawFirstLine;//绘制首选路线
Toggle drawSecondLine;//绘制备选路线
Toggle toggle_setFirstCar;//首车设置按钮
Toggle toggle_setLastCar;//尾车设置按钮
bool markCarState;//车辆标记模式
bool markFirstCar;//true 标记首车 false 标记尾车
PermissibleArea permissible;//记录场景提供的车辆出发点位置信息 等待地图更新完成后 显示位置
public StudentSub1EditorPanel() : base(new UIType(panelName))
{
}
protected override void InitEvent()
{
EventCenter.Instance.AddEventListener<CarIconInstanceCtrl, bool>("SelectCarEditor", SelectCar);
EventCenter.Instance.AddEventListener<IconBase>("CreatCar", CreatCarInstance);
EventCenter.Instance.AddEventListener<IconBase>("CreatOnboard", CreatOnboardInstance);
//subRecordClass = SubjectMsgManager.recordBase as SubRecordClass;
subName = ActivePanel.Find("Text_subName").GetComponent<Text>();
terrainName = ActivePanel.Find("Text_terrainName").GetComponent<Text>();
contentCtrl_cars = ActivePanel.FindChildTrans("Content_car").GetComponent<EditorContentCtrl>();
contentCtrl_person = ActivePanel.FindChildTrans("Content_person").GetComponent<EditorContentCtrl>();
map = ActivePanel.FindChildTrans("Image_map").GetComponent<MyDragMap>();
carDetail = ActivePanel.FindChildTrans("Image_装备详情").GetComponent<CarDetailCtrl>();
button_lookTerrain = ActivePanel.FindChildTrans("Button_查看地形").GetComponent<Button>();
button_editorOver = ActivePanel.FindChildTrans("Button_finish").GetComponent<Button>();
editorButtonPrompt = button_editorOver.GetComponentInChildren<PromptTextCtrl>();
drawFirstLine = ActivePanel.FindChildTrans("Toggle_firstLine").GetComponent<Toggle>();
drawSecondLine = ActivePanel.FindChildTrans("Toggle_secondLine").GetComponent<Toggle>();
image_PermissibleArea = map.transform.Find("Image_startArea").GetComponent<RectTransform>();
planningCtrl = map.GetComponent<PlanningPlaneCtrl>();
drawFirstLine.onValueChanged.AddListener(DrawFirstLineToggleChanged);
drawSecondLine.onValueChanged.AddListener(DrawSecondLineToggleChanged);
slider_Speed = ActivePanel.Find("Slider_speed").GetComponent<Slider>();
slider_Speed.onValueChanged.AddListener(SpeedChanged);
text_Speed = slider_Speed.transform.Find("Text_Value").GetComponent<Text>();
text_Speed.text = slider_Speed.value.ToString("0.0") + " km/h";
dropdown_carsGroup = ActivePanel.Find("Dropdown_Group").GetComponent<Dropdown>();
inputField_taskDetail = ActivePanel.Find("InputField_renwujianjie").GetComponent<InputField>();
inputField_taskDetail.interactable = false;//禁用输入功能
carInstacnePrefab = Resources.Load<GameObject>(ResourcesManager.GetPath("Image_carInstance"));
onboardInstancePrefab = Resources.Load<GameObject>(ResourcesManager.GetPath("Image_onboardInstance"));
button_lookTerrain.onClick.AddListener(LookTerrain);
button_editorOver.onClick.AddListener(EditorOver);
toggle_setFirstCar = ActivePanel.Find("Toggle_firstCar").GetComponent<Toggle>();
toggle_setLastCar = ActivePanel.Find("Toggle_lastCar").GetComponent<Toggle>();
toggle_setFirstCar.onValueChanged.AddListener(FirstMarkToggle);
toggle_setLastCar.onValueChanged.AddListener(LastMarkToggle);
button_quitLearn = ActivePanel.FindChildTrans("Button_QuitLearn").GetComponent<Button>();
if (StudentStateManager.Instance.LearnState == LearnState.examing)//考核模式不能主动结束
button_quitLearn.gameObject.SetActive(false);
button_quitLearn.onClick.AddListener(QuitButton);
}
public override void OnStart()
{
base.OnStart();
carDetail.Hide();
}
//编辑完成 弹出推演面板 生成对应模型 启用相机控制
private void EditorOver()
{
string unaccomplished = CheckEditor();//检查是否存在未完成操作
if (!string.IsNullOrEmpty(unaccomplished))//部分操作未完成时无法结束编辑
{
editorButtonPrompt.SetPrompt(unaccomplished, Color.red, 3f);
StudentOperateRecorder.Instance.InjectOperateMsg($"点击结束编辑时{unaccomplished}"); //记录未完成的操作 上传至教员端
return;
}
CardingCarOrder();//根据UI位置计算距离梳理行军顺序
EditorAppraiser editorAppraiser = new EditorAppraiser();
editorAppraiser.AppraiseTeam(onMapCarsGroups);//评估编队规划部分
Pop();//弹出本界面
//Debug.Log($"编队部分得分{score}");
if (subMsg.isContinuity)//连续训练时 进入行军推演界面
{
Push(new Sub1LearnPanel()).Change(map.transform.parent);
planningCtrl.StopDrawLine();//编辑完成后自动关闭线路标记模式
List<CarData> carDatas = new List<CarData>();
for (int i = 0; i < onMapCars.Count; i++)
{
CarData carData = new CarData();
if (i == 0)//第一辆车是首车
carData.carType = CarPosType.firstCar;
else
carData.carType = CarPosType.fllowCar;
onMapCars[i].Interactable = false;
carData.Speed = slider_Speed.value;//km/h 转换为m/秒
carData.carName = onMapCars[i].text_IconName.text;
string carType = carData.carName.Split('(')[0];
if (OnboardManager.Instance.onboardDic.ContainsKey(carType))//具备车载装备
carData.onboardEquip = OnboardManager.Instance.onboardDic[carType];
carData.postion = MapHelper.GetWorldPosFromAnchPos(onMapCars[i].GetComponent<RectTransform>().anchoredPosition);
carData.rotation = new Vector3(0, 180 - onMapCars[i].dir.transform.localEulerAngles.z, 0);
carData.icon = onMapCars[i];
carDatas.Add(carData);
}
for (int i = 0; i < subMsg.recordClass.tuFaDatas.Count; i++)//创建时梳理ID便于后续删除
{
subMsg.recordClass.tuFaDatas[i].ID = i;
}
EventCenter.Instance.EventTrigger<List<TuFaData>>("CreatTuFaTrigger", subMsg.recordClass.tuFaDatas);
GameObjectCreater.CreateCarFromAB(carDatas, GameObjectManager.Instance.transform, CarsCreaterOver);
}
else//否则完成本科目 返回初始界面
{
NetAnswerSheet resultMsg = StudentOperateRecorder.Instance.GetResultMsg();
TCPManager.Instance.SendNetMsg<NetAnswerSheet>(resultMsg, NetMsgName.AnswerSheet);//上传答卷
Push(new WaitResultPanel());
}
}
//等待车队创建完成后 开始自动移动
private void CarsCreaterOver()
{
string error;
List<Vector3> wayPoints = planningCtrl.GetPlaneWayPoints(WayType., out error);
wayPoints.RemoveAt(0);//首点位需忽略 从第二个点位开始移动 避免出现首车摆在首选点位横向位置无法到达的情况
GameObjectManager.Instance.SetWayPoints(wayPoints);
GameObjectManager.Instance.StartAIMove();
CameraCtrl.Instance.enabled = true;
CameraCtrl.Instance.LookAtTarget(GameObjectManager.Instance.GetFirstCar());//以首车为观察点 跟随车队
//准备导调环境
GetReadyAdjusting();
}
//准备导调环境
void GetReadyAdjusting()
{
TCPManager.Instance.SendSubStage(NetSubStage.);//进入行军状态 同步给教员端
AdjustingManager.Instance.InjectMapName(subMsg.recordClass.TerrainName);//准备导调初始化数据
AdjustingManager.Instance.InjectTuFaData(subMsg.recordClass.tuFaDatas);//注入当前的突发事件信息
AdjustingManager.Instance.InjectLinesDataSources(planningCtrl.GetLines());
}
//首车标记开关
private void FirstMarkToggle(bool value)
{
markCarState = value;
markFirstCar = value;
Debug.Log(markCarState);
ChangedCarMatkState(value);
}
//尾车标记开关
private void LastMarkToggle(bool value)
{
markCarState = value;
markFirstCar = !value;
Debug.Log(markCarState);
ChangedCarMatkState(value);
}
//开启或关闭子物体标记模式
private void ChangedCarMatkState(bool state)
{
for (int i = 0; i < onMapCars.Count; i++)
{
onMapCars[i].SetMarkModel(state, MarkedSelectedCar);
}
}
//标记模式下选中车辆时执行
private void MarkedSelectedCar(CarIconInstanceCtrl carIconInstance)
{
if (markFirstCar)
{
if (!firstCarMarkIcon)//首次标记首车 或首车删除后再次标记首车
{
firstCarMarkIcon = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>(ResourcesManager.GetPath("Image_firstCarIcon"))).GetComponent<Image>();
}
firstCar = carIconInstance;
if (lastCar == carIconInstance)//标记尾车为尾车时取消尾车标记 标记效果会被UI层级覆盖
lastCar = null;
firstCar.SetMarkedActive(firstCarMarkIcon);
toggle_setFirstCar.isOn = false;
}
else
{
if (!lastCarMarkIcon)
{
lastCarMarkIcon = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>(ResourcesManager.GetPath("Image_lastCarIcon"))).GetComponent<Image>();
}
lastCar = carIconInstance;
if (firstCar == carIconInstance)//标记首车为尾车时取消首车标记 标记效果会被UI层级覆盖
firstCar = null;
lastCar.SetMarkedActive(lastCarMarkIcon);
toggle_setLastCar.isOn = false;
}
}
//检测评估当前编辑完成情况 待完善
private string CheckEditor()
{
string unaccomplished = "";
if (tempIcon != null)//存在拖拽中的人员/装备
{
unaccomplished = LocalizationManager.Get("装备/人员未完成编辑");
return unaccomplished;
}
if (subMsg.isContinuity)//连贯科目1条件下 判断是否完成了五个分组的编辑
{
if (!CheckGroupHasCar(FormationGroupingEnum.))
{
unaccomplished = LocalizationManager.Get("未编辑主体编队");
return unaccomplished;
}
if (!CheckGroupHasCar(FormationGroupingEnum.))
{
unaccomplished = LocalizationManager.Get("未编辑前出保障组");
return unaccomplished;
}
if (!CheckGroupHasCar(FormationGroupingEnum.))
{
unaccomplished = LocalizationManager.Get("未编辑抢救抢修队");
return unaccomplished;
}
if (!CheckGroupHasCar(FormationGroupingEnum.))
{
unaccomplished = LocalizationManager.Get("未编辑指挥组");
return unaccomplished;
}
if (!CheckGroupHasCar(FormationGroupingEnum.殿))
{
unaccomplished = LocalizationManager.Get("未编辑殿后保障组");
return unaccomplished;
}
}
else//科目2 默认只有前进保障队
{
if (!CheckGroupHasCar(FormationGroupingEnum.))
{
unaccomplished = LocalizationManager.Get("未编辑前进保障队");
return unaccomplished;
}
}
List<CarIconInstanceCtrl> noPersonCar = new List<CarIconInstanceCtrl>();//统计未设置人员的车辆
for (int i = 0; i < onMapCars.Count; i++)
{
if (onMapCars[i].personNames.Count == 0)
noPersonCar.Add(onMapCars[i]);
}
if (noPersonCar.Count > 0)//存在未设置人员的车辆 无法继续进行
{
unaccomplished = LocalizationManager.Get("编队中存在空车");
return unaccomplished;
}
string error = planningCtrl.CheckPlanningFinished();
if (!string.IsNullOrEmpty(error))//标记线路问题
{
unaccomplished = error;
return unaccomplished;
}
if (!firstCar || !lastCar)
{
unaccomplished = LocalizationManager.Get("未标记首/尾车辆");
return unaccomplished;
}
return unaccomplished;
}
//检查某分组是否存在车辆 true存在 false不存在
private bool CheckGroupHasCar( FormationGroupingEnum groupingEnum)
{
if (!onMapCarsGroups.ContainsKey(groupingEnum) || onMapCarsGroups[groupingEnum].Count == 0)//某分组不存在或者数量为0
{
return false;
}
return true;
}
//梳理车队行军顺序
private void CardingCarOrder()
{
List<CarIconInstanceCtrl> orderedCars = new List<CarIconInstanceCtrl>();
orderedCars.Add(firstCar);
onMapCars.Remove(firstCar);
onMapCars.Remove(lastCar);
//按照距离首车最小距离的编队规则进行编队 首尾车默认顺序无需计算
for (int i = 0; i < onMapCars.Count; i++)
{
onMapCars[i].firstCarDistacne = Vector3.SqrMagnitude(onMapCars[i].transform.position - firstCar.transform.position);
}
for (int i = 0; i < onMapCars.Count - 1; i++) //双重循环 冒泡排序
{
for (int j = 0; j < onMapCars.Count - i - 1; j++)
{
if (onMapCars[j].firstCarDistacne > onMapCars[j + 1].firstCarDistacne) //采⽤升序来排列
{
CarIconInstanceCtrl temp; //定义⼀个临时值来存储数据,以此来达到交换数值的⽬的
temp = onMapCars[j];
onMapCars[j] = onMapCars[j + 1];
onMapCars[j + 1] = temp;
}
}
}
//排序完成后依次放入排序数组
for (int i = 0; i < onMapCars.Count; i++)
{
orderedCars.Add(onMapCars[i]);
}
orderedCars.Add(lastCar);
onMapCars = orderedCars;//完成梳理后的车队列表
}
//放置/删除一辆运行车辆 并为车辆生成唯一名称
private void SelectCar(CarIconInstanceCtrl arg0, bool arg1)
{
string carTypeName = arg0.text_IconName.text;
if (arg1)
{
FormationGroupingEnum groupingEnum = (FormationGroupingEnum)dropdown_carsGroup.value;//增加时根据当前选择的编队 进行分组
onMapCars.Add(arg0);
if (!onMapCarsGroups.ContainsKey(groupingEnum))
{
onMapCarsGroups[groupingEnum] = new List<CarIconInstanceCtrl>();
}
onMapCarsGroups[groupingEnum].Add(arg0);
int number = 1;
string tempName = carTypeName;
while (allCarNames.Contains(tempName))
{
tempName = $"{carTypeName}({number})";
number++;
}
carTypeName = tempName;
arg0.SetName(carTypeName);
allCarNames.Add(carTypeName);
arg0.SetUpdateIconMsg(groupingEnum, "PlanningGroup");
}
else
{
allCarNames.Remove(carTypeName);
onMapCars.Remove(arg0);
if (onMapCarsGroups.ContainsKey(arg0.GetGroup()))
onMapCarsGroups[arg0.GetGroup()].Remove(arg0);
}
if (arg0 == tempIcon)
{
tempIcon = null;
}
}
//创建一个待放置的汽车图标实例
private void CreatCarInstance(IconBase arg1)
{
if (tempIcon != null) return;
CarIconInstanceCtrl newCarInstance = GameObject.Instantiate(carInstacnePrefab, map.transform).GetComponent<CarIconInstanceCtrl>();
newCarInstance.creater = arg1 as CountIconCreaterCtrl;
tempIcon = newCarInstance;
tempIcon.SetName(arg1.text_IconName.text);
tempIcon.SetSprite(arg1.image_Icon.sprite);
CarIconIntacneInitData initData = new CarIconIntacneInitData();
initData.angle = (int)image_PermissibleArea.transform.localEulerAngles.z;
initData.setInTargetImage = "Image_startArea";
if (OnboardManager.Instance.onboardDic.ContainsKey(arg1.text_IconName.text))
initData.onboardEquipDic[OnboardManager.Instance.onboardDic[arg1.text_IconName.text]] = 1;
//Vector4 initMsg = new Vector4(0, 0, image_PermissibleArea.transform.localEulerAngles.z, 1);
tempIcon.SetUpdateIconMsg(initData, "Init");
}
//创建一个待放置的 车载物体示例图标
private void CreatOnboardInstance(IconBase arg1)
{
if (tempIcon != null) return;
IconCreaterCtrl iconCreater = arg1 as IconCreaterCtrl;
tempIcon = GameObject.Instantiate(onboardInstancePrefab, map.transform).GetComponent<OnboardInstanceCtrl>();
tempIcon.SetName(arg1.text_IconName.text);
tempIcon.SetSprite(arg1.image_Icon.sprite);
tempIcon.SetUpdateIconMsg(iconCreater.createrType);
}
//观察地形按钮
private void LookTerrain()
{
//if (tempIcon!=null) return;//存在为完成设置的图标时 不允许观察地形
Pop();
Push(new LookTerrainPanel()).Change(map.Texture);
//image_Map.transform.parent.gameObject.SetActive(isOn);
CameraCtrl.Instance.enabled = true;//侦察地形时 启用相机
}
//首选路线绘制状态变化
private void DrawFirstLineToggleChanged(bool value)
{
if (value)
{
planningCtrl.StartDrawLine(WayType., Color.green, Color.red);
}
else
planningCtrl.StopDrawLine();
}
//备选路线
private void DrawSecondLineToggleChanged(bool arg0)
{
if (arg0)
{
planningCtrl.StartDrawLine(WayType., Color.yellow, Color.red);
}
else
planningCtrl.StopDrawLine();
}
/// <summary>
/// 根据科目信息更新编辑界面
/// </summary>
/// <param name="newData"></param>
public override void Change(object newData)
{
subMsg = newData as NetSub1Msg;
for (int i = 0; i < subMsg.recordClass.usedEquips.Count; i++)
{
contentCtrl_cars.AddChild(subMsg.recordClass.usedEquips[i].name, subMsg.recordClass.usedEquips[i].count);
}
contentCtrl_person.CreatChildren(subMsg.recordClass.usedPersons.ToArray());
if (subMsg.isContinuity)
{
dropdown_carsGroup.interactable = true;
dropdown_carsGroup.value = 0;
}
else
{
dropdown_carsGroup.interactable = false;
dropdown_carsGroup.options[0].text = LocalizationManager.Get("前进保障队");//不连贯训练时 默认分组为前进保障队
dropdown_carsGroup.RefreshShownValue();
//Debug.Log(dropdown_carsGroup.captionText.text);
}
inputField_taskDetail.text = subMsg.recordClass.taskIntr;
InitMap();
}
//根据科目信息初始化地图信息
async void InitMap()
{
terrainName.text = subMsg.recordClass.TerrainName;
bool result = await map.SetTextureAsyncPath(StreamingAssetsManager.GetPath($"{subMsg.recordClass.TerrainName}_Map"));
if (!result)
{
ShowPromoptText(LocalizationManager.Format("{0}地图加载失败,请检查资源文件后重试!", LocalizationManager.DisplayName(subMsg.recordClass.TerrainName)), Color.red, Vector2.zero);
}
else
{
Debug.Log("地图初始化成功");
}
MapHelper.InitHelper(new Vector2(10000, 10000), new Vector2(8192, 8192));
InitPermissibleArea(permissible);//地图更新完成后 初始化出发区域
}
//根据消息类型 更新界面
public override void Change(string msgType, object value)
{
switch (msgType)
{
case "PermissibleArea"://根据地形附带的位置信息更新允许摆放区域UI
permissible = (PermissibleArea)value;
break;
default:
break;
}
}
//初始化 允许摆放区域 初始化线路线路首个点位
void InitPermissibleArea(PermissibleArea permissibleArea)
{
image_PermissibleArea.sizeDelta = permissibleArea.areaSize/MapHelper.mapScale;
image_PermissibleArea.eulerAngles = new Vector3(0, 0, 360 - permissibleArea.angle);
image_PermissibleArea.anchoredPosition = MapHelper.GetAnchorPostionFromWorld(permissibleArea.areaWorldPostion);
Vector2 startAnchoredPostion = image_PermissibleArea.anchoredPosition + (Vector2)image_PermissibleArea.transform.up * permissibleArea.areaSize.y / 2;
planningCtrl.SetDefultFirstPoint(startAnchoredPostion);
}
//设置车速
void SpeedChanged(float sliderValue)
{
text_Speed.text = slider_Speed.value.ToString("0.0") + " km/h";
}
//退出训练
private void QuitButton()
{
//跳转到登陆场景
SceneSystem.Instance.SetScene(new StudentLoginScene(LoadSceneType.Login));
}
}