using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; using System.Collections; using System.IO; namespace Kill.UI { public class AvatarSelection : MonoBehaviour { [Header("UI Elements")] public Button selectPhotoButton; public RawImage avatarPreview; public GameObject cropCanvas; // 用于裁剪的全屏Canvas public RawImage cropImagePreview; public RectTransform cropArea; public Button confirmCropButton; public Button cancelCropButton; [Header("Avatar Settings")] [Tooltip("生成头像的尺寸(宽高相等)")] public int avatarSize = 100; private Texture2D selectedImage; private Vector2 startPoint; private Vector2 endPoint; private bool isSelecting = false; private RectTransform cropImageRectTransform; private CropAreaHandler cropAreaHandler; void Start() { // 初始化按钮事件 if (selectPhotoButton != null) selectPhotoButton.onClick.AddListener(SelectPhotoFromGallery); if (confirmCropButton != null) confirmCropButton.onClick.AddListener(ConfirmCrop); if (cancelCropButton != null) cancelCropButton.onClick.AddListener(CancelCrop); // 隐藏裁剪界面 if (cropCanvas != null) cropCanvas.SetActive(false); // 获取裁剪图像的RectTransform if (cropImagePreview != null) cropImageRectTransform = cropImagePreview.GetComponent(); // 为裁剪区域添加处理组件 if (cropArea != null) { cropAreaHandler = cropArea.gameObject.AddComponent(); cropAreaHandler.Initialize(this, cropImageRectTransform); } } /// /// 从相册选择照片 /// public void SelectPhotoFromGallery() { #if UNITY_EDITOR // 在编辑器中测试时,加载一个默认图片 string testImagePath = UnityEditor.EditorUtility.OpenFilePanel("Select Image", "", "jpg,png,jpeg"); if (!string.IsNullOrEmpty(testImagePath)) { byte[] fileData = File.ReadAllBytes(testImagePath); selectedImage = new Texture2D(2, 2); selectedImage.LoadImage(fileData); ShowCropInterface(selectedImage); } #else // 使用NativeGallery插件从相册选择图片 bool permission = NativeGallery.CheckPermission(NativeGallery.PermissionType.Read, NativeGallery.MediaType.Image); if (permission) { PickImage(); } else { NativeGallery.RequestPermissionAsync((perm) => { if (perm == NativeGallery.Permission.Granted) { PickImage(); } else { Debug.LogWarning("没有获得相册访问权限"); } }, NativeGallery.PermissionType.Read, NativeGallery.MediaType.Image); } #endif } private void PickImage() { NativeGallery.GetImageFromGallery((path) => { if (path != null) { // 加载选中的图片,并确保纹理是可读的 selectedImage = NativeGallery.LoadImageAtPath(path, -1, false); // 设置 markTextureNonReadable 为 false if (selectedImage != null) { ShowCropInterface(selectedImage); } else { Debug.LogError("无法加载选定的图片"); } } }, "选择一张图片"); } /// /// 显示裁剪界面 /// /// 要裁剪的图片 private void ShowCropInterface(Texture2D image) { if (cropCanvas != null && cropImagePreview != null) { cropImagePreview.texture = image; cropCanvas.SetActive(true); // 调整图片显示大小以适应屏幕并保持原图比例 FitImageToScreen(image); // 重置裁剪区域 if (cropAreaHandler != null) { cropAreaHandler.ResetCropArea(); } } } /// /// 调整图片大小以适应屏幕并保持原图比例,同时确保不超过cropCanvas的大小 /// /// 要调整的图片 private void FitImageToScreen(Texture2D image) { if (cropImageRectTransform == null) return; // 获取cropCanvas的RectTransform RectTransform canvasRect = cropCanvas.GetComponent(); if (canvasRect == null) return; // 计算可用空间(考虑到一些边距) float maxWidth = canvasRect.rect.width * 0.9f; float maxHeight = canvasRect.rect.height * 0.7f; // 计算图片比例 float imageRatio = (float)image.width / image.height; // 根据图片比例调整尺寸 float width, height; if (imageRatio > 1) // 宽图 { width = Mathf.Min(maxWidth, image.width); height = width / imageRatio; // 检查高度是否超出限制 if (height > maxHeight) { height = maxHeight; width = height * imageRatio; } } else // 高图或正方形 { height = Mathf.Min(maxHeight, image.height); width = height * imageRatio; // 检查宽度是否超出限制 if (width > maxWidth) { width = maxWidth; height = width / imageRatio; } } // 设置尺寸 cropImageRectTransform.sizeDelta = new Vector2(width, height); } /// /// 确认裁剪 /// public void ConfirmCrop() { if (selectedImage == null || cropArea == null || cropImageRectTransform == null) { CancelCrop(); return; } // 计算在原图中的裁剪坐标 Rect cropRect = CalculateCropRect(); if (cropRect.width <= 0 || cropRect.height <= 0) { CancelCrop(); return; } // 创建新的指定尺寸的纹理 Texture2D croppedTexture = CropTexture(selectedImage, cropRect); // 检查裁剪纹理是否小于目标尺寸,并决定使用哪种缩放算法 Texture2D resizedTexture; if (croppedTexture.width < avatarSize || croppedTexture.height < avatarSize) { // 如果裁剪区域小于目标尺寸,使用高质量的双线性插值算法 resizedTexture = ResizeTextureWithBilinear(croppedTexture, avatarSize, avatarSize); } else { // 否则使用普通缩放 resizedTexture = ResizeTexture(croppedTexture, avatarSize, avatarSize); } // 显示结果 if (avatarPreview != null) { avatarPreview.texture = resizedTexture; } // 隐藏裁剪界面 if (cropCanvas != null) { cropCanvas.SetActive(false); } Debug.Log("头像裁剪完成,尺寸: " + avatarSize + "x" + avatarSize); } /// /// 计算在原图中的裁剪区域 /// /// 裁剪区域 private Rect CalculateCropRect() { if (cropArea == null || cropImageRectTransform == null || selectedImage == null) return new Rect(0, 0, 0, 0); // 获取裁剪区域在图片中的相对位置 Rect cropAreaRect = cropAreaHandler.GetCropRect(); // 获取图片显示区域的世界角点 Vector3[] imageCorners = new Vector3[4]; cropImageRectTransform.GetWorldCorners(imageCorners); // 获取裁剪区域的世界角点 Vector3[] cropAreaCorners = new Vector3[4]; cropArea.GetWorldCorners(cropAreaCorners); // 计算裁剪区域在图片中的UV坐标 float uMin = Mathf.InverseLerp(imageCorners[0].x, imageCorners[2].x, cropAreaCorners[0].x); float uMax = Mathf.InverseLerp(imageCorners[0].x, imageCorners[2].x, cropAreaCorners[2].x); float vMin = Mathf.InverseLerp(imageCorners[0].y, imageCorners[2].y, cropAreaCorners[0].y); float vMax = Mathf.InverseLerp(imageCorners[0].y, imageCorners[2].y, cropAreaCorners[2].y); // 转换为像素坐标 int x = Mathf.RoundToInt(uMin * selectedImage.width); int y = Mathf.RoundToInt(vMin * selectedImage.height); int width = Mathf.RoundToInt((uMax - uMin) * selectedImage.width); int height = Mathf.RoundToInt((vMax - vMin) * selectedImage.height); // 确保不超过图片边界 x = Mathf.Clamp(x, 0, selectedImage.width); y = Mathf.Clamp(y, 0, selectedImage.height); width = Mathf.Clamp(width, 0, selectedImage.width - x); height = Mathf.Clamp(height, 0, selectedImage.height - y); return new Rect(x, y, width, height); } /// /// 裁剪纹理 /// /// 原纹理 /// 裁剪区域 /// 裁剪后的纹理 private Texture2D CropTexture(Texture2D texture, Rect rect) { int x = Mathf.RoundToInt(rect.x); int y = Mathf.RoundToInt(rect.y); int width = Mathf.RoundToInt(rect.width); int height = Mathf.RoundToInt(rect.height); // 确保是正方形 int size = Mathf.Min(width, height); Color[] pixels = texture.GetPixels(x, y, size, size); Texture2D croppedTexture = new Texture2D(size, size, TextureFormat.RGBA32, false); croppedTexture.SetPixels(pixels); croppedTexture.Apply(); return croppedTexture; } /// /// 调整纹理大小(普通质量) /// /// 原纹理 /// 目标宽度 /// 目标高度 /// 调整大小后的纹理 private Texture2D ResizeTexture(Texture2D texture, int targetWidth, int targetHeight) { // 创建渲染纹理 RenderTexture renderTexture = new RenderTexture(targetWidth, targetHeight, 24); RenderTexture.active = renderTexture; // 绘制原纹理到渲染纹理 Graphics.Blit(texture, renderTexture); // 从渲染纹理创建新的纹理 Texture2D resizedTexture = new Texture2D(targetWidth, targetHeight, TextureFormat.RGBA32, false); resizedTexture.ReadPixels(new Rect(0, 0, targetWidth, targetHeight), 0, 0); resizedTexture.Apply(); // 清理 RenderTexture.active = null; return resizedTexture; } /// /// 使用双线性插值算法调整纹理大小(高质量) /// 当源纹理小于目标尺寸时使用,以获得更好的图像质量 /// /// 原纹理 /// 目标宽度 /// 目标高度 /// 调整大小后的纹理 private Texture2D ResizeTextureWithBilinear(Texture2D texture, int targetWidth, int targetHeight) { Color[] originalPixels = texture.GetPixels(); int originalWidth = texture.width; int originalHeight = texture.height; Color[] newPixels = new Color[targetWidth * targetHeight]; float xRatio = (float)originalWidth / targetWidth; float yRatio = (float)originalHeight / targetHeight; for (int y = 0; y < targetHeight; y++) { for (int x = 0; x < targetWidth; x++) { // 计算在原始图像中的位置 float px = x * xRatio; float py = y * yRatio; // 获取相邻的四个像素 int x1 = (int)Mathf.Floor(px); int y1 = (int)Mathf.Floor(py); int x2 = Mathf.Min(x1 + 1, originalWidth - 1); int y2 = Mathf.Min(y1 + 1, originalHeight - 1); // 计算权重 float fx = px - x1; float fy = py - y1; float w1 = (1 - fx) * (1 - fy); float w2 = fx * (1 - fy); float w3 = (1 - fx) * fy; float w4 = fx * fy; // 获取四个像素的颜色 Color c1 = originalPixels[y1 * originalWidth + x1]; Color c2 = originalPixels[y1 * originalWidth + x2]; Color c3 = originalPixels[y2 * originalWidth + x1]; Color c4 = originalPixels[y2 * originalWidth + x2]; // 双线性插值 Color finalColor = c1 * w1 + c2 * w2 + c3 * w3 + c4 * w4; newPixels[y * targetWidth + x] = finalColor; } } Texture2D resizedTexture = new Texture2D(targetWidth, targetHeight, TextureFormat.RGBA32, false); resizedTexture.SetPixels(newPixels); resizedTexture.Apply(); return resizedTexture; } /// /// 取消裁剪 /// public void CancelCrop() { if (cropCanvas != null) { cropCanvas.SetActive(false); } Debug.Log("取消裁剪"); } public RectTransform GetCropImageRectTransform() { return cropImageRectTransform; } public RectTransform GetCropCanvasRectTransform() { if (cropCanvas != null) { return cropCanvas.GetComponent(); } return null; } } /// /// 裁剪区域处理组件 /// public class CropAreaHandler : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerDownHandler { private AvatarSelection avatarSelection; private RectTransform cropArea; private RectTransform cropImageRectTransform; private Vector2 lastPointerPosition; private Vector2 dragStartPosition; private float MIN_CROP_SIZE = 50f; private bool isScaling = false; // 添加标志位,用于在缩放时屏蔽移动操作 public void Initialize(AvatarSelection avatarSelection, RectTransform cropImageRectTransform) { this.avatarSelection = avatarSelection; this.cropImageRectTransform = cropImageRectTransform; cropArea = GetComponent(); } public void ResetCropArea() { if (cropArea == null || cropImageRectTransform == null) return; // 设置默认裁剪区域为图片显示区域的正方形,并确保不小于图片尺寸的一半 float defaultSize = Mathf.Min(cropImageRectTransform.rect.width, cropImageRectTransform.rect.height); MIN_CROP_SIZE =defaultSize/2.0f; cropArea.sizeDelta = new Vector2(defaultSize, defaultSize); cropArea.anchoredPosition = Vector2.zero; } public Rect GetCropRect() { if (cropArea == null) return new Rect(0, 0, 0, 0); // 获取世界角点以获得准确的边界 Vector3[] corners = new Vector3[4]; cropArea.GetWorldCorners(corners); // 返回基于屏幕坐标的矩形 return new Rect( corners[0].x, corners[0].y, corners[2].x - corners[0].x, corners[2].y - corners[0].y ); } public void OnPointerDown(PointerEventData eventData) { // 只有在非缩放状态下才允许移动 if (!isScaling) { lastPointerPosition = eventData.position; dragStartPosition = cropArea.anchoredPosition; } } public void OnBeginDrag(PointerEventData eventData) { // 只有在非缩放状态下才允许移动 if (!isScaling) { lastPointerPosition = eventData.position; } } public void OnDrag(PointerEventData eventData) { // 只有在非缩放状态下才允许移动 if (cropArea == null || cropImageRectTransform == null || isScaling) return; Vector2 currentPointerPosition = eventData.position; Vector2 deltaPosition = currentPointerPosition - lastPointerPosition; // 移动裁剪区域 cropArea.anchoredPosition += deltaPosition; // 限制裁剪区域在图片范围内 ConstrainCropArea(); lastPointerPosition = currentPointerPosition; } public void OnEndDrag(PointerEventData eventData) { // 只有在非缩放状态下才处理拖拽结束 if (!isScaling) { // 拖拽结束时确保裁剪区域仍在有效范围内 ConstrainCropArea(); } } private void ConstrainCropArea() { if (cropArea == null || cropImageRectTransform == null) return; // 获取图片显示区域的边界 Rect imageRect = cropImageRectTransform.rect; Vector2 imageSize = new Vector2(imageRect.width, imageRect.height); // 获取裁剪区域边界 Vector2 cropSize = cropArea.sizeDelta; // 计算最大偏移量,确保裁剪区域不会超出图片边界 float maxXOffset = (imageSize.x - cropSize.x) / 2; float maxYOffset = (imageSize.y - cropSize.y) / 2; // 限制裁剪区域位置 Vector2 newPosition = cropArea.anchoredPosition; newPosition.x = Mathf.Clamp(newPosition.x, -maxXOffset, maxXOffset); newPosition.y = Mathf.Clamp(newPosition.y, -maxYOffset, maxYOffset); cropArea.anchoredPosition = newPosition; } // 添加缩放功能 void Update() { if (cropArea == null) return; // 处理双指缩放(移动端) if (Input.touchCount == 2) { isScaling = true; // 设置缩放标志 Touch touchZero = Input.GetTouch(0); Touch touchOne = Input.GetTouch(1); Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition; Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition; float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude; float touchDeltaMag = (touchZero.position - touchOne.position).magnitude; float deltaMagnitudeDiff = touchDeltaMag - prevTouchDeltaMag; ResizeCropArea(deltaMagnitudeDiff * 0.5f); } // 处理鼠标滚轮缩放(PC端) else if (Input.mouseScrollDelta.y != 0) { isScaling = true; // 设置缩放标志 ResizeCropArea(Input.mouseScrollDelta.y * 10f); } else { // 没有缩放操作时,重置标志 isScaling = false; } } private void ResizeCropArea(float deltaSize) { if (cropArea == null || cropImageRectTransform == null) return; // 获取当前尺寸 Vector2 currentSize = cropArea.sizeDelta; // 计算新尺寸 Vector2 newSize = new Vector2( currentSize.x + deltaSize, currentSize.y + deltaSize ); // 保持正方形并确保在有效范围内 float maxSize = Mathf.Min(cropImageRectTransform.rect.width, cropImageRectTransform.rect.height); float finalSize = Mathf.Clamp(newSize.x, MIN_CROP_SIZE, maxSize); cropArea.sizeDelta = new Vector2(finalSize, finalSize); // 调整位置以保持在图片范围内 ConstrainCropArea(); } } }