From d77df89ddd470183a6791889aa0104b6db4461aa Mon Sep 17 00:00:00 2001 From: wuwenbo Date: Wed, 31 Dec 2025 17:11:45 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=9F=B3=E9=A2=91=E6=8A=A5?= =?UTF-8?q?=E9=94=99=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=9C=B0=E5=9B=BE=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Unity/Assets/Resources/MapRecord.meta | 8 + .../Assets/Scripts/TH1_Audio/AudioManager.cs | 50 ++--- Unity/Assets/Scripts/TH1_Logic/Core/Main.cs | 8 +- .../Scripts/TH1_Logic/Editor/DialogEditor.cs | 55 +++++ .../TH1_Logic/Editor/DialogEditor.cs.meta | 3 + .../TH1_Logic/Editor/MapEditorWindow.cs | 208 ++++++++++++++++++ .../TH1_Logic/Editor/MapEditorWindow.cs.meta | 3 + .../Scripts/TH1_Logic/Map/MapInteraction.cs | 2 +- Unity/Assets/Scripts/TH1_Logic/MapRecord.meta | 3 + .../TH1_Logic/MapRecord/MapRecordManager.cs | 93 ++++++++ .../MapRecord/MapRecordManager.cs.meta | 3 + 11 files changed, 404 insertions(+), 32 deletions(-) create mode 100644 Unity/Assets/Resources/MapRecord.meta create mode 100644 Unity/Assets/Scripts/TH1_Logic/Editor/DialogEditor.cs create mode 100644 Unity/Assets/Scripts/TH1_Logic/Editor/DialogEditor.cs.meta create mode 100644 Unity/Assets/Scripts/TH1_Logic/Editor/MapEditorWindow.cs create mode 100644 Unity/Assets/Scripts/TH1_Logic/Editor/MapEditorWindow.cs.meta create mode 100644 Unity/Assets/Scripts/TH1_Logic/MapRecord.meta create mode 100644 Unity/Assets/Scripts/TH1_Logic/MapRecord/MapRecordManager.cs create mode 100644 Unity/Assets/Scripts/TH1_Logic/MapRecord/MapRecordManager.cs.meta diff --git a/Unity/Assets/Resources/MapRecord.meta b/Unity/Assets/Resources/MapRecord.meta new file mode 100644 index 000000000..053b0ce6e --- /dev/null +++ b/Unity/Assets/Resources/MapRecord.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5fc75ba2ac9c4aa44be95bcd6889dd8f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Unity/Assets/Scripts/TH1_Audio/AudioManager.cs b/Unity/Assets/Scripts/TH1_Audio/AudioManager.cs index 8a5285d98..c9237ce03 100644 --- a/Unity/Assets/Scripts/TH1_Audio/AudioManager.cs +++ b/Unity/Assets/Scripts/TH1_Audio/AudioManager.cs @@ -106,12 +106,7 @@ namespace Logic.Audio if (!_clips.ContainsKey(musicName) || _clips[musicName].Count == 0) return; if (_musicPlayer != null) StopMusic(); else _musicPlayer = GetPlayer(); - if (_clips[musicName].Count > 1) - { - _musicPlayer.Clip = _clips[musicName][1]; - _clips[musicName].RemoveAt(1); - } - else _musicPlayer.Clip = Object.Instantiate(_clips[musicName][0]); + _musicPlayer.Clip = _clips[musicName][0]; if (!_musicPlayer.Clip) { LogSystem.LogError($"音乐资源 {musicName} 未找到或加载失败!"); @@ -129,19 +124,20 @@ namespace Logic.Audio public void StopMusic() { if (_musicPlayer == null) return; - if (_musicPlayer.Stop()) _musicRecord[_musicPlayer.MusicName] = _musicPlayer.Source.time + _musicPlayer.FadeOutDuration; + if (_musicPlayer.Stop()) + { + // 确保记录的时间不超过音频长度 + float currentTime = _musicPlayer.Source.time + _musicPlayer.FadeOutDuration; + float maxTime = _musicPlayer.Length - 0.5f; + _musicRecord[_musicPlayer.MusicName] = Mathf.Clamp(currentTime, 0, Mathf.Max(0, maxTime)); + } } public void PlayAudio(string musicName, float fadeIn = 0f, float fadeOut = 0f, bool isLoop = false) { if (!_clips.ContainsKey(musicName)) return; var player = GetPlayer(); - if (_clips[musicName].Count > 1) - { - player.Clip = _clips[musicName][1]; - _clips[musicName].RemoveAt(1); - } - else player.Clip = Object.Instantiate(_clips[musicName][0]); + player.Clip = _clips[musicName][0]; if (!player.Clip) { LogSystem.LogError($"音频资源 {musicName} 未找到或加载失败!"); @@ -273,26 +269,26 @@ namespace Logic.Audio } StartTime = Time.time; + Source.time = 0; Source.clip = Clip; - - // 确保 recordTime 在有效范围内,留一点余量避免边界问题 - float maxSeekTime = Mathf.Max(0, Clip.length - 0.1f); - recordTime = Mathf.Clamp(recordTime, 0, maxSeekTime); - Source.volume = 0; Source.loop = IsLoop; - Source.Play(); - // 先播放再设置时间,某些音频格式需要这样 - if (recordTime > 0 && Clip.loadState == AudioDataLoadState.Loaded) + Source.Play(); + // 设置播放位置(在 Play 之后) + if (recordTime > 0 && Clip.length > 0) { - try + float maxSeekTime = Clip.length - 0.5f; + if (maxSeekTime > 0 && recordTime < maxSeekTime) { - Source.time = recordTime; - } - catch - { - Source.time = 0; + try + { + Source.time = recordTime; + } + catch (System.Exception e) + { + LogSystem.LogWarning($"AudioPlayer: 设置播放位置失败 - {e.Message}"); + } } } diff --git a/Unity/Assets/Scripts/TH1_Logic/Core/Main.cs b/Unity/Assets/Scripts/TH1_Logic/Core/Main.cs index 3583b24ad..5f23db2a1 100644 --- a/Unity/Assets/Scripts/TH1_Logic/Core/Main.cs +++ b/Unity/Assets/Scripts/TH1_Logic/Core/Main.cs @@ -181,16 +181,17 @@ namespace TH1_Logic.Core } // 继续单机游戏 - public void ContinueGame() + public void ContinueGame(MapData map = null) { //如果没有存档,退出 if (!HasArchive()) return; //step #1 初始化Audio InitGameAudio(); - + //step #2 读取存档的map - MapData = MapData.GetMapData(); + if (map != null) MapData = map; + else MapData = MapData.GetMapData(); MapData.PlayerMap.SelfPlayerId = MapData.PlayerMap.PlayerDataList[0].Id; AIActionScoreCalculator.RefreshCalMap(MapData, true); @@ -211,7 +212,6 @@ namespace TH1_Logic.Core System.GC.WaitForPendingFinalizers(); } - // 开始单机游戏 public void StartGame() { diff --git a/Unity/Assets/Scripts/TH1_Logic/Editor/DialogEditor.cs b/Unity/Assets/Scripts/TH1_Logic/Editor/DialogEditor.cs new file mode 100644 index 000000000..472f2cce8 --- /dev/null +++ b/Unity/Assets/Scripts/TH1_Logic/Editor/DialogEditor.cs @@ -0,0 +1,55 @@ +/* +* @Author: 白哉 +* @Description: +* @Date: 2025年12月31日 星期三 16:12:59 +* @Modify: +*/ + + +using UnityEditor; +using UnityEngine; + + +public class SaveMapDialog : EditorWindow +{ + private string _name; + private string _defaultName; + private System.Action _onConfirm; + + + public static void Show(string title, string name, string defaultName, System.Action onConfirm) + { + var window = CreateInstance(); + window.titleContent = new GUIContent(title); + window._name = name; + window._defaultName = defaultName; + window._onConfirm = onConfirm; + window.minSize = new Vector2(300, 80); + window.maxSize = new Vector2(300, 80); + window.ShowModalUtility(); + } + + private void OnGUI() + { + EditorGUILayout.Space(10); + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField(_name, GUILayout.Width(60)); // 改为 _name + _defaultName = EditorGUILayout.TextField(_defaultName); // 用户输入更新到 _defaultName + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.Space(10); + EditorGUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (GUILayout.Button("确定", GUILayout.Width(80))) + { + _onConfirm?.Invoke(_defaultName); // 传递用户输入的值 + Close(); + } + if (GUILayout.Button("取消", GUILayout.Width(80))) + { + Close(); + } + GUILayout.FlexibleSpace(); + EditorGUILayout.EndHorizontal(); + } +} \ No newline at end of file diff --git a/Unity/Assets/Scripts/TH1_Logic/Editor/DialogEditor.cs.meta b/Unity/Assets/Scripts/TH1_Logic/Editor/DialogEditor.cs.meta new file mode 100644 index 000000000..9b790a41d --- /dev/null +++ b/Unity/Assets/Scripts/TH1_Logic/Editor/DialogEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e1b995e574fc4a35be1807905f516812 +timeCreated: 1767169008 \ No newline at end of file diff --git a/Unity/Assets/Scripts/TH1_Logic/Editor/MapEditorWindow.cs b/Unity/Assets/Scripts/TH1_Logic/Editor/MapEditorWindow.cs new file mode 100644 index 000000000..833ae0a9a --- /dev/null +++ b/Unity/Assets/Scripts/TH1_Logic/Editor/MapEditorWindow.cs @@ -0,0 +1,208 @@ +/* +* @Author: 白哉 +* @Description: 编辑器 +* @Date: 2025年04月22日 星期二 15:04:14 +* @Modify: +*/ + + +using System.Collections.Generic; +using System.IO; +using Logic.Achievement; +using Logic.CrashSight; +using Logic.HeroTask; +using Logic.Multilingual; +using NUnit.Framework; +using RuntimeData; +using TH1_Logic.Core; +using TH1_Logic.HeroTask; +using UnityEditor; +using UnityEngine; + + +namespace Logic.Editor +{ + public class MapEditorWindow : EditorWindow + { + // 滑条 + private Vector2 _barPosition; + + // 背景 + private GUIStyle _redBoxStyle; + private GUIStyle _whiteBoxStyle; + + private List _mapNameList; + private string _selectedMapName; + + + [MenuItem("Tools/地图编辑器")] + private static void ShowWindow() + { + var window = CreateWindow(); + window.titleContent = new GUIContent("地图编辑器"); + window.Show(); + } + + protected virtual void OnEnable() + { + EditorApplication.update += OnEditorUpdate; + RefreshMapNameList(); + } + + private void OnDisable() + { + EditorApplication.update -= OnEditorUpdate; + } + + private void OnEditorUpdate() + { + // 每帧刷新窗口 + Repaint(); + } + + private void OnGUI() + { + var grid = MapRecordManager.Instance.RecordGridData; + if (_redBoxStyle == null) + { + _redBoxStyle = InspectorUtils.GetHelpBoxStyle(); + InspectorUtils.AddBorder(_redBoxStyle, new Color(0.5f, 0.4f, 0.4f, 0.6f)); + } + if (_whiteBoxStyle == null) + { + _whiteBoxStyle = InspectorUtils.GetHelpBoxStyle(); + InspectorUtils.AddBorder(_whiteBoxStyle, new Color(1f, 1f, 1f, 0.2f)); + } + + GUI.skin.button.wordWrap = true; + _barPosition = EditorGUILayout.BeginScrollView(_barPosition); + + EditorGUILayout.BeginHorizontal(); + if (InspectorUtils.InspectorButtonWithTextWidth("刷新")) + { + RefreshMapNameList(); + grid?.Renderer(Main.MapData)?.InstantUpdateGrid(); + } + if (InspectorUtils.InspectorButtonWithTextWidth("保存当前地图")) + { + string defaultName = string.IsNullOrEmpty(_selectedMapName) ? "NewMap" : _selectedMapName; + // 使用 EditorApplication.delayCall 延迟到布局结束后再显示对话框 + EditorApplication.delayCall += () => + { + SaveMapDialog.Show($"保存确认", $"地图名称", defaultName, (string value) => + { + if (!string.IsNullOrEmpty(value)) + { + MapRecordManager.Instance.SaveMapRecord(Main.MapData, value); + _selectedMapName = value; + RefreshMapNameList(); + } + }); + }; + } + EditorGUILayout.EndHorizontal(); + + // 切换存储地图 + EditorGUILayout.BeginHorizontal(); + if (_mapNameList == null || _mapNameList.Count == 0) + { + InspectorUtils.InspectorTextWidthRich($" 暂无已存储地图 "); + } + else + { + InspectorUtils.InspectorTextWidthRich($" 选择地图 : "); + var index = GetIndexInMapNameList(_selectedMapName); + index = EditorGUILayout.Popup(index, _mapNameList.ToArray(), GUILayout.Width(300)); + _selectedMapName = _mapNameList[index]; + if (InspectorUtils.InspectorButtonWithTextWidth("切换")) + { + var map = MapRecordManager.Instance.LoadMapRecord(_selectedMapName); + Main.Instance.ContinueGame(map); + } + } + EditorGUILayout.EndHorizontal(); + + OnGUIGridData(); + EditorGUILayout.EndScrollView(); + } + + private void OnGUIGridData() + { + var grid = MapRecordManager.Instance.RecordGridData; + if (grid == null) return; + EditorGUILayout.BeginVertical(_whiteBoxStyle); + + EditorGUILayout.BeginHorizontal(); + InspectorUtils.InspectorTextWidthRich($" ID : {grid.Id} "); + InspectorUtils.InspectorTextWidthRich($" 位置 : x {grid.Pos.X} y {grid.Pos.Y} "); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + InspectorUtils.InspectorTextWidthRich($" CivId : "); + grid.CivId = (uint)EditorGUILayout.IntField((int)grid.CivId, GUILayout.Width(150)); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + InspectorUtils.InspectorTextWidthRich($" Terrain : "); + grid.Terrain = (TerrainType) EditorGUILayout.EnumPopup(grid.Terrain, GUILayout.Width(150)); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + InspectorUtils.InspectorTextWidthRich($" Feature : "); + grid.Feature = (TerrainFeature) EditorGUILayout.EnumPopup(grid.Feature, GUILayout.Width(150)); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + InspectorUtils.InspectorTextWidthRich($" Vegetation : "); + grid.Vegetation = (Vegetation) EditorGUILayout.EnumPopup(grid.Vegetation, GUILayout.Width(150)); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + InspectorUtils.InspectorTextWidthRich($" Resource : "); + grid.Resource = (ResourceType) EditorGUILayout.EnumPopup(grid.Resource, GUILayout.Width(150)); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + InspectorUtils.InspectorTextWidthRich($" Wonder : "); + grid.Wonder = (WonderLibrary) EditorGUILayout.EnumPopup(grid.Wonder, GUILayout.Width(150)); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + InspectorUtils.InspectorTextWidthRich($" ResourceUnderBuilding : "); + grid.ResourceUnderBuilding = (ResourceType) EditorGUILayout.EnumPopup(grid.ResourceUnderBuilding, GUILayout.Width(150)); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.EndVertical(); + } + + private void RefreshMapNameList() + { + _mapNameList ??= new List(); + _mapNameList.Clear(); + string folderPath = "Assets/Resources/MapRecord"; + if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath); + + string[] files = Directory.GetFiles(folderPath, "*.bytes"); + foreach (string file in files) + { + string fileName = Path.GetFileNameWithoutExtension(file); + if (!string.IsNullOrEmpty(fileName)) + { + _mapNameList.Add(fileName); + } + } + + LogSystem.LogInfo($"Init: 扫描完成,找到 {_mapNameList.Count} 个地图记录"); + } + + private int GetIndexInMapNameList(string name) + { + if (_mapNameList == null) return 0; + for (int i = 0; i < _mapNameList.Count; i++) + { + if (_mapNameList[i] == name) return i; + } + return 0; + } + } +} \ No newline at end of file diff --git a/Unity/Assets/Scripts/TH1_Logic/Editor/MapEditorWindow.cs.meta b/Unity/Assets/Scripts/TH1_Logic/Editor/MapEditorWindow.cs.meta new file mode 100644 index 000000000..de5ea4ff1 --- /dev/null +++ b/Unity/Assets/Scripts/TH1_Logic/Editor/MapEditorWindow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 21c511dbd95449c49b3da0816a319807 +timeCreated: 1767165289 \ No newline at end of file diff --git a/Unity/Assets/Scripts/TH1_Logic/Map/MapInteraction.cs b/Unity/Assets/Scripts/TH1_Logic/Map/MapInteraction.cs index 57ba218c3..89c25016b 100644 --- a/Unity/Assets/Scripts/TH1_Logic/Map/MapInteraction.cs +++ b/Unity/Assets/Scripts/TH1_Logic/Map/MapInteraction.cs @@ -64,7 +64,7 @@ namespace Logic public void OnTileClicked(MapData mapData,GridData gridData) { - + MapRecordManager.Instance.RecordGridData = gridData; AudioManager.Instance.PlayAudio("SFX/UNIT_click",0,0,false); //如果当前是AI正在进行,我方不可点击格子操作,格子不会相应 diff --git a/Unity/Assets/Scripts/TH1_Logic/MapRecord.meta b/Unity/Assets/Scripts/TH1_Logic/MapRecord.meta new file mode 100644 index 000000000..a52a37457 --- /dev/null +++ b/Unity/Assets/Scripts/TH1_Logic/MapRecord.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e00554aa172645f583dbc6f276651145 +timeCreated: 1767080984 \ No newline at end of file diff --git a/Unity/Assets/Scripts/TH1_Logic/MapRecord/MapRecordManager.cs b/Unity/Assets/Scripts/TH1_Logic/MapRecord/MapRecordManager.cs new file mode 100644 index 000000000..f6c84d066 --- /dev/null +++ b/Unity/Assets/Scripts/TH1_Logic/MapRecord/MapRecordManager.cs @@ -0,0 +1,93 @@ +/* +* @Author: 白哉 +* @Description: +* @Date: 2025年09月05日 星期五 15:09:36 +* @Modify: +*/ + + +using System.Collections.Generic; +using System.IO; +using Logic.CrashSight; +using MemoryPack; +using RuntimeData; +using UnityEngine; + + +namespace Logic +{ + public class MapRecordManager + { + public static MapRecordManager Instance = new MapRecordManager(); + + public List MapNameList; + public GridData RecordGridData; + + + // 保存 Map 到 Resource 文件夹下 + public void SaveMapRecord(MapData map, string name) + { + if (map == null) + { + LogSystem.LogWarning("SaveMapRecord: map 为 null,无法保存"); + return; + } + + if (string.IsNullOrEmpty(name)) + { + LogSystem.LogWarning("SaveMapRecord: name 为空,无法保存"); + return; + } + +#if UNITY_EDITOR + try + { + // 确保文件夹存在 + string folderPath = Path.Combine(Application.dataPath, "Resources", "MapRecord"); + if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath); + + string filePath = Path.Combine(folderPath, $"{name}.bytes"); + byte[] bytes = MemoryPackSerializer.Serialize(map); + File.WriteAllBytes(filePath, bytes); + + // 刷新 AssetDatabase + UnityEditor.AssetDatabase.Refresh(); + LogSystem.LogInfo($"SaveMapRecord: 地图 {name} 保存成功,路径: {filePath}"); + } + catch (System.Exception e) + { + LogSystem.LogWarning($"SaveMapRecord: 保存失败 - {e.Message}"); + } +#endif + } + + // 读取 Map 从 Resource 文件夹下 + public MapData LoadMapRecord(string name) + { + if (string.IsNullOrEmpty(name)) + { + LogSystem.LogWarning("LoadMapRecord: name 为空,无法加载"); + return null; + } + try + { + string resourcePath = $"MapRecord/{name}"; + TextAsset textAsset = Resources.Load(resourcePath); + if (textAsset == null) + { + LogSystem.LogWarning($"LoadMapRecord: 未找到资源 {resourcePath}"); + return null; + } + + MapData map = MemoryPackSerializer.Deserialize(textAsset.bytes); + LogSystem.LogInfo($"LoadMapRecord: 地图 {name} 加载成功"); + return map; + } + catch (System.Exception e) + { + LogSystem.LogWarning($"LoadMapRecord: 加载失败 - {e.Message}"); + return null; + } + } + } +} \ No newline at end of file diff --git a/Unity/Assets/Scripts/TH1_Logic/MapRecord/MapRecordManager.cs.meta b/Unity/Assets/Scripts/TH1_Logic/MapRecord/MapRecordManager.cs.meta new file mode 100644 index 000000000..a9b6e1e67 --- /dev/null +++ b/Unity/Assets/Scripts/TH1_Logic/MapRecord/MapRecordManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 54fa5862d8aa47d6bec5037966d979b7 +timeCreated: 1767081016 \ No newline at end of file