在游戏开发过程中,UI组件的拖动功能是一个常见的需求。特别是在需要实现拖动、边界检测、透明度控制以及动画反馈等功能时,编写一个高级UI拖动控制器将非常有用。在本文中,我们将创建一个支持多种Canvas模式和更精确边界检测的高级UI拖动控制器。

1. 脚本概述

AdvancedUIDragController是一个实现了IBeginDragHandlerIDragHandlerIEndDragHandler接口的Unity脚本。它提供了UI元素拖动功能,并且支持透明度调整、边界限制、鼠标指针变化、边框显示等多种特性。

2. 脚本结构

这个脚本包含多个功能模块:

  • 拖动设置:控制拖动是否启用、拖动时的透明度等。
  • 边界限制:控制UI元素是否会被限制在屏幕、Canvas或父物体的边界内。
  • 拖动反馈:改变鼠标指针、显示拖动边框等。
  • 动画设置:拖动结束时是否使用缓动动画。

3. 代码分析

3.1. 变量声明
// 拖动设置
public bool enableDrag = true; // 是否启用拖动功能
public bool showTransparencyOnDrag = true; // 拖动时是否显示半透明效果
public float dragTransparency = 1f; // 拖动时的透明度// 边界限制
public bool limitToScreenBounds = true; // 是否限制在屏幕边界内
public float boundaryMargin = 10f; // 边界边距
public BoundaryMode boundaryMode = BoundaryMode.ScreenSpace; // 边界限制模式// 拖动反馈
public bool changeCursorOnDrag = true; // 拖动时是否改变鼠标指针
public Texture2D dragCursor; // 拖动时的鼠标指针
public bool showBorderOnDrag = true; // 拖动时是否显示边框
public Color dragBorderColor = Color.yellow; // 拖动时的边框颜色// 动画设置
public bool useEasingAnimation = true; // 拖动结束时是否使用缓动动画
public float animationDuration = 0.2f; // 缓动动画持续时间
public AnimationCurve easingCurve = AnimationCurve.EaseInOut(0, 0, 1, 1); // 缓动动画曲线
3.2. 初始化组件

Start()函数中,获取UI组件引用并设置必要的初始化。

void Start()
{InitializeComponents();SetupBorder();
}private void InitializeComponents()
{rectTransform = GetComponent<RectTransform>();canvas = GetComponentInParent<Canvas>();canvasScaler = canvas.GetComponent<CanvasScaler>();canvasGroup = GetComponent<CanvasGroup>();if (canvasGroup == null && showTransparencyOnDrag){canvasGroup = gameObject.AddComponent<CanvasGroup>();}originalAlpha = canvasGroup != null ? canvasGroup.alpha : 1f;
}
3.3. 拖动开始

当拖动开始时,记录鼠标偏移量、设置透明度、显示边框等。

public void OnBeginDrag(PointerEventData eventData)
{if (!enableDrag) return;isDragging = true;Vector2 localPointerPosition;RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position, eventData.pressEventCamera, out localPointerPosition);dragOffset = localPointerPosition;originalPosition = rectTransform.anchoredPosition;if (showTransparencyOnDrag && canvasGroup != null){canvasGroup.alpha = dragTransparency;}if (showBorderOnDrag && borderImage != null){borderImage.gameObject.SetActive(true);}if (changeCursorOnDrag && dragCursor != null){Cursor.SetCursor(dragCursor, hotSpot, cursorMode);}
}
3.4. 拖动中

在拖动过程中,计算UI元素的新位置,并应用边界限制。

public void OnDrag(PointerEventData eventData)
{if (!enableDrag || !isDragging) return;Vector2 localPointerPosition;RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform.parent as RectTransform, eventData.position, eventData.pressEventCamera, out localPointerPosition);Vector2 newPosition = localPointerPosition - dragOffset;if (limitToScreenBounds){newPosition = ClampToBounds(newPosition);}rectTransform.anchoredPosition = newPosition;targetPosition = newPosition;
}
3.5. 拖动结束

拖动结束时,恢复透明度、隐藏边框、恢复鼠标指针,并应用缓动动画。

public void OnEndDrag(PointerEventData eventData)
{if (!isDragging) return;isDragging = false;if (showTransparencyOnDrag && canvasGroup != null){canvasGroup.alpha = originalAlpha;}if (showBorderOnDrag && borderImage != null){borderImage.gameObject.SetActive(false);}if (changeCursorOnDrag){Cursor.SetCursor(null, Vector2.zero, cursorMode);}if (useEasingAnimation){Vector2 finalPosition = limitToScreenBounds ? ClampToBounds(targetPosition) : targetPosition;if (Vector2.Distance(rectTransform.anchoredPosition, finalPosition) > 0.1f){easingCoroutine = StartCoroutine(EaseToPosition(finalPosition));}}
}
3.6. 边界限制与动画

ClampToBounds方法用于根据不同的模式限制拖动元素的位置,EaseToPosition方法则负责缓动动画的执行。

4. 边界限制模式

我们提供了三种边界限制模式:

  • ScreenSpace:限制元素在屏幕空间内。
  • CanvasSpace:限制元素在Canvas空间内。
  • ParentSpace:限制元素在父物体空间内。

5. 实现拖动动画

缓动动画会在拖动结束时平滑地将UI元素移动到目标位置。

private System.Collections.IEnumerator EaseToPosition(Vector2 targetPos)
{Vector2 startPos = rectTransform.anchoredPosition;float elapsedTime = 0f;while (elapsedTime < animationDuration){t = easingCurve.Evaluate(t);rectTransform.anchoredPosition = Vector2.Lerp(startPos, targetPos, t);yield return null;}rectTransform.anchoredPosition = targetPos;easingCoroutine = null;
}

6. 使用方法

  1. 将该脚本附加到任何UI元素(如Panel、Image等)。
  2. 在Inspector面板中设置各项参数,例如是否启用拖动、透明度、边界限制等。
  3. 运行游戏并测试拖动效果。

7. 完整代码

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;/// <summary>
/// 高级UI拖动控制器 - 支持多种Canvas模式和更精确的边界检测
/// </summary>
public class AdvancedUIDragController : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{[Header("拖动设置")][Tooltip("是否启用拖动功能")]public bool enableDrag = true;[Tooltip("拖动时是否显示半透明效果")]public bool showTransparencyOnDrag = true;[Tooltip("拖动时的透明度")][Range(0f, 1f)]public float dragTransparency = 0.7f;[Header("边界限制")][Tooltip("是否限制在屏幕边界内")]public bool limitToScreenBounds = true;[Tooltip("边界边距(像素)")]public float boundaryMargin = 10f;[Tooltip("边界限制模式")]public BoundaryMode boundaryMode = BoundaryMode.ScreenSpace;[Header("拖动反馈")][Tooltip("拖动时是否改变鼠标指针")]public bool changeCursorOnDrag = true;[Tooltip("拖动时的鼠标指针")]public Texture2D dragCursor;[Tooltip("拖动时是否显示边框")]public bool showBorderOnDrag = true;[Tooltip("拖动时的边框颜色")]public Color dragBorderColor = Color.yellow;[Header("动画设置")][Tooltip("拖动结束时是否使用缓动动画")]public bool useEasingAnimation = true;[Tooltip("缓动动画持续时间")]public float animationDuration = 0.2f;[Tooltip("缓动动画曲线")]public AnimationCurve easingCurve = AnimationCurve.EaseInOut(0, 0, 1, 1);// 边界模式枚举public enum BoundaryMode{ScreenSpace,    // 屏幕空间CanvasSpace,    // Canvas空间ParentSpace     // 父对象空间}// 私有变量private RectTransform rectTransform;private Canvas canvas;private CanvasScaler canvasScaler;private Vector2 originalPosition;private Vector2 dragOffset;private float originalAlpha;private CanvasGroup canvasGroup;private bool isDragging = false;private CursorMode cursorMode = CursorMode.Auto;private Vector2 hotSpot = Vector2.zero;private Coroutine easingCoroutine;private UnityEngine.UI.Image borderImage;private Vector2 targetPosition;void Start(){InitializeComponents();SetupBorder();}private void InitializeComponents(){// 获取组件引用rectTransform = GetComponent<RectTransform>();canvas = GetComponentInParent<Canvas>();canvasScaler = canvas.GetComponent<CanvasScaler>();// 如果没有CanvasGroup,添加一个用于透明度控制canvasGroup = GetComponent<CanvasGroup>();if (canvasGroup == null && showTransparencyOnDrag){canvasGroup = gameObject.AddComponent<CanvasGroup>();}// 保存原始透明度if (canvasGroup != null){originalAlpha = canvasGroup.alpha;}// 设置鼠标指针热点if (dragCursor != null){hotSpot = new Vector2(dragCursor.width / 2, dragCursor.height / 2);}}private void SetupBorder(){if (showBorderOnDrag){// 创建边框UIGameObject borderObj = new GameObject("DragBorder");borderObj.transform.SetParent(transform, false);borderImage = borderObj.AddComponent<UnityEngine.UI.Image>();borderImage.color = dragBorderColor;borderImage.raycastTarget = false;RectTransform borderRect = borderObj.GetComponent<RectTransform>();borderRect.anchorMin = Vector2.zero;borderRect.anchorMax = Vector2.one;borderRect.offsetMin = Vector2.zero;borderRect.offsetMax = Vector2.zero;// 添加边框效果borderImage.sprite = CreateBorderSprite();borderImage.type = UnityEngine.UI.Image.Type.Sliced;borderObj.SetActive(false);}}private Sprite CreateBorderSprite(){// 创建一个简单的边框精灵Texture2D borderTexture = new Texture2D(3, 3);Color[] pixels = new Color[9];// 设置边框像素为白色,内部为透明for (int i = 0; i < 9; i++){int x = i % 3;int y = i / 3;if (x == 0 || x == 2 || y == 0 || y == 2)pixels[i] = Color.white;elsepixels[i] = Color.clear;}borderTexture.SetPixels(pixels);borderTexture.Apply();return Sprite.Create(borderTexture, new Rect(0, 0, 3, 3), new Vector2(0.5f, 0.5f), 1f, 0, SpriteMeshType.FullRect, new Vector4(1, 1, 1, 1));}public void OnBeginDrag(PointerEventData eventData){if (!enableDrag) return;isDragging = true;// 停止任何正在进行的缓动动画if (easingCoroutine != null){StopCoroutine(easingCoroutine);easingCoroutine = null;}// 计算拖动偏移量Vector2 localPointerPosition;RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, eventData.position, eventData.pressEventCamera, out localPointerPosition);dragOffset = localPointerPosition;// 保存原始位置originalPosition = rectTransform.anchoredPosition;// 设置透明度if (showTransparencyOnDrag && canvasGroup != null){canvasGroup.alpha = dragTransparency;}// 显示边框if (showBorderOnDrag && borderImage != null){borderImage.gameObject.SetActive(true);}// 改变鼠标指针if (changeCursorOnDrag && dragCursor != null){Cursor.SetCursor(dragCursor, hotSpot, cursorMode);}}public void OnDrag(PointerEventData eventData){if (!enableDrag || !isDragging) return;// 计算新位置Vector2 localPointerPosition;RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform.parent as RectTransform, eventData.position, eventData.pressEventCamera, out localPointerPosition);Vector2 newPosition = localPointerPosition - dragOffset;// 应用屏幕边界限制if (limitToScreenBounds){newPosition = ClampToBounds(newPosition);}// 更新位置rectTransform.anchoredPosition = newPosition;targetPosition = newPosition;}public void OnEndDrag(PointerEventData eventData){if (!isDragging) return;isDragging = false;// 恢复透明度if (showTransparencyOnDrag && canvasGroup != null){canvasGroup.alpha = originalAlpha;}// 隐藏边框if (showBorderOnDrag && borderImage != null){borderImage.gameObject.SetActive(false);}// 恢复鼠标指针if (changeCursorOnDrag){Cursor.SetCursor(null, Vector2.zero, cursorMode);}// 应用缓动动画if (useEasingAnimation){Vector2 finalPosition = limitToScreenBounds ? ClampToBounds(targetPosition) : targetPosition;if (Vector2.Distance(rectTransform.anchoredPosition, finalPosition) > 0.1f){easingCoroutine = StartCoroutine(EaseToPosition(finalPosition));}}}/// <summary>/// 根据边界模式限制位置/// </summary>private Vector2 ClampToBounds(Vector2 position){switch (boundaryMode){case BoundaryMode.ScreenSpace:return ClampToScreenBounds(position);case BoundaryMode.CanvasSpace:return ClampToCanvasBounds(position);case BoundaryMode.ParentSpace:return ClampToParentBounds(position);default:return position;}}/// <summary>/// 限制在屏幕边界内/// </summary>private Vector2 ClampToScreenBounds(Vector2 position){if (canvas == null) return position;// 获取屏幕尺寸Vector2 screenSize = new Vector2(Screen.width, Screen.height);// 获取UI元素在屏幕空间中的尺寸Vector2 uiSize = GetUISizeInScreenSpace();// 计算边界float minX = uiSize.x / 2 + boundaryMargin;float maxX = screenSize.x - uiSize.x / 2 - boundaryMargin;float minY = uiSize.y / 2 + boundaryMargin;float maxY = screenSize.y - uiSize.y / 2 - boundaryMargin;// 将屏幕坐标转换为本地坐标Vector2 screenPosition = RectTransformUtility.WorldToScreenPoint(null, rectTransform.TransformPoint(position));screenPosition.x = Mathf.Clamp(screenPosition.x, minX, maxX);screenPosition.y = Mathf.Clamp(screenPosition.y, minY, maxY);// 转换回本地坐标Vector2 localPosition;RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform.parent as RectTransform, screenPosition, null, out localPosition);return localPosition;}/// <summary>/// 限制在Canvas边界内/// </summary>private Vector2 ClampToCanvasBounds(Vector2 position){if (canvas == null) return position;RectTransform canvasRect = canvas.GetComponent<RectTransform>();if (canvasRect == null) return position;Vector2 uiSize = rectTransform.sizeDelta;float minX = -canvasRect.sizeDelta.x / 2 + uiSize.x / 2 + boundaryMargin;float maxX = canvasRect.sizeDelta.x / 2 - uiSize.x / 2 - boundaryMargin;float minY = -canvasRect.sizeDelta.y / 2 + uiSize.y / 2 + boundaryMargin;float maxY = canvasRect.sizeDelta.y / 2 - uiSize.y / 2 - boundaryMargin;position.x = Mathf.Clamp(position.x, minX, maxX);position.y = Mathf.Clamp(position.y, minY, maxY);return position;}/// <summary>/// 限制在父对象边界内/// </summary>private Vector2 ClampToParentBounds(Vector2 position){RectTransform parentRect = rectTransform.parent as RectTransform;if (parentRect == null) return position;Vector2 uiSize = rectTransform.sizeDelta;Vector2 parentSize = parentRect.sizeDelta;float minX = -parentSize.x / 2 + uiSize.x / 2 + boundaryMargin;float maxX = parentSize.x / 2 - uiSize.x / 2 - boundaryMargin;float minY = -parentSize.y / 2 + uiSize.y / 2 + boundaryMargin;float maxY = parentSize.y / 2 - uiSize.y / 2 - boundaryMargin;position.x = Mathf.Clamp(position.x, minX, maxX);position.y = Mathf.Clamp(position.y, minY, maxY);return position;}/// <summary>/// 获取UI元素在屏幕空间中的尺寸/// </summary>private Vector2 GetUISizeInScreenSpace(){Vector3[] corners = new Vector3[4];rectTransform.GetWorldCorners(corners);Vector2 size = new Vector2(Vector3.Distance(corners[0], corners[3]),Vector3.Distance(corners[0], corners[1]));return size;}/// <summary>/// 缓动到目标位置/// </summary>private System.Collections.IEnumerator EaseToPosition(Vector2 targetPos){Vector2 startPos = rectTransform.anchoredPosition;float elapsedTime = 0f;while (elapsedTime < animationDuration){elapsedTime += Time.deltaTime;float t = elapsedTime / animationDuration;t = easingCurve.Evaluate(t);rectTransform.anchoredPosition = Vector2.Lerp(startPos, targetPos, t);yield return null;}rectTransform.anchoredPosition = targetPos;easingCoroutine = null;}/// <summary>/// 重置到原始位置/// </summary>public void ResetToOriginalPosition(){if (rectTransform != null){if (useEasingAnimation){if (easingCoroutine != null){StopCoroutine(easingCoroutine);}easingCoroutine = StartCoroutine(EaseToPosition(originalPosition));}else{rectTransform.anchoredPosition = originalPosition;}}}/// <summary>/// 设置拖动是否启用/// </summary>public void SetDragEnabled(bool enabled){enableDrag = enabled;}/// <summary>/// 设置边界限制是否启用/// </summary>public void SetBoundaryLimitEnabled(bool enabled){limitToScreenBounds = enabled;}/// <summary>/// 设置边界模式/// </summary>public void SetBoundaryMode(BoundaryMode mode){boundaryMode = mode;}void OnDisable(){// 确保在禁用时恢复鼠标指针和停止动画if (isDragging && changeCursorOnDrag){Cursor.SetCursor(null, Vector2.zero, cursorMode);}if (easingCoroutine != null){StopCoroutine(easingCoroutine);easingCoroutine = null;}}void OnDestroy(){// 清理边框精灵if (borderImage != null && borderImage.sprite != null){DestroyImmediate(borderImage.sprite.texture);DestroyImmediate(borderImage.sprite);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/95018.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/95018.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/95018.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

零基础上手:Cursor + MCP 爬取 YouTube 视频数据

前言 大模型与 AI 应用越来越普及的今天&#xff0c;实时、稳定地获取网络数据变得尤为重要。无论是做内容分析、趋势研究还是自动化任务&#xff0c;爬取和处理数据始终是绕不开的一环。 传统爬虫往往面临封禁、验证码、动态渲染等难题&#xff0c;而 Bright Data MCP&#x…

frp 一个高性能的反向代理服务

文章目录项目概述核心特性系统架构快速开始1. 下载安装2. 服务端快速配置3. 客户端快速配置4. 验证连接配置文件说明代理类型TCP/UDP 代理HTTP/HTTPS 代理安全代理 (STCP/SUDP)P2P 代理 (XTCP)插件系统静态文件服务HTTP/SOCKS5 代理协议转换使用场景远程办公Web 服务发布游戏服…

Android -第二十一次技术总结

一、activity与Fragment的通信有哪些&#xff1f;使用接口进行通信的逻辑与代码示例使用接口通信的核心是解耦&#xff0c;通过定义一个接口作为通信契约&#xff0c;让 Fragment 不依赖于具体的 Activity 类型。1. 定义通信接口&#xff08;在 Fragment 内&#xff09;首先&am…

【算法】78.子集--通俗讲解

通俗易懂讲解“子集”算法题目 一、题目是啥?一句话说清 给你一个不含重复元素的整数数组,返回所有可能的子集(包括空集和它本身)。 示例: 输入:nums = [1,2,3] 输出:[[], [1], [2], [1,2], [3], [1,3], [2,3], [1,2,3]] 二、解题核心 使用回溯法(递归)或位运算来…

Cherrystudio的搭建和使用

1、下载和安装 Cherry Studio 官方网站 - 全能的 AI 助手 2、配置LLM 3、聊天助手 3.1 添加和编辑助手 3.2 选择LLM 3.3 对话聊天 4、配置MCP 4.1 安装MCP执行插件 4.2 安装 node和npm Node.js — Download Node.js npm -v 10.9.3 node -v v22…

基于Matlab结合肤色检测与卷积神经网络的人脸识别方法研究

近年来&#xff0c;随着人工智能与计算机视觉技术的发展&#xff0c;人脸识别在人机交互、安防监控、身份认证等领域得到了广泛应用。本文提出了一种基于 MATLAB 平台&#xff0c;结合 肤色检测与卷积神经网络&#xff08;CNN&#xff09; 的人脸识别方法。该方法首先利用肤色模…

在八月点燃AI智慧之火:CSDN创作之星挑战赛开启灵感盛宴

在八月点燃AI智慧之火&#xff1a;CSDN创作之星挑战赛开启灵感盛宴八月骄阳似火&#xff0c;智能时代的技术热情同样炽热。在这个充满创新活力的季节&#xff0c;「AIcoding八月创作之星挑战赛」正式拉开帷幕&#xff0c;为CSDN的创作者们打造一个展示才华、碰撞灵感的专业舞台…

解密 Vue 3 shallowRef:浅层响应式 vs 深度响应式的性能对决

&#x1f4d6; 概述 shallowRef() 是 Vue 3 中的一个组合式 API 函数&#xff0c;用于创建浅层响应式引用。与 ref() 不同&#xff0c;shallowRef() 只在其 .value 被直接替换时触发响应式更新&#xff0c;不会深度监听对象内部属性的变化。 &#x1f3af; 基本概念 什么是 sh…

Linux进程间通信(IPC)深入解析

Linux进程间通信&#xff08;IPC&#xff09;深入解析 1 概述 Linux 进程间通信 (Inter-Process Communication, IPC) 是不同进程之间交换数据与同步操作的机制。现代 Linux 内核提供了多种 IPC 方式&#xff0c;从传统的管道和 System V IPC 到现代的套接字和 D-Bus&#xff0…

TensorFlow-GPU版本安装

前言&#xff1a; &#xff08;1&#xff09;因项目需求&#xff0c;需要安装TensorFlow-GPU版本&#xff0c;故本文在此记录安装过程。 &#xff08;2&#xff09;有注释&#xff0c;优先看注释 &#xff08;3&#xff09;本文所使用的GPU为NVIDIA GeForce RTX 5080 Laptop GP…

Elasticsearch 索引字段删除,除了 Reindex 重建索引还有没有别的解决方案?

unsetunset1、问题来源unsetunset在生产环境维护 Elasticsearch 集群的过程中&#xff0c;经常会遇到这样的场景&#xff1a;业务需求变更导致某些字段不再使用&#xff0c;或者早期设计时添加了一些冗余字段&#xff0c;现在需要清理掉。最近球友在公司的一个项目中就遇到了这…

Ubuntu虚拟机磁盘空间扩展指南

这是一份详细且易于理解的 Ubuntu 虚拟机磁盘空间扩展指南。本指南涵盖了两种主流虚拟机软件&#xff08;VirtualBox 和 VMware&#xff09;的操作步骤&#xff0c;并分为 “扩展虚拟磁盘” 和 “在 Ubuntu 内部分配新空间” 两大部分。重要提示&#xff1a;在进行任何磁盘操作…

教程1:用vscode->ptvsd-创建和调试一个UI(python)-转载官方翻译(有修正)

vscode用python开发maya联动调试设置 3dsMax Python开发环境搭建 3文联动之debugpy调试max‘python. 3文联动之socket插槽注入max‘python 本教程是max主动接收创建代码的方式&#xff08;预先运行界面&#xff0c;通过按钮主动读取py脚本&#xff0c;执行断点&#xff09;&…

龙迅#LT7621GX适用于两路HDMI2.1/DP1.4A转HDMI2.1混切应用,分辨率高达8K60HZ!

1. 描述LT7621GX是一款高性能两路HDMI2.1/DP1.4转HDMI2.1混合开关芯片&#xff0c;用于显示应用。 HDCP RX作为HDCP中继器的上游&#xff0c;可以与其他芯片的HDCP TX配合&#xff0c;实现中继器功能。 对于HDMI2.1输入&#xff0c;LT7621GX可以配置为3/4通道。自适应均衡使其适…

【Ruoyi 解密 - 12. JDK17的新特性】------ 从Java 8 到 Java 17:向Scala看齐的“简洁革命”,同宗JVM下的效率狂飙

从Java 8到Java 17&#xff1a;抄作业Scala&#xff1f;JVM同宗下的Ruoyi开发效率狂飙&#xff01; 上一篇我们聊到JDK 17对Python的柔性借鉴&#xff0c;可深入用下来才发现——这哪够&#xff01;对Ruoyi开发者来说&#xff0c;JDK 17真正的“王炸”&#xff0c;是把同根JVM的…

大模型 “轻量化” 之战:从千亿参数到端侧部署,AI 如何走进消费电子?

一、大模型 “轻量化” 的行业背景在 AI 技术蓬勃发展的当下&#xff0c;大模型已然成为行业焦点。从 GPT-4 突破万亿级参数量&#xff0c;到 DeepSeek-R1 迈向千亿参数规模&#xff0c;大模型的参数扩张趋势显著。然而&#xff0c;这种规模的增长也带来了诸多挑战。以 GPT-4 为…

香港电讯与Microsoft香港推出新世代“Teams Phone” 解决方案

香港电讯成为香港首家提供 “Microsoft Operator Connect”的本地电讯营运商1 香港电讯&#xff08;股份代号&#xff1a;6823&#xff09;【香港 • 2025年2月11日】 – 香港电讯宣布与 Microsoft 香港合作推出 “Operator Connect”&#xff0c;成为全港首家为企业客户提供全…

PlantUML描述《分析模式》第3章观察和测量(2)

lantUML描述《分析模式》第2章“当责”&#xff08;1&#xff09; PlantUML描述《分析模式》第2章“当责”&#xff08;2&#xff09; PlantUML描述《分析模式》第3章观察和测量&#xff08;1&#xff09; 原图3.8 EA绘制 图3.8 递归关系用于记录证据和评估。 PlantUML sta…

轮廓周长,面积,外界圆,外界矩形近似轮廓和模板匹配和argparse模块实现代码参数的动态配置

目录 一.轮廓操作 1.轮廓特征的引入与筛选 2.轮廓排序和精准定位 3.外接圆与外接矩形的计算与绘制 二.轮廓近似 1.轮廓近似的基本概念 2.轮廓近似的实现方法和核心步骤 3. 近似精度参数的设定逻辑 4.轮廓定位方法 三.模板匹配 1.模板匹配技术原理与实现流程 2.技术要…

【第三方网站测评:会话管理漏洞的测试与加固】

会话管理是Web应用安全的用于在无状态的HTTP协议上维持用户状态。漏洞主要源于会话令牌(Session Token)的生成、传输、验证和销毁过程中的缺陷。攻击者利用这些缺陷可劫持用户会话,未经授权访问敏感数据或执行特权操作,属于OWASP TOP 10中身份验证失效的高频风险。 会话管…