using UnityEngine; using System.IO; using System.Collections.Generic; using OperatorSettings; /// /// 操作員選單截圖工具 v4 /// /// 自動讀取 PageController.currentPage.pageTitle 作為檔名 /// 支援簡體/繁體/英文三語自動命名 /// /// 按鍵: /// - F12:截圖(可重複按,同名自動加 _2, _3...) /// - F9:清除同名計數(切到新頁面後按一下重置計數) /// - Inspector 即時調整裁切框 /// public class OperatorScreenshotCapture : MonoBehaviour { [Header("=== 儲存設定 ===")] [SerializeField] private string basePath = "C:/Users/tomliu/Desktop/A9+相關/A9+操作員手冊相關/TW手冊/A9+手冊截圖_繁體"; [Header("=== 裁切設定(Inspector 即時調整)===")] [SerializeField] private bool enableCrop = true; [SerializeField] private int cropX = 225; [SerializeField] private int cropY = 76; [SerializeField] private int cropWidth = 715; [SerializeField] private int cropHeight = 669; [SerializeField] private int baseWidth = 952; [SerializeField] private int baseHeight = 763; [Header("=== 預覽設定 ===")] [SerializeField] private bool showCropPreview = true; [SerializeField] private Color borderColor = Color.green; [SerializeField] private int borderWidth = 2; [Header("=== 按鍵設定 ===")] [Tooltip("截圖(可重複按覆蓋,不前進)")] [SerializeField] private KeyCode captureKey = KeyCode.F12; [Tooltip("重置同名計數(切到新頁面後按)")] [SerializeField] private KeyCode resetCountKey = KeyCode.F9; [Header("=== 裁切框微調 ===")] [Tooltip("每次微調的像素數")] [SerializeField] private int adjustStep = 5; [Tooltip("快速微調的像素數(Shift加速)")] [SerializeField] private int adjustStepFast = 20; [Header("=== 狀態(自動更新)===")] [SerializeField] private string lastPageTitle = ""; [SerializeField] private int samePageCount = 0; [SerializeField] private int totalCaptured = 0; private Texture2D borderTexture; private PageController pageController; private void Awake() { // 載入儲存的設定 LoadSettings(); // 找到 PageController pageController = FindObjectOfType(); if (pageController == null) Debug.LogError("❌ 找不到 PageController!請確認場景中有操作員選單。"); } private void OnDestroy() { // 離開時自動儲存設定 SaveSettings(); } private void OnApplicationQuit() { SaveSettings(); } private void SaveSettings() { PlayerPrefs.SetInt("SSCrop_X", cropX); PlayerPrefs.SetInt("SSCrop_Y", cropY); PlayerPrefs.SetInt("SSCrop_W", cropWidth); PlayerPrefs.SetInt("SSCrop_H", cropHeight); PlayerPrefs.SetInt("SSCrop_BaseW", baseWidth); PlayerPrefs.SetInt("SSCrop_BaseH", baseHeight); PlayerPrefs.SetInt("SSCrop_Enabled", enableCrop ? 1 : 0); PlayerPrefs.SetString("SSCrop_Path", basePath); PlayerPrefs.Save(); } private void LoadSettings() { if (PlayerPrefs.HasKey("SSCrop_X")) { cropX = PlayerPrefs.GetInt("SSCrop_X"); cropY = PlayerPrefs.GetInt("SSCrop_Y"); cropWidth = PlayerPrefs.GetInt("SSCrop_W"); cropHeight = PlayerPrefs.GetInt("SSCrop_H"); baseWidth = PlayerPrefs.GetInt("SSCrop_BaseW"); baseHeight = PlayerPrefs.GetInt("SSCrop_BaseH"); enableCrop = PlayerPrefs.GetInt("SSCrop_Enabled") == 1; basePath = PlayerPrefs.GetString("SSCrop_Path"); Debug.Log($"✅ 已載入儲存的裁切設定: ({cropX},{cropY}) {cropWidth}x{cropHeight}"); } } private void Update() { // F12: 截圖 if (Input.GetKeyDown(captureKey)) { CaptureCurrentPage(); } // F9: 重置同名計數 if (Input.GetKeyDown(resetCountKey)) { samePageCount = 0; Debug.Log("🔄 同名計數已重置"); } // Ctrl + 方向鍵:移動框位置 // Ctrl + Shift + 方向鍵:調整框大小 if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) { bool shift = Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift); int step = shift ? adjustStepFast : adjustStep; bool changed = false; if (Input.GetKey(KeyCode.LeftAlt) || Input.GetKey(KeyCode.RightAlt)) { // Ctrl + Alt + 方向鍵 = 調整大小 if (Input.GetKeyDown(KeyCode.RightArrow)) { cropWidth += step; changed = true; } if (Input.GetKeyDown(KeyCode.LeftArrow)) { cropWidth -= step; changed = true; } if (Input.GetKeyDown(KeyCode.DownArrow)) { cropHeight += step; changed = true; } if (Input.GetKeyDown(KeyCode.UpArrow)) { cropHeight -= step; changed = true; } } else { // Ctrl + 方向鍵 = 移動位置 if (Input.GetKeyDown(KeyCode.RightArrow)) { cropX += step; changed = true; } if (Input.GetKeyDown(KeyCode.LeftArrow)) { cropX -= step; changed = true; } if (Input.GetKeyDown(KeyCode.DownArrow)) { cropY += step; changed = true; } if (Input.GetKeyDown(KeyCode.UpArrow)) { cropY -= step; changed = true; } } if (changed) { // 確保不為負數 cropX = Mathf.Max(0, cropX); cropY = Mathf.Max(0, cropY); cropWidth = Mathf.Max(10, cropWidth); cropHeight = Mathf.Max(10, cropHeight); SaveSettings(); } } } private void CaptureCurrentPage() { if (pageController == null || pageController.currentPage == null) { Debug.LogWarning("⚠️ 目前沒有頁面,無法截圖"); return; } // 讀取當前頁面標題 string title = pageController.currentPage.pageTitle; if (string.IsNullOrEmpty(title)) { title = pageController.currentPage.name; // fallback 用 GameObject 名 Debug.LogWarning($"⚠️ pageTitle 為空,使用 GameObject 名: {title}"); } // 清理檔名(移除不合法字元) string cleanTitle = CleanFileName(title); // 判斷是否同頁面重複截圖 if (cleanTitle == lastPageTitle) { samePageCount++; } else { lastPageTitle = cleanTitle; samePageCount = 0; } // 產生檔名 string fileName = samePageCount == 0 ? cleanTitle : $"{cleanTitle}_{samePageCount + 1}"; StartCoroutine(CaptureAndSave(fileName)); } private System.Collections.IEnumerator CaptureAndSave(string fileName) { yield return new WaitForEndOfFrame(); int screenWidth = Screen.width; int screenHeight = Screen.height; Texture2D screenshot = new Texture2D(screenWidth, screenHeight, TextureFormat.RGB24, false); screenshot.ReadPixels(new Rect(0, 0, screenWidth, screenHeight), 0, 0); screenshot.Apply(); Texture2D finalImage; if (enableCrop) { float scaleX = (float)screenWidth / (float)baseWidth; float scaleY = (float)screenHeight / (float)baseHeight; int actualCropX = Mathf.RoundToInt(cropX * scaleX); int actualCropY = Mathf.RoundToInt((baseHeight - cropY - cropHeight) * scaleY); int actualCropW = Mathf.RoundToInt(cropWidth * scaleX); int actualCropH = Mathf.RoundToInt(cropHeight * scaleY); actualCropX = Mathf.Clamp(actualCropX, 0, screenWidth - 1); actualCropY = Mathf.Clamp(actualCropY, 0, screenHeight - 1); actualCropW = Mathf.Min(actualCropW, screenWidth - actualCropX); actualCropH = Mathf.Min(actualCropH, screenHeight - actualCropY); finalImage = new Texture2D(actualCropW, actualCropH, TextureFormat.RGB24, false); Color[] pixels = screenshot.GetPixels(actualCropX, actualCropY, actualCropW, actualCropH); finalImage.SetPixels(pixels); finalImage.Apply(); Destroy(screenshot); } else { finalImage = screenshot; } // 存檔(直接存到 basePath,不分子資料夾) if (!Directory.Exists(basePath)) Directory.CreateDirectory(basePath); byte[] bytes = finalImage.EncodeToPNG(); string filePath = Path.Combine(basePath, $"{fileName}.png"); File.WriteAllBytes(filePath, bytes); Destroy(finalImage); totalCaptured++; Debug.Log($"📸 [{totalCaptured}] 已存: {fileName}.png ({bytes.Length / 1024}KB)"); } // 即時顯示裁切框 private void OnGUI() { if (!showCropPreview || !enableCrop) return; if (borderTexture == null) { borderTexture = new Texture2D(1, 1); borderTexture.SetPixel(0, 0, borderColor); borderTexture.Apply(); } if (borderTexture.GetPixel(0, 0) != borderColor) { borderTexture.SetPixel(0, 0, borderColor); borderTexture.Apply(); } float scaleX = (float)Screen.width / (float)baseWidth; float scaleY = (float)Screen.height / (float)baseHeight; float drawX = cropX * scaleX; float drawY = cropY * scaleY; float drawW = cropWidth * scaleX; float drawH = cropHeight * scaleY; // 四條邊框 GUI.DrawTexture(new Rect(drawX, drawY, drawW, borderWidth), borderTexture); GUI.DrawTexture(new Rect(drawX, drawY + drawH - borderWidth, drawW, borderWidth), borderTexture); GUI.DrawTexture(new Rect(drawX, drawY, borderWidth, drawH), borderTexture); GUI.DrawTexture(new Rect(drawX + drawW - borderWidth, drawY, borderWidth, drawH), borderTexture); // 資訊 GUIStyle style = new GUIStyle(GUI.skin.label); style.normal.textColor = borderColor; style.fontSize = 12; GUI.Label(new Rect(drawX + 4, drawY + 4, 300, 18), $"裁切: {cropWidth}x{cropHeight} 位置: ({cropX},{cropY})", style); // 顯示目前頁面名稱 if (pageController != null && pageController.currentPage != null) { string title = pageController.currentPage.pageTitle; if (!string.IsNullOrEmpty(title)) { style.fontSize = 14; GUI.Label(new Rect(drawX + 4, drawY + drawH - 22, 400, 20), $"📋 {title}", style); } } } private string CleanFileName(string name) { // 移除 Windows 檔名不允許的字元 char[] invalid = Path.GetInvalidFileNameChars(); foreach (char c in invalid) name = name.Replace(c, '_'); return name.Trim(); } [ContextMenu("重置所有計數")] public void ResetAll() { samePageCount = 0; totalCaptured = 0; lastPageTitle = ""; Debug.Log("已重置所有計數"); } [ContextMenu("儲存目前設定")] public void ForceSave() { SaveSettings(); Debug.Log("✅ 設定已儲存"); } [ContextMenu("清除儲存的設定")] public void ClearSavedSettings() { PlayerPrefs.DeleteKey("SSCrop_X"); PlayerPrefs.DeleteKey("SSCrop_Y"); PlayerPrefs.DeleteKey("SSCrop_W"); PlayerPrefs.DeleteKey("SSCrop_H"); PlayerPrefs.DeleteKey("SSCrop_BaseW"); PlayerPrefs.DeleteKey("SSCrop_BaseH"); PlayerPrefs.DeleteKey("SSCrop_Enabled"); PlayerPrefs.DeleteKey("SSCrop_Path"); PlayerPrefs.Save(); Debug.Log("🗑️ 已清除儲存的設定"); } }